From 72a7104f94dba47b64bf9b057cf23fa0b0bfa7b1 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 24 Jul 2017 20:05:50 +0200 Subject: [PATCH 01/54] Refactoring: Merge BsqBlockVo into BsqBlock, TxInputVo into TxInput and TxOutputVo into TxOutput --- common/src/main/proto/pb.proto | 44 ++---- .../core/dao/blockchain/parse/BsqParser.java | 14 +- .../core/dao/blockchain/parse/RpcService.java | 10 +- .../bisq/core/dao/blockchain/vo/BsqBlock.java | 38 ++--- .../core/dao/blockchain/vo/BsqBlockVo.java | 56 ------- .../bisq/core/dao/blockchain/vo/TxInput.java | 46 +++--- .../core/dao/blockchain/vo/TxInputVo.java | 49 ------ .../bisq/core/dao/blockchain/vo/TxOutput.java | 149 +++++++++++------- .../core/dao/blockchain/vo/TxOutputVo.java | 79 ---------- 9 files changed, 149 insertions(+), 336 deletions(-) delete mode 100644 core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlockVo.java delete mode 100644 core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInputVo.java delete mode 100644 core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutputVo.java diff --git a/common/src/main/proto/pb.proto b/common/src/main/proto/pb.proto index 60b0435b07..b2d7004143 100644 --- a/common/src/main/proto/pb.proto +++ b/common/src/main/proto/pb.proto @@ -410,24 +410,10 @@ message PubKeyScript { string hex = 5; } -message TxInputVo { +message TxInput { string tx_id = 1; int32 tx_output_index = 2; -} - -message TxInput { - TxInputVo tx_input_vo = 1; - TxOutput connected_tx_output = 2; -} - -message TxOutputVo { - int32 index = 1; - int64 value = 2; - string tx_id = 3; - PubKeyScript pub_key_script = 4; - string address = 5; - bytes op_return_data = 6; - int32 block_height= 7; + TxOutput connected_tx_output = 3; } message SpentInfo { @@ -449,11 +435,17 @@ enum TxOutputType { } message TxOutput { - TxOutputVo tx_output_vo = 1; - bool is_unspent = 2; - bool is_verified = 3; - TxOutputType tx_output_type = 4; - SpentInfo spent_info = 5; + int32 index = 1; + int64 value = 2; + string tx_id = 3; + PubKeyScript pub_key_script = 4; + string address = 5; + bytes op_return_data = 6; + int32 block_height= 7; + bool is_unspent = 8; + bool is_verified = 9; + TxOutputType tx_output_type = 10; + SpentInfo spent_info = 11; } message TxVo { @@ -486,16 +478,12 @@ message Tx { int64 burnt_fee = 4; TxType tx_type = 5; } - -message BsqBlockVo { + +message BsqBlock { int32 height = 1; string hash = 2; string previous_block_hash = 3; -} - -message BsqBlock { - BsqBlockVo bsq_block_vo = 1; - repeated Tx txs = 2; + repeated Tx txs = 4; } message TxIdIndexTuple { diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqParser.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqParser.java index 0fd0585a81..c17f524137 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqParser.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqParser.java @@ -108,10 +108,9 @@ public class BsqParser { List bsqTxsInBlock = findBsqTxsInBlock(btcdBlock, genesisBlockHeight, genesisTxId); - final BsqBlockVo bsqBlockVo = new BsqBlockVo(btcdBlock.getHeight(), + final BsqBlock bsqBlock = new BsqBlock(btcdBlock.getHeight(), btcdBlock.getHash(), - btcdBlock.getPreviousBlockHash()); - final BsqBlock bsqBlock = new BsqBlock(bsqBlockVo, + btcdBlock.getPreviousBlockHash(), ImmutableList.copyOf(bsqTxsInBlock)); bsqChainState.addBlock(bsqBlock); @@ -173,10 +172,9 @@ public class BsqParser { List bsqTxsInBlock = findBsqTxsInBlock(btcdBlock, genesisBlockHeight, genesisTxId); - final BsqBlockVo bsqBlockVo = new BsqBlockVo(btcdBlock.getHeight(), + final BsqBlock bsqBlock = new BsqBlock(btcdBlock.getHeight(), btcdBlock.getHash(), - btcdBlock.getPreviousBlockHash()); - final BsqBlock bsqBlock = new BsqBlock(bsqBlockVo, + btcdBlock.getPreviousBlockHash(), ImmutableList.copyOf(bsqTxsInBlock)); bsqChainState.addBlock(bsqBlock); return bsqBlock; @@ -252,7 +250,7 @@ public class BsqParser { // we check if we have any valid BSQ from that tx set bsqTxsInBlock.addAll(txsWithoutInputsFromSameBlock.stream() - .filter(tx -> isValidBsqTx(blockHeight, tx)) + .filter(tx -> isBsqTx(blockHeight, tx)) .collect(Collectors.toList())); log.debug("Parsing of all txsWithoutInputsFromSameBlock is done."); @@ -277,7 +275,7 @@ public class BsqParser { } } - private boolean isValidBsqTx(int blockHeight, Tx tx) { + private boolean isBsqTx(int blockHeight, Tx tx) { boolean isBsqTx = false; long availableValue = 0; for (int inputIndex = 0; inputIndex < tx.getInputs().size(); inputIndex++) { diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java index 1b40433f5b..b3b369cfa1 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java @@ -33,7 +33,10 @@ import com.neemre.btcdcli4j.daemon.event.BlockListener; import io.bisq.core.dao.DaoOptionKeys; import io.bisq.core.dao.blockchain.btcd.PubKeyScript; import io.bisq.core.dao.blockchain.exceptions.BsqBlockchainException; -import io.bisq.core.dao.blockchain.vo.*; +import io.bisq.core.dao.blockchain.vo.Tx; +import io.bisq.core.dao.blockchain.vo.TxInput; +import io.bisq.core.dao.blockchain.vo.TxOutput; +import io.bisq.core.dao.blockchain.vo.TxVo; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; @@ -157,7 +160,7 @@ public class RpcService { final List txInputs = rawTransaction.getVIn() .stream() .filter(rawInput -> rawInput != null && rawInput.getVOut() != null && rawInput.getTxId() != null) - .map(rawInput -> new TxInput(new TxInputVo(rawInput.getTxId(), rawInput.getVOut()))) + .map(rawInput -> new TxInput(rawInput.getTxId(), rawInput.getVOut())) .collect(Collectors.toList()); final List txOutputs = rawTransaction.getVOut() @@ -184,14 +187,13 @@ public class RpcService { String address = scriptPubKey.getAddresses() != null && scriptPubKey.getAddresses().size() == 1 ? scriptPubKey.getAddresses().get(0) : null; final PubKeyScript pubKeyScript = dumpBlockchainData ? new PubKeyScript(scriptPubKey) : null; - final TxOutputVo txOutputVo = new TxOutputVo(rawOutput.getN(), + return new TxOutput(rawOutput.getN(), rawOutput.getValue().movePointRight(8).longValue(), rawTransaction.getTxId(), pubKeyScript, address, opReturnData, blockHeight); - return new TxOutput(txOutputVo); } ) .collect(Collectors.toList()); diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlock.java b/core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlock.java index 9886bb3945..c1001e8ab3 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlock.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlock.java @@ -27,11 +27,15 @@ import java.util.stream.Collectors; @Data public class BsqBlock implements PersistablePayload { - private final BsqBlockVo bsqBlockVo; + private final int height; + private final String hash; + private final String previousBlockHash; private final List txs; - public BsqBlock(BsqBlockVo bsqBlockVo, List txs) { - this.bsqBlockVo = bsqBlockVo; + public BsqBlock(int height, String hash, String previousBlockHash, List txs) { + this.height = height; + this.hash = hash; + this.previousBlockHash = previousBlockHash; this.txs = txs; } @@ -42,7 +46,9 @@ public class BsqBlock implements PersistablePayload { public PB.BsqBlock toProtoMessage() { return PB.BsqBlock.newBuilder() - .setBsqBlockVo(bsqBlockVo.toProtoMessage()) + .setHeight(height) + .setHash(hash) + .setPreviousBlockHash(previousBlockHash) .addAllTxs(txs.stream() .map(Tx::toProtoMessage) .collect(Collectors.toList())) @@ -50,7 +56,9 @@ public class BsqBlock implements PersistablePayload { } public static BsqBlock fromProto(PB.BsqBlock proto) { - return new BsqBlock(BsqBlockVo.fromProto(proto.getBsqBlockVo()), + return new BsqBlock(proto.getHeight(), + proto.getHash(), + proto.getPreviousBlockHash(), proto.getTxsList().isEmpty() ? new ArrayList<>() : proto.getTxsList().stream() @@ -67,7 +75,6 @@ public class BsqBlock implements PersistablePayload { txs.stream().forEach(Tx::reset); } - @Override public String toString() { return "BsqBlock{" + @@ -77,23 +84,4 @@ public class BsqBlock implements PersistablePayload { ",\n txs='" + txs + '\'' + "\n}"; } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Delegates - /////////////////////////////////////////////////////////////////////////////////////////// - - public int getHeight() { - return bsqBlockVo.getHeight(); - } - - public String getHash() { - return bsqBlockVo.getHash(); - } - - public String getPreviousBlockHash() { - return bsqBlockVo.getPreviousBlockHash(); - } - - } diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlockVo.java b/core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlockVo.java deleted file mode 100644 index 1608e8b84d..0000000000 --- a/core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlockVo.java +++ /dev/null @@ -1,56 +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 io.bisq.core.dao.blockchain.vo; - -import io.bisq.common.proto.persistable.PersistablePayload; -import io.bisq.generated.protobuffer.PB; -import lombok.Getter; -import lombok.Value; - -@Value -@Getter -public class BsqBlockVo implements PersistablePayload { - private final int height; - private final String hash; - private final String previousBlockHash; - - public BsqBlockVo(int height, String hash, String previousBlockHash) { - this.height = height; - this.hash = hash; - this.previousBlockHash = previousBlockHash; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - - public PB.BsqBlockVo toProtoMessage() { - return PB.BsqBlockVo.newBuilder() - .setHeight(height) - .setHash(hash) - .setPreviousBlockHash(previousBlockHash).build(); - } - - public static BsqBlockVo fromProto(PB.BsqBlockVo proto) { - return new BsqBlockVo(proto.getHeight(), - proto.getHash(), - proto.getPreviousBlockHash()); - } -} diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInput.java b/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInput.java index d7b1ff4cda..e8e41c22d9 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInput.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInput.java @@ -26,12 +26,13 @@ import java.util.Optional; @Data public class TxInput implements PersistablePayload { - private final TxInputVo txInputVo; + private final String txId; + private final int txOutputIndex; @Nullable private TxOutput connectedTxOutput; - public TxInput(TxInputVo txInputVo) { - this(txInputVo, null); + public TxInput(String txId, int txOutputIndex) { + this(txId, txOutputIndex, null); } @@ -39,20 +40,25 @@ public class TxInput implements PersistablePayload { // PROTO BUFFER /////////////////////////////////////////////////////////////////////////////////////////// - private TxInput(TxInputVo txInputVo, @Nullable TxOutput connectedTxOutput) { - this.txInputVo = txInputVo; + private TxInput(String txId, int txOutputIndex, @Nullable TxOutput connectedTxOutput) { + this.txId = txId; + this.txOutputIndex = txOutputIndex; this.connectedTxOutput = connectedTxOutput; } public PB.TxInput toProtoMessage() { final PB.TxInput.Builder builder = PB.TxInput.newBuilder() - .setTxInputVo(txInputVo.toProtoMessage()); + .setTxId(txId) + .setTxOutputIndex(txOutputIndex); + Optional.ofNullable(connectedTxOutput).ifPresent(e -> builder.setConnectedTxOutput(e.toProtoMessage())); + return builder.build(); } public static TxInput fromProto(PB.TxInput proto) { - return new TxInput(TxInputVo.fromProto(proto.getTxInputVo()), + return new TxInput(proto.getTxId(), + proto.getTxOutputIndex(), proto.hasConnectedTxOutput() ? TxOutput.fromProto(proto.getConnectedTxOutput()) : null); } @@ -65,28 +71,16 @@ public class TxInput implements PersistablePayload { connectedTxOutput = null; } + public TxIdIndexTuple getTxIdIndexTuple() { + return new TxIdIndexTuple(txId, txOutputIndex); + } + @Override public String toString() { return "TxInput{" + - "\n txId=" + getTxId() + - ",\n txOutputIndex=" + getTxOutputIndex() + - ",\n txOutput='" + connectedTxOutput + '\'' + + "\n txId=" + txId + + ",\n txOutputIndex=" + txOutputIndex + + ",\n connectedTxOutput='" + connectedTxOutput + '\'' + "\n}"; } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Delegates - /////////////////////////////////////////////////////////////////////////////////////////// - - public String getTxId() { - return txInputVo.getTxId(); - } - - public int getTxOutputIndex() { - return txInputVo.getTxOutputIndex(); - } - - public TxIdIndexTuple getTxIdIndexTuple() { - return txInputVo.getTxIdIndexTuple(); - } } diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInputVo.java b/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInputVo.java deleted file mode 100644 index a632673fc8..0000000000 --- a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInputVo.java +++ /dev/null @@ -1,49 +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 io.bisq.core.dao.blockchain.vo; - -import io.bisq.common.proto.persistable.PersistablePayload; -import io.bisq.generated.protobuffer.PB; -import lombok.Value; - -@Value -public class TxInputVo implements PersistablePayload { - private final String txId; - private final int txOutputIndex; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public PB.TxInputVo toProtoMessage() { - return PB.TxInputVo.newBuilder() - .setTxId(txId) - .setTxOutputIndex(txOutputIndex) - .build(); - } - - public static TxInputVo fromProto(PB.TxInputVo proto) { - return new TxInputVo(proto.getTxId(), - proto.getTxOutputIndex()); - } - - public TxIdIndexTuple getTxIdIndexTuple() { - return new TxIdIndexTuple(txId, txOutputIndex); - } -} diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutput.java b/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutput.java index 3f98ccdee6..fce16a6927 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutput.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutput.java @@ -17,10 +17,11 @@ package io.bisq.core.dao.blockchain.vo; +import com.google.protobuf.ByteString; import io.bisq.common.proto.persistable.PersistablePayload; +import io.bisq.common.util.JsonExclude; import io.bisq.core.dao.blockchain.btcd.PubKeyScript; import io.bisq.generated.protobuffer.PB; -import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.bitcoinj.core.Utils; @@ -29,18 +30,43 @@ import javax.annotation.Nullable; import java.util.Optional; @Data -@AllArgsConstructor @Slf4j public class TxOutput implements PersistablePayload { - private final TxOutputVo txOutputVo; + private final int index; + private final long value; + private final String txId; + @Nullable + private final PubKeyScript pubKeyScript; + @Nullable + private final String address; + @Nullable + @JsonExclude + private final byte[] opReturnData; + private final int blockHeight; private boolean isUnspent; private boolean isVerified; private TxOutputType txOutputType = TxOutputType.UNDEFINED; @Nullable private SpentInfo spentInfo; - public TxOutput(TxOutputVo txOutputVo) { - this.txOutputVo = txOutputVo; + public TxOutput(int index, + long value, + String txId, + @Nullable PubKeyScript pubKeyScript, + @Nullable String address, + @Nullable byte[] opReturnData, + int blockHeight) { + this(index, + value, + txId, + pubKeyScript, + address, + opReturnData, + blockHeight, + false, + false, + TxOutputType.UNDEFINED, + null); } @@ -48,20 +74,56 @@ public class TxOutput implements PersistablePayload { // PROTO BUFFER /////////////////////////////////////////////////////////////////////////////////////////// + private TxOutput(int index, + long value, + String txId, + @Nullable PubKeyScript pubKeyScript, + @Nullable String address, + @Nullable byte[] opReturnData, + int blockHeight, + boolean isUnspent, + boolean isVerified, + TxOutputType txOutputType, + @Nullable SpentInfo spentInfo) { + this.index = index; + this.value = value; + this.txId = txId; + this.pubKeyScript = pubKeyScript; + this.address = address; + this.opReturnData = opReturnData; + this.blockHeight = blockHeight; + this.isUnspent = isUnspent; + this.isVerified = isVerified; + this.txOutputType = txOutputType; + this.spentInfo = spentInfo; + } + public PB.TxOutput toProtoMessage() { final PB.TxOutput.Builder builder = PB.TxOutput.newBuilder() - .setTxOutputVo(txOutputVo.toProtoMessage()) + .setIndex(index) + .setValue(value) + .setTxId(txId) + .setBlockHeight(blockHeight) .setIsUnspent(isUnspent) .setIsVerified(isVerified) .setTxOutputType(txOutputType.toProtoMessage()); + Optional.ofNullable(pubKeyScript).ifPresent(e -> builder.setPubKeyScript(pubKeyScript.toProtoMessage())); + Optional.ofNullable(address).ifPresent(e -> builder.setAddress(address)); + Optional.ofNullable(opReturnData).ifPresent(e -> builder.setOpReturnData(ByteString.copyFrom(opReturnData))); Optional.ofNullable(spentInfo).ifPresent(e -> builder.setSpentInfo(e.toProtoMessage())); return builder.build(); } public static TxOutput fromProto(PB.TxOutput proto) { - return new TxOutput(TxOutputVo.fromProto(proto.getTxOutputVo()), + return new TxOutput(proto.getIndex(), + proto.getValue(), + proto.getTxId(), + proto.hasPubKeyScript() ? PubKeyScript.fromProto(proto.getPubKeyScript()) : null, + proto.getAddress().isEmpty() ? null : proto.getAddress(), + proto.getOpReturnData().isEmpty() ? null : proto.getOpReturnData().toByteArray(), + proto.getBlockHeight(), proto.getIsUnspent(), proto.getIsVerified(), TxOutputType.fromProto(proto.getTxOutputType()), @@ -80,23 +142,6 @@ public class TxOutput implements PersistablePayload { spentInfo = null; } - @Override - public String toString() { - return "TxOutput{" + - "\n index=" + getIndex() + - ",\n value=" + getValue() + - ",\n txId='" + getId() + '\'' + - ",\n pubKeyScript=" + getPubKeyScript() + - ",\n address='" + getAddress() + '\'' + - ",\n opReturnData=" + (getOpReturnData() != null ? Utils.HEX.encode(getOpReturnData()) : "null") + - ",\n blockHeight=" + getBlockHeight() + - ",\n isUnspent=" + isUnspent + - ",\n isVerified=" + isVerified + - ",\n txOutputType=" + txOutputType + - ",\n spentInfo=" + (spentInfo != null ? spentInfo.toString() : "null") + - "\n}"; - } - public boolean isCompensationRequestBtcOutput() { return txOutputType == TxOutputType.COMPENSATION_REQUEST_BTC_OUTPUT; } @@ -105,46 +150,28 @@ public class TxOutput implements PersistablePayload { return txOutputType == TxOutputType.SPONSORING_BTC_OUTPUT; } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Delegates - /////////////////////////////////////////////////////////////////////////////////////////// - - public int getIndex() { - return txOutputVo.getIndex(); - } - - public long getValue() { - return txOutputVo.getValue(); - } - - public String getTxId() { - return txOutputVo.getTxId(); - } - - public PubKeyScript getPubKeyScript() { - return txOutputVo.getPubKeyScript(); - } - - @Nullable - public String getAddress() { - return txOutputVo.getAddress(); - } - - @Nullable - public byte[] getOpReturnData() { - return txOutputVo.getOpReturnData(); - } - - public int getBlockHeight() { - return txOutputVo.getBlockHeight(); - } - public String getId() { - return txOutputVo.getId(); + return txId + ":" + index; } public TxIdIndexTuple getTxIdIndexTuple() { - return txOutputVo.getTxIdIndexTuple(); + return new TxIdIndexTuple(txId, index); + } + + @Override + public String toString() { + return "TxOutput{" + + "\n index=" + index + + ",\n value=" + value + + ",\n txId='" + getId() + '\'' + + ",\n pubKeyScript=" + pubKeyScript + + ",\n address='" + address + '\'' + + ",\n opReturnData=" + (opReturnData != null ? Utils.HEX.encode(opReturnData) : "null") + + ",\n blockHeight=" + blockHeight + + ",\n isUnspent=" + isUnspent + + ",\n isVerified=" + isVerified + + ",\n txOutputType=" + txOutputType + + ",\n spentInfo=" + (spentInfo != null ? spentInfo.toString() : "null") + + "\n}"; } } diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutputVo.java b/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutputVo.java deleted file mode 100644 index 462c473454..0000000000 --- a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutputVo.java +++ /dev/null @@ -1,79 +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 io.bisq.core.dao.blockchain.vo; - -import com.google.protobuf.ByteString; -import io.bisq.common.proto.persistable.PersistablePayload; -import io.bisq.common.util.JsonExclude; -import io.bisq.core.dao.blockchain.btcd.PubKeyScript; -import io.bisq.generated.protobuffer.PB; -import lombok.Value; - -import javax.annotation.Nullable; -import java.util.Optional; - -@Value -public class TxOutputVo implements PersistablePayload { - private final int index; - private final long value; - private final String txId; - @Nullable - private final PubKeyScript pubKeyScript; - @Nullable - private final String address; - @Nullable - @JsonExclude - private final byte[] opReturnData; - private final int blockHeight; - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public PB.TxOutputVo toProtoMessage() { - final PB.TxOutputVo.Builder builder = PB.TxOutputVo.newBuilder() - .setIndex(index) - .setValue(value) - .setTxId(txId) - .setBlockHeight(blockHeight); - - Optional.ofNullable(pubKeyScript).ifPresent(e -> builder.setPubKeyScript(pubKeyScript.toProtoMessage())); - Optional.ofNullable(address).ifPresent(e -> builder.setAddress(address)); - Optional.ofNullable(opReturnData).ifPresent(e -> builder.setOpReturnData(ByteString.copyFrom(opReturnData))); - - return builder.build(); - } - - public static TxOutputVo fromProto(PB.TxOutputVo proto) { - return new TxOutputVo(proto.getIndex(), - proto.getValue(), - proto.getTxId(), - proto.hasPubKeyScript() ? PubKeyScript.fromProto(proto.getPubKeyScript()) : null, - proto.getAddress().isEmpty() ? null : proto.getAddress(), - proto.getOpReturnData().isEmpty() ? null : proto.getOpReturnData().toByteArray(), - proto.getBlockHeight()); - } - - public String getId() { - return txId + ":" + index; - } - - public TxIdIndexTuple getTxIdIndexTuple() { - return new TxIdIndexTuple(txId, index); - } -} From 40fd9a664bb966f869962a6f30a9becfb1a8331c Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 25 Jul 2017 22:14:27 +0200 Subject: [PATCH 02/54] Enable BSQ --- common/src/main/java/io/bisq/common/locale/CurrencyUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index 97b244c320..13430e941b 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -94,7 +94,7 @@ public class CurrencyUtil { result.add(new CryptoCurrency("REP", "Augur", true)); result.add(new CryptoCurrency("BATL", "Battlestars")); result.add(new CryptoCurrency("BIGUP", "BigUp")); - // result.add(new CryptoCurrency("BSQ", "Bisq Token")); + result.add(new CryptoCurrency("BSQ", "Bisq Token")); if (!baseCurrencyCode.equals("BTC")) result.add(new CryptoCurrency("BTC", "Bitcoin")); result.add(new CryptoCurrency("BITAUD", "BitAUD", true)); @@ -243,7 +243,7 @@ public class CurrencyUtil { public static List getMainCryptoCurrencies() { final List result = new ArrayList<>(); - // result.add(new CryptoCurrency("BSQ", "Bisq Token")); + result.add(new CryptoCurrency("BSQ", "Bisq Token")); if (!baseCurrencyCode.equals("BTC")) result.add(new CryptoCurrency("BTC", "Bitcoin")); if (!baseCurrencyCode.equals("DASH")) From 894923fb5e6a36c0a1787a20893aff0c0519dea2 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 25 Jul 2017 22:14:35 +0200 Subject: [PATCH 03/54] Enable dev mode --- common/src/main/java/io/bisq/common/app/DevEnv.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/io/bisq/common/app/DevEnv.java b/common/src/main/java/io/bisq/common/app/DevEnv.java index 0d2ea99318..eecf7433e9 100644 --- a/common/src/main/java/io/bisq/common/app/DevEnv.java +++ b/common/src/main/java/io/bisq/common/app/DevEnv.java @@ -10,7 +10,7 @@ public class DevEnv { // peer (click user icon and alt+r), filter/block offers by various data like offer ID (cmd + f). // The user can set a program argument to ignore all of those privileged network_messages. They are intended for // emergency cases only (beside update message and arbitrator registration). - public static final boolean USE_DEV_PRIVILEGE_KEYS = false; + public static final boolean USE_DEV_PRIVILEGE_KEYS = true; public static final String DEV_PRIVILEGE_PUB_KEY = "027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee"; public static final String DEV_PRIVILEGE_PRIV_KEY = "6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a"; @@ -18,8 +18,8 @@ public class DevEnv { // If set to true we ignore several UI behavior like confirmation popups as well dummy accounts are created and // offers are filled with default values. Intended to make dev testing faster. @SuppressWarnings("PointlessBooleanExpression") - public static final boolean DEV_MODE = STRESS_TEST_MODE || false; + public static final boolean DEV_MODE = STRESS_TEST_MODE || true; - public static final boolean DAO_ACTIVATED = false; + public static final boolean DAO_ACTIVATED = true; public static final boolean DAO_PHASE2_ACTIVATED = false; } From 493b3a4f117032a7d315606beb0370ca25f8cfd8 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 25 Jul 2017 22:14:48 +0200 Subject: [PATCH 04/54] Add BSQ address validation --- .../validation/AltCoinAddressValidator.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java index 1dcb69ce1b..dd7c3dee05 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java @@ -173,6 +173,28 @@ public final class AltCoinAddressValidator extends InputValidator { } catch (AddressFormatException e) { return new ValidationResult(false, getErrorMessage(e)); } + case "BSQ": + if (!input.startsWith("B")) + return new ValidationResult(false, Res.get("validation.altcoin.invalidAddress", + currencyCode, "BSQ address must start with \"B\"")); + + String addressAsBtc = input.substring(1, input.length()); + try { + switch (BisqEnvironment.getBaseCurrencyNetwork()) { + case BTC_MAINNET: + Address.fromBase58(MainNetParams.get(), addressAsBtc); + break; + case BTC_TESTNET: + Address.fromBase58(TestNet3Params.get(), addressAsBtc); + break; + case BTC_REGTEST: + Address.fromBase58(RegTestParams.get(), addressAsBtc); + break; + } + return new ValidationResult(true); + } catch (AddressFormatException e) { + return new ValidationResult(false, getErrorMessage(e)); + } case "ETH": // https://github.com/ethereum/web3.js/blob/master/lib/utils/utils.js#L403 if (!input.matches("^(0x)?[0-9a-fA-F]{40}$")) From 03c33964a7a1bf1f0ea519854aecad29830c37d4 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Wed, 26 Jul 2017 00:07:44 +0200 Subject: [PATCH 05/54] Add more selectors to filter. --- common/src/main/proto/pb.proto | 2 + .../resources/i18n/displayStrings.properties | 4 ++ .../main/java/io/bisq/core/filter/Filter.java | 24 ++++++- .../io/bisq/core/filter/FilterManager.java | 71 ++++++++++++++++++- .../core/trade/protocol/ProcessModel.java | 35 --------- .../protocol/tasks/CheckIfPeerIsBanned.java | 30 +++++--- .../core/user/UserPayloadModelVOTest.java | 3 +- .../createoffer/CreateOfferDataModel.java | 11 ++- .../main/offer/offerbook/OfferBookView.java | 26 +++++-- .../offer/offerbook/OfferBookViewModel.java | 22 +++--- .../offer/takeoffer/TakeOfferDataModel.java | 45 +++++++----- .../main/overlays/windows/FilterWindow.java | 30 +++++++- 12 files changed, 215 insertions(+), 88 deletions(-) diff --git a/common/src/main/proto/pb.proto b/common/src/main/proto/pb.proto index b2d7004143..d437cbbcb3 100644 --- a/common/src/main/proto/pb.proto +++ b/common/src/main/proto/pb.proto @@ -539,6 +539,8 @@ message Filter { string signature_as_base64 = 4; bytes owner_pub_key_bytes = 5; map extra_data = 6; + repeated string banned_currencies = 7; + repeated string banned_payment_methods = 8; } message TradeStatistics { diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index a0c44a805c..1d302189a8 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -315,6 +315,8 @@ offerbook.warning.noMatchingAccount.msg=You don't have a trading account with th offerbook.warning.wrongTradeProtocol=That offer requires a different protocol version as the one used in your version of the software.\n\nPlease check if you have the latest version installed, otherwise the user who created the offer has used an older version.\n\nUsers cannot trade with an incompatible trade protocol version. offerbook.warning.userIgnored=You have added that user's onion address to your ignore list. offerbook.warning.offerBlocked=That offer was blocked by the Bisq developers.\nProbably there is an unhandled bug causing issues when taking that offer. +offerbook.warning.currencyBanned=The currency used in that offer was blocked by the Bisq developers.\nPlease visit the Bisq Forum for more information. +offerbook.warning.paymentMethodBanned=The payment method used in that offer was blocked by the Bisq developers.\nPlease visit the Bisq Forum for more information. offerbook.warning.nodeBlocked=The onion address of that trader was blocked by the Bisq developers.\nProbably there is an unhandled bug causing issues when taking offers from that trader. @@ -1071,6 +1073,8 @@ filterWindow.headline=Edit filter list filterWindow.offers=Filtered offers (comma sep.): filterWindow.onions=Filtered onion addresses (comma sep.): filterWindow.accounts=Filtered trading account data:\nFormat: comma sep. list of [payment method id | data field | value] +filterWindow.bannedCurrencies=Filtered currency codes (comma sep.): +filterWindow.bannedPaymentMethods=Filtered payment method IDs (comma sep.): filterWindow.add=Add filter filterWindow.remove=Remove filter diff --git a/core/src/main/java/io/bisq/core/filter/Filter.java b/core/src/main/java/io/bisq/core/filter/Filter.java index e340ca1758..4a6e20d1a1 100644 --- a/core/src/main/java/io/bisq/core/filter/Filter.java +++ b/core/src/main/java/io/bisq/core/filter/Filter.java @@ -47,6 +47,12 @@ public final class Filter implements StoragePayload { private final List bannedNodeAddress; private final List bannedPaymentAccounts; + // Because we added those fields in v 0.5.4 and old versions do not have it we annotate it with @Nullable + @Nullable + private final List bannedCurrencies; + @Nullable + private final List bannedPaymentMethods; + private String signatureAsBase64; private byte[] ownerPubKeyBytes; // Should be only used in emergency case if we need to add data but do not want to break backward compatibility @@ -58,10 +64,14 @@ public final class Filter implements StoragePayload { public Filter(List bannedOfferIds, List bannedNodeAddress, - List bannedPaymentAccounts) { + List bannedPaymentAccounts, + @Nullable List bannedCurrencies, + @Nullable List bannedPaymentMethods) { this.bannedOfferIds = bannedOfferIds; this.bannedNodeAddress = bannedNodeAddress; this.bannedPaymentAccounts = bannedPaymentAccounts; + this.bannedCurrencies = bannedCurrencies; + this.bannedPaymentMethods = bannedPaymentMethods; } @@ -73,12 +83,16 @@ public final class Filter implements StoragePayload { public Filter(List bannedOfferIds, List bannedNodeAddress, List bannedPaymentAccounts, + @Nullable List bannedCurrencies, + @Nullable List bannedPaymentMethods, String signatureAsBase64, byte[] ownerPubKeyBytes, @Nullable Map extraDataMap) { this(bannedOfferIds, bannedNodeAddress, - bannedPaymentAccounts); + bannedPaymentAccounts, + bannedCurrencies, + bannedPaymentMethods); this.signatureAsBase64 = signatureAsBase64; this.ownerPubKeyBytes = ownerPubKeyBytes; this.extraDataMap = extraDataMap; @@ -99,7 +113,11 @@ public final class Filter implements StoragePayload { .addAllBannedPaymentAccounts(paymentAccountFilterList) .setSignatureAsBase64(signatureAsBase64) .setOwnerPubKeyBytes(ByteString.copyFrom(ownerPubKeyBytes)); + + Optional.ofNullable(bannedCurrencies).ifPresent(builder::addAllBannedCurrencies); + Optional.ofNullable(bannedPaymentMethods).ifPresent(builder::addAllBannedPaymentMethods); Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData); + return PB.StoragePayload.newBuilder().setFilter(builder).build(); } @@ -109,6 +127,8 @@ public final class Filter implements StoragePayload { proto.getBannedPaymentAccountsList().stream() .map(PaymentAccountFilter::fromProto) .collect(Collectors.toList()), + CollectionUtils.isEmpty(proto.getBannedCurrenciesList()) ? null : proto.getBannedCurrenciesList().stream().collect(Collectors.toList()), + CollectionUtils.isEmpty(proto.getBannedPaymentMethodsList()) ? null : proto.getBannedPaymentMethodsList().stream().collect(Collectors.toList()), proto.getSignatureAsBase64(), proto.getOwnerPubKeyBytes().toByteArray(), CollectionUtils.isEmpty(proto.getExtraDataMap()) ? null : proto.getExtraDataMap()); diff --git a/core/src/main/java/io/bisq/core/filter/FilterManager.java b/core/src/main/java/io/bisq/core/filter/FilterManager.java index 044fb0b7bb..d48807af1e 100644 --- a/core/src/main/java/io/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/io/bisq/core/filter/FilterManager.java @@ -22,6 +22,8 @@ import com.google.inject.name.Named; import io.bisq.common.app.DevEnv; import io.bisq.common.crypto.KeyRing; import io.bisq.core.app.AppOptionKeys; +import io.bisq.core.payment.payload.PaymentAccountPayload; +import io.bisq.core.payment.payload.PaymentMethod; import io.bisq.core.user.User; import io.bisq.generated.protobuffer.PB; import io.bisq.network.p2p.P2PService; @@ -36,8 +38,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nullable; +import java.lang.reflect.Method; import java.math.BigInteger; import java.security.SignatureException; +import java.util.Optional; import java.util.stream.Collectors; import static org.bitcoinj.core.Utils.HEX; @@ -162,12 +166,18 @@ public class FilterManager { } } + // We dont use full data from Filter as we are only interested in the filter data not the sig and keys private String getHexFromData(Filter filter) { - PB.Filter.Builder builder = PB.Filter.newBuilder().addAllBannedNodeAddress(filter.getBannedNodeAddress()) + PB.Filter.Builder builder = PB.Filter.newBuilder() .addAllBannedOfferIds(filter.getBannedOfferIds()) + .addAllBannedNodeAddress(filter.getBannedNodeAddress()) .addAllBannedPaymentAccounts(filter.getBannedPaymentAccounts().stream() .map(PaymentAccountFilter::toProtoMessage) .collect(Collectors.toList())); + + Optional.ofNullable(filter.getBannedCurrencies()).ifPresent(builder::addAllBannedCurrencies); + Optional.ofNullable(filter.getBannedPaymentMethods()).ifPresent(builder::addAllBannedPaymentMethods); + return Utils.HEX.encode(builder.build().toByteArray()); } @@ -175,4 +185,63 @@ public class FilterManager { public Filter getDevelopersFilter() { return user.getDevelopersFilter(); } + + public boolean isCurrencyBanned(String currencyCode) { + return getFilter() != null && + getFilter().getBannedCurrencies() != null && + getFilter().getBannedCurrencies().stream() + .filter(e -> e.equals(currencyCode)) + .findAny() + .isPresent(); + } + + public boolean isPaymentMethodBanned(PaymentMethod paymentMethod) { + return getFilter() != null && + getFilter().getBannedPaymentMethods() != null && + getFilter().getBannedPaymentMethods().stream() + .filter(e -> e.equals(paymentMethod.getId())) + .findAny() + .isPresent(); + } + + public boolean isOfferIdBanned(String offerId) { + return getFilter() != null && + getFilter().getBannedOfferIds().stream() + .filter(e -> e.equals(offerId)) + .findAny() + .isPresent(); + } + + public boolean isNodeAddressBanned(String nodeAddress) { + return getFilter() != null && + getFilter().getBannedNodeAddress().stream() + .filter(e -> e.equals(nodeAddress)) + .findAny() + .isPresent(); + } + + public boolean isPeersPaymentAccountDataAreBanned(PaymentAccountPayload paymentAccountPayload, + PaymentAccountFilter[] appliedPaymentAccountFilter) { + return getFilter() != null && + getFilter().getBannedPaymentAccounts().stream() + .filter(paymentAccountFilter -> { + final boolean samePaymentMethodId = paymentAccountFilter.getPaymentMethodId().equals( + paymentAccountPayload.getPaymentMethodId()); + if (samePaymentMethodId) { + try { + Method method = paymentAccountPayload.getClass().getMethod(paymentAccountFilter.getGetMethodName()); + String result = (String) method.invoke(paymentAccountPayload); + appliedPaymentAccountFilter[0] = paymentAccountFilter; + return result.equals(paymentAccountFilter.getValue()); + } catch (Throwable e) { + log.error(e.getMessage()); + return false; + } + } else { + return false; + } + }) + .findAny() + .isPresent(); + } } diff --git a/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java b/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java index 25c82d611a..722b3f18a6 100644 --- a/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java +++ b/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java @@ -29,7 +29,6 @@ import io.bisq.core.btc.wallet.BsqWalletService; import io.bisq.core.btc.wallet.BtcWalletService; import io.bisq.core.btc.wallet.TradeWalletService; import io.bisq.core.filter.FilterManager; -import io.bisq.core.filter.PaymentAccountFilter; import io.bisq.core.offer.Offer; import io.bisq.core.offer.OpenOfferManager; import io.bisq.core.payment.PaymentAccount; @@ -54,7 +53,6 @@ import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Transaction; import javax.annotation.Nullable; -import java.lang.reflect.Method; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -262,39 +260,6 @@ public class ProcessModel implements Model, PersistablePayload { return paymentAccount != null ? paymentAccount.getPaymentAccountPayload() : null; } - public boolean isPeersPaymentAccountDataAreBanned(PaymentAccountPayload paymentAccountPayload, - PaymentAccountFilter[] appliedPaymentAccountFilter) { - return filterManager.getFilter() != null && - filterManager.getFilter().getBannedPaymentAccounts().stream() - .filter(paymentAccountFilter -> { - final boolean samePaymentMethodId = paymentAccountFilter.getPaymentMethodId().equals( - paymentAccountPayload.getPaymentMethodId()); - if (samePaymentMethodId) { - try { - Method method = paymentAccountPayload.getClass().getMethod(paymentAccountFilter.getGetMethodName()); - String result = (String) method.invoke(paymentAccountPayload); - appliedPaymentAccountFilter[0] = paymentAccountFilter; - return result.equals(paymentAccountFilter.getValue()); - } catch (Throwable e) { - log.error(e.getMessage()); - return false; - } - } else { - return false; - } - }) - .findAny() - .isPresent(); - } - - public boolean isNodeBanned(NodeAddress nodeAddress) { - return filterManager.getFilter() != null && - filterManager.getFilter().getBannedNodeAddress().stream() - .filter(e -> e.equals(nodeAddress.getHostNameWithoutPostFix())) - .findAny() - .isPresent(); - } - public Coin getFundsNeededForTradeAsLong() { return Coin.valueOf(fundsNeededForTradeAsLong); } diff --git a/core/src/main/java/io/bisq/core/trade/protocol/tasks/CheckIfPeerIsBanned.java b/core/src/main/java/io/bisq/core/trade/protocol/tasks/CheckIfPeerIsBanned.java index ab091fd8f8..c1624f6014 100644 --- a/core/src/main/java/io/bisq/core/trade/protocol/tasks/CheckIfPeerIsBanned.java +++ b/core/src/main/java/io/bisq/core/trade/protocol/tasks/CheckIfPeerIsBanned.java @@ -38,18 +38,26 @@ public class CheckIfPeerIsBanned extends TradeTask { try { runInterceptHook(); - final NodeAddress tempTradingPeerNodeAddress = processModel.getTempTradingPeerNodeAddress(); - if (tempTradingPeerNodeAddress != null && processModel.isNodeBanned(tempTradingPeerNodeAddress)) { - failed("Other trader is banned by his node address.\n" + - "tradingPeerNodeAddress=" + tempTradingPeerNodeAddress); + final NodeAddress nodeAddress = processModel.getTempTradingPeerNodeAddress(); + PaymentAccountPayload paymentAccountPayload = checkNotNull(processModel.getTradingPeer().getPaymentAccountPayload()); + final PaymentAccountFilter[] appliedPaymentAccountFilter = new PaymentAccountFilter[1]; - PaymentAccountPayload paymentAccountPayload = checkNotNull(processModel.getTradingPeer().getPaymentAccountPayload()); - final PaymentAccountFilter[] appliedPaymentAccountFilter = new PaymentAccountFilter[1]; - if (processModel.isPeersPaymentAccountDataAreBanned(paymentAccountPayload, appliedPaymentAccountFilter)) { - failed("Other trader is banned by his trading account data.\n" + - "paymentAccountPayload=" + paymentAccountPayload.getPaymentDetails() + "\n" + - "banFilter=" + appliedPaymentAccountFilter[0].toString()); - } + if (nodeAddress != null && processModel.getFilterManager().isNodeAddressBanned(nodeAddress.getHostNameWithoutPostFix())) { + failed("Other trader is banned by his node address.\n" + + "tradingPeerNodeAddress=" + nodeAddress); + } else if (processModel.getFilterManager().isOfferIdBanned(trade.getId())) { + failed("Offer ID is banned.\n" + + "Offer ID=" + trade.getId()); + } else if (processModel.getFilterManager().isCurrencyBanned(trade.getOffer().getCurrencyCode())) { + failed("Currency is banned.\n" + + "Currency code=" + trade.getOffer().getCurrencyCode()); + } else if (processModel.getFilterManager().isPaymentMethodBanned(trade.getOffer().getPaymentMethod())) { + failed("Payment method is banned.\n" + + "Payment method=" + trade.getOffer().getPaymentMethod().getId()); + } else if (processModel.getFilterManager().isPeersPaymentAccountDataAreBanned(paymentAccountPayload, appliedPaymentAccountFilter)) { + failed("Other trader is banned by his trading account data.\n" + + "paymentAccountPayload=" + paymentAccountPayload.getPaymentDetails() + "\n" + + "banFilter=" + appliedPaymentAccountFilter[0].toString()); } else { complete(); } diff --git a/core/src/test/java/io/bisq/core/user/UserPayloadModelVOTest.java b/core/src/test/java/io/bisq/core/user/UserPayloadModelVOTest.java index 6b9d26f796..01023b37ac 100644 --- a/core/src/test/java/io/bisq/core/user/UserPayloadModelVOTest.java +++ b/core/src/test/java/io/bisq/core/user/UserPayloadModelVOTest.java @@ -41,7 +41,8 @@ public class UserPayloadModelVOTest { UserPayload vo = new UserPayload(); vo.setAccountId("accountId"); vo.setDisplayedAlert(new Alert("message", true, "version", new byte[]{12, -64, 12}, "string", null)); - vo.setDevelopersFilter(new Filter(Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), "string", new byte[]{10, 0, 0}, null)); + vo.setDevelopersFilter(new Filter(Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), + Lists.newArrayList(), Lists.newArrayList(), "string", new byte[]{10, 0, 0}, null)); vo.setRegisteredArbitrator(ArbitratorTest.getArbitratorMock()); vo.setRegisteredMediator(MediatorTest.getMediatorMock()); vo.setAcceptedArbitrators(Lists.newArrayList(ArbitratorTest.getArbitratorMock())); diff --git a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferDataModel.java b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferDataModel.java index 1d3650b979..a61f917cc0 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferDataModel.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferDataModel.java @@ -36,6 +36,7 @@ import io.bisq.core.btc.listeners.BalanceListener; import io.bisq.core.btc.wallet.BsqBalanceListener; import io.bisq.core.btc.wallet.BsqWalletService; import io.bisq.core.btc.wallet.BtcWalletService; +import io.bisq.core.filter.FilterManager; import io.bisq.core.offer.Offer; import io.bisq.core.offer.OfferPayload; import io.bisq.core.offer.OpenOfferManager; @@ -80,6 +81,7 @@ class CreateOfferDataModel extends ActivatableDataModel { private final P2PService p2PService; private final PriceFeedService priceFeedService; final String shortOfferId; + private final FilterManager filterManager; private final FeeService feeService; private final BSFormatter formatter; private final String offerId; @@ -129,7 +131,7 @@ class CreateOfferDataModel extends ActivatableDataModel { @Inject CreateOfferDataModel(OpenOfferManager openOfferManager, BtcWalletService btcWalletService, BsqWalletService bsqWalletService, Preferences preferences, User user, KeyRing keyRing, P2PService p2PService, - PriceFeedService priceFeedService, + PriceFeedService priceFeedService, FilterManager filterManager, FeeService feeService, BSFormatter formatter) { this.openOfferManager = openOfferManager; this.btcWalletService = btcWalletService; @@ -139,6 +141,7 @@ class CreateOfferDataModel extends ActivatableDataModel { this.keyRing = keyRing; this.p2PService = p2PService; this.priceFeedService = priceFeedService; + this.filterManager = filterManager; this.feeService = feeService; this.formatter = formatter; @@ -349,6 +352,12 @@ class CreateOfferDataModel extends ActivatableDataModel { checkArgument(buyerSecurityDepositAsCoin.compareTo(Restrictions.getMinBuyerSecurityDeposit()) >= 0, "securityDeposit must be not be less than " + Restrictions.getMinBuyerSecurityDeposit().toFriendlyString()); + + checkArgument(!filterManager.isCurrencyBanned(currencyCode), + Res.get("offerbook.warning.currencyBanned")); + checkArgument(!filterManager.isPaymentMethodBanned(paymentAccount.getPaymentMethod()), + Res.get("offerbook.warning.paymentMethodBanned")); + OfferPayload offerPayload = new OfferPayload(offerId, new Date().getTime(), p2PService.getAddress(), diff --git a/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookView.java b/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookView.java index cecb697db1..2e363a2dc0 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookView.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookView.java @@ -362,7 +362,8 @@ public class OfferBookView extends ActivatableViewAndModel().warning(Res.get("offerbook.warning.userIgnored")).show(); } else if (isOfferBanned) { new Popup<>().warning(Res.get("offerbook.warning.offerBlocked")).show(); - } else if (isNodeBanned) { + } else if (isCurrencyBanned) { + new Popup<>().warning(Res.get("offerbook.warning.currencyBanned")).show(); + } else if (isPaymentMethodBanned) { + new Popup<>().warning(Res.get("offerbook.warning.paymentMethodBanned")).show(); + } else if (isNodeAddressBanned) { new Popup<>().warning(Res.get("offerbook.warning.nodeBlocked")).show(); } } @@ -672,7 +677,8 @@ public class OfferBookView extends ActivatableViewAndModel onShowInfo(isPaymentAccountValidForOffer, hasMatchingArbitrator, hasSameProtocolVersion, - isIgnored, isOfferBanned, isNodeBanned)); + isIgnored, isOfferBanned, isCurrencyBanned, isPaymentMethodBanned, + isNodeAddressBanned)); button.setText(title); setGraphic(button); diff --git a/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookViewModel.java b/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookViewModel.java index e3f9a7869e..8eb070b8a2 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookViewModel.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookViewModel.java @@ -448,19 +448,19 @@ class OfferBookViewModel extends ActivatableViewModel { } boolean isOfferBanned(Offer offer) { - return filterManager.getFilter() != null && - filterManager.getFilter().getBannedOfferIds().stream() - .filter(e -> e.equals(offer.getId())) - .findAny() - .isPresent(); + return filterManager.isOfferIdBanned(offer.getId()); } - boolean isNodeBanned(Offer offer) { - return filterManager.getFilter() != null && - filterManager.getFilter().getBannedNodeAddress().stream() - .filter(e -> e.equals(offer.getMakerNodeAddress().getHostNameWithoutPostFix())) - .findAny() - .isPresent(); + boolean isCurrencyBanned(Offer offer) { + return filterManager.isCurrencyBanned(offer.getCurrencyCode()); + } + + boolean isPaymentMethodBanned(Offer offer) { + return filterManager.isPaymentMethodBanned(offer.getPaymentMethod()); + } + + boolean isNodeAddressBanned(Offer offer) { + return filterManager.isNodeAddressBanned(offer.getMakerNodeAddress().getHostNameWithoutPostFix()); } boolean hasSameProtocolVersion(Offer offer) { diff --git a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferDataModel.java b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferDataModel.java index 3477c3f230..153edc6ca8 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferDataModel.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferDataModel.java @@ -29,6 +29,7 @@ import io.bisq.core.btc.Restrictions; import io.bisq.core.btc.listeners.BalanceListener; import io.bisq.core.btc.wallet.BsqWalletService; import io.bisq.core.btc.wallet.BtcWalletService; +import io.bisq.core.filter.FilterManager; import io.bisq.core.offer.Offer; import io.bisq.core.offer.OfferPayload; import io.bisq.core.payment.PaymentAccount; @@ -66,6 +67,7 @@ class TakeOfferDataModel extends ActivatableDataModel { private final BsqWalletService bsqWalletService; private final User user; private final FeeService feeService; + private final FilterManager filterManager; private final Preferences preferences; private final PriceFeedService priceFeedService; private final BSFormatter formatter; @@ -103,7 +105,7 @@ class TakeOfferDataModel extends ActivatableDataModel { @Inject TakeOfferDataModel(TradeManager tradeManager, BtcWalletService btcWalletService, BsqWalletService bsqWalletService, - User user, FeeService feeService, + User user, FeeService feeService, FilterManager filterManager, Preferences preferences, PriceFeedService priceFeedService, BSFormatter formatter) { this.tradeManager = tradeManager; @@ -111,6 +113,7 @@ class TakeOfferDataModel extends ActivatableDataModel { this.bsqWalletService = bsqWalletService; this.user = user; this.feeService = feeService; + this.filterManager = filterManager; this.preferences = preferences; this.priceFeedService = priceFeedService; this.formatter = formatter; @@ -274,21 +277,31 @@ class TakeOfferDataModel extends ActivatableDataModel { if (isBuyOffer()) fundsNeededForTrade = fundsNeededForTrade.add(amount.get()); - tradeManager.onTakeOffer(amount.get(), - txFeeFromFeeService, - getTakerFee(), - isCurrencyForTakerFeeBtc(), - tradePrice.getValue(), - fundsNeededForTrade, - offer, - paymentAccount.getId(), - useSavingsWallet, - tradeResultHandler, - errorMessage -> { - log.warn(errorMessage); - new Popup<>().warning(errorMessage).show(); - } - ); + if (filterManager.isCurrencyBanned(offer.getCurrencyCode())) { + new Popup<>().warning(Res.get("offerbook.warning.currencyBanned")).show(); + } else if (filterManager.isPaymentMethodBanned(offer.getPaymentMethod())) { + new Popup<>().warning(Res.get("offerbook.warning.paymentMethodBanned")).show(); + } else if (filterManager.isOfferIdBanned(offer.getId())) { + new Popup<>().warning(Res.get("offerbook.warning.offerBlocked")).show(); + } else if (filterManager.isNodeAddressBanned(offer.getMakerNodeAddress().getHostNameWithoutPostFix())) { + new Popup<>().warning(Res.get("offerbook.warning.nodeBlocked")).show(); + } else { + tradeManager.onTakeOffer(amount.get(), + txFeeFromFeeService, + getTakerFee(), + isCurrencyForTakerFeeBtc(), + tradePrice.getValue(), + fundsNeededForTrade, + offer, + paymentAccount.getId(), + useSavingsWallet, + tradeResultHandler, + errorMessage -> { + log.warn(errorMessage); + new Popup<>().warning(errorMessage).show(); + } + ); + } } public void onPaymentAccountSelected(PaymentAccount paymentAccount) { diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/FilterWindow.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/FilterWindow.java index 7c6c0ed119..704cdd4acb 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/FilterWindow.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/FilterWindow.java @@ -115,6 +115,8 @@ public class FilterWindow extends Overlay { InputTextField offerIdsInputTextField = addLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.offers")).second; InputTextField nodesInputTextField = addLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.onions")).second; InputTextField paymentAccountFilterInputTextField = addLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.accounts")).second; + InputTextField bannedCurrenciesInputTextField = addLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.bannedCurrencies")).second; + InputTextField bannedPaymentMethodsInputTextField = addLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.bannedPaymentMethods")).second; GridPane.setHalignment(paymentAccountFilterInputTextField, HPos.RIGHT); final Filter filter = filterManager.getDevelopersFilter(); @@ -135,22 +137,33 @@ public class FilterWindow extends Overlay { }); paymentAccountFilterInputTextField.setText(sb.toString()); } + + if (filter.getBannedCurrencies() != null) + bannedCurrenciesInputTextField.setText(filter.getBannedCurrencies().stream().collect(Collectors.joining(", "))); + + if (filter.getBannedPaymentMethods() != null) + bannedPaymentMethodsInputTextField.setText(filter.getBannedPaymentMethods().stream().collect(Collectors.joining(", "))); } Button sendButton = new Button(Res.get("filterWindow.add")); sendButton.setOnAction(e -> { ArrayList offerIds = new ArrayList<>(); ArrayList nodes = new ArrayList<>(); ArrayList paymentAccountFilters = new ArrayList<>(); + ArrayList bannedCurrencies = new ArrayList<>(); + ArrayList bannedPaymentMethods = new ArrayList<>(); if (!offerIdsInputTextField.getText().isEmpty()) { offerIds = new ArrayList<>(Arrays.asList(StringUtils.deleteWhitespace(offerIdsInputTextField.getText()) .split(","))); } - if (!nodesInputTextField.getText().isEmpty()) + + if (!nodesInputTextField.getText().isEmpty()) { nodes = new ArrayList<>(Arrays.asList(StringUtils.deleteWhitespace(nodesInputTextField.getText()).replace(":9999", "") .replace(".onion", "") .split(","))); - if (!paymentAccountFilterInputTextField.getText().isEmpty()) + } + + if (!paymentAccountFilterInputTextField.getText().isEmpty()) { paymentAccountFilters = new ArrayList<>(Arrays.asList(paymentAccountFilterInputTextField.getText() .replace(", ", ",") .split(",")) @@ -162,8 +175,19 @@ public class FilterWindow extends Overlay { return new PaymentAccountFilter("", "", ""); }) .collect(Collectors.toList())); + } - if (sendFilterMessageHandler.handle(new Filter(offerIds, nodes, paymentAccountFilters), keyInputTextField.getText())) + if (!bannedCurrenciesInputTextField.getText().isEmpty()) { + bannedCurrencies = new ArrayList<>(Arrays.asList(StringUtils.deleteWhitespace(bannedCurrenciesInputTextField.getText()) + .split(","))); + } + + if (!bannedPaymentMethodsInputTextField.getText().isEmpty()) { + bannedPaymentMethods = new ArrayList<>(Arrays.asList(StringUtils.deleteWhitespace(bannedPaymentMethodsInputTextField.getText()) + .split(","))); + } + + if (sendFilterMessageHandler.handle(new Filter(offerIds, nodes, paymentAccountFilters, bannedCurrencies, bannedPaymentMethods), keyInputTextField.getText())) hide(); else new Popup<>().warning(Res.get("shared.invalidKey")).width(300).onClose(this::blurAgain).show(); From 1857cc0b308a008415ca57a003566033f9410284 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Wed, 26 Jul 2017 11:44:28 +0200 Subject: [PATCH 06/54] Show latest trade price as market price for currencies which have no external price feed provided but have been traded in Bisq --- .../resources/i18n/displayStrings.properties | 3 ++ .../bisq/core/dao/blockchain/BsqLiteNode.java | 2 +- .../main/java/io/bisq/core/offer/Offer.java | 2 +- .../bisq/core/provider/price/MarketPrice.java | 17 ++++++++--- .../core/provider/price/PriceFeedService.java | 19 +++++++++++-- .../core/provider/price/PriceProvider.java | 2 +- .../statistics/TradeStatisticsManager.java | 20 +++++++++++++ .../main/java/io/bisq/gui/main/MainView.java | 28 +++++++++++++++---- .../java/io/bisq/gui/main/MainViewModel.java | 15 ++++++---- .../bisq/gui/main/PriceFeedComboBoxItem.java | 19 +++++-------- .../main/market/spread/SpreadViewModel.java | 2 +- .../createoffer/CreateOfferViewModel.java | 10 +++---- .../gui/util/validation/BsqValidator.java | 2 +- 13 files changed, 102 insertions(+), 39 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index 1d302189a8..a216e2789c 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -210,6 +210,9 @@ mainView.menu.account=Account mainView.menu.dao=DAO mainView.marketPrice.provider=Market price provider: +mainView.marketPrice.bisqInternalPrice=Price of latest Bisq trade +mainView.marketPrice.tooltip.bisqInternalPrice=There is no market price from external price feed providers available.\n\ + The displayed price is the latest Bisq trade price for that currency. mainView.marketPrice.tooltip=Market price is provided by {0}{1}\nLast update: {2}\nProvider node URL: {3} mainView.marketPrice.tooltip.altcoinExtra=If the altcoin is not available at Poloniex we use https://coinmarketcap.com mainView.balance.available=Available balance diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/BsqLiteNode.java b/core/src/main/java/io/bisq/core/dao/blockchain/BsqLiteNode.java index f4df369f87..da17081fc6 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/BsqLiteNode.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/BsqLiteNode.java @@ -85,7 +85,7 @@ public class BsqLiteNode extends BsqNode { @Override public void onBlockReceived(GetBsqBlocksResponse getBsqBlocksResponse) { List bsqBlockList = new ArrayList<>(getBsqBlocksResponse.getBsqBlocks()); - log.info("received msg with {} items", bsqBlockList.size(), bsqBlockList.get(bsqBlockList.size() - 1).getHeight()); + log.info("received msg with {} items", bsqBlockList.size()); if (bsqBlockList.size() > 0) log.info("block height of last item: {}", bsqBlockList.get(bsqBlockList.size() - 1).getHeight()); // Be safe and reset all mutable data in case the provider would not have done it diff --git a/core/src/main/java/io/bisq/core/offer/Offer.java b/core/src/main/java/io/bisq/core/offer/Offer.java index 82be39aeec..8bf4f0896d 100644 --- a/core/src/main/java/io/bisq/core/offer/Offer.java +++ b/core/src/main/java/io/bisq/core/offer/Offer.java @@ -128,7 +128,7 @@ public class Offer implements NetworkPayload, PersistablePayload { if (offerPayload.isUseMarketBasedPrice()) { checkNotNull(priceFeedService, "priceFeed must not be null"); MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode); - if (marketPrice != null && marketPrice.isValid()) { + if (marketPrice != null && marketPrice.isRecentExternalPriceAvailable()) { double factor; double marketPriceMargin = offerPayload.getMarketPriceMargin(); if (CurrencyUtil.isCryptoCurrency(currencyCode)) { diff --git a/core/src/main/java/io/bisq/core/provider/price/MarketPrice.java b/core/src/main/java/io/bisq/core/provider/price/MarketPrice.java index 0a2a6c7461..271fdac836 100644 --- a/core/src/main/java/io/bisq/core/provider/price/MarketPrice.java +++ b/core/src/main/java/io/bisq/core/provider/price/MarketPrice.java @@ -27,15 +27,24 @@ public class MarketPrice { private final String currencyCode; private final double price; private final long timestampSec; + private final boolean isExternallyProvidedPrice; // if we get it from btc average or others. - public MarketPrice(String currencyCode, double price, long timestampSec) { + public MarketPrice(String currencyCode, double price, long timestampSec, boolean isExternallyProvidedPrice) { this.currencyCode = currencyCode; this.price = price; this.timestampSec = timestampSec; + this.isExternallyProvidedPrice = isExternallyProvidedPrice; } - public boolean isValid() { - long limit = Instant.now().getEpochSecond() - MARKET_PRICE_MAX_AGE_SEC; - return timestampSec > limit && price > 0; + public boolean isPriceAvailable() { + return price > 0; + } + + private boolean isRecentPriceAvailable() { + return timestampSec > (Instant.now().getEpochSecond() - MARKET_PRICE_MAX_AGE_SEC) && isPriceAvailable(); + } + + public boolean isRecentExternalPriceAvailable() { + return isExternallyProvidedPrice && isRecentPriceAvailable(); } } diff --git a/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java b/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java index 747acf47e2..1a89784741 100644 --- a/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java +++ b/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java @@ -25,6 +25,8 @@ import io.bisq.common.app.Log; import io.bisq.common.handlers.FaultHandler; import io.bisq.common.locale.CurrencyUtil; import io.bisq.common.locale.TradeCurrency; +import io.bisq.common.monetary.Price; +import io.bisq.common.util.MathUtils; import io.bisq.common.util.Tuple2; import io.bisq.core.app.BisqEnvironment; import io.bisq.core.provider.ProvidersRepository; @@ -135,10 +137,21 @@ public class PriceFeedService { public MarketPrice getMarketPrice(String currencyCode) { if (cache.containsKey(currencyCode)) return cache.get(currencyCode); - else + else return null; } + + public void setBisqMarketPrice(String currencyCode, Price price) { + if (!cache.containsKey(currencyCode) || !cache.get(currencyCode).isExternallyProvidedPrice()) { + cache.put(currencyCode, new MarketPrice(currencyCode, + MathUtils.scaleDownByPowerOf10(price.getValue(), CurrencyUtil.isCryptoCurrency(currencyCode) ? 8 : 4), + 0, + false)); + updateCounter.set(updateCounter.get() + 1); + } + } + /////////////////////////////////////////////////////////////////////////////////////////// // Setter /////////////////////////////////////////////////////////////////////////////////////////// @@ -198,7 +211,7 @@ public class PriceFeedService { if (cache.containsKey(currencyCode)) { try { MarketPrice marketPrice = cache.get(currencyCode); - if (marketPrice.isValid()) + if (marketPrice.isRecentExternalPriceAvailable()) priceConsumer.accept(marketPrice.getPrice()); } catch (Throwable t) { log.warn("Error at applyPriceToConsumer " + t.getMessage()); @@ -243,7 +256,7 @@ public class PriceFeedService { convertedPrice = value.getPrice() / baseCurrencyPrice.getPrice(); else convertedPrice = value.getPrice() * baseCurrencyPrice.getPrice(); - convertedPriceMap.put(e.getKey(), new MarketPrice(value.getCurrencyCode(), convertedPrice, value.getTimestampSec())); + convertedPriceMap.put(e.getKey(), new MarketPrice(value.getCurrencyCode(), convertedPrice, value.getTimestampSec(), true)); }); cache.putAll(convertedPriceMap); break; diff --git a/core/src/main/java/io/bisq/core/provider/price/PriceProvider.java b/core/src/main/java/io/bisq/core/provider/price/PriceProvider.java index 622bec803f..fe2260d689 100644 --- a/core/src/main/java/io/bisq/core/provider/price/PriceProvider.java +++ b/core/src/main/java/io/bisq/core/provider/price/PriceProvider.java @@ -58,7 +58,7 @@ public class PriceProvider extends HttpClientProvider { final double price = (double) treeMap.get("price"); // json uses double for our timestampSec long value... final long timestampSec = MathUtils.doubleToLong((double) treeMap.get("timestampSec")); - marketPriceMap.put(currencyCode, new MarketPrice(currencyCode, price, timestampSec)); + marketPriceMap.put(currencyCode, new MarketPrice(currencyCode, price, timestampSec, true)); } catch (Throwable t) { log.error(t.toString()); t.printStackTrace(); diff --git a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java index 4b9f64d122..b320a7eefc 100644 --- a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -13,6 +13,7 @@ import io.bisq.common.storage.Storage; import io.bisq.common.util.Utilities; import io.bisq.core.app.AppOptionKeys; import io.bisq.core.app.BisqEnvironment; +import io.bisq.core.provider.price.PriceFeedService; import io.bisq.network.p2p.P2PService; import io.bisq.network.p2p.storage.HashMapChangedListener; import io.bisq.network.p2p.storage.payload.ProtectedStorageEntry; @@ -32,6 +33,7 @@ public class TradeStatisticsManager implements PersistedDataHost { private final Storage statisticsStorage; private final JsonFileManager jsonFileManager; private final P2PService p2PService; + private final PriceFeedService priceFeedService; private final boolean dumpStatistics; private final ObservableSet observableTradeStatisticsSet = FXCollections.observableSet(); private final HashSet tradeStatisticsSet = new HashSet<>(); @@ -40,10 +42,12 @@ public class TradeStatisticsManager implements PersistedDataHost { @Inject public TradeStatisticsManager(Storage statisticsStorage, P2PService p2PService, + PriceFeedService priceFeedService, @Named(Storage.STORAGE_DIR) File storageDir, @Named(AppOptionKeys.DUMP_STATISTICS) boolean dumpStatistics) { this.statisticsStorage = statisticsStorage; this.p2PService = p2PService; + this.priceFeedService = priceFeedService; this.dumpStatistics = dumpStatistics; jsonFileManager = new JsonFileManager(storageDir); @@ -129,6 +133,8 @@ public class TradeStatisticsManager implements PersistedDataHost { }); + applyBisqMarketPrice(); + statisticsStorage.queueUpForSave(new TradeStatisticsList(new ArrayList<>(tradeStatisticsSet)), 2000); dump(); @@ -137,6 +143,16 @@ public class TradeStatisticsManager implements PersistedDataHost { } + private void applyBisqMarketPrice() { + List sortedList = new ArrayList<>(tradeStatisticsSet); + // sort by date so we have most recent as last entry which we use for displaying the latest price + sortedList.sort((o1, o2) -> o1.getTradeDate().compareTo(o2.getTradeDate())); + if (!sortedList.isEmpty()) { + TradeStatistics tradeStatistics = sortedList.get(sortedList.size() - 1); + priceFeedService.setBisqMarketPrice(tradeStatistics.getCurrencyCode(), tradeStatistics.getTradePrice()); + } + } + public void add(TradeStatistics tradeStatistics, boolean storeLocally) { if (!tradeStatisticsSet.contains(tradeStatistics)) { boolean itemAlreadyAdded = tradeStatisticsSet.stream().filter(e -> (e.getOfferId().equals(tradeStatistics.getOfferId()))).findAny().isPresent(); @@ -144,7 +160,11 @@ public class TradeStatisticsManager implements PersistedDataHost { tradeStatisticsSet.add(tradeStatistics); observableTradeStatisticsSet.add(tradeStatistics); + tradeStatistics.getTradePrice().getValue(); + if (storeLocally) { + applyBisqMarketPrice(); + statisticsStorage.queueUpForSave(new TradeStatisticsList(new ArrayList<>(tradeStatisticsSet)), 2000); dump(); } diff --git a/gui/src/main/java/io/bisq/gui/main/MainView.java b/gui/src/main/java/io/bisq/gui/main/MainView.java index 0c095f3f97..7284f0cd3c 100644 --- a/gui/src/main/java/io/bisq/gui/main/MainView.java +++ b/gui/src/main/java/io/bisq/gui/main/MainView.java @@ -159,7 +159,7 @@ public class MainView extends InitializableView { }); }, 1); } - + HBox leftNavPane = new HBox(marketButton, buyButton, sellButton, portfolioButtonHolder, fundsButton, disputesButtonHolder) {{ setLeftAnchor(this, 10d); setTopAnchor(this, 0d); @@ -330,7 +330,7 @@ public class MainView extends InitializableView { HBox.setMargin(btcAverageIconButton, new Insets(0, 5, 0, 0)); btcAverageIconButton.setOnAction(e -> GUIUtil.openWebPage("https://bitcoinaverage.com")); btcAverageIconButton.setVisible(model.isFiatCurrencyPriceFeedSelected.get()); - btcAverageIconButton.setManaged(model.isFiatCurrencyPriceFeedSelected.get()); + btcAverageIconButton.setManaged(btcAverageIconButton.isVisible()); btcAverageIconButton.visibleProperty().bind(model.isFiatCurrencyPriceFeedSelected); btcAverageIconButton.managedProperty().bind(model.isFiatCurrencyPriceFeedSelected); btcAverageIconButton.setOnMouseEntered(e -> { @@ -354,7 +354,7 @@ public class MainView extends InitializableView { HBox.setMargin(poloniexIconButton, new Insets(2, 3, 0, 0)); poloniexIconButton.setOnAction(e -> GUIUtil.openWebPage("https://poloniex.com")); poloniexIconButton.setVisible(model.isCryptoCurrencyPriceFeedSelected.get()); - poloniexIconButton.setManaged(model.isCryptoCurrencyPriceFeedSelected.get()); + poloniexIconButton.setManaged(poloniexIconButton.isVisible()); poloniexIconButton.visibleProperty().bind(model.isCryptoCurrencyPriceFeedSelected); poloniexIconButton.managedProperty().bind(model.isCryptoCurrencyPriceFeedSelected); poloniexIconButton.setOnMouseEntered(e -> { @@ -372,10 +372,28 @@ public class MainView extends InitializableView { Label label = new Label(Res.get("mainView.marketPrice.provider")); label.setId("nav-balance-label"); label.setPadding(new Insets(0, 5, 0, 2)); - label.visibleProperty().bind(createBooleanBinding(() -> model.isCryptoCurrencyPriceFeedSelected.get() || model.isFiatCurrencyPriceFeedSelected.get(), - model.isCryptoCurrencyPriceFeedSelected, model.isFiatCurrencyPriceFeedSelected)); + label.visibleProperty().bind(createBooleanBinding(() -> model.isPriceAvailable.get() && + (model.isCryptoCurrencyPriceFeedSelected.get() || + model.isFiatCurrencyPriceFeedSelected.get() || + !model.isExternallyProvidedPrice.get()), + model.isPriceAvailable, + model.isCryptoCurrencyPriceFeedSelected, + model.isFiatCurrencyPriceFeedSelected, + model.isExternallyProvidedPrice)); label.managedProperty().bind(label.visibleProperty()); + model.isExternallyProvidedPrice.addListener((observable, oldValue, newValue) -> { + if (newValue) { + label.setText(Res.get("mainView.marketPrice.provider")); + label.setTooltip(null); + } else { + label.setText(Res.get("mainView.marketPrice.bisqInternalPrice")); + final Tooltip tooltip = new Tooltip(Res.get("mainView.marketPrice.tooltip.bisqInternalPrice")); + tooltip.setStyle("-fx-font-size: 12"); + label.setTooltip(tooltip); + } + }); + HBox hBox2 = new HBox(); hBox2.getChildren().setAll(label, btcAverageIconButton, poloniexIconButton); diff --git a/gui/src/main/java/io/bisq/gui/main/MainViewModel.java b/gui/src/main/java/io/bisq/gui/main/MainViewModel.java index 06a9106d3a..ea87f15df6 100644 --- a/gui/src/main/java/io/bisq/gui/main/MainViewModel.java +++ b/gui/src/main/java/io/bisq/gui/main/MainViewModel.java @@ -148,6 +148,8 @@ public class MainViewModel implements ViewModel { final ObjectProperty selectedPriceFeedComboBoxItemProperty = new SimpleObjectProperty<>(); final BooleanProperty isFiatCurrencyPriceFeedSelected = new SimpleBooleanProperty(true); final BooleanProperty isCryptoCurrencyPriceFeedSelected = new SimpleBooleanProperty(false); + final BooleanProperty isExternallyProvidedPrice = new SimpleBooleanProperty(true); + final BooleanProperty isPriceAvailable = new SimpleBooleanProperty(false); final StringProperty availableBalance = new SimpleStringProperty(); final StringProperty reservedBalance = new SimpleStringProperty(); final StringProperty lockedBalance = new SimpleStringProperty(); @@ -1026,12 +1028,13 @@ public class MainViewModel implements ViewModel { String currencyCode = item.currencyCode; MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode); String priceString; - if (marketPrice != null && marketPrice.isValid()) { + if (marketPrice != null && marketPrice.isPriceAvailable()) { priceString = formatter.formatMarketPrice(marketPrice.getPrice(), currencyCode); - item.setIsPriceAvailable(true); + item.setPriceAvailable(true); + item.setExternallyProvidedPrice(marketPrice.isExternallyProvidedPrice()); } else { priceString = Res.get("shared.na"); - item.setIsPriceAvailable(false); + item.setPriceAvailable(false); } item.setDisplayString(formatter.getCurrencyPair(currencyCode) + ": " + priceString); }); @@ -1057,8 +1060,10 @@ public class MainViewModel implements ViewModel { UserThread.runAfter(() -> { if (item != null) { String code = item.currencyCode; - isFiatCurrencyPriceFeedSelected.set(CurrencyUtil.isFiatCurrency(code) && CurrencyUtil.getFiatCurrency(code).isPresent() && item.isPriceAvailable()); - isCryptoCurrencyPriceFeedSelected.set(CurrencyUtil.isCryptoCurrency(code) && CurrencyUtil.getCryptoCurrency(code).isPresent() && item.isPriceAvailable()); + isFiatCurrencyPriceFeedSelected.set(CurrencyUtil.isFiatCurrency(code) && CurrencyUtil.getFiatCurrency(code).isPresent() && item.isPriceAvailable() && item.isExternallyProvidedPrice()); + isCryptoCurrencyPriceFeedSelected.set(CurrencyUtil.isCryptoCurrency(code) && CurrencyUtil.getCryptoCurrency(code).isPresent() && item.isPriceAvailable() && item.isExternallyProvidedPrice()); + isExternallyProvidedPrice.set(item.isExternallyProvidedPrice()); + isPriceAvailable.set(item.isPriceAvailable()); } }, 100, TimeUnit.MILLISECONDS); } diff --git a/gui/src/main/java/io/bisq/gui/main/PriceFeedComboBoxItem.java b/gui/src/main/java/io/bisq/gui/main/PriceFeedComboBoxItem.java index 19d5737e48..638fadd485 100644 --- a/gui/src/main/java/io/bisq/gui/main/PriceFeedComboBoxItem.java +++ b/gui/src/main/java/io/bisq/gui/main/PriceFeedComboBoxItem.java @@ -2,15 +2,18 @@ package io.bisq.gui.main; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.Getter; +import lombok.Setter; public class PriceFeedComboBoxItem { - private static final Logger log = LoggerFactory.getLogger(PriceFeedComboBoxItem.class); - public final String currencyCode; public final StringProperty displayStringProperty = new SimpleStringProperty(); + @Setter + @Getter private boolean isPriceAvailable; + @Setter + @Getter + private boolean isExternallyProvidedPrice; public PriceFeedComboBoxItem(String currencyCode) { this.currencyCode = currencyCode; @@ -19,12 +22,4 @@ public class PriceFeedComboBoxItem { public void setDisplayString(String displayString) { this.displayStringProperty.set(displayString); } - - public void setIsPriceAvailable(boolean isPriceAvailable) { - this.isPriceAvailable = isPriceAvailable; - } - - public boolean isPriceAvailable() { - return isPriceAvailable; - } } diff --git a/gui/src/main/java/io/bisq/gui/main/market/spread/SpreadViewModel.java b/gui/src/main/java/io/bisq/gui/main/market/spread/SpreadViewModel.java index b0bbbea707..b9493ce994 100644 --- a/gui/src/main/java/io/bisq/gui/main/market/spread/SpreadViewModel.java +++ b/gui/src/main/java/io/bisq/gui/main/market/spread/SpreadViewModel.java @@ -146,7 +146,7 @@ class SpreadViewModel extends ActivatableViewModel { // TODO maybe show extra colums with spread and use real amount diff // not % based. e.g. diff between best buy and sell offer (of small amounts its a smaller gain) - if (spread != null && marketPrice != null && marketPrice.isValid()) { + if (spread != null && marketPrice != null && marketPrice.isPriceAvailable()) { double marketPriceAsDouble = marketPrice.getPrice(); final double precision = isFiatCurrency ? Math.pow(10, Fiat.SMALLEST_UNIT_EXPONENT) : diff --git a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferViewModel.java b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferViewModel.java index 38cab7e1d3..1dcde2a84f 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferViewModel.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferViewModel.java @@ -301,7 +301,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel Date: Thu, 27 Jul 2017 01:10:07 +0200 Subject: [PATCH 07/54] Fix bug with sorting offers https://github.com/bitsquare/bitsquare/issues/869#issuecomment-318004502 --- .../bisq/gui/main/offer/offerbook/OfferBookView.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookView.java b/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookView.java index 2e363a2dc0..d2146b7333 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookView.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookView.java @@ -17,7 +17,6 @@ package io.bisq.gui.main.offer.offerbook; -import io.bisq.common.UserThread; import io.bisq.common.locale.FiatCurrency; import io.bisq.common.locale.Res; import io.bisq.common.locale.TradeCurrency; @@ -220,8 +219,9 @@ public class OfferBookView extends ActivatableViewAndModel priceColumn.sortTypeProperty().bind(model.priceSortTypeProperty)); + model.priceSortTypeProperty.addListener((observable, oldValue, newValue) -> { + priceColumn.setSortType(newValue); + }); paymentMethodComboBox.setItems(model.getPaymentMethods()); paymentMethodComboBox.setOnAction(e -> model.onSetPaymentMethod(paymentMethodComboBox.getSelectionModel().getSelectedItem())); @@ -268,9 +268,11 @@ public class OfferBookView extends ActivatableViewAndModel Date: Thu, 27 Jul 2017 11:50:35 +0200 Subject: [PATCH 08/54] Add command for git checkout of bisq --- doc/build.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/build.md b/doc/build.md index ff5bf2c98d..67c6b8442a 100644 --- a/doc/build.md +++ b/doc/build.md @@ -120,6 +120,7 @@ Build bisq Now we have all prepared to build the correct bisq jar. + $ git clone https://github.com/bitsquare/bitsquare.git $ cd bisq $ mvn clean package verify -DskipTests -Dmaven.javadoc.skip=true From 53de6ee4addaa10024088c05a19582ee3d2eb14d Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 27 Jul 2017 11:51:15 +0200 Subject: [PATCH 09/54] Append bisq to check out command --- doc/build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/build.md b/doc/build.md index 67c6b8442a..8f014cc74f 100644 --- a/doc/build.md +++ b/doc/build.md @@ -120,7 +120,7 @@ Build bisq Now we have all prepared to build the correct bisq jar. - $ git clone https://github.com/bitsquare/bitsquare.git + $ git clone https://github.com/bitsquare/bitsquare.git bisq $ cd bisq $ mvn clean package verify -DskipTests -Dmaven.javadoc.skip=true From 2e8030cfb4c063dfda10268f79612a06492cff26 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 27 Jul 2017 17:59:05 +0200 Subject: [PATCH 10/54] Add default configs --- .../java/io/bisq/core/dao/blockchain/parse/RpcService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java index b3b369cfa1..8101b84b69 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java @@ -99,6 +99,9 @@ public class RpcService { nodeConfig.setProperty("node.bitcoind.rpc.password", rpcPassword); nodeConfig.setProperty("node.bitcoind.rpc.port", rpcPort); nodeConfig.setProperty("node.bitcoind.notification.block.port", rpcBlockPort); + nodeConfig.setProperty("node.bitcoind.notification.alert.port", "64647"); + nodeConfig.setProperty("node.bitcoind.notification.wallet.port", "64648"); + nodeConfig.setProperty("node.bitcoind.http.auth_scheme", "Basic"); BtcdClientImpl client = new BtcdClientImpl(httpProvider, nodeConfig); daemon = new BtcdDaemonImpl(client); log.info("Setup took {} ms", System.currentTimeMillis() - startTs); From 0016e3364063dfab1da3fb934b8a46b31595ab82 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 27 Jul 2017 18:00:18 +0200 Subject: [PATCH 11/54] Add network selector to resource EntryMap file name --- .../main/java/io/bisq/core/app/AppSetupWithP2P.java | 7 ++++--- .../{EntryMap_BTC => EntryMap_BTC_MAINNET} | Bin 2 files changed, 4 insertions(+), 3 deletions(-) rename network/src/main/resources/{EntryMap_BTC => EntryMap_BTC_MAINNET} (100%) diff --git a/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java b/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java index d983f92dad..1145517733 100644 --- a/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java +++ b/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java @@ -71,7 +71,7 @@ public class AppSetupWithP2P extends AppSetup { p2pNetWorkReady = initP2PNetwork(); p2pNetWorkReady.addListener((observable, oldValue, newValue) -> { - if (newValue) + if (newValue) onBasicServicesInitialized(); }); } @@ -150,9 +150,10 @@ public class AppSetupWithP2P extends AppSetup { protected void onBasicServicesInitialized() { log.info("onBasicServicesInitialized"); // Used to load different EntryMap files per base currency (EntryMap_BTC, EntryMap_LTC,...) - final String storageFileName = "EntryMap_" + BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode(); + final String storageFileName = "EntryMap_" + BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode() + + "_" + BisqEnvironment.getBaseCurrencyNetwork().getNetwork(); p2PService.readEntryMapFromResources(storageFileName); - + p2PService.onAllServicesInitialized(); } } diff --git a/network/src/main/resources/EntryMap_BTC b/network/src/main/resources/EntryMap_BTC_MAINNET similarity index 100% rename from network/src/main/resources/EntryMap_BTC rename to network/src/main/resources/EntryMap_BTC_MAINNET From b593f8963839aae709f5a18195d2b6f24cabc584 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 27 Jul 2017 18:00:48 +0200 Subject: [PATCH 12/54] Add BTC TESTNET genesis tx, remvoe LTC genesis txs --- .../dao/blockchain/parse/BsqChainState.java | 54 ++++--------------- .../network/p2p/seed/SeedNodesRepository.java | 2 +- 2 files changed, 10 insertions(+), 46 deletions(-) diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java index 929c965230..4e1d41825d 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java @@ -67,6 +67,7 @@ public class BsqChainState implements PersistableEnvelope { // https://blockchain.info/de/tx/ee921650ab3f978881b8fe291e0c025e0da2b7dc684003d7a03d9649dfee2e15 // BLOCK_HEIGHT 411779 // 411812 has 693 recursions + // block 376078 has 2843 recursions and caused once a StackOverflowError, a second run worked. Took 1,2 sec. // BTC MAIN NET // private static final String BTC_GENESIS_TX_ID = "b26371e2145f52c94b3d30713a9e38305bfc665fc27cd554e794b5e369d99ef5"; @@ -74,39 +75,14 @@ public class BsqChainState implements PersistableEnvelope { private static final String BTC_GENESIS_TX_ID = "4371a1579bccc672231178cc5fe9fbb9366774d3bcbf21545a82f637f4b61a06"; private static final int BTC_GENESIS_BLOCK_HEIGHT = 473000; // 2017-06-26 - // LTC MAIN NET - private static final String LTC_GENESIS_TX_ID = "44074e68c1168d67871b3e9af0e65d6d7c820b03ba15445df2c4089729985fb6"; - private static final int LTC_GENESIS_BLOCK_HEIGHT = 1220170; // 2017-06-11 - // 1186935 - - //1220127 - // block 300000 2014-05-10 - // block 350000 2015-03-30 - // block 400000 2016-02-25 - // block 450000 2017-01-25 + // TEST NET + private static final String BTC_TEST_NET_GENESIS_TX_ID = "34efdaed087084855bfbdad5f1f73b4586e64912153b0afa86bdeb5c03d4ad1c"; + private static final int BTC_TEST_NET_GENESIS_BLOCK_HEIGHT = 1155218; // REG TEST private static final String BTC_REG_TEST_GENESIS_TX_ID = "da216721fb915da499fe0400d08362f44b672096f37c74501c2f9bcaa7760656"; private static final int BTC_REG_TEST_GENESIS_BLOCK_HEIGHT = 363; - // LTC REG TEST - private static final String LTC_REG_TEST_GENESIS_TX_ID = "3551aa22fbf2e237df3d96d94f286aecc4f3109a7dcd873c5c51e30a6398172c"; - private static final int LTC_REG_TEST_GENESIS_BLOCK_HEIGHT = 105; - - - // TEST NET - // 0.5 BTC to grazcoin ms4ewGfJEv5RTnBD2moDoP5Kp1uJJwDGSX - // 0.3 BTC to alice: myjn5JVuQLN9S4QwGzY4VrD86819Zc2uhj - // 0.2BTC to bob: mx3xo655TAjC5r7ScuVEU8b6FMLomnKSeX - private static final String BTC_TEST_NET_GENESIS_TX_ID = "e360c3c77f43d53cbbf3dc8064c888a10310930a6427770ce4c8ead388edf17c"; - private static final int BTC_TEST_NET_GENESIS_BLOCK_HEIGHT = 1119668; - - private static final String LTC_TEST_NET_GENESIS_TX_ID = "not set"; - private static final int LTC_TEST_NET_GENESIS_BLOCK_HEIGHT = 1; - - - // block 376078 has 2843 recursions and caused once a StackOverflowError, a second run worked. Took 1,2 sec. - /////////////////////////////////////////////////////////////////////////////////////////// // Instance fields @@ -151,10 +127,6 @@ public class BsqChainState implements PersistableEnvelope { storage = new Storage<>(storageDir, persistenceProtoResolver); switch (BisqEnvironment.getBaseCurrencyNetwork()) { - case BTC_MAINNET: - genesisTxId = BTC_GENESIS_TX_ID; - genesisBlockHeight = BTC_GENESIS_BLOCK_HEIGHT; - break; case BTC_TESTNET: genesisTxId = BTC_TEST_NET_GENESIS_TX_ID; genesisBlockHeight = BTC_TEST_NET_GENESIS_BLOCK_HEIGHT; @@ -163,20 +135,12 @@ public class BsqChainState implements PersistableEnvelope { genesisTxId = BTC_REG_TEST_GENESIS_TX_ID; genesisBlockHeight = BTC_REG_TEST_GENESIS_BLOCK_HEIGHT; break; - - case LTC_TESTNET: - genesisTxId = LTC_TEST_NET_GENESIS_TX_ID; - genesisBlockHeight = LTC_TEST_NET_GENESIS_BLOCK_HEIGHT; - break; - case LTC_REGTEST: - genesisTxId = LTC_REG_TEST_GENESIS_TX_ID; - genesisBlockHeight = LTC_REG_TEST_GENESIS_BLOCK_HEIGHT; - break; - case LTC_MAINNET: + case BTC_MAINNET: default: - genesisTxId = LTC_GENESIS_TX_ID; - genesisBlockHeight = LTC_GENESIS_BLOCK_HEIGHT; + genesisTxId = BTC_GENESIS_TX_ID; + genesisBlockHeight = BTC_GENESIS_BLOCK_HEIGHT; break; + } lock = new FunctionalReadWriteLock(true); @@ -523,7 +487,7 @@ public class BsqChainState implements PersistableEnvelope { " unspentTxOutputsMap.size={}\n" + " compensationRequestFees.size={}\n" + " votingFees.size={}\n" + - getChainHeadHeight(), + getChainHeadHeight(), bsqBlocks.size(), txMap.size(), unspentTxOutputsMap.size(), diff --git a/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java b/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java index 6c2a078d2e..5948038d99 100644 --- a/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java +++ b/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java @@ -95,7 +95,7 @@ public class SeedNodesRepository { String networkIdAsString = String.valueOf(networkId); Set nodeAddresses = useLocalhostForP2P ? localhostSeedNodeAddresses : torSeedNodeAddresses; Set filtered = nodeAddresses.stream() - .filter(e -> String.valueOf(e.getPort()).endsWith(networkIdAsString)) + .filter(e -> String.valueOf(e.getPort()).endsWith("0" + networkIdAsString)) .filter(e -> !e.equals(nodeAddressToExclude)) .collect(Collectors.toSet()); log.debug("SeedNodeAddresses (useLocalhostForP2P={}) for networkId {}:\nnetworkId={}", From 473da57db1d7081771eb90633a2db590f497331f Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 27 Jul 2017 18:14:34 +0200 Subject: [PATCH 13/54] Fix pom files (BC not included) --- provider/pom.xml | 24 ++++++++++++++++++++++++ seednode/pom.xml | 24 ++++++++++++++++++++++++ statistics/pom.xml | 25 +++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/provider/pom.xml b/provider/pom.xml index f76a47cbe6..96b01af881 100644 --- a/provider/pom.xml +++ b/provider/pom.xml @@ -1,4 +1,21 @@ + + @@ -71,6 +88,11 @@ maven-shade-plugin 2.3 + + + org.bouncycastle:*:*:* + + false @@ -83,11 +105,13 @@ + true *:* + META-INF/maven/**/pom.properties META-INF/*.SF META-INF/*.DSA META-INF/*.RSA diff --git a/seednode/pom.xml b/seednode/pom.xml index b655386873..82af1c578b 100644 --- a/seednode/pom.xml +++ b/seednode/pom.xml @@ -1,4 +1,21 @@ + + @@ -71,6 +88,11 @@ maven-shade-plugin 2.3 + + + org.bouncycastle:*:*:* + + false @@ -83,11 +105,13 @@ + true *:* + META-INF/maven/**/pom.properties META-INF/*.SF META-INF/*.DSA META-INF/*.RSA diff --git a/statistics/pom.xml b/statistics/pom.xml index 81d614a82a..9f0fdf819c 100644 --- a/statistics/pom.xml +++ b/statistics/pom.xml @@ -1,4 +1,21 @@ + + @@ -12,6 +29,7 @@ statistics + false @@ -70,6 +88,11 @@ maven-shade-plugin 2.3 + + + org.bouncycastle:*:*:* + + false @@ -82,11 +105,13 @@ + true *:* + META-INF/maven/**/pom.properties META-INF/*.SF META-INF/*.DSA META-INF/*.RSA From 667ad0bc95a0a8e8732c7ba75bab64957457a608 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 27 Jul 2017 19:48:37 +0200 Subject: [PATCH 14/54] Update btc conf file with stricter access --- doc/rpc_regtest/bitcoin.conf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/rpc_regtest/bitcoin.conf b/doc/rpc_regtest/bitcoin.conf index 676861adaa..72c845ed97 100644 --- a/doc/rpc_regtest/bitcoin.conf +++ b/doc/rpc_regtest/bitcoin.conf @@ -4,10 +4,13 @@ regtest=1 txindex=1 #rpc +whitelist=127.0.0.1 +rpcallowip=127.0.0.1 + server=1 rpcuser=bisq rpcpassword=bisqPW # we want to test with 2 local apps so we need the output at 2 diff ports. # Unix process substitution does not work from Bitcoin Core, so we use a bash script -blocknotify=bash /Users/dev/Library/Application\ Support/Bitcoin-rt/blocknotify %s \ No newline at end of file +blocknotify=bash [PATH_TO_BTC_APP_DIR]/blocknotify %s \ No newline at end of file From 6cee58824eb0cc7f1f78dd05f91ae6859312a3ea Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 28 Jul 2017 00:40:17 +0200 Subject: [PATCH 15/54] Add new mainnet geneis tx. Make genesisTx nullable in BsqChainState. Shuffle seedNodeAddresses and remove candiates from list. Add null checks. Filter invlaid OP_RETURN data. --- .../io/bisq/core/dao/blockchain/BsqFullNode.java | 9 +++++---- .../core/dao/blockchain/p2p/RequestManager.java | 4 +++- .../core/dao/blockchain/parse/BsqChainState.java | 16 +++++++++------- .../core/dao/blockchain/parse/RpcService.java | 14 ++++++++++---- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/BsqFullNode.java b/core/src/main/java/io/bisq/core/dao/blockchain/BsqFullNode.java index d98e5972ad..dffa28414a 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/BsqFullNode.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/BsqFullNode.java @@ -89,10 +89,11 @@ public class BsqFullNode extends BsqNode { @Override protected void parseBlocksWithChainHeadHeight(int startBlockHeight, int genesisBlockHeight, String genesisTxId) { log.info("parseBlocksWithChainHeadHeight startBlockHeight={}", startBlockHeight); - bsqFullNodeExecutor.requestChainHeadHeight(chainHeadHeight -> parseBlocks(startBlockHeight, genesisBlockHeight, genesisTxId, chainHeadHeight), throwable -> { - log.error(throwable.toString()); - throwable.printStackTrace(); - }); + bsqFullNodeExecutor.requestChainHeadHeight(chainHeadHeight -> parseBlocks(startBlockHeight, genesisBlockHeight, genesisTxId, chainHeadHeight), + throwable -> { + log.error(throwable.toString()); + throwable.printStackTrace(); + }); } @Override diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/p2p/RequestManager.java b/core/src/main/java/io/bisq/core/dao/blockchain/p2p/RequestManager.java index fa5199d966..3a0ac986f1 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/p2p/RequestManager.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/p2p/RequestManager.java @@ -174,7 +174,7 @@ public class RequestManager implements MessageListener, ConnectionListener, Peer @Override public void onAwakeFromStandby() { - Log.traceCall(); + log.info("onAwakeFromStandby"); closeAllHandlers(); stopped = false; if (!networkNode.getAllConnections().isEmpty()) @@ -327,9 +327,11 @@ public class RequestManager implements MessageListener, ConnectionListener, Peer List list = seedNodeAddresses.stream() .filter(e -> peerManager.isSeedNode(e) && !peerManager.isSelf(e)) .collect(Collectors.toList()); + Collections.shuffle(list); if (!list.isEmpty()) { NodeAddress nextCandidate = list.get(0); + seedNodeAddresses.remove(nextCandidate); log.info("We try requestBlocks with {}", nextCandidate); requestBlocks(nextCandidate, startBlockHeight); } else { diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java index 4e1d41825d..38f5625cfe 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java @@ -70,10 +70,8 @@ public class BsqChainState implements PersistableEnvelope { // block 376078 has 2843 recursions and caused once a StackOverflowError, a second run worked. Took 1,2 sec. // BTC MAIN NET - // private static final String BTC_GENESIS_TX_ID = "b26371e2145f52c94b3d30713a9e38305bfc665fc27cd554e794b5e369d99ef5"; - //private static final int BTC_GENESIS_BLOCK_HEIGHT = 461718; // 2017-04-13 - private static final String BTC_GENESIS_TX_ID = "4371a1579bccc672231178cc5fe9fbb9366774d3bcbf21545a82f637f4b61a06"; - private static final int BTC_GENESIS_BLOCK_HEIGHT = 473000; // 2017-06-26 + private static final String BTC_GENESIS_TX_ID = "e5c8313c4144d219b5f6b2dacf1d36f2d43a9039bb2fcd1bd57f8352a9c9809a"; + private static final int BTC_GENESIS_BLOCK_HEIGHT = 477865; // 2017-07-28 // TEST NET private static final String BTC_TEST_NET_GENESIS_TX_ID = "34efdaed087084855bfbdad5f1f73b4586e64912153b0afa86bdeb5c03d4ad1c"; @@ -95,6 +93,7 @@ public class BsqChainState implements PersistableEnvelope { private final String genesisTxId; private final int genesisBlockHeight; private int chainHeadHeight = 0; + @Nullable private Tx genesisTx; // not impl in PB yet @@ -178,7 +177,7 @@ public class BsqChainState implements PersistableEnvelope { } private PB.BsqChainState.Builder getBsqChainStateBuilder() { - return PB.BsqChainState.newBuilder() + final PB.BsqChainState.Builder builder = PB.BsqChainState.newBuilder() .addAllBsqBlocks(bsqBlocks.stream() .map(BsqBlock::toProtoMessage) .collect(Collectors.toList())) @@ -190,8 +189,11 @@ public class BsqChainState implements PersistableEnvelope { v -> v.getValue().toProtoMessage()))) .setGenesisTxId(genesisTxId) .setGenesisBlockHeight(genesisBlockHeight) - .setChainHeadHeight(chainHeadHeight) - .setGenesisTx(genesisTx.toProtoMessage()); + .setChainHeadHeight(chainHeadHeight); + + Optional.ofNullable(genesisTx).ifPresent(e -> builder.setGenesisTx(genesisTx.toProtoMessage())); + + return builder; } public static PersistableEnvelope fromProto(PB.BsqChainState proto) { diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java index 8101b84b69..2130576141 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java @@ -126,9 +126,13 @@ public class RpcService { public void blockDetected(Block block) { if (block != null) { log.info("New block received: height={}, id={}", block.getHeight(), block.getHash()); - blockHandler.accept(block); + if (block.getHeight() != null && block.getHash() != null) { + blockHandler.accept(block); + } else { + log.warn("We received a block with block.getHeight()=null or block.getHash()=null. That should not happen."); + } } else { - log.error("We received a block with value null. That should not happen."); + log.warn("We received a block with value null. That should not happen."); } } }); @@ -175,14 +179,16 @@ public class RpcService { if (scriptPubKey.getType().equals(ScriptTypes.NULL_DATA)) { String[] chunks = scriptPubKey.getAsm().split(" "); // TODO only store BSQ OP_RETURN date filtered by type byte - if (chunks.length == 2 && chunks[0].equals("OP_RETURN")) { + + // We get on testnet a lot of "OP_RETURN 0" data, so we filter those away + if (chunks.length == 2 && chunks[0].equals("OP_RETURN") && !"0".equals(chunks[1])) { try { opReturnData = Utils.HEX.decode(chunks[1]); } catch (Throwable t) { // We get sometimes exceptions, seems BitcoinJ // cannot handle all existing OP_RETURN data, but we ignore them // anyway as our OP_RETURN data is valid in BitcoinJ - log.warn(t.toString()); + log.warn("Error at Utils.HEX.decode(chunks[1]): " + t.toString() + " / chunks[1]=" + chunks[1]); } } } From 50065bb5c90e37519af1970f7840c0f20ad7b97d Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 28 Jul 2017 01:32:44 +0200 Subject: [PATCH 16/54] Use BSQ testnet explorer for testnet --- .../main/java/io/bisq/core/user/Preferences.java | 14 ++++++++------ .../java/io/bisq/core/user/PreferencesPayload.java | 3 +-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/io/bisq/core/user/Preferences.java b/core/src/main/java/io/bisq/core/user/Preferences.java index 70112931d5..8087b57530 100644 --- a/core/src/main/java/io/bisq/core/user/Preferences.java +++ b/core/src/main/java/io/bisq/core/user/Preferences.java @@ -56,6 +56,11 @@ public final class Preferences implements PersistedDataHost { new BlockChainExplorer("Blockr.io", "https://tbtc.blockr.io/tx/info/", "https://tbtc.blockr.io/address/info/") )); + public static final BlockChainExplorer BSQ_MAIN_NET_EXPLORER = new BlockChainExplorer("BSQ", "https://explorer.bisq.io/tx.html?tx=", + "https://explorer.bisq.io/Address.html?addr="); + public static final BlockChainExplorer BSQ_TEST_NET_EXPLORER = new BlockChainExplorer("BSQ", "https://explorer.bisq.io/testnet/tx.html?tx=", + "https://explorer.bisq.io/testnet/Address.html?addr="); + private static final ArrayList LTC_MAIN_NET_EXPLORERS = new ArrayList<>(Arrays.asList( new BlockChainExplorer("CryptoID", "https://chainz.cryptoid.info/ltc/tx.dws?", "https://chainz.cryptoid.info/ltc/address.dws?"), new BlockChainExplorer("Abe Search", "http://explorer.litecoin.net/tx/", "http://explorer.litecoin.net/address/"), @@ -160,6 +165,7 @@ public final class Preferences implements PersistedDataHost { @Override public void readPersisted() { PreferencesPayload persisted = storage.initAndGetPersistedWithFileName("PreferencesPayload"); + final BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); TradeCurrency preferredTradeCurrency; if (persisted != null) { prefPayload = persisted; @@ -181,7 +187,6 @@ public final class Preferences implements PersistedDataHost { setFiatCurrencies(CurrencyUtil.getMainFiatCurrencies()); setCryptoCurrencies(CurrencyUtil.getMainCryptoCurrencies()); - final BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); switch (baseCurrencyNetwork.getCurrencyCode()) { case "BTC": setBlockChainExplorerMainNet(BTC_MAIN_NET_EXPLORERS.get(0)); @@ -211,6 +216,8 @@ public final class Preferences implements PersistedDataHost { prefPayload.setSellScreenCurrencyCode(preferredTradeCurrency.getCode()); } + prefPayload.setBsqBlockChainExplorer(baseCurrencyNetwork.isMainnet() ? BSQ_MAIN_NET_EXPLORER : BSQ_TEST_NET_EXPLORER); + // We don't want to pass Preferences to all popups where the dont show again checkbox is used, so we use // that static lookup class to avoid static access to the Preferences directly. @@ -439,11 +446,6 @@ public final class Preferences implements PersistedDataHost { persist(); } - public void setBsqBlockChainExplorer(BlockChainExplorer bsqBlockChainExplorer) { - prefPayload.setBsqBlockChainExplorer(bsqBlockChainExplorer); - persist(); - } - public void setPayFeeInBtc(boolean payFeeInBtc) { prefPayload.setPayFeeInBtc(payFeeInBtc); persist(); diff --git a/core/src/main/java/io/bisq/core/user/PreferencesPayload.java b/core/src/main/java/io/bisq/core/user/PreferencesPayload.java index 73ae914e9d..3933d51226 100644 --- a/core/src/main/java/io/bisq/core/user/PreferencesPayload.java +++ b/core/src/main/java/io/bisq/core/user/PreferencesPayload.java @@ -32,8 +32,7 @@ public final class PreferencesPayload implements PersistableEnvelope { private List cryptoCurrencies = new ArrayList<>(); private BlockChainExplorer blockChainExplorerMainNet; private BlockChainExplorer blockChainExplorerTestNet; - private BlockChainExplorer bsqBlockChainExplorer = new BlockChainExplorer("BSQ", "https://explorer.bisq.io/tx.html?tx=", - "https://explorer.bisq.io/Address.html?addr="); + private BlockChainExplorer bsqBlockChainExplorer = Preferences.BSQ_MAIN_NET_EXPLORER; @Nullable private String backupDirectory; private boolean autoSelectArbitrators = true; From db865800fb8f0e3f217520f558b17f841da0efc4 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 28 Jul 2017 13:37:26 +0200 Subject: [PATCH 17/54] Check for null and 0 values at setting market price --- .../core/provider/price/PriceFeedService.java | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java b/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java index 1a89784741..72c49e00e6 100644 --- a/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java +++ b/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java @@ -137,7 +137,7 @@ public class PriceFeedService { public MarketPrice getMarketPrice(String currencyCode) { if (cache.containsKey(currencyCode)) return cache.get(currencyCode); - else + else return null; } @@ -248,20 +248,36 @@ public class PriceFeedService { case "DASH": // apply conversion of btc based price to baseCurrencyCode based with btc/baseCurrencyCode price MarketPrice baseCurrencyPrice = priceMap.get(baseCurrencyCode); - Map convertedPriceMap = new HashMap<>(); - priceMap.entrySet().stream().forEach(e -> { - final MarketPrice value = e.getValue(); - double convertedPrice; - if (CurrencyUtil.isCryptoCurrency(e.getKey())) - convertedPrice = value.getPrice() / baseCurrencyPrice.getPrice(); - else - convertedPrice = value.getPrice() * baseCurrencyPrice.getPrice(); - convertedPriceMap.put(e.getKey(), new MarketPrice(value.getCurrencyCode(), convertedPrice, value.getTimestampSec(), true)); - }); - cache.putAll(convertedPriceMap); + if (baseCurrencyPrice != null) { + Map convertedPriceMap = new HashMap<>(); + priceMap.entrySet().stream().forEach(e -> { + final MarketPrice marketPrice = e.getValue(); + if (marketPrice != null) { + double convertedPrice; + final double marketPriceAsDouble = marketPrice.getPrice(); + final double baseCurrencyPriceAsDouble = baseCurrencyPrice.getPrice(); + if (marketPriceAsDouble > 0 && baseCurrencyPriceAsDouble > 0) { + if (CurrencyUtil.isCryptoCurrency(e.getKey())) + convertedPrice = marketPriceAsDouble / baseCurrencyPriceAsDouble; + else + convertedPrice = marketPriceAsDouble * baseCurrencyPriceAsDouble; + convertedPriceMap.put(e.getKey(), + new MarketPrice(marketPrice.getCurrencyCode(), convertedPrice, marketPrice.getTimestampSec(), true)); + } else { + log.warn("marketPriceAsDouble or baseCurrencyPriceAsDouble is 0: marketPriceAsDouble={}, " + + "baseCurrencyPriceAsDouble={}", marketPriceAsDouble, baseCurrencyPriceAsDouble); + } + } else { + log.warn("marketPrice is null"); + } + }); + cache.putAll(convertedPriceMap); + } else { + log.warn("baseCurrencyPrice is null"); + } break; default: - throw new RuntimeException("baseCurrencyCode not dfined. baseCurrencyCode=" + baseCurrencyCode); + throw new RuntimeException("baseCurrencyCode not defined. baseCurrencyCode=" + baseCurrencyCode); } resultHandler.run(); From 5b5c0e66c7a557ec87ab93cd7b167b492743f371 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 28 Jul 2017 20:04:38 +0200 Subject: [PATCH 18/54] Improve wording --- common/src/main/resources/i18n/displayStrings.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index a216e2789c..34922f833c 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -972,8 +972,8 @@ dao.wallet.send.send=Send BSQ funds dao.wallet.send.sendFunds.headline=Confirm withdrawal request dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequired transaction fee is: {2} ({3} Satoshis/byte)\nTransaction size: {4} Kb\n\nThe recipient will receive: {5}\n\nAre you sure you want to withdraw that amount? dao.wallet.bsqFee=BSQ fee payment -dao.wallet.chainHeightSynced=Synchronized with blockchain height. Current height: {0} / Best chain height: {1} -dao.wallet.chainHeightSyncing=Synchronizing with blockchain height. Current height: {0} / Best chain height: {1} +dao.wallet.chainHeightSynced=Synchronized block:{0} (latest block: {1}) +dao.wallet.chainHeightSyncing=Synchronizing block: {0} (latest block: {1}) dao.wallet.tx.type=Type dao.tx.type.enum.UNVERIFIED=Unverified BSQ transaction From 594fe882d99e7f5b451a655c53fbcc527ab7de95 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 28 Jul 2017 20:04:57 +0200 Subject: [PATCH 19/54] update dev input field data --- .../bisq/gui/main/offer/createoffer/CreateOfferViewModel.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferViewModel.java b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferViewModel.java index 1dcde2a84f..980446747f 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferViewModel.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferViewModel.java @@ -196,8 +196,8 @@ class CreateOfferViewModel extends ActivatableWithDataModel { switch (BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode()) { case "BTC": - amount.set("0.01"); - price.set("2000"); + amount.set("0.0101"); + price.set("3500"); break; case "LTC": amount.set("50"); From 2a67d39d53820dd2d30afb7f004ff1452e0b7c79 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 29 Jul 2017 00:04:31 +0200 Subject: [PATCH 20/54] Cleanup --- .../resources/i18n/displayStrings.properties | 2 +- .../io/bisq/core/btc/wallet/WalletConfig.java | 17 +++-------------- .../io/bisq/core/btc/wallet/WalletsSetup.java | 11 +++++------ .../bisq/core/trade/protocol/ProcessModel.java | 4 +--- .../pendingtrades/PendingTradesDataModel.java | 5 +---- .../network/p2p/seed/SeedNodesRepository.java | 9 ++++++--- .../network/p2p/mocks/MockMailboxPayload.java | 4 ++-- .../io/bisq/network/p2p/mocks/MockPayload.java | 4 ++-- .../network/p2p/storage/mocks/MockData.java | 4 ++-- 9 files changed, 23 insertions(+), 37 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index 34922f833c..c13672e8b0 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -972,7 +972,7 @@ dao.wallet.send.send=Send BSQ funds dao.wallet.send.sendFunds.headline=Confirm withdrawal request dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequired transaction fee is: {2} ({3} Satoshis/byte)\nTransaction size: {4} Kb\n\nThe recipient will receive: {5}\n\nAre you sure you want to withdraw that amount? dao.wallet.bsqFee=BSQ fee payment -dao.wallet.chainHeightSynced=Synchronized block:{0} (latest block: {1}) +dao.wallet.chainHeightSynced=Synchronized up to block:{0} (latest block: {1}) dao.wallet.chainHeightSyncing=Synchronizing block: {0} (latest block: {1}) dao.wallet.tx.type=Type diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletConfig.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletConfig.java index b21f80800c..15d321d6a4 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletConfig.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletConfig.java @@ -101,8 +101,6 @@ public class WalletConfig extends AbstractIdleService { private InputStream checkpoints; private boolean blockingStartup = true; - private String userAgent; - private String version; @Nullable private PeerDiscovery discovery; @@ -265,18 +263,6 @@ public class WalletConfig extends AbstractIdleService { return this; } - /** - * Sets the string that will appear in the subver field of the version message. - * - * @param userAgent A short string that should be the name of your app, e.g. "My Wallet" - * @param version A short string that contains the version number, e.g. "1.0-BETA" - */ - public WalletConfig setUserAgent(String userAgent, String version) { - this.userAgent = checkNotNull(userAgent); - this.version = checkNotNull(version); - return this; - } - /** * If a seed is set here then any existing wallet that matches the file name will be renamed to a backup name, * the chain file will be deleted, and the wallet object will be instantiated with the given seed instead of @@ -440,7 +426,9 @@ public class WalletConfig extends AbstractIdleService { vPeerGroup.addWallet(vBtcWallet); if (vBsqWallet != null) { + //noinspection ConstantConditions vChain.addWallet(vBsqWallet); + //noinspection ConstantConditions vPeerGroup.addWallet(vBsqWallet); } @@ -560,6 +548,7 @@ public class WalletConfig extends AbstractIdleService { vPeerGroup.stop(); vBtcWallet.saveToFile(vBtcWalletFile); if (vBsqWallet != null && vBsqWalletFile != null) + //noinspection ConstantConditions,ConstantConditions vBsqWallet.saveToFile(vBsqWalletFile); vStore.close(); diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java index 103f42a625..dae0895428 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java @@ -29,7 +29,10 @@ import io.bisq.common.handlers.ExceptionHandler; import io.bisq.common.handlers.ResultHandler; import io.bisq.common.storage.FileUtil; import io.bisq.core.app.BisqEnvironment; -import io.bisq.core.btc.*; +import io.bisq.core.btc.AddressEntry; +import io.bisq.core.btc.AddressEntryList; +import io.bisq.core.btc.BtcOptionKeys; +import io.bisq.core.btc.RegTestHost; import io.bisq.core.user.Preferences; import io.bisq.network.DnsLookupTor; import io.bisq.network.Socks5MultiDiscovery; @@ -74,7 +77,6 @@ public class WalletsSetup { private final RegTestHost regTestHost; private final AddressEntryList addressEntryList; - private final UserAgent userAgent; private final Preferences preferences; private final Socks5ProxyProvider socks5ProxyProvider; private final BisqEnvironment bisqEnvironment; @@ -96,7 +98,6 @@ public class WalletsSetup { @Inject public WalletsSetup(RegTestHost regTestHost, AddressEntryList addressEntryList, - UserAgent userAgent, Preferences preferences, Socks5ProxyProvider socks5ProxyProvider, BisqEnvironment bisqEnvironment, @@ -104,7 +105,6 @@ public class WalletsSetup { @Named(BtcOptionKeys.SOCKS5_DISCOVER_MODE) String socks5DiscoverModeString) { this.regTestHost = regTestHost; this.addressEntryList = addressEntryList; - this.userAgent = userAgent; this.preferences = preferences; this.socks5ProxyProvider = socks5ProxyProvider; this.bisqEnvironment = bisqEnvironment; @@ -179,8 +179,7 @@ public class WalletsSetup { configPeerNodes(socks5Proxy); walletConfig.setDownloadListener(downloadListener) - .setBlockingStartup(false) - .setUserAgent(userAgent.getName(), userAgent.getVersion()); + .setBlockingStartup(false); // If seed is non-null it means we are restoring from backup. walletConfig.setSeed(seed); diff --git a/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java b/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java index 722b3f18a6..0fe3e5e9e3 100644 --- a/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java +++ b/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java @@ -47,7 +47,6 @@ import io.bisq.network.p2p.P2PService; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.NotImplementedException; import org.bitcoinj.core.Coin; import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Transaction; @@ -237,12 +236,11 @@ public class ProcessModel implements Model, PersistablePayload { @Override public void persist() { - throw new NotImplementedException("persist is not implemented in that class"); + log.warn("persist is not implemented in that class"); } @Override public void onComplete() { - throw new NotImplementedException("persist is not implemented in that class"); } public void setTakeOfferFeeTx(Transaction takeOfferFeeTx) { diff --git a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java index c401619530..9219056907 100644 --- a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java +++ b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java @@ -37,7 +37,6 @@ import io.bisq.core.trade.SellerTrade; import io.bisq.core.trade.Trade; import io.bisq.core.trade.TradeManager; import io.bisq.core.user.Preferences; -import io.bisq.core.user.User; import io.bisq.gui.Navigation; import io.bisq.gui.common.model.ActivatableDataModel; import io.bisq.gui.main.MainView; @@ -70,7 +69,6 @@ import static com.google.common.base.Preconditions.checkNotNull; public class PendingTradesDataModel extends ActivatableDataModel { public final TradeManager tradeManager; public final BtcWalletService btcWalletService; - private final User user; private final KeyRing keyRing; public final DisputeManager disputeManager; private final P2PService p2PService; @@ -96,13 +94,12 @@ public class PendingTradesDataModel extends ActivatableDataModel { @Inject public PendingTradesDataModel(TradeManager tradeManager, BtcWalletService btcWalletService, - User user, KeyRing keyRing, DisputeManager disputeManager, + KeyRing keyRing, DisputeManager disputeManager, Preferences preferences, P2PService p2PService, Navigation navigation, WalletPasswordWindow walletPasswordWindow, NotificationCenter notificationCenter) { this.tradeManager = tradeManager; this.btcWalletService = btcWalletService; - this.user = user; this.keyRing = keyRing; this.disputeManager = disputeManager; this.preferences = preferences; diff --git a/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java b/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java index 5948038d99..a40bd7890d 100644 --- a/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java +++ b/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java @@ -19,13 +19,16 @@ public class SeedNodesRepository { @SuppressWarnings("ConstantConditions") private Set torSeedNodeAddresses = Sets.newHashSet( // BTC mainnet - new NodeAddress("3f3cu2yw7u457ztq.onion:8000"), + + //TODO dev + /*new NodeAddress("3f3cu2yw7u457ztq.onion:8000"), new NodeAddress("723ljisnynbtdohi.onion:8000"), new NodeAddress("rm7b56wbrcczpjvl.onion:8000"), - new NodeAddress("fl3mmribyxgrv63c.onion:8000"), + new NodeAddress("fl3mmribyxgrv63c.onion:8000"),*/ + //TODO dev // local dev - //new NodeAddress("joehwtpe7ijnz4df.onion:8000"), + new NodeAddress("joehwtpe7ijnz4df.onion:8000"), // BTC testnet new NodeAddress("nbphlanpgbei4okt.onion:8001"), diff --git a/network/src/test/java/io/bisq/network/p2p/mocks/MockMailboxPayload.java b/network/src/test/java/io/bisq/network/p2p/mocks/MockMailboxPayload.java index f651bfe73b..1427f775e3 100644 --- a/network/src/test/java/io/bisq/network/p2p/mocks/MockMailboxPayload.java +++ b/network/src/test/java/io/bisq/network/p2p/mocks/MockMailboxPayload.java @@ -8,7 +8,7 @@ import io.bisq.network.p2p.NodeAddress; import io.bisq.network.p2p.storage.payload.ExpirablePayload; import lombok.EqualsAndHashCode; import lombok.Value; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; +import org.apache.commons.lang3.NotImplementedException; import java.util.UUID; @@ -31,7 +31,7 @@ public final class MockMailboxPayload extends NetworkEnvelope implements Mailbox @Override public PB.NetworkEnvelope toProtoNetworkEnvelope() { - throw new NotImplementedException(); + throw new NotImplementedException("toProtoNetworkEnvelope not impl."); } diff --git a/network/src/test/java/io/bisq/network/p2p/mocks/MockPayload.java b/network/src/test/java/io/bisq/network/p2p/mocks/MockPayload.java index 9fc9e3c660..f75518d120 100644 --- a/network/src/test/java/io/bisq/network/p2p/mocks/MockPayload.java +++ b/network/src/test/java/io/bisq/network/p2p/mocks/MockPayload.java @@ -4,7 +4,7 @@ import io.bisq.common.app.Version; import io.bisq.common.proto.network.NetworkEnvelope; import io.bisq.generated.protobuffer.PB; import io.bisq.network.p2p.storage.payload.ExpirablePayload; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; +import org.apache.commons.lang3.NotImplementedException; @SuppressWarnings("ALL") public final class MockPayload extends NetworkEnvelope implements ExpirablePayload { @@ -24,7 +24,7 @@ public final class MockPayload extends NetworkEnvelope implements ExpirablePaylo @Override public PB.NetworkEnvelope toProtoNetworkEnvelope() { - throw new NotImplementedException(); + throw new NotImplementedException("toProtoNetworkEnvelope not impl."); } @Override diff --git a/network/src/test/java/io/bisq/network/p2p/storage/mocks/MockData.java b/network/src/test/java/io/bisq/network/p2p/storage/mocks/MockData.java index ffd06cd525..92baea4b25 100644 --- a/network/src/test/java/io/bisq/network/p2p/storage/mocks/MockData.java +++ b/network/src/test/java/io/bisq/network/p2p/storage/mocks/MockData.java @@ -2,7 +2,7 @@ package io.bisq.network.p2p.storage.mocks; import io.bisq.generated.protobuffer.PB; import io.bisq.network.p2p.storage.payload.StoragePayload; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; +import org.apache.commons.lang3.NotImplementedException; import javax.annotation.Nullable; import java.security.PublicKey; @@ -63,6 +63,6 @@ public class MockData implements StoragePayload { @Override public PB.ProtectedMailboxStorageEntry toProtoMessage() { - throw new NotImplementedException(); + throw new NotImplementedException("toProtoMessage not impl."); } } From 94203b9101247d025ccc8c746f65b9284aa5d53b Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 29 Jul 2017 03:09:39 +0200 Subject: [PATCH 21/54] Add BSQ dashboard/statistics --- .../resources/i18n/displayStrings.properties | 14 ++- .../dao/blockchain/parse/BsqChainState.java | 34 +++++- .../wallet/dashboard/BsqDashboardView.java | 111 +++++++++++++++++- .../java/io/bisq/gui/util/BsqFormatter.java | 26 +++- 4 files changed, 172 insertions(+), 13 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index c13672e8b0..cacf33d328 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -959,9 +959,21 @@ dao.wallet.menuItem.send=Send dao.wallet.menuItem.receive=Receive dao.wallet.menuItem.transactions=Transactions +dao.wallet.dashboard.statistics=Statistics +dao.wallet.dashboard.genesisBlockHeight=Genesis block height: +dao.wallet.dashboard.genesisTxId=Genesis transaction ID: +dao.wallet.dashboard.issuedAmount=Total issued amount: +dao.wallet.dashboard.availableAmount=Available amount: +dao.wallet.dashboard.burntAmount=Burnt amount (fees): +dao.wallet.dashboard.allTx=No. of all BSQ transactions: +dao.wallet.dashboard.utxo=No. of all unspent transaction outputs: +dao.wallet.dashboard.spentTxo=No. of all spent transaction outputs: +dao.wallet.dashboard.burntTx=No. of all fee payments transactions (burnt): +dao.wallet.dashboard.price=Price: +dao.wallet.dashboard.marketCap=Market capitalisation: + dao.wallet.receive.fundBSQWallet=Fund Bisq BSQ wallet dao.wallet.receive.fundYourWallet=Fund your BSQ wallet -dao.wallet.receive.amountOptional=Amount (optional) dao.wallet.send.sendFunds=Send funds dao.wallet.send.amount=Amount in BSQ: diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java index 38f5625cfe..d09ca5f99c 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java @@ -29,6 +29,7 @@ import io.bisq.core.dao.blockchain.exceptions.BlockNotConnectingException; import io.bisq.core.dao.blockchain.vo.*; import io.bisq.generated.protobuffer.PB; import lombok.extern.slf4j.Slf4j; +import org.bitcoinj.core.Coin; import javax.annotation.Nullable; import javax.inject.Inject; @@ -61,6 +62,7 @@ public class BsqChainState implements PersistableEnvelope { private static final int SNAPSHOT_GRID = 100; // set high to deactivate private static final int ISSUANCE_MATURITY = 144 * 30; // 30 days + public static final Coin GENESIS_TOTAL_SUPPLY = Coin.COIN.multiply(25); //mainnet // this tx has a lot of outputs @@ -78,8 +80,8 @@ public class BsqChainState implements PersistableEnvelope { private static final int BTC_TEST_NET_GENESIS_BLOCK_HEIGHT = 1155218; // REG TEST - private static final String BTC_REG_TEST_GENESIS_TX_ID = "da216721fb915da499fe0400d08362f44b672096f37c74501c2f9bcaa7760656"; - private static final int BTC_REG_TEST_GENESIS_BLOCK_HEIGHT = 363; + private static final String BTC_REG_TEST_GENESIS_TX_ID = "321a2156d6cac631d3e574caf54a5a401e51971280c14b18b5f5877026a94d47"; + private static final int BTC_REG_TEST_GENESIS_BLOCK_HEIGHT = 111; /////////////////////////////////////////////////////////////////////////////////////////// @@ -155,7 +157,7 @@ public class BsqChainState implements PersistableEnvelope { String genesisTxId, int genesisBlockHeight, int chainHeadHeight, - Tx genesisTx) { + @Nullable Tx genesisTx) { this.bsqBlocks = bsqBlocks; this.txMap = txMap; this.unspentTxOutputsMap = unspentTxOutputsMap; @@ -207,8 +209,7 @@ public class BsqChainState implements PersistableEnvelope { proto.getGenesisTxId(), proto.getGenesisBlockHeight(), proto.getChainHeadHeight(), - Tx.fromProto(proto.getGenesisTx()) - ); + proto.hasGenesisTx() ? Tx.fromProto(proto.getGenesisTx()) : null); } @@ -371,6 +372,29 @@ public class BsqChainState implements PersistableEnvelope { }); } + public Coin getTotalBurntFee() { + return lock.read(() -> Coin.valueOf(getTxMap().entrySet().stream().mapToLong(e -> e.getValue().getBurntFee()).sum())); + } + + public Set getFeeTransactions() { + return lock.read(() -> getTxMap().entrySet().stream().filter(e -> e.getValue().getBurntFee() > 0).map(Map.Entry::getValue).collect(Collectors.toSet())); + } + + public Coin getIssuedAmount() { + return lock.read(() -> BsqChainState.GENESIS_TOTAL_SUPPLY); + } + + public Set getUnspentTxOutputs() { + return lock.read(() -> getAllTxOutputs().stream().filter(e -> e.isVerified() && e.isUnspent()).collect(Collectors.toSet())); + } + + public Set getSpentTxOutputs() { + return lock.read(() -> getAllTxOutputs().stream().filter(e -> e.isVerified() && !e.isUnspent()).collect(Collectors.toSet())); + } + + public Set getTransactions() { + return lock.read(() -> getTxMap().entrySet().stream().map(Map.Entry::getValue).collect(Collectors.toSet())); + } /////////////////////////////////////////////////////////////////////////////////////////// // Package scope read access diff --git a/gui/src/main/java/io/bisq/gui/main/dao/wallet/dashboard/BsqDashboardView.java b/gui/src/main/java/io/bisq/gui/main/dao/wallet/dashboard/BsqDashboardView.java index 18cbe3ee08..71bec75dbb 100644 --- a/gui/src/main/java/io/bisq/gui/main/dao/wallet/dashboard/BsqDashboardView.java +++ b/gui/src/main/java/io/bisq/gui/main/dao/wallet/dashboard/BsqDashboardView.java @@ -17,45 +17,148 @@ package io.bisq.gui.main.dao.wallet.dashboard; +import de.jensd.fx.fontawesome.AwesomeIcon; +import io.bisq.common.locale.Res; +import io.bisq.common.monetary.Altcoin; +import io.bisq.common.monetary.Price; +import io.bisq.common.util.MathUtils; +import io.bisq.core.dao.blockchain.BsqBlockchainManager; +import io.bisq.core.dao.blockchain.BsqChainStateListener; +import io.bisq.core.dao.blockchain.parse.BsqChainState; +import io.bisq.core.provider.price.MarketPrice; +import io.bisq.core.provider.price.PriceFeedService; +import io.bisq.core.user.Preferences; import io.bisq.gui.common.view.ActivatableView; import io.bisq.gui.common.view.FxmlView; +import io.bisq.gui.components.HyperlinkWithIcon; import io.bisq.gui.main.dao.wallet.BsqBalanceUtil; +import io.bisq.gui.util.BsqFormatter; +import io.bisq.gui.util.GUIUtil; +import io.bisq.gui.util.Layout; +import javafx.beans.value.ChangeListener; +import javafx.geometry.Insets; +import javafx.scene.control.Label; import javafx.scene.control.TextField; +import javafx.scene.control.Tooltip; import javafx.scene.layout.GridPane; +import org.bitcoinj.core.Coin; import javax.inject.Inject; -@FxmlView -public class BsqDashboardView extends ActivatableView { +import static io.bisq.gui.util.FormBuilder.addLabelTextField; +import static io.bisq.gui.util.FormBuilder.addTitledGroupBg; - private TextField balanceTextField; +@FxmlView +public class BsqDashboardView extends ActivatableView implements BsqChainStateListener { private final BsqBalanceUtil bsqBalanceUtil; + private final BsqBlockchainManager bsqBlockchainManager; + private final BsqChainState bsqChainState; + private final PriceFeedService priceFeedService; + private final Preferences preferences; + private final BsqFormatter bsqFormatter; private int gridRow = 0; + private TextField issuedAmountTextField, availableAmountTextField, burntAmountTextField, allTxTextField, + burntTxTextField, spentTxTextField, + utxoTextField, priceTextField, marketCapTextField; + private ChangeListener priceChangeListener; + private HyperlinkWithIcon hyperlinkWithIcon; + /////////////////////////////////////////////////////////////////////////////////////////// // Constructor, lifecycle /////////////////////////////////////////////////////////////////////////////////////////// @Inject - private BsqDashboardView(BsqBalanceUtil bsqBalanceUtil) { + private BsqDashboardView(BsqBalanceUtil bsqBalanceUtil, BsqBlockchainManager bsqBlockchainManager, + BsqChainState bsqChainState, PriceFeedService priceFeedService, + Preferences preferences, BsqFormatter bsqFormatter) { this.bsqBalanceUtil = bsqBalanceUtil; + this.bsqBlockchainManager = bsqBlockchainManager; + this.bsqChainState = bsqChainState; + this.priceFeedService = priceFeedService; + this.preferences = preferences; + this.bsqFormatter = bsqFormatter; } @Override public void initialize() { gridRow = bsqBalanceUtil.addGroup(root, gridRow); + + addTitledGroupBg(root, ++gridRow, 12, Res.get("dao.wallet.dashboard.statistics"), Layout.GROUP_DISTANCE); + + addLabelTextField(root, gridRow, Res.get("dao.wallet.dashboard.genesisBlockHeight"), + String.valueOf(bsqChainState.getGenesisBlockHeight()), Layout.FIRST_ROW_AND_GROUP_DISTANCE); + + Label label = new Label(Res.get("dao.wallet.dashboard.genesisTxId")); + GridPane.setRowIndex(label, ++gridRow); + root.getChildren().add(label); + hyperlinkWithIcon = new HyperlinkWithIcon(bsqChainState.getGenesisTxId(), AwesomeIcon.EXTERNAL_LINK); + hyperlinkWithIcon.setOnAction(event -> GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + bsqChainState.getGenesisTxId())); + hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", bsqChainState.getGenesisTxId()))); + GridPane.setRowIndex(hyperlinkWithIcon, gridRow); + GridPane.setColumnIndex(hyperlinkWithIcon, 1); + GridPane.setMargin(hyperlinkWithIcon, new Insets(0, 0, 0, -4)); + root.getChildren().add(hyperlinkWithIcon); + + issuedAmountTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.issuedAmount")).second; + availableAmountTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.availableAmount")).second; + burntAmountTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.burntAmount")).second; + + allTxTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.allTx")).second; + utxoTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.utxo")).second; + spentTxTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.spentTxo")).second; + burntTxTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.burntTx")).second; + + priceTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.price")).second; + marketCapTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.marketCap")).second; + + priceChangeListener = (observable, oldValue, newValue) -> updatePrice(); } @Override protected void activate() { bsqBalanceUtil.activate(); + + bsqBlockchainManager.addBsqChainStateListener(this); + priceFeedService.updateCounterProperty().addListener(priceChangeListener); + + onBsqChainStateChanged(); + updatePrice(); } @Override protected void deactivate() { bsqBalanceUtil.deactivate(); + bsqBlockchainManager.removeBsqChainStateListener(this); + priceFeedService.updateCounterProperty().removeListener(priceChangeListener); + hyperlinkWithIcon.setOnAction(null); + } + + + @Override + public void onBsqChainStateChanged() { + issuedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(bsqChainState.getIssuedAmount())); + final Coin burntFee = bsqChainState.getTotalBurntFee(); + final Coin availableAmount = bsqChainState.getIssuedAmount().subtract(burntFee); + availableAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(availableAmount)); + burntAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(burntFee)); + allTxTextField.setText(String.valueOf(bsqChainState.getTransactions().size())); + utxoTextField.setText(String.valueOf(bsqChainState.getUnspentTxOutputs().size())); + spentTxTextField.setText(String.valueOf(bsqChainState.getSpentTxOutputs().size())); + burntTxTextField.setText(String.valueOf(bsqChainState.getFeeTransactions().size())); + } + + private void updatePrice() { + final Coin issuedAmount = bsqChainState.getIssuedAmount(); + final MarketPrice bsqMarketPrice = priceFeedService.getMarketPrice("BSQ"); + if (bsqMarketPrice != null) { + long bsqPrice = MathUtils.roundDoubleToLong(MathUtils.scaleUpByPowerOf10(bsqMarketPrice.getPrice(), Altcoin.SMALLEST_UNIT_EXPONENT)); + priceTextField.setText(bsqFormatter.formatPrice(Price.valueOf("BSQ", bsqPrice)) + " BSQ/BTC"); + + marketCapTextField.setText(bsqFormatter.formatMarketCap(bsqMarketPrice, priceFeedService.getMarketPrice("USD"), issuedAmount)); + } } } diff --git a/gui/src/main/java/io/bisq/gui/util/BsqFormatter.java b/gui/src/main/java/io/bisq/gui/util/BsqFormatter.java index 9034e89757..5eb322c72d 100644 --- a/gui/src/main/java/io/bisq/gui/util/BsqFormatter.java +++ b/gui/src/main/java/io/bisq/gui/util/BsqFormatter.java @@ -18,20 +18,25 @@ package io.bisq.gui.util; import io.bisq.common.app.DevEnv; +import io.bisq.common.util.MathUtils; import io.bisq.core.app.BisqEnvironment; +import io.bisq.core.provider.price.MarketPrice; +import lombok.extern.slf4j.Slf4j; import org.bitcoinj.core.Address; import org.bitcoinj.core.AddressFormatException; +import org.bitcoinj.core.Coin; import org.bitcoinj.utils.MonetaryFormat; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import javax.inject.Inject; +import java.text.DecimalFormat; +@Slf4j public class BsqFormatter extends BSFormatter { - private static final Logger log = LoggerFactory.getLogger(BsqFormatter.class); @SuppressWarnings("PointlessBooleanExpression") private static final boolean useBsqAddressFormat = true || !DevEnv.DEV_MODE; private final String prefix = "B"; + private final DecimalFormat amountFormat = new DecimalFormat("###,###,###.###"); + private final DecimalFormat marketCapFormat = new DecimalFormat("###,###,###"); @Inject private BsqFormatter() { @@ -56,6 +61,8 @@ public class BsqFormatter extends BSFormatter { default: throw new RuntimeException("baseCurrencyCode not defined. baseCurrencyCode=" + baseCurrencyCode); } + + amountFormat.setMinimumFractionDigits(3); } /** @@ -83,4 +90,17 @@ public class BsqFormatter extends BSFormatter { throw new RuntimeException(e); } } + + public String formatAmountWithGroupSeparatorAndCode(Coin amount) { + return amountFormat.format(MathUtils.scaleDownByPowerOf10(amount.value, 3)) + " BSQ"; + } + + public String formatMarketCap(MarketPrice bsqPriceMarketPrice, MarketPrice fiatMarketPrice, Coin issuedAmount) { + if (bsqPriceMarketPrice != null && fiatMarketPrice != null) { + double marketCap = bsqPriceMarketPrice.getPrice() * fiatMarketPrice.getPrice() * (MathUtils.scaleDownByPowerOf10(issuedAmount.value, 3)); + return marketCapFormat.format(MathUtils.doubleToLong(marketCap)) + " " + fiatMarketPrice.getCurrencyCode(); + } else { + return ""; + } + } } From 2dee533d37713b3d05ef252ef021b62ac0293b62 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 29 Jul 2017 03:16:57 +0200 Subject: [PATCH 22/54] Set onAction in activate method --- .../bisq/gui/main/dao/wallet/dashboard/BsqDashboardView.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gui/src/main/java/io/bisq/gui/main/dao/wallet/dashboard/BsqDashboardView.java b/gui/src/main/java/io/bisq/gui/main/dao/wallet/dashboard/BsqDashboardView.java index 71bec75dbb..1f547f3f07 100644 --- a/gui/src/main/java/io/bisq/gui/main/dao/wallet/dashboard/BsqDashboardView.java +++ b/gui/src/main/java/io/bisq/gui/main/dao/wallet/dashboard/BsqDashboardView.java @@ -95,7 +95,6 @@ public class BsqDashboardView extends ActivatableView implements GridPane.setRowIndex(label, ++gridRow); root.getChildren().add(label); hyperlinkWithIcon = new HyperlinkWithIcon(bsqChainState.getGenesisTxId(), AwesomeIcon.EXTERNAL_LINK); - hyperlinkWithIcon.setOnAction(event -> GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + bsqChainState.getGenesisTxId())); hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", bsqChainState.getGenesisTxId()))); GridPane.setRowIndex(hyperlinkWithIcon, gridRow); GridPane.setColumnIndex(hyperlinkWithIcon, 1); @@ -124,6 +123,8 @@ public class BsqDashboardView extends ActivatableView implements bsqBlockchainManager.addBsqChainStateListener(this); priceFeedService.updateCounterProperty().addListener(priceChangeListener); + hyperlinkWithIcon.setOnAction(event -> GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + bsqChainState.getGenesisTxId())); + onBsqChainStateChanged(); updatePrice(); } From b72d71691083055c37bcf2d740103770dc3b802c Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 17 Sep 2017 11:28:34 -0500 Subject: [PATCH 23/54] Merge Development --- README.md | 6 +- .../io/bisq/common/locale/CurrencyUtil.java | 2 +- .../resources/i18n/displayStrings.properties | 22 ++--- .../i18n/displayStrings_de.properties | 4 +- .../i18n/displayStrings_el.properties | 4 +- .../i18n/displayStrings_es.properties | 6 +- .../i18n/displayStrings_pt.properties | 4 +- .../i18n/displayStrings_sr.properties | 4 +- .../i18n/in_dev/displayStrings_fr.properties | 4 +- core/pom.xml | 4 +- .../java/io/bisq/core/app/SetupUtils.java | 2 +- ...MakerProcessDepositTxPublishedMessage.java | 1 - doc/build.md | 10 +- doc/install_on_unix.sh | 2 +- .../java/io/bisq/gui/main/MainViewModel.java | 2 +- .../io/bisq/gui/main/overlays/Overlay.java | 4 +- .../windows/downloadupdate/BisqInstaller.java | 12 +-- .../DisplayUpdateDownloadWindow.java | 8 +- .../windows/downloadupdate/DownloadTask.java | 8 +- .../windows/downloadupdate/VerifyTask.java | 8 +- .../gui/main/settings/about/AboutView.java | 4 +- .../util/validation/AccountNrValidator.java | 2 +- .../validation/AltCoinAddressValidator.java | 59 ++++++++++++ .../altcoins/PNCAddressValidator.java | 64 +++++++++++++ .../gui/util/validation/params/PNCParams.java | 87 ++++++++++++++++++ .../util/validation/params/WACoinsParams.java | 71 ++++++++++++++ .../AltCoinAddressValidatorTest.java | 47 ++++++++++ .../src/main/resources/EntryMap_BTC_MAINNET | Bin 5248193 -> 6430740 bytes package/linux/32bitBuild.sh | 2 +- package/win/Bisq.iss | 2 +- pom.xml | 12 +-- 31 files changed, 397 insertions(+), 70 deletions(-) create mode 100644 gui/src/main/java/io/bisq/gui/util/validation/altcoins/PNCAddressValidator.java create mode 100644 gui/src/main/java/io/bisq/gui/util/validation/params/PNCParams.java create mode 100644 gui/src/main/java/io/bisq/gui/util/validation/params/WACoinsParams.java diff --git a/README.md b/README.md index 6ab624b34f..f8fd5bd158 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Status ------ Bisq has released the beta version on the 27th of April 2016. It is operational since that time without any major incident. Please follow the current development state at our [road map]( https://bisq.io/roadmap). -For the latest version checkout our [releases page](https://github.com/bitsquare/bitsquare/releases) at Github. +For the latest version checkout our [releases page](https://github.com/bisq-network/exchange/releases) at Github. Building from source -------------------- @@ -34,9 +34,9 @@ Staying in Touch Contact the team and keep up to date using any of the following: - The [Bisq Website](https://bisq.io) - - GitHub [Issues](https://github.com/bitsquare/bitsquare/issues) + - GitHub [Issues](https://github.com/bisq-network/exchange/issues) - The [Bisq Forum]( https://forum.bisq.io) - - The [#Bisq](https://webchat.freenode.net/?channels=bitsquare) IRC channel on Freenode ([logs](https://botbot.me/freenode/bitsquare)) + - The [#bitsquare](https://webchat.freenode.net/?channels=bitsquare) IRC channel on Freenode ([logs](https://botbot.me/freenode/bitsquare)) - Our [mailing list](https://groups.google.com/forum/#!forum/bitsquare) - [@Bitsquare_](https://twitter.com/bitsquare_) on Twitter diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index 13430e941b..562d28f0b7 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -180,6 +180,7 @@ public class CurrencyUtil { result.add(new CryptoCurrency("OK", "OKCash")); result.add(new CryptoCurrency("OMNI", "Omni")); result.add(new CryptoCurrency("OPAL", "Opal")); + result.add(new CryptoCurrency("PART", "Particl")); result.add(new CryptoCurrency("PASC", "Pascal Coin", true)); result.add(new CryptoCurrency("PPC", "Peercoin")); result.add(new CryptoCurrency("PEPECASH", "Pepe Cash")); @@ -195,7 +196,6 @@ public class CurrencyUtil { result.add(new CryptoCurrency("RDD", "ReddCoin")); result.add(new CryptoCurrency("XRP", "Ripple")); result.add(new CryptoCurrency("SFSC", "Safe FileSystem Coin")); - result.add(new CryptoCurrency("SDC", "ShadowCash")); result.add(new CryptoCurrency("SHIFT", "Shift")); result.add(new CryptoCurrency("SC", "Siacoin")); result.add(new CryptoCurrency("SF", "Siafund")); diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index 93990f8182..c95f1c5ea0 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -363,7 +363,7 @@ createOffer.warnCancelOffer=You have already funded that offer.\nIf you cancel n createOffer.fixed=Fixed createOffer.percentage=Percentage createOffer.timeoutAtPublishing=A timeout occurred at publishing the offer. -createOffer.errorInfo=\n\nThe maker fee is already paid. In the worst case you have lost that fee. We are sorry about that but keep in mind it is a very small amount.\nPlease try to restart you application and check your network connection to see if you can resolve the issue. +createOffer.errorInfo=\n\nThe maker fee is already paid. In the worst case you have lost that fee. We are sorry about that but keep in mind it is a very small amount.\nPlease try to restart your application and check your network connection to see if you can resolve the issue. createOffer.tooLowSecDeposit.warning=You have set the security deposit to a lower value than the recommended default value of {0}.\n\ Are you sure you want to use a lower security deposit? createOffer.tooLowSecDeposit.makerIsSeller=It gives you less protection in case the trading peer does not follow the trade protocol. @@ -510,7 +510,7 @@ The trade ID (\"reason for payment\" text) of the transaction is: \"{2}\" portfolio.pending.step3_seller.cash=\n\nBecause the payment is done via Cash Deposit the BTC buyer has to write \"NO REFUND\" on the paper receipt, tear it in 2 parts and send you a photo by email.\n\n\ To avoid chargeback risk, only confirm if you received the email and if you are sure the paper receipt is valid.\n\ If you are not sure, {0} -portfolio.pending.step3_seller.bankCheck=\n\nPlease also verify that the senders name in your bank statement matches that one from the trade contract:\nSenders name: {0}\n\n\ +portfolio.pending.step3_seller.bankCheck=\n\nPlease also verify that the sender's name in your bank statement matches that one from the trade contract:\nSender's name: {0}\n\n\ If the name is not the same as the one displayed here, {1} portfolio.pending.step3_seller.openDispute=please don't confirm but open a dispute by entering \"alt + o\" or \"option + o\". portfolio.pending.step3_seller.confirmPaymentReceipt=Confirm payment receipt @@ -532,7 +532,7 @@ portfolio.pending.step3_seller.onPaymentReceived.part1=Have you received the {0} # suppress inspection "TrailingSpacesInProperty" portfolio.pending.step3_seller.onPaymentReceived.fiat=The trade ID (\"reason for payment\" text) of the transaction is: \"{0}\"\n\n # suppress inspection "TrailingSpacesInProperty" -portfolio.pending.step3_seller.onPaymentReceived.name=Please also verify that the senders name in your bank statement matches that one from the trade contract:\nSenders name: {0}\n\nIf the name is not the same as the one displayed here, please don't confirm but open a dispute by entering \"alt + o\" or \"option + o\".\n\n +portfolio.pending.step3_seller.onPaymentReceived.name=Please also verify that the sender's name in your bank statement matches that one from the trade contract:\nSender's name: {0}\n\nIf the name is not the same as the one displayed here, please don't confirm but open a dispute by entering \"alt + o\" or \"option + o\".\n\n portfolio.pending.step3_seller.onPaymentReceived.note=Please note, that as soon you have confirmed the receipt, the locked trade amount will be released to the BTC buyer and the security deposit will be refunded. portfolio.pending.step3_seller.onPaymentReceived.confirm.headline=Confirm that you have received the payment portfolio.pending.step3_seller.onPaymentReceived.confirm.yes=Yes, I have received the payment @@ -698,12 +698,12 @@ bisq.io web page or post in the Bisq forum at the support section.\n\n\ If you are sure that you want to open a support ticket please select the trade which causes the problem \ under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open \ the support ticket. -support.initialInfo="Please note the basic rules for the dispute process:\n\ +support.initialInfo=Please note the basic rules for the dispute process:\n\ 1. You need to respond to the arbitrators requests in between 2 days.\n\ 2. The maximum period for the dispute is 14 days.\n\ 3. You need to fulfill what the arbitrator is requesting from you to deliver evidence for your case.\n\ 4. You accepted the rules outlined in the wiki in the user agreement when you first started the application.\n\n\ -Please read more in detail about the dispute process in our wiki:\nhttps://github.com/bitsquare/bitsquare/wiki/Dispute-process +Please read more in detail about the dispute process in our wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=System message: {0} support.youOpenedTicket=You opened a request for support. support.youOpenedDispute=You opened a request for a dispute.\n\n{0} @@ -899,7 +899,7 @@ account.seed.warn.noPw.msg=You have not setup a wallet password which would prot Do you want to display the seed words? account.seed.warn.noPw.yes=Yes, and don't ask me again account.seed.enterPw=Enter password to view seed words -account.seed.restore.info=Please note that you cannot import a wallet from an old Bitsquare version (any version before 0.5.0), \ +account.seed.restore.info=Please note that you cannot import a wallet from an old bisq version (any version before 0.5.0), \ because the wallet format has changed!\n\n\ If you want to move the funds from the old version to the new Bisq application send it with a Bitcoin transaction.\n\n\ Also be aware that wallet restore is only for emergency cases and might cause problems with the internal wallet database. @@ -1123,7 +1123,7 @@ You can find the correct transaction by opening the trade details window (click and following the trading fee payment transaction output to the next transaction where you see \ the Multisig deposit transaction (the address starts with 3). That transaction ID should be \ visible in the list presented here. Once you found the correct transaction select that transaction here and continue.\n\n\ -Sorry for the inconvenience but that error case should be happen very rare and in future we will try \ +Sorry for the inconvenience but that error case should happen very rarely and in future we will try \ to find better ways to resolve it. selectDepositTxWindow.select=Select deposit transaction @@ -1212,7 +1212,7 @@ popup.warning.cannotConnectAtStartup=You still did not get connected to the {0} popup.warning.unknownProblemAtStartup=There is an unknown problem at startup.\nPlease restart and if the problem continues file a bug report. popup.warning.startupFailed.timeout=The application could not startup after 4 minutes.\n\n{0} popup.warning.startupFailed.twoInstances=Bisq is already running. You cannot run two instances of Bisq. -popup.warning.cryptoTestFailed=Seems that you use a self compiled binary and have not following the build instructions in https://github.com/bitsquare/bitsquare/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys.\n\nIf that is not the case and you use the official Bisq binary, please file a bug report to the Github page.\nError={0} +popup.warning.cryptoTestFailed=Seems that you use a self compiled binary and have not following the build instructions in https://github.com/bisq-network/exchange/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys.\n\nIf that is not the case and you use the official Bisq binary, please file a bug report to the Github page.\nError={0} popup.warning.oldOffers.msg=You have open offers which have been created with an older version of Bisq.\nPlease remove those offers as they are not valid anymore.\n\nOffers (ID): {0} popup.warning.oldOffers.buttonText=Remove outdated offer(s) popup.warning.tradePeriod.halfReached=Your trade with ID {0} has reached the half of the max. allowed trading period and is still not completed.\n\nThe trade period ends on {1}\n\nPlease check your trade state at \"Portfolio/Open trades\" for further information. @@ -1532,7 +1532,7 @@ payment.checking=Checking payment.savings=Savings payment.personalId=Personal ID: payment.clearXchange.info=Please be sure that you fulfill the requirements for the usage of Zelle (ClearXchange).\n\n\ -1. You need to have your Zelle (ClearXchange) account verified at their platform \ +1. You need to have your Zelle (ClearXchange) account verified on their platform \ before starting a trade or creating an offer.\n\n\ 2. You need to have a bank account at one of the following member banks:\n\ \t● Bank of America\n\ @@ -1545,8 +1545,8 @@ before starting a trade or creating an offer.\n\n\ 3. You need to be sure to not exceed the daily or monthly Zelle (ClearXchange) transfer limits.\n\n\ Please use Zelle (ClearXchange) only if you fulfill all those requirements, \ otherwise it is very likely that the Zelle (ClearXchange) transfer fails and the trade ends up in a dispute.\n\ -If you have not fulfilled the above requirements you would lose your security deposit in such a case.\n\n\ - Please be also aware of a higher chargeback risk when using Zelle (ClearXchange).\n\ +If you have not fulfilled the above requirements you will lose your security deposit.\n\n\ + Please also be aware of a higher chargeback risk when using Zelle (ClearXchange).\n\ For the {0} seller it is highly recommended \ to get in contact with the {1} buyer by using the provided email address or mobile number to verify that he or she \ is really the owner of the Zelle (ClearXchange) account. diff --git a/common/src/main/resources/i18n/displayStrings_de.properties b/common/src/main/resources/i18n/displayStrings_de.properties index 6b67663bee..0a272c4cb6 100644 --- a/common/src/main/resources/i18n/displayStrings_de.properties +++ b/common/src/main/resources/i18n/displayStrings_de.properties @@ -661,7 +661,7 @@ support.sellerOfferer=BTC Verkäufer/Ersteller support.buyerTaker=BTC Käufer/Abnehmer support.sellerTaker=BTC Verkäufer/Abnehmer support.backgroundInfo=Bisq ist keine Firma und betreibt keine Form von Kundendienst.\n\nGibt es Konflikte während des Handelsprozess (z.B. ein Händler befolgt nicht das Handelsprotokoll), wird die Anwendung nach der Handelsdauer eine \"Konflikt öffnen\" Schaltfläche anzeigen, um den Vermittler zu kontaktieren.\nIm Falle von Softwarefehlern oder anderen Problemen, die von der Anwendung entdeckt werden, wird eine \"Unterstützungsticket öffnen\" Schaltfläche angezeigt, um den Vermittler zu kontaktieren, der die Probleme an die Entwickler weiterleitet.\n\nIm Falle, dass ein Nutzer durch einen Fehler fest hängt, ohne dass die \"Unterstützungsticket öffnen\" Schaltfläche angezeigt wird, können Sie ein Unterstützungsticket mit einer speziellen Tastenkombination manuell öffnen.\n\nBitte nutzen Sie diese nur, wenn Sie sicher sind, dass die Software sich nicht wie erwartet verhält. Falls Sie Probleme wie man Bisq verwendet oder andere Fragen haben, überprüfen Sie bitte das FAQ auf der bisq.io Website oder posten Sie in das Bisq Forum in die Unterstützungsabteilung.\n\nFalls Sie sicher sind, dass Sie ein Unterstützungsticket öffnen wollen, wählen Sie bitte den Handel, der Probleme bereitet, unter \"Mappe/Offene Händel\" und drücken Sie die Tastenkombination \"alt + o\" oder \"option + o\" um das Unterstützungsticket zu öffnen. -support.initialInfo="Bitte beachten Sie die grundlegenen Regeln für den Konfliktprozess:\n1. Sie müssen innerhalb von 2 Tagen auf die Anfrage des Vermittlers reagieren.\n2. Die maximale Dauer des Konflikts ist 14 Tage.\n3. Sie müssen erfüllen, was der Vermittler von ihnen verlangt, um Beweise für ihren Fall zu liefern.\n4. Sie akzeptieren die Regeln, die im Wiki im Nutzungsvereinbarung umrissen wurden, als Sie das erste Mal die Anwendung gestartet haben.\n\nErfahren Sie mehr über den Konfliktprozess in unserem Wiki:\nhttps://github.com/bitsquare/bitsquare/wiki/Dispute-process +support.initialInfo=Bitte beachten Sie die grundlegenen Regeln für den Konfliktprozess:\n1. Sie müssen innerhalb von 2 Tagen auf die Anfrage des Vermittlers reagieren.\n2. Die maximale Dauer des Konflikts ist 14 Tage.\n3. Sie müssen erfüllen, was der Vermittler von ihnen verlangt, um Beweise für ihren Fall zu liefern.\n4. Sie akzeptieren die Regeln, die im Wiki im Nutzungsvereinbarung umrissen wurden, als Sie das erste Mal die Anwendung gestartet haben.\n\nErfahren Sie mehr über den Konfliktprozess in unserem Wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Systemnachricht: {0} support.youOpenedTicket=Sie haben eine Anfrage auf Unterstützung geöffnet. support.youOpenedDispute=Sie haben eine Anfrage für einen Konflikt geöffnet.\n\n{0} @@ -1069,7 +1069,7 @@ popup.warning.cannotConnectAtStartup=Sie wurden immer noch nicht mit dem {0} Net popup.warning.unknownProblemAtStartup=Es gibt ein unbekanntes Problem beim Starten.\nBitte starten Sie neu und wenn das Problem besteht, füllen Sie eine Bugmeldung aus. popup.warning.startupFailed.timeout=Die Anwendung konnte nicht nach 4 Minuten starten.\n\n{0} popup.warning.startupFailed.twoInstances=Bisq läuft bereits. Sie können nicht zwei Instanzen von Bisq laufen lassen. -popup.warning.cryptoTestFailed=Scheint so als nützen Sie selbst kompilierte Binärdateien und haben nicht den Anweisung aus https://github.com/bitsquare/bitsquare/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys gefolgt.\n\nWenn dies nicht der Fall ist und Sie die offiziellen Bisq Binärdateien genutzt haben, füllen Sie bitte eine Bugmeldung auf der Githubseite aus.\nFehler={0} +popup.warning.cryptoTestFailed=Scheint so als nützen Sie selbst kompilierte Binärdateien und haben nicht den Anweisung aus https://github.com/bisq-network/exchange/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys gefolgt.\n\nWenn dies nicht der Fall ist und Sie die offiziellen Bisq Binärdateien genutzt haben, füllen Sie bitte eine Bugmeldung auf der Githubseite aus.\nFehler={0} popup.warning.oldOffers.msg=Sie haben offene Angebote, die mit einer älteren Version von Bisq erstellt wurden.\nBitte entfernen Sie diese Angebote, da Sie nicht mehr gültig sind.\n\nAngebote (Kennung): {0} popup.warning.oldOffers.buttonText=Veraltete Angebot(e) entfernen popup.warning.tradePeriod.halfReached=Ihr Handel mit Kennung {0} hat die Hälfte der maximal erlaubten Handelsdauer erreicht und ist immer noch nicht abgeschlossen.\n\nDie Handelsdauer endet am {1}\n\nBitte überprüfen Sie den Status ihres Handels unter \"Mappe/Offene Händel\" für weitere Informationen. diff --git a/common/src/main/resources/i18n/displayStrings_el.properties b/common/src/main/resources/i18n/displayStrings_el.properties index ce1673233b..b59de0de97 100644 --- a/common/src/main/resources/i18n/displayStrings_el.properties +++ b/common/src/main/resources/i18n/displayStrings_el.properties @@ -661,7 +661,7 @@ support.sellerOfferer=Πωλητής/Maker BTC support.buyerTaker=Αγοραστής/Taker BTC support.sellerTaker=Πωλητής/Taker BTC support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.io web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. -support.initialInfo="Please note the basic rules for the dispute process:\n1. You need to respond to the arbitrators requests in between 2 days.\n2. The maximum period for the dispute is 14 days.\n3. You need to fulfill what the arbitrator is requesting from you to deliver evidence for your case.\n4. You accepted the rules outlined in the wiki in the user agreement when you first started the application.\n\nPlease read more in detail about the dispute process in our wiki:\nhttps://github.com/bitsquare/bitsquare/wiki/Dispute-process +support.initialInfo=Please note the basic rules for the dispute process:\n1. You need to respond to the arbitrators requests in between 2 days.\n2. The maximum period for the dispute is 14 days.\n3. You need to fulfill what the arbitrator is requesting from you to deliver evidence for your case.\n4. You accepted the rules outlined in the wiki in the user agreement when you first started the application.\n\nPlease read more in detail about the dispute process in our wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Μήνυμα συστήματος: {0} support.youOpenedTicket=Άνοιξες ένα αίτημα υποστήριξης. support.youOpenedDispute=Άνοιξες ένα αίτημα για επίλυση διένεξης.\n\n{0} @@ -1071,7 +1071,7 @@ popup.warning.cannotConnectAtStartup=Παραμένεις αποσυνδεδεμ popup.warning.unknownProblemAtStartup=Υπάρχει άγνωστο πρόβλημα κατά την εκκίνηση.\nΕπανεκκίνησε την εφαρμογή και αν το πρόβλημα επιμείνει, κατάθεσε μια αναφορά σφάλματος. popup.warning.startupFailed.timeout=Η εφαρμογή δεν μπόρεσε να εκκινήσει εντός 4 λεπτών.\n\n{0} popup.warning.startupFailed.twoInstances=Το Bisq λειτουργεί ήδη. Δεν μπορεί να τρέχει δύο φορές. -popup.warning.cryptoTestFailed=Seems that you use a self compiled binary and have not following the build instructions in https://github.com/bitsquare/bitsquare/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys.\n\nIf that is not the case and you use the official Bisq binary, please file a bug report to the Github page.\nError={0} +popup.warning.cryptoTestFailed=Seems that you use a self compiled binary and have not following the build instructions in https://github.com/bisq-network/exchange/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys.\n\nIf that is not the case and you use the official Bisq binary, please file a bug report to the Github page.\nError={0} popup.warning.oldOffers.msg=You have open offers which have been created with an older version of Bisq.\nPlease remove those offers as they are not valid anymore.\n\nOffers (ID): {0} popup.warning.oldOffers.buttonText=Ακύρωσε παρωχημένες προσφορές popup.warning.tradePeriod.halfReached=Η συναλλαγή σου με ταυτότητα {0} έφτασε το μισό της επιτρεπόμενης χρονικής περιόδου και δεν έχει ολοκληρωθεί ακόμα.\n\nΗ περίοδος συναλλαγής κλείνει στις {1}\n\nΈλεγξε την κατάσταση της συναλλαγής στο \"Χαρτοφυλάκιο/Ανοιχτές συναλλαγές\" για περαιτέρω πληροφορίες. diff --git a/common/src/main/resources/i18n/displayStrings_es.properties b/common/src/main/resources/i18n/displayStrings_es.properties index 1509381977..89bfd54e2e 100644 --- a/common/src/main/resources/i18n/displayStrings_es.properties +++ b/common/src/main/resources/i18n/displayStrings_es.properties @@ -336,7 +336,7 @@ createOffer.fundsBox.totalsNeeded.prompt=Será calculada de la cantidada de bitc createOffer.fundsBox.offerFee=Tasa del creador de mercado. createOffer.fundsBox.networkFee=Tasa de minado: createOffer.fundsBox.placeOfferSpinnerInfo=Publicación de oferta en curso... -createOffer.fundsBox.paymentLabel=Intercambio en Bitsquare con ID {0} +createOffer.fundsBox.paymentLabel=Intercambio en bisq con ID {0} createOffer.success.headline=Su oferta ha sido publicada. createOffer.success.info=Puede gestionar sus ofertas abiertas en \"Portafolio/Mis ofertas abiertas\". @@ -661,7 +661,7 @@ support.sellerOfferer=vendedor/creador BTC support.buyerTaker=comprador/Tomador BTC support.sellerTaker=vendedor/Tomador BTC support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.io web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. -support.initialInfo="Por favor, tenga en cuenta las reglas básicas para el proceso de disputa:\n1. Necesita responder a las peticiones de los árbitros en menos de 2 días.\n2. El periodo máximo total de la disputa es de 14 días.\n3. Necesitará completar todas las peticiones del árbitro para entregar evidencia de su caso.\n4. Acepta las reglas destacadas en la wiki de acuerdo de usuario al comenzar la aplicación.\n\nPor favor lea en más detalle acerca del proceso de disputa en nuestra wiki:\nhttps://github.com/bitsquare/bitsquare/wiki/Dispute-process +support.initialInfo=Por favor, tenga en cuenta las reglas básicas para el proceso de disputa:\n1. Necesita responder a las peticiones de los árbitros en menos de 2 días.\n2. El periodo máximo total de la disputa es de 14 días.\n3. Necesitará completar todas las peticiones del árbitro para entregar evidencia de su caso.\n4. Acepta las reglas destacadas en la wiki de acuerdo de usuario al comenzar la aplicación.\n\nPor favor lea en más detalle acerca del proceso de disputa en nuestra wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Mensaje de sistema: {0} support.youOpenedTicket=Ha abierto una solicitud de soporte. support.youOpenedDispute=Ha abierto una solicitud de disputa.\n\n{0} @@ -1069,7 +1069,7 @@ popup.warning.cannotConnectAtStartup=Aún no se ha conecta a la red {0}.\nSi usa popup.warning.unknownProblemAtStartup=Hay un problema desconocido al inicio.\nPor favor, reinicia y si el problema continua realice un reporte de error. popup.warning.startupFailed.timeout=La aplicación no se pudo iniciar después de 4 minutos.\n\n{0} popup.warning.startupFailed.twoInstances=Ya está ejecutando Bisq. No puede ejecutar dos instancias de Bisq. -popup.warning.cryptoTestFailed=Al parecer está utilizando un binario que ha compilado usted mismo siguiendo las instrucciones en https://github.com/bitsquare/bitsquare/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys.\n\nSi no es el caso y está utilizando el binario oficial de Bisq, por favor informe del error en la página de Github.\nError={0} +popup.warning.cryptoTestFailed=Al parecer está utilizando un binario que ha compilado usted mismo siguiendo las instrucciones en https://github.com/bisq-network/exchange/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys.\n\nSi no es el caso y está utilizando el binario oficial de Bisq, por favor informe del error en la página de Github.\nError={0} popup.warning.oldOffers.msg=Tiene ofertas abiertas que han sido creadas con una versión antigua de Bisq.\nPor favor, elimine esas ofertas puesto que ya no son válidas.\n\nOfertas (ID): {0} popup.warning.oldOffers.buttonText=Eliminar oferta/s anticuada/s popup.warning.tradePeriod.halfReached=Su intercambio con ID {0} ha alcanzado la mitad de el periodo máximo permitido de intercambio y aún no está completada.\n\nEl periodo de intercambio termina el {1}\n\nPor favor, compruebe su estado de intercambio en \"Portafolio/Intercambios abiertos\" para más información. diff --git a/common/src/main/resources/i18n/displayStrings_pt.properties b/common/src/main/resources/i18n/displayStrings_pt.properties index ab08efe768..854e5cef11 100644 --- a/common/src/main/resources/i18n/displayStrings_pt.properties +++ b/common/src/main/resources/i18n/displayStrings_pt.properties @@ -661,7 +661,7 @@ support.sellerOfferer=Vendedor de BTC / Ofertante support.buyerTaker=Comprador de BTC / Aceitador da oferta support.sellerTaker=Vendedor de BTC / Aceitador da oferta support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.io web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. -support.initialInfo="Favor note as regras básicas para o processo de disputa:\n1. Você precisa responder aos pedidos de árbitros em até 2 dias.\n2. O período máximo de uma disputa é 14 dias.\n3. Você precisa atender ao que o árbitro está pedindo e fornecer evidências necessárias para seu caso.\n4. Você aceitou as regras delineadas na wiki no acordo de usuário quando você iniciou o programa.\n\nMais detalhes sobre o processo de disputa se encontram na wiki:\nhttps://github.com/bitsquare/bitsquare/wiki/Dispute-process +support.initialInfo=Favor note as regras básicas para o processo de disputa:\n1. Você precisa responder aos pedidos de árbitros em até 2 dias.\n2. O período máximo de uma disputa é 14 dias.\n3. Você precisa atender ao que o árbitro está pedindo e fornecer evidências necessárias para seu caso.\n4. Você aceitou as regras delineadas na wiki no acordo de usuário quando você iniciou o programa.\n\nMais detalhes sobre o processo de disputa se encontram na wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Mensagem do sistema: {0} support.youOpenedTicket=Você abriu uma solicitação para suporte. support.youOpenedDispute=Você abriu uma solicitação para disputa.\n\n{0} @@ -1071,7 +1071,7 @@ popup.warning.cannotConnectAtStartup=Você ainda não foi conectado à rede {0}. popup.warning.unknownProblemAtStartup=Houve um problema desconhecido ao iniciar.\nFavor reiniciar e se o problema persistir abrir um bug report (informe de problema). popup.warning.startupFailed.timeout=Falha ao iniciar aplicativo após 4 minutos.\n\n{0} popup.warning.startupFailed.twoInstances=Bisq is already running. You cannot run two instances of Bisq. -popup.warning.cryptoTestFailed=Seems that you use a self compiled binary and have not following the build instructions in https://github.com/bitsquare/bitsquare/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys.\n\nIf that is not the case and you use the official Bisq binary, please file a bug report to the Github page.\nError={0} +popup.warning.cryptoTestFailed=Seems that you use a self compiled binary and have not following the build instructions in https://github.com/bisq-network/exchange/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys.\n\nIf that is not the case and you use the official Bisq binary, please file a bug report to the Github page.\nError={0} popup.warning.oldOffers.msg=You have open offers which have been created with an older version of Bisq.\nPlease remove those offers as they are not valid anymore.\n\nOffers (ID): {0} popup.warning.oldOffers.buttonText=Remover oferta(s) ultrapassada(s) popup.warning.tradePeriod.halfReached=Sua negociação com ID {0} atingiu metade do período máximo permitido e ainda não foi concluída.\n\nO período de negociação acaba em {1}\n\nFavor verifique o estado de sua negociação em \"Portfolio/Negociações em aberto\" para mais informações. diff --git a/common/src/main/resources/i18n/displayStrings_sr.properties b/common/src/main/resources/i18n/displayStrings_sr.properties index 322e20ca47..61e3200f32 100644 --- a/common/src/main/resources/i18n/displayStrings_sr.properties +++ b/common/src/main/resources/i18n/displayStrings_sr.properties @@ -661,7 +661,7 @@ support.sellerOfferer=BTC prodavac/Tvorac support.buyerTaker=BTC kupac/Uzimalac support.sellerTaker=BTC prodavac/Tvorac support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.io web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. -support.initialInfo="Imajte na umu osnovna pravila za proces rasprave:\n1. Potrebno je da odgovorite zahtevima arbitra u razmaku od 2 dana.\n2. Maksimalni period rasprave je 14 dana.\n3. Morate da ispunite što arbitar zahteva od vas radi dostavljanja dokaza za vaš slučaj.\n4. Prihvatili ste pravila navedena u wiki u korisničkom ugovoru kada ste prvi put pokrenuli aplikaciju.\n\nMolimo pročitajte detaljnije o procesu rasprave u našoj wiki:\nhttps://github.com/bitsquare/bitsquare/wiki/Dispute-process +support.initialInfo=Imajte na umu osnovna pravila za proces rasprave:\n1. Potrebno je da odgovorite zahtevima arbitra u razmaku od 2 dana.\n2. Maksimalni period rasprave je 14 dana.\n3. Morate da ispunite što arbitar zahteva od vas radi dostavljanja dokaza za vaš slučaj.\n4. Prihvatili ste pravila navedena u wiki u korisničkom ugovoru kada ste prvi put pokrenuli aplikaciju.\n\nMolimo pročitajte detaljnije o procesu rasprave u našoj wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Poruka sistema: {0} support.youOpenedTicket=Otvorili ste zahtev za podršku. support.youOpenedDispute=Otvorili ste zahtev za raspravu.\n\n{0} @@ -1069,7 +1069,7 @@ popup.warning.cannotConnectAtStartup=Još uvek niste povezani na {0} mrežu.\nAk popup.warning.unknownProblemAtStartup=Postoji nepoznat problem pri pokretanju.\nMolimo pokrenite ponovo i ako se problem nastavi podnesite prijavu o grešci. popup.warning.startupFailed.timeout=Aplikacija nije mogla da se pokrene nakon 4 minuta.\n\n{0} popup.warning.startupFailed.twoInstances=Bisq je već pokrenut. Ne možete pokrenuti dve instance Bisq. -popup.warning.cryptoTestFailed=Izgleda da koristite samo kompajlirani izvršni fajl i niste pratili build instrukcije u https://github.com/bitsquare/bitsquare/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys.\n\nAko to nije slučaj i koristite zvaničnu bisq izvršnu datoteku, molimo podnesite prijavu o grešci na Github stranici.\nGreška={0} +popup.warning.cryptoTestFailed=Izgleda da koristite samo kompajlirani izvršni fajl i niste pratili build instrukcije u https://github.com/bisq-network/exchange/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys.\n\nAko to nije slučaj i koristite zvaničnu bisq izvršnu datoteku, molimo podnesite prijavu o grešci na Github stranici.\nGreška={0} popup.warning.oldOffers.msg=Imate otvorene ponude koje su napravljene sa starijom verzijom Bisq.\nMolimo uklonite te ponude pošto nisu više važeće.\n\nPonude (ID): {0} popup.warning.oldOffers.buttonText=Ukloni zastarele ponde popup.warning.tradePeriod.halfReached=Vaša trgovina sa ID {0} je dostigla polovinu od maks. dozvoljenog perioda trgovine i još uvek nije završena.\n\nPeriod trgovine se završava {1}\n\nMolimo proverite stanje vaše trgovine u \"Portfolio/Otvorene trgovine\" za više informacija. diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties index 6d2525f8de..56ba2d2b6d 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties @@ -726,7 +726,7 @@ support.sellerOfferer=Vendeur BTC / Créateur support.buyerTaker=Acheteur BTC / Preneur support.sellerTaker=vendeur BTC / Preneur support.backgroundInfo=Bisq n'est pas une entreprise et ne gère aucun type de support client. \n\nS'il y a des conflits dans le processus commercial (par exemple, un commerçant ne suit pas le protocole commercial) L'application affichera un bouton "conflit ouvert" après la fin de la période d'échange Pour contacter l'arbitre. \nEn cas de bogues logiciels ou d'autres problèmes, qui sont détectés par l'application, Afficher un bouton "Open support ticket" pour contacter l'arbitre qui transmettra le problème Aux développeurs. \n\nDans les cas où un utilisateur a été bloqué par un bugsans afficher le bouton "Ouvrir le ticket de support" Vous pouvez ouvrir manuellement un ticket de support avec un raccourci spécial. \n\nUtilisez-le uniquement si vous êtes sûr que le logiciel ne fonctionne pas comme prévu.Si vous avez des problèmes sur l'utilisation de Bisq ou des questions, veuillez consulter les FAQ au Bisq.io ou publiez-vous dans le forum Bisq à la section support.\n\ni vous êtes sûr de vouloir ouvrir un ticket de support, sélectionnez le commerce qui cause le problème Sous "Portfolio / Open trades" et tapez la combinaison de touches "alt + o" ou "option + o" pour ouvrir Le ticket de support. -support.initialInfo="Veuillez noter les règles de base pour le processus de litige: \n1. Vous devez répondre aux demandes des arbitres en moins de 2 jours. \n2. La période maximale pour le litige est de 14 jours. \n3. Vous devez répondre aux demandes de l'arbitre et fournir des preuves appuyant votre cas. \n4. Vous avez accepté les règles décrites dans le wiki dans l'accord utilisateur lorsque vous avez commencé la demande. \n \nVeuillez lire plus en détail le processus de litige dans notre wiki: \nhttps: //github.com/bitsquare/bitsquare/wiki/Dispute-process +support.initialInfo=Veuillez noter les règles de base pour le processus de litige: \n1. Vous devez répondre aux demandes des arbitres en moins de 2 jours. \n2. La période maximale pour le litige est de 14 jours. \n3. Vous devez répondre aux demandes de l'arbitre et fournir des preuves appuyant votre cas. \n4. Vous avez accepté les règles décrites dans le wiki dans l'accord utilisateur lorsque vous avez commencé la demande. \n \nVeuillez lire plus en détail le processus de litige dans notre wiki: \nhttps: //github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Message système: {0} support.youOpenedTicket=Vous avez ouvert une demande de support. support.youOpenedDispute=Vous avez ouvert une demande de littige. \n\n {0} @@ -1086,7 +1086,7 @@ popup.warning.cannotConnectAtStartup=Vous ne vous êtes toujours pas connecté a popup.warning.unknownProblemAtStartup=Il y a un problème inconnu lors du démarrage.\nVeuillez redémarrer et si le problème continue de déposer un rapport de bogue. popup.warning.startupFailed.timeout=L'application n'a pas pu démarrer après 4 minutes. \n \n {0}\ popup.warning.startupFailed.twoInstances=Bisq est déjà en cours d'exécution. Vous ne pouvez pas exécuter deux instances de Bisq. -popup.warning.cryptoTestFailed=Il semble que vous utilisez un binaire auto compilé et que vous n'avez pas suivi les instructions de construction dans https://github.com/bitsquare/bitsquare/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic- Clés. \n \nSi ce n'est pas le cas et vous utilisez le bin bin Bisq officiel, veuillez déposer un rapport de bogue sur la page Github. \n Error = {0} +popup.warning.cryptoTestFailed=Il semble que vous utilisez un binaire auto compilé et que vous n'avez pas suivi les instructions de construction dans https://github.com/bisq-network/exchange/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic- Clés. \n \nSi ce n'est pas le cas et vous utilisez le bin bin Bisq officiel, veuillez déposer un rapport de bogue sur la page Github. \n Error = {0} popup.warning.oldOffers.msg=Vous avez des offres ouvertes qui ont été créées avec une ancienne version de Bisq. \nVeuillez supprimer les offres telles qui ne sont plus valides. \n\nOffres (ID): {0} popup.warning.oldOffers.buttonText=Supprimer les offres périmées popup.warning.tradePeriod.halfReached=Votre commerce avec ID {0} a atteint la moitié du maximum. Période de négociation autorisée et n'est toujours pas terminée. \n \nLa période d'échanges se termine sur {1} \n \nVeuillez vérifier votre état d'échange sur "Portfolio / Opérations ouverte" pour plus d'informations. diff --git a/core/pom.xml b/core/pom.xml index d5684d8371..a412505c8e 100755 --- a/core/pom.xml +++ b/core/pom.xml @@ -30,7 +30,7 @@ - com.github.bitsquare.btcd-cli4j + com.github.bisq-network.btcd-cli4j btcd-cli4j-core 29f99be @@ -62,7 +62,7 @@ - com.github.bitsquare.btcd-cli4j + com.github.bisq-network.btcd-cli4j btcd-cli4j-daemon 29f99be diff --git a/core/src/main/java/io/bisq/core/app/SetupUtils.java b/core/src/main/java/io/bisq/core/app/SetupUtils.java index 503f304d3c..b57f98d7a5 100644 --- a/core/src/main/java/io/bisq/core/app/SetupUtils.java +++ b/core/src/main/java/io/bisq/core/app/SetupUtils.java @@ -39,7 +39,7 @@ public class SetupUtils { // and if the unlimited Strength for cryptographic keys is set. // If users compile themselves they might miss that step and then would get an exception in the trade. // To avoid that we add here at startup a sample encryption and signing to see if it don't causes an exception. - // See: https://github.com/bitsquare/bitsquare/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys + // See: https://github.com/bisq-network/exchange/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys Thread checkCryptoThread = new Thread() { @Override public void run() { diff --git a/core/src/main/java/io/bisq/core/trade/protocol/tasks/maker/MakerProcessDepositTxPublishedMessage.java b/core/src/main/java/io/bisq/core/trade/protocol/tasks/maker/MakerProcessDepositTxPublishedMessage.java index f3e303ebae..15b17a3988 100644 --- a/core/src/main/java/io/bisq/core/trade/protocol/tasks/maker/MakerProcessDepositTxPublishedMessage.java +++ b/core/src/main/java/io/bisq/core/trade/protocol/tasks/maker/MakerProcessDepositTxPublishedMessage.java @@ -60,7 +60,6 @@ public class MakerProcessDepositTxPublishedMessage extends TradeTask { processModel.removeMailboxMessageAfterProcessing(trade); trade.setState(Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG); - log.error("MakerProcessDepositTxPublishedMessage, offerid={}, RESERVED_FOR_TRADE", trade.getId()); processModel.getBtcWalletService().swapTradeEntryToAvailableEntry(trade.getId(), AddressEntry.Context.RESERVED_FOR_TRADE); complete(); diff --git a/doc/build.md b/doc/build.md index f5e5f7fbe2..dca9cc5d4a 100644 --- a/doc/build.md +++ b/doc/build.md @@ -3,7 +3,7 @@ This guide will walk you through the process of building bisq from source. -> _**NOTE:** For most users, building from source is not necessary. See the [releases page](https://github.com/bitsquare/bitsquare/releases), where you'll find installers for Windows, Linux and Mac OS X._ +> _**NOTE:** For most users, building from source is not necessary. See the [releases page](https://github.com/bisq-network/exchange/releases), where you'll find installers for Windows, Linux and Mac OS X._ There is an install script (2 parts) for setup (JDK, git, maven, Bitcoinj, bisq) on Linux in that directory (install_on_unix.sh, install_on_unix_fin.sh). @@ -114,14 +114,14 @@ DAO full node ----------------- If you want to run your own BSQ transaction verification node you have to run Bitcoin Core with RPC enabled and use dedicated program arguments for the bisq node. -See https://github.com/bitsquare/bitsquare/blob/master/doc/rpc.md for more details. +See https://github.com/bisq-network/exchange/blob/master/doc/rpc.md for more details. Development mode ----------------- -Please check out our wiki for more information about [testing](https://github.com/bitsquare/bitsquare/wiki/Testing-bisq-with-Mainnet) -and how to use [regtest](https://github.com/bitsquare/bitsquare/wiki/How-to-use-bisq-with-regtest-%28advanced%29) +Please check out our wiki for more information about [testing](https://github.com/bisq-network/exchange/wiki/Testing-bisq-with-Mainnet) +and how to use [regtest](https://github.com/bisq-network/exchange/wiki/How-to-use-bisq-with-regtest-%28advanced%29) Here are example program arguments for using regtest with localhost environment (not using Tor): @@ -164,4 +164,4 @@ Here are example program arguments for using regtest and using the Tor network ( Problems? --------- -If the instructions above don't work for you, please [raise an issue](https://github.com/bitsquare/bitsquare/issues/new?labels=%5Bbuild%5D). Thanks! +If the instructions above don't work for you, please [raise an issue](https://github.com/bisq-network/exchange/issues/new?labels=%5Bbuild%5D). Thanks! diff --git a/doc/install_on_unix.sh b/doc/install_on_unix.sh index a1e35264b7..4315688486 100755 --- a/doc/install_on_unix.sh +++ b/doc/install_on_unix.sh @@ -51,7 +51,7 @@ rm -r UnlimitedJCEPolicyJDK8 jce_policy-8.zip echo "Install and resolve dependencies for bisq" cd ~ -git clone -b DAO https://github.com/bitsquare/bitsquare.git +git clone -b DAO https://github.com/bisq-network/exchange.git cd bisq mvn clean package verify -DskipTests -Dmaven.javadoc.skip=true diff --git a/gui/src/main/java/io/bisq/gui/main/MainViewModel.java b/gui/src/main/java/io/bisq/gui/main/MainViewModel.java index ea87f15df6..c5ba2cbdfb 100644 --- a/gui/src/main/java/io/bisq/gui/main/MainViewModel.java +++ b/gui/src/main/java/io/bisq/gui/main/MainViewModel.java @@ -782,7 +782,7 @@ public class MainViewModel implements ViewModel { // and if the unlimited Strength for cryptographic keys is set. // If users compile themselves they might miss that step and then would get an exception in the trade. // To avoid that we add here at startup a sample encryption and signing to see if it don't causes an exception. - // See: https://github.com/bitsquare/bitsquare/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys + // See: https://github.com/bisq-network/exchange/blob/master/doc/build.md#7-enable-unlimited-strength-for-cryptographic-keys Thread checkCryptoThread = new Thread() { @Override public void run() { diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/Overlay.java b/gui/src/main/java/io/bisq/gui/main/overlays/Overlay.java index f69df8b148..a4d976ca80 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/Overlay.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/Overlay.java @@ -335,7 +335,7 @@ public abstract class Overlay { public T useReportBugButton() { this.closeButtonText = Res.get("shared.reportBug"); - this.closeHandlerOptional = Optional.of(() -> GUIUtil.openWebPage("https://github.com/bitsquare/bitsquare/issues")); + this.closeHandlerOptional = Optional.of(() -> GUIUtil.openWebPage("https://github.com/bisq-network/exchange/issues")); //noinspection unchecked return (T) this; } @@ -734,7 +734,7 @@ public abstract class Overlay { gitHubButton.setOnAction(event -> { if (message != null) Utilities.copyToClipboard(message); - GUIUtil.openWebPage("https://github.com/bitsquare/bitsquare/issues"); + GUIUtil.openWebPage("https://github.com/bisq-network/exchange/issues"); }); Button forumButton = new Button(Res.get("popup.reportError.forum")); diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/BisqInstaller.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/BisqInstaller.java index c0428cb5f4..c4760b30a8 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/BisqInstaller.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/BisqInstaller.java @@ -1,18 +1,18 @@ /* - * This file is part of Bitsquare. + * This file is part of Bisq. * - * Bitsquare is free software: you can redistribute it and/or modify it + * 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. * - * Bitsquare is distributed in the hope that it will be useful, but WITHOUT + * 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 Bitsquare. If not, see . + * along with Bisq. If not, see . */ package io.bisq.gui.main.overlays.windows.downloadupdate; @@ -45,7 +45,7 @@ public class BisqInstaller { } public Optional download(String version) { - String partialUrl = "https://github.com/bitsquare/bitsquare/releases/download/v" + version + "/"; + String partialUrl = "https://github.com/bisq-network/exchange/releases/download/v" + version + "/"; // Get installer filename on all platforms FileDescriptor installerFileDescriptor = getInstallerDescriptor(version, partialUrl); @@ -171,7 +171,7 @@ public class BisqInstaller { public FileDescriptor getInstallerDescriptor(String version, String partialUrl) { String fileName; String prefix = "Bisq-"; - // https://github.com/bitsquare/bitsquare/releases/download/v0.5.1/Bisq-0.5.1.dmg + // https://github.com/bisq-network/exchange/releases/download/v0.5.1/Bisq-0.5.1.dmg if (Utilities.isOSX()) fileName = prefix + version + ".dmg"; else if (Utilities.isWindows()) diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DisplayUpdateDownloadWindow.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DisplayUpdateDownloadWindow.java index b847273e8b..2179dcbd6e 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DisplayUpdateDownloadWindow.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DisplayUpdateDownloadWindow.java @@ -1,18 +1,18 @@ /* - * This file is part of Bitsquare. + * This file is part of Bisq. * - * Bitsquare is free software: you can redistribute it and/or modify it + * 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. * - * Bitsquare is distributed in the hope that it will be useful, but WITHOUT + * 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 Bitsquare. If not, see . + * along with Bisq. If not, see . */ package io.bisq.gui.main.overlays.windows.downloadupdate; diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DownloadTask.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DownloadTask.java index 42fa1149bb..5402f99204 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DownloadTask.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DownloadTask.java @@ -1,18 +1,18 @@ /* - * This file is part of Bitsquare. + * This file is part of Bisq. * - * Bitsquare is free software: you can redistribute it and/or modify it + * 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. * - * Bitsquare is distributed in the hope that it will be useful, but WITHOUT + * 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 Bitsquare. If not, see . + * along with Bisq. If not, see . */ package io.bisq.gui.main.overlays.windows.downloadupdate; diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/VerifyTask.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/VerifyTask.java index c7579bb637..1584c7ea59 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/VerifyTask.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/VerifyTask.java @@ -1,18 +1,18 @@ /* - * This file is part of Bitsquare. + * This file is part of Bisq. * - * Bitsquare is free software: you can redistribute it and/or modify it + * 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. * - * Bitsquare is distributed in the hope that it will be useful, but WITHOUT + * 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 Bitsquare. If not, see . + * along with Bisq. If not, see . */ package io.bisq.gui.main.overlays.windows.downloadupdate; diff --git a/gui/src/main/java/io/bisq/gui/main/settings/about/AboutView.java b/gui/src/main/java/io/bisq/gui/main/settings/about/AboutView.java index 21fd0a0750..80bf150e7a 100644 --- a/gui/src/main/java/io/bisq/gui/main/settings/about/AboutView.java +++ b/gui/src/main/java/io/bisq/gui/main/settings/about/AboutView.java @@ -53,9 +53,9 @@ public class AboutView extends ActivatableViewAndModel { GridPane.setHalignment(label, HPos.LEFT); HyperlinkWithIcon hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.web"), "https://bisq.io"); GridPane.setColumnSpan(hyperlinkWithIcon, 2); - hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.code"), "https://github.com/bitsquare/bitsquare"); + hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.code"), "https://github.com/bisq-network/exchange"); GridPane.setColumnSpan(hyperlinkWithIcon, 2); - hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.agpl"), "https://github.com/bitsquare/bitsquare/blob/master/LICENSE"); + hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.agpl"), "https://github.com/bisq-network/exchange/blob/master/LICENSE"); GridPane.setColumnSpan(hyperlinkWithIcon, 2); titledGroupBg = addTitledGroupBg(root, ++gridRow, 3, Res.get("setting.about.support"), Layout.GROUP_DISTANCE); diff --git a/gui/src/main/java/io/bisq/gui/util/validation/AccountNrValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/AccountNrValidator.java index 472677fa07..89ebadeff6 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/AccountNrValidator.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/AccountNrValidator.java @@ -80,7 +80,7 @@ public final class AccountNrValidator extends BankValidator { if (input != null) { length = 11; // Provided by sturles: - // https://github.com/bitsquare/bitsquare/pull/707 + // https://github.com/bisq-network/exchange/pull/707 // https://no.wikipedia.org/wiki/MOD11#Implementasjoner_i_forskjellige_programmeringspr.C3.A5k // https://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits6 diff --git a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java index dd7c3dee05..ee117f5087 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java @@ -23,11 +23,15 @@ import io.bisq.core.app.BisqEnvironment; import io.bisq.gui.util.validation.altcoins.ByteballAddressValidator; import io.bisq.gui.util.validation.altcoins.NxtReedSolomonValidator; import io.bisq.gui.util.validation.altcoins.OctocoinAddressValidator; +import io.bisq.gui.util.validation.altcoins.PNCAddressValidator; import io.bisq.gui.util.validation.params.IOPParams; import io.bisq.gui.util.validation.params.OctocoinParams; +import io.bisq.gui.util.validation.params.PNCParams; import io.bisq.gui.util.validation.params.PivxParams; +import io.bisq.gui.util.validation.params.WACoinsParams; import io.bisq.gui.util.validation.params.btc.BtcMainNetParams; import lombok.extern.slf4j.Slf4j; +import org.bitcoinj.core.Base58; import org.bitcoinj.core.Address; import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.params.MainNetParams; @@ -267,6 +271,61 @@ public final class AltCoinAddressValidator extends InputValidator { } catch (NxtReedSolomonValidator.DecodeException e) { return wrongChecksum; } + case "PNC": + if (input.matches("^[P3][a-km-zA-HJ-NP-Z1-9]{25,34}$")) { + if (PNCAddressValidator.ValidateAddress(input)) { + try { + Address.fromBase58(PNCParams.get(), input); + return new ValidationResult(true); + } catch (AddressFormatException e) { + return new ValidationResult(false, getErrorMessage(e)); + } + } else { + return wrongChecksum; + } + } else { + return regexTestFailed; + } + case "ZEN": + try { + // Get the non Base58 form of the address and the bytecode of the first two bytes + byte [] byteAddress = Base58.decodeChecked(input); + int version0 = byteAddress[0] & 0xFF; + int version1 = byteAddress[1] & 0xFF; + + // We only support public ("zn" (0x20,0x89), "t1" (0x1C,0xB8)) + // and multisig ("zs" (0x20,0x96), "t3" (0x1C,0xBD)) addresses + + // Fail for private addresses + if (version0 == 0x16 && version1 == 0x9A) { + // Address starts with "zc" + return new ValidationResult(false, Res.get("validation.altcoin.zAddressesNotSupported")); + } + else if (version0 == 0x1C && (version1 == 0xB8 || version1 == 0xBD)) { + // "t1" or "t3" address + return new ValidationResult(true); + } + else if (version0 == 0x20 && (version1 == 0x89 || version1 == 0x96)) { + // "zn" or "zs" address + return new ValidationResult(true); + } + else { + // Unknown Type + return new ValidationResult(false); + } + } + catch (AddressFormatException e) { + // Unhandled Exception (probably a checksum error) + return new ValidationResult(false); + } + case "WAC": + try { + Address.fromBase58(WACoinsParams.get(), input); + } + catch (AddressFormatException e) { + return new ValidationResult(false, getErrorMessage(e)); + } + return new ValidationResult(true); default: log.debug("Validation for AltCoinAddress not implemented yet. currencyCode: " + currencyCode); return validationResult; diff --git a/gui/src/main/java/io/bisq/gui/util/validation/altcoins/PNCAddressValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/altcoins/PNCAddressValidator.java new file mode 100644 index 0000000000..d025a788ca --- /dev/null +++ b/gui/src/main/java/io/bisq/gui/util/validation/altcoins/PNCAddressValidator.java @@ -0,0 +1,64 @@ +/* + * 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 io.bisq.gui.util.validation.altcoins; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +public class PNCAddressValidator { + private final static String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + + public static boolean ValidateAddress(String addr) { + if (addr.length() < 26 || addr.length() > 35) return false; + byte[] decoded = decodeBase58(addr, 58, 25); + if (decoded == null) return false; + + byte[] hash = getSha256(decoded, 0, 21, 2); + return hash != null && Arrays.equals(Arrays.copyOfRange(hash, 0, 4), Arrays.copyOfRange(decoded, 21, 25)); + } + + private static byte[] decodeBase58(String input, int base, int len) { + byte[] output = new byte[len]; + for (int i = 0; i < input.length(); i++) { + char t = input.charAt(i); + + int p = ALPHABET.indexOf(t); + if (p == -1) return null; + for (int j = len - 1; j >= 0; j--, p /= 256) { + p += base * (output[j] & 0xFF); + output[j] = (byte) (p % 256); + } + if (p != 0) return null; + } + + return output; + } + + private static byte[] getSha256(byte[] data, int start, int len, int recursion) { + if (recursion == 0) return data; + + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(Arrays.copyOfRange(data, start, start + len)); + return getSha256(md.digest(), 0, 32, recursion - 1); + } catch (NoSuchAlgorithmException e) { + return null; + } + } +} diff --git a/gui/src/main/java/io/bisq/gui/util/validation/params/PNCParams.java b/gui/src/main/java/io/bisq/gui/util/validation/params/PNCParams.java new file mode 100644 index 0000000000..a95dbde4a0 --- /dev/null +++ b/gui/src/main/java/io/bisq/gui/util/validation/params/PNCParams.java @@ -0,0 +1,87 @@ +/* + * 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 io.bisq.gui.util.validation.params; + +import org.bitcoinj.core.*; +import org.bitcoinj.store.BlockStore; +import org.bitcoinj.store.BlockStoreException; +import org.bitcoinj.utils.MonetaryFormat; + +public class PNCParams extends NetworkParameters { + + private static PNCParams instance; + + public static synchronized PNCParams get() { + if (instance == null) { + instance = new PNCParams(); + } + return instance; + } + + public PNCParams() { + super(); + addressHeader = 55; + p2shHeader = 5; + acceptableAddressCodes = new int[] {addressHeader, p2shHeader}; + } + + @Override + public String getPaymentProtocolId() { + return PAYMENT_PROTOCOL_ID_MAINNET; + } + + @Override + public void checkDifficultyTransitions(StoredBlock storedPrev, Block next, BlockStore blockStore) throws VerificationException, BlockStoreException { + } + + @Override + public Coin getMaxMoney() { + return null; + } + + @Override + public Coin getMinNonDustOutput() { + return null; + } + + @Override + public MonetaryFormat getMonetaryFormat() { + return null; + } + + @Override + public String getUriScheme() { + return null; + } + + @Override + public boolean hasMaxMoney() { + return false; + } + + @Override + public BitcoinSerializer getSerializer(boolean parseRetain) { + return null; + } + + @Override + public int getProtocolVersionNum(ProtocolVersion version) { + return 0; + } + +} diff --git a/gui/src/main/java/io/bisq/gui/util/validation/params/WACoinsParams.java b/gui/src/main/java/io/bisq/gui/util/validation/params/WACoinsParams.java new file mode 100644 index 0000000000..4d4b1fec5c --- /dev/null +++ b/gui/src/main/java/io/bisq/gui/util/validation/params/WACoinsParams.java @@ -0,0 +1,71 @@ +package io.bisq.gui.util.validation.params; + +import org.bitcoinj.core.*; +import org.bitcoinj.store.BlockStore; +import org.bitcoinj.store.BlockStoreException; +import org.bitcoinj.utils.MonetaryFormat; + +public class WACoinsParams extends NetworkParameters { + + private static WACoinsParams instance; + + public static synchronized WACoinsParams get() { + if (instance == null) { + instance = new WACoinsParams(); + } + return instance; + } + + // We only use the properties needed for address validation + public WACoinsParams() { + super(); + addressHeader = 73; + p2shHeader = 3; + acceptableAddressCodes = new int[]{addressHeader, p2shHeader}; + } + + // default dummy implementations, not used... + @Override + public String getPaymentProtocolId() { + return PAYMENT_PROTOCOL_ID_MAINNET; + } + + @Override + public void checkDifficultyTransitions(StoredBlock storedPrev, Block next, BlockStore blockStore) throws VerificationException, BlockStoreException { + } + + @Override + public Coin getMaxMoney() { + return null; + } + + @Override + public Coin getMinNonDustOutput() { + return null; + } + + @Override + public MonetaryFormat getMonetaryFormat() { + return null; + } + + @Override + public String getUriScheme() { + return null; + } + + @Override + public boolean hasMaxMoney() { + return false; + } + + @Override + public BitcoinSerializer getSerializer(boolean parseRetain) { + return null; + } + + @Override + public int getProtocolVersionNum(ProtocolVersion version) { + return 0; + } + } diff --git a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java index 819f97303f..e844e10582 100644 --- a/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java +++ b/gui/src/test/java/io/bisq/gui/util/validation/AltCoinAddressValidatorTest.java @@ -135,4 +135,51 @@ public class AltCoinAddressValidatorTest { assertFalse(validator.validate("NXT-JM2U-U4AE-G7WF-3Np9F").isValid); assertFalse(validator.validate("NXT-2222-2222-2222-22222").isValid); } + + @Test + public void testPNC() { + AltCoinAddressValidator validator = new AltCoinAddressValidator(); + validator.setCurrencyCode("PNC"); + + assertTrue(validator.validate("3AB1qXhaU3hK5oAPQfwzN3QkM8LxAgL8vB").isValid); + assertTrue(validator.validate("PD57PGdk69yioZ6FD3zFNzVUeJhMf6Kti4").isValid); + + assertFalse(validator.validate("3AB1qXhaU3hK5oAPQfwzN3QkM8LxAgL8v").isValid); + assertFalse(validator.validate("PD57PGdk69yioZ6FD3zFNzVUeJhMf6Kti42").isValid); + assertFalse(validator.validate("PD57PGdk69yioZ6FD3zFNzVUeJhMMMKti4").isValid); + assertFalse(validator.validate("PD57PG").isValid); + } + + @Test + public void testZEN() { + AltCoinAddressValidator validator = new AltCoinAddressValidator(); + validator.setCurrencyCode("ZEN"); + + assertTrue(validator.validate("znk62Ey7ptTyHgYLaLDTEwhLF6uN1DXTBfa").isValid); + assertTrue(validator.validate("znTqzi5rTXf6KJnX5tLaC5CMGHfeWJwy1c7").isValid); + assertTrue(validator.validate("t1V9h2P9n4sYg629Xn4jVDPySJJxGmPb1HK").isValid); // Random address from ZCash blockchain + assertTrue(validator.validate("t3Ut4KUq2ZSMTPNE67pBU5LqYCi2q36KpXQ").isValid); // Random address from ZCash blockchain + + assertFalse(validator.validate("zcKffBrza1cirFY47aKvXiV411NZMscf7zUY5bD1HwvkoQvKHgpxLYUHtMCLqBAeif1VwHmMjrMAKNrdCknCVqCzRNizHUq").isValid); + assertFalse(validator.validate("AFTqzi5rTXf6KJnX5tLaC5CMGHfeWJwy1c7").isValid); + assertFalse(validator.validate("zig-zag").isValid); + assertFalse(validator.validate("0123456789").isValid); + assertFalse(validator.validate("").isValid); + } + + @Test + public void testWAC() { + AltCoinAddressValidator validator = new AltCoinAddressValidator(); + validator.setCurrencyCode("WAC"); + + assertTrue(validator.validate("WfEnB3VGrBqW7uamJMymymEwxMBYQKELKY").isValid); + assertTrue(validator.validate("WTLWtNN5iJJQyTeMfZMMrfrDvdGZrYGP5U").isValid); + assertTrue(validator.validate("WemK3MgwREsEaF4vdtYLxmMqAXp49C2LYQ").isValid); + assertTrue(validator.validate("WZggcFY5cJdAxx9unBW5CVPAH8VLTxZ6Ym").isValid); + + assertFalse(validator.validate("").isValid); + assertFalse(validator.validate("abcde").isValid); + assertFalse(validator.validate("mWvZ7nZAUzpRMFp2Bfjxz27Va47nUfB79E").isValid); + assertFalse(validator.validate("WemK3MgwREsE23fgsadtYLxmMqAX9C2LYQ").isValid); + } } diff --git a/network/src/main/resources/EntryMap_BTC_MAINNET b/network/src/main/resources/EntryMap_BTC_MAINNET index f840bd4debf67e6000e719d34f139b558185f7a3..ebbba57faaf8337abdfa6068f695f81ba8ee000c 100644 GIT binary patch delta 882646 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

B**IXS0HW94Bu+ksMjbPn|Z?Ive#qp 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

?T~qVv>BZfX zZ52u!B$Kn1Z?9kE#63t2yVvC?ChAkPMdkHOXIG!dDzEkNzMqa~_m-adrKaY8^sQWF zojL7fbN7bPn+4)o7c$SQ$>u&eyvTY~O}vc6N3A&SZC}C5&)p;azxYWnnREX&Yxewy z^A}lk2ZuD~_p5~nTb8D$H>5v0?kk{vq3_k?9En(_Vsf5_W)?fy%6|$H4x=p`?uX^a zr@1_ttGr5{Mp7zI^n2%`x;|w^1)BuDmAXXuc!N`!slKLhp7CKt{ z`>){uaZM9c-cxiPl1(BN{Y!DoF^HAomxWB4DQoQWG{c z*x9>N2zE1vJ`sC#4kDr8%}4#h&TY6X_3U4>ez(klbmt}3nv#>uo9#aCv0R*~eKF#@ z%EyDBU;FK{*}bpBCE>Elhk&ce2Tacon$wgkII2@Cd#XA{x0q4H_rJc_#rt%qVtuBe zU(^$m<-AO_k9L<^G;NDgCb5nj7q##0%M5NZE2*li8KwK-VhPhGE8@$v#5IR+kUtiW z;qF6OI{RpK+!+$*^wKqrZ#@0qzVXv(f)77zR3&TwWpBStw?pL0i*q8ME6%ArevP~` zeT`N`$SbnjkuM6Y-sO`Q*{8PT8{TgS{XPa8!ik&FI_j~VL z*ET<}@7qsRG{XR8fv*zl3%U11gD0!xHfLT^-G_X5VGdFzkS^B3hBW3CDVg1p=TJw8{ zB|oemlo1kG+2=83kQaYQplAK|M)w(x9jqaW?}8(K80cJ7jwp-J-ng;kn`iF#W9vISb32#zx$Gay-m4fAA?{+h6zOdV2xNQd zG7WL_qrvcM0Hhv~uLBq#hi0S;$UV9tKteFV8g7p;SEGWhh?|@nV4)}iq9tf;6+`Dj{A?%$X(i6eF!!`bqv9X0+QbOP&%c`Ixq6)zQM%>`k)5HW5nFvvi7>8&iBw zPVW`JvSZmX?n9i+-sX#0X<=h0w8ox{*v(Qstt@9Mf@QC7veAdL3r}B-yIbtq{QdNg zwLdCb=Fc_@j(0NK73@1nbl3Nn)fI}JlQu62JeRGh9+JDPjUU#sFAxjUCt zHhYx9bx%jeqKK2NN=FiCjdk~IN1qg4)vPpNVj3vBFKRryeCf=yQ_d)mpWF>@crr)I zJUQ;YmzTQb=b7^2Pbp-#DJLs-?KO23TsfmCn6xdc^jKNYF00g&qby4!3x~aCe~;>q z9+mTN9n~*cwEu>W?jl=i)sv5ZYzY|^bhn{(L$sibfc7$YrLlVR5K#f%MHOa#6*P{6=9J|gM|g6J~E~@z-s~pMDRZmV`;=8@+Tl< zhE<5lreQ$uF11fA{6E{F02ixBda+K27`g35nwIk;!Y^D|6yO2XWKXk|id zO!PMf`m7HOz-olffaVZ$eliQn^gK{vkN4&42FZhsI^w9jeZb&r&IjjbAX~@F9lE z*_?Rpw20<~4pu21p9)ocZ*f(}t~RsrJJ`|q`Q~;3+4);vNHR?1ZVBV%Xr!s zqg>RLu8oZjjoQ8AhuZ6N_qNVS89)8bt}vzwm;X+a&YX=m5pIF zg(e5*WwdMx^|d!>Evv{_eQ9OkuObv}Y_NTT10%-r@YIBL4hBbG}pF5Wfa>z%5~b2+!3 zTuRyg{c+x_Pw&`gcd9Rv+>n!hm!fujS?J>{gM+lXae--GF3n+GpF3{%6c=jA`c(HSpUHHt{EFX_gF2O4x4hK>fIp2|yY^!uklEl6gZK`c zM(iCKP^o~9MnxJghib^dl5|K?GVH<^Hk)f_IqbuSCGlyf5Gm&29TjqHXHF&%yZ`CK zfAe{7<}4V;)6E?YrwHl4G9|sks{`%OM#6Ztr7TgZgd1*zbrjN*=CC z*i#zfnuu#8E|h>w6YMlPG=xxI!wif11GFv&dN_oY0qlm34|+uGqdAb7AezH~&bz%w zaVj@n?(wlS(}yAC3wy_>ZW>H@@L(28#HJysXa7|_8Pn4i)APOzuHZLXDeCX+uf6bX z?4i0XS_|z@wi#`6Sh_FoJ;fqoO?;)C?=`QBt)CkU6pwtGAuXPo1w9P?zD~)Kc&U=w z=aV!YTtp4C9w!8o=o}RtDw2|<*TYcuNGWqBGczq`C-fYp!#S9*-n#>tD~M$ zRbDE$md`4w-}t1~dGc4uYgZjgd#0u*Mz5lOHu$dQ^}IzXC{y^L{%Da+Gi8ONM!guI z%*$W1s!HW7!lVVItL|+!<*w_Bd3;pjc{@iL0@5A zUpm|WyZ)qgMmD$e4%I3m@zm08?Q$Po45(0l;KIjp9g`s_fk8}1>NkWRaAZMS$Ks5X z9YYULy)hc6Gf*s%^$poG`0=Ptpz6Zs!ofy)BmKDKH>55w_{qCPw!lJIP*Y( z;pt+dLyII;l%}sKo>bP{zaXWfI@3LZ-(Tt|aNt~-XYQPr6+$;mzHC?_CwA!+wZ*V) zx||2|$mCUlt6$Y5UbdkTnMR%H@;*A;4 zYSb=!mLivLENYNn&7*GZ@ulbDMr%vE9Mel)e6ehnX32CNHJi`&rE@ONHl?@6bVqE7 zIyK9^FUwi+WtZ$FNw;HJ);7KZ_p-A2Wz99rOHnTK(glt@w8=6|-e8|1s-biFb7An~ zgjJsgS0B8S5GlYOJM>zflp4)QMCvC(YcwTu7!n*H1Ad`ev#wx<^;m`qHlsp)y8{rKS0347(7L+?vX z69~Fe?59{bcSC^hBE{algGak^ZxouY+~DQ@b#7Cr6O`7Egfho<@_Qz;n% zFBv^3vK;KMYjcj@-|QBA_uV%Oy$&zO#2ci(8yWXs^INPmj|tBwd|ZH}F+oX$g%qkD zLeORiJq}pjVE7}-m4srtv>v94aLE(k&RGGxRP|+hxN{c)! zMsE7vM(Aq)TTK+58;LJF5)W}DK6vXdT4?4I;G^pSAswdGbX~gp5RCzWgNS3GL0LqH z?h1Y{sG3GDW>hE&AYh|nCWRtIWC#$67l;C4sS|#A?D;T9Cqti$TN#UtKn@JL@FPH3 z#-yk??AEczWAhiL6nFTPh}^n<^tqIvjQQ5G z=w7+N_O|r#lD=g=_Oco=+ey*q+cR4ETpP_yVZoo*`TBO6>kNiR)tLW6T2EM~2n`^W z0puYS+cI!=$Q*P=02YDtjj6C3Y^RtOVWsa5poklY-w@}xjaW)=rYB-l-4;HPe4y+{ z9{Qo-QFVJ?*ymQB>3cc!K@J;&*4pX<VZB8rH8@h;3hTV zC^dAWz-#XTcRC9l4x2+IK4SKR^%`d76j%e?5rGOB5-wsKEbbw1C3FBtU}gdRMrHon z&i*&~{crMn{ucek&g1nQ#z;TvS@-x$hI!q!I)Sr>f}7vhoxOhWgO;&(!wf&=vb91U z8yqA>OR|RU&As6hBp6k}jTS*G)jqz|wfpS7cj&H|Wzji7`{nwJ z4ZB9sjta{7jh!U#Tx)Dwr}q3k7OoBFyY6lz-8_-s+0IXl)a)1MUPmv4AY+1V=mFXp zoZv8PVo>BxI7*E?SfJ8_Qcrbnu; z)mWjSIH6WSFpu0P7+E0_8g7h?$ZUdd1bh%~YS5~K4GWn+14}XjorcvO1(!=?s50m@ z{0;aykmU^G5V|Z64pQNb{#~4Si>cO9{DT#u?m~vc)3yT#_1rx!tuyd&^nAZly76$5 z?8*$x{KrpFPpkhT*YvEBZ*24NaZBUJWu%fj`04oj4r{(r=H}ta^uWpytSXdAsF#dT z)(toYz}XV62=Kok#{z8&8P;gn7(}r}7>fa-vC2(usog+UbhEE)&)Q&_Ncfo5Y6 zfMB5RM$*9W#NwR>TL&t#WT{ZH!x3OWguuZ;K_;MNG|(>5{o;KCF9ckf=}4hqg1`kk zh#{7f|8d>rU49I3z|j)FL<_UUcLlakh5W`ncrE*)ePhV9v;3DxOO^?GEh zWp}#FzEyNam~UdO|LD=8rv_NGrF|v zs$=P5rm{|oj_olsuFUpx+WBGMZ*5+&J|-pP)913L`J$4Gnwo67e{h3v-A!Kq)7xWu zscxZZ(ydF5AJ*T_6*!O5A=K4Y=JcS_YE)6M*KQ-PlAgcXIpfXeQnjxx0G&la3COa{J;%sqmbYiJ-f40+=2 zicFIs^+tD!kz4YzFTMC*ue^7E9Kn|ziHEonAFzvD@4eB(4^uY`??BGg1vMAp1Q_cR z{7aZ=0DQw_4cBUTxrX(k@Vk?#Fce|>hd(qTB3oz-c=g@g)#0ImJlz0TIIy5F_Xp9F zf;%*tSX6o{g0sLy8PSU-`D!KZEz;Q8HYwa9+sZ=k`t-V%a`W*w1*HtGIK;F`-%q=@ z^?5~4U)j3;JQcC6_H%6ZNZbnXOsr6$}P*$MN1jE;E+~ zE?N4@GCb?_C-2Vh%72ZDVH8PG9v`w_G|91JPF*%N#jI&7%^}p-M zcf6ume0KcRnyg0qEt%LwhK<_m*>_BI$+m-+-oG?iLpPFt^)>w6_#~})S55cZ2!^?p z`}A*Upx2wL2FSL3RFV04WSf5LtmIbesVd87u3_Fy#*?mIFj%lHo^>hjSZQjnU8$$N zWv;I3jC=EUi5xn0?HRTBU?10TcBszr>poM1PE&Iv&Fo%UuW0O6-%Eb`oYHl41`}(7yR&CuX6MzWxMwHpp5C73+$ZEG^bM? zdTe&z2(}6IodDl)7-#} z@WEpNNTs5wfQ$ejQUr|{u_;(a!f*l`BpFHrF#jpAyhD!wBMLaK7-qrmhp89*HL$zz z=9-zvao_zsBTfpf-y=cZfB070|0b=eHRn@J4P2ynh%52ITMx7hnDyMDu%Hm^SPC5Y zs6w!|quYd&6sA(tIv7|D2w4^k?ZovPd`?5GOo_|{tP_DM0fiSl6Y_M}Al=%b61+6QaNTNl_0*-@X$cs@IQsT$RO$Nzi;GjT}z;IHk!$GA{`fjUdt^E9>h4JFZ z4u@~VE&U3!sSyjy`I9B4U>bJr(i@A()~{P!zuHT>i1q;MlHu{JR8wJt?k5Z3Phe`(VP zcK_emrvEFimp40%A;sO*=DqCeL%Ho%arH4r+YUbYkocxxekbj2#rql0Y&$2N4ba%y zddO2bf;DNnV$T`Hrgulpqc(`m6na2!P9qzd>`g9|y1j+eeq!#=%RXnK8#QKEyF2BW zJr2}PE0Ad)zwOoAr16`yQtfSiuDK<1bJw~>^{)M$HbFn0OwpsqO`6JAMHZhUD9ZT47%U z7CM$mx3+K4py){Xj(FjHIEJ=L$+ymqjR=HfBOBh=-n(W^)Iir-q<6|3!Dy=YCkwrFX| zWw!;9KMME1-d?`CShvk&O3!WCO2>Y-L)a960Uk+hmlo?>`^u@@yLN7MEV{O=)$ip^-5H% zOIW+}pJho<`Eu^8;p^`H%7g7s{`)MFEZz z{}>#SFdpebNlS*{jd*tf_X&l2xV#hBa~fbzSOj6kjHE^6rh?E3XC(#pcZAj;Y6g*o zXf}=D=0?0Agu-NZL>)qhf++-xxb*T~kC{1{AYF5W$cx#^u|Z9C7pGi4?mf|P>sIyL z;4y+S^OcoEK9;-*UAVTZVu#&kP1kF3KS|e0&;5A9-{ncl79M&A2`f9S?wIbnBR~w} zUIr5mD7=RtZNeST939^cKz=k11@BO3c|lgE(69)CTZ#fJ1(kRP{~3X8zI+)Xu-|;k z8AmG0iV-8CGm*6L<Ip9t*H(Zd+trVvt2coJt~mcqu4+)mM`nWL>w6>$%q-5VJnyn<8)Ap2t3(uRL5A1lnhvqWiNvH|T~a ztTJpj|0uQAKe>uoXYAz{{bu!y`!dqn+^U3C=2`-QM+$kbs*=)dRYzS2lySJHN_n^K z;O!?PMP1F=YMgjMmt%*~3vWMrC;RbJ#@qQk3!O3Wfq&L9)q#AY_P}g+pL(HXF0Fg} z^GPqZe*E!l`KwCP$ioXBpPfdXQ0(3lTz<}TZ|KFM9nZJzobfs)zWs^Dm!{E0lhm)( zuQL!!FWtjGxzqfp&ZhP6e%{$TJ^S<>-_KurZt3dt-bLMQyfn->{d-h@^r+VS)={BN zAC93-iT9Y(u}EFZr{ZdqKzWLddqe<{w|wEi-F@<8JF{o!FWaT)4?0K$bsi_x4x&o% zX+ES2!b5ovEH*@X9E97z5DsN9mB_Y79{}_ zv==H-X>}2?%|WM5hjy6_WC;@lL6{+cF(r>^m!Z(Xo{HrLY#(IP@N(ckFlhiBOAW!a zR5uW}0A%&R`w`Y&902qHEI~Re0Dp)=;~4NlKaWxRi#q*_l>f{A{|)xP({G_pN0w5X zCMi^07$ik7SL)D&br>Cm{{1@yWoBMdsEjaLl<{y!@+#xJkWOhapHHNBJ#Fu6`Ar*0 z$POb0mLMn-5K$(6v9Xf^MG8xAz}@NCt3bH}Bs^Ff*ggO-1^Xrk!%J8kjDS_go`p!_ zqDgg&iuDO=_n@sD!v736MGepM!CfZ26mJsl9s3tUX;1c*kJph>M2WS0fI%y$(z>`Rh=7w}^D zYoU*KBpgoF`x%ROr6!q93|qLKx#-GNU274C?$A^0HQr@)4f;-BbPmstiA-3;%@k|3 zE8X<|?(_3%>&1L(+VkIz%`G;O*_&J}#M>AUvGvZhMbAstC0*}1CVH>hU*LiJ{-f*n ziUdEM`r`QwpHWDUi9*(lN z_x388)4<}rQ>*Tqb!jJm_l}#MjR^_gw;w1d@>9JPAyHu5``z|nh|j^?iIR##gMz;Y z^+yki`#T3UvVxKwmCM=vMc0#ir#z)C^#jvYdic`(MuC&j;W}j^4g;~)iaVc|+tQJ@- zU*e$4#%KYEQV6|qjYjk=1Ws;ngy^CN#6JW>FsJ~-cUd|@o7~Y9f{a3#-;ia6TqHUh z1(As@7#$N;Dz3T6n#CiCxd5Cpi1VOfYYvGe=EDD2fAdn+A!mroF-@r~IDEnKt9sQ* zCt23KsEMx)T)wvR553>?-2c!_%ac`}%S(foU#Py2H>tUfA7miCW?ft6sLx&746oh4 z7WZoZwx}+btcd>2TOZjg9N6lu(Xxtuf1KuAi>2ymkF;*N>e$V{vQ_Gf>VVQZJIA9} zdKn^zLN=Skb5$Zfh{hb?-bCd|S+IJHRa9ko$JR4y!V)3Yhn5ctpz;XLkPl*o?w-&k zoE`n}D&J=9IthyhlfO+0S?Tg}M_kN_rQn3DpYU|~=$kHnG4EG|HOF3B{6l6*1=al7 zv9?>&=2#h5F6M2$7kg)BecWb?xRqP%QZ77gPKquLlACU`Z}f)=C!<`r739ZcLD#)G zcRLo$JaVe2GAC_Ki*$(LyH8Kb_K#E^^SNiv`w1*oTGxAJ>V)Z;&&sB)H;nvrZG*Vc zDU+j#k}%?#MTR+8vMQ7QQ^wU+-cxKCg1l>Y|-7Dl@rvbs94SmphFT9rcrG zvOGItPpjzMj?OFd($kZI*Ql+xWd68oQnsVZy=VN}sg>lW?GNV}x{0Xwjmu%Xmk9)1 zTOpP?RC)d$)E_-4r{6iKUn?iIfSk7PlXG%e?>vKg-QyFY)LkqEWrVu&(|Zidv;@*s z#=PE`#tW8_J4t$QyYo^r|NKtU?O8)MT!?`n4x%uL!5*<2p)-K?0PKTm1X~<_p^-fX zToGoLSV+;>Waz806vw5OP51DC3EybghTGpih)8Urbv##*)bKAGZuaZTg+utwq1G{Y z(|_1-!4k#7NEfP2B~eYlB%!i(IjB&`xW+!rXhhTmz$63nQ5Jx09t0L2 zR5|43V*2K$0Zlpwi6BUebcZ8%h@%XpIO-;XvLVQVR|b6?YuJW+kM=$NZoHmTLr>#z zee*P_<(`JsT^7v`9?0Dj>J0rjZ$>lCQD{w>;*9r7Zxe${XDRH6%#!FC=sCBcQu+#) zGmsniX|o|yPnxzmQ#9r9z88Df?OOV5wd^^OA73e#^4h#gFE8!x?tCmh^H8O@tGjK- z*DV9T^%-{~Cu}hjG!Z&*+QL3y;i=-C zvR4kX+~%jAbR%8tXxCe;*gj^m=8E-6lTBSUi)XJCxfuRnX2q4ru&R^uxT#7RwePh3 z=Xz;NwX|g3s7ZI76+y6kkL<6M=T z^D1XZD;6f45h2~=i%z~fS$L(g>rnwQvOl;Xy+H|)(X*`wPmUIJulVtg*g56kDX&N*5%YQ9oj1uvt7M&CyK4l z4UHVO;r>0UKYCQ$-#RLJ$zk@ptJ3)XQ=?MaqFC&vb4`=9q~@e%;iVqqyK|QOskZ91 zQ(m_yF23zuUSW|0PPQUAnq9D!(YNeA}PfXq$FHe5kNs^Xo84Fqas+9 zs6`l@b3pxt2nTBv4muG;C*s0JrAmF_E$z~kL2ySWuCG5Xh?AR>YF|G8UqUF(?YW&r z;M#0;cFR3`^qI&3#e-dm58iq{>pX)u=yHI`1(=;o5NpvKv1n{$c`%rONFs#816yU> z^oIK)h|8g?gcA(=qE>Y~2h>LrbV z@h|rEtr*ix%H7lbs-EvtL^>}#pXectz`OUr^4kp;RBU)Kf+T~eiNsl&JFI-5>9Y_+ z!a_?4x+UlYNZjCfkg?Z6pN4UO8&7g2=8+YLDj{F$`5;D)yz)L0BKiMz?6iIU7TON^ z5+348eDKx_*tC(e31a}jec?vdC0KfZPaClzNu_gi!Rg25hJz?`qmgMOR0H7R0Vib$ zXEhCsdo1ROxK#wexvS#ofsVnD274h&IXKif4zT8BgvGd~@;2#M1ILqxN@SAyy(0 z+b5VF9CyGdJ10SYUsp(MOuB9hcjEykuTvsU?^Zj`zV=+p^i8M#i(S0edot&Hx8F>x z60zRa!nG54qmX4KnJ+?yrgH6am!<{uP z@uAn9N(W@8($6Irk4if&I={?kqO+z#mhv}6p-(&R_SKn@-)xi(3fgGCMOxp+V|9Vq zb*9GFBZGl<7sw;xrN4*uM-R*8_YUh!SBc8nF2k=1%# zb}8@6Y#~ze_~jo=A0^T<_@tY+ugA3WW$%(s3wsd>;&f~i*tkj((cF-6dvH*?5!OKf z$PwBBR5~zDP&XoUZHS?ci6q)Q)I?yusT|mtrFulgJ_>8U|232xW|BAO9}p)sw?vO6 z-+j>@_KbHcSY4WXw(aNr1}sSj7Lp0BaJV(s_$M+)A5)N*Q@J?0Bf-A;UAe{07V{#j-5QCz8Dqdw!=?KhgGTWhvZtetOM);UjqCr6R1UD`Er zoekQ0+`~aH2RAMI!@Y8_Uh7yKT! z-mi%mOLu=P9c=MQTXU9dR^}uA2D(herH$(8jG05%*}n(%M-R&7cMj@btJ!-gIy2vV z5qLD3QLrFSOJd?vy1X-9=nqPo${G8Yr-f%-{Y1~-@;X+p^cacvu`TQwKkor)lL41V zf%8BPkUIj{5X(U%B0vg)e0!)9k&gj~28=HRlN_P!EG$Qm9fXZ7G~d`VK#PI2AS9zV zi;BGx)^7b(hUVL>gkYpRBEysvV?*X=Ts6GGzCUyox%wHlEml`mKh-IJvDnrH)je@{R zL=~ZJB9;-TK}2+eA!I@bVuJw^qHjVAgE$WePB0-fLKDUL=VFAX`$}XYx1x2!vFi$x z)U!mFIoMv8U)Zl?wK-5eb$+pT^@hBxFXJZvkUQ>mU;IG8(}KrK?AtBXLlvFh%=$7l zw#-iE^w}zUdVyoU+v3A}VVy8MyXjD4LgeLvyR8nax4k}(u^Ul8=yTuiPTfyE_RS@g z!fJEb(h?I*lNXG69xZuECGHlN8;_F;w}^8V$gYr{p8fn)x2Wn3uDEI$(H&hK6jXcM zsq8K9b}zU{af)Be&Yi|5?0-i2G^Lh&*5BSm4;}DZaKvCD??w>gky)Fg&*R=|?bnI1 zcbDD!@v*1i=uP?|m)BNWI^IHx3-wxhV*4X4x2taRy-#nN?D2x}QzGwuD7Sifg1}gj zJydelxXgWfCt6r4G_1Bh^wxt`d-rMQNKI0GI^BMv%K6RS1-bh3#<{T7XSd~bQ)Vw-KY4|WFV zT7MZ`@o88v^7p9z=utWU)=~Xxjg%{5U2jZHp5?NsEITk;>0Tfpkb*L#?-fo`64KlD z8yznUaFupV*jwd6s(Vn{`Gh~?JIQyf8DSzM!oS#9yy7a0*1-_uTso4?;4X$7kZ=oP z!HGBbPd7N`iw?rATMZ0&JE-qu3(txlRa?H z1=|WDI%Jw5_W1^ClLA}8$0k&^9?7gXmMlih?;KuDi(Dld=!b8=_!1WNIP-x^C zY+P%Rq-n?`te5``xtVXd0F`|2rDRyai9N}YwxUZ&7yhrT%GBC(nJ;-NVGHEBcB^bw z+Ah|maE9WN$WYhdDMT0%%sF^d0U(D1sJs!LQx0*xg)AOl1&hZJ$-x|qA2E_NgEPBArsMXSg9ENV_QOOxs`JSCvcwz&R1&FEU+&0 zf3{HGtgm==Xlbfvk4S)s$*JOo1&qM23+W!$T$jGA&k#r?km#6Qrf4Mem%4CBGfys8qDRk{s zJ9|B+G2ZTM@0#{eox5sReKmIq%K5&NXA*t3QrQsTN*%KIHp)&$fgo3TD$rs=06_~nflIdRsu|4nD-Brd z*rznG`IT5g^)`xso$k&)qxl&b`mP`1`u03h^vmtf5j($AV27K|sIxcvCG`qzC!ATe zbB-W$%7vLlNz`D@EWy);8EU3Y2ead5)H#IbjWy$6i1OReOTH|8+IY>8M%BXX6ERcF z4w6)cI?TTZ^+yki`#T3EFFee2*Un~#wSRkAkXE;+;r(6vqGg?yf`T&Bq%TOncDfh+ zEMv@j!Tg!JTi2VWkUqRVmOrwor7@X6g`$k(YKYp6JP9miG3x`Gi_TUDf|ziB(@~ok zgmxR)3Pj*w;)ddmrHVVmBT`*F)>k>1sn2hPu<2nm~JNCd3kyC&sG@uP%X8|On6VVWm&EPH!bRm*# z5Qhw;J9vfIj2OV6Oz6##4~L%vH52!5j60#e!5@+hhBVvZ0=eXH0q`hzOE-;`;3hnA zSo~1ylWpOTvEI8vw#T={2|C}eP58(S+PGzT%A!LGU4mH~yS9Z%bgwN47?4&p`uJws z$7c&aDQ3ip)OT*$+Txnz)Gbjh@kBq3LJ#(*3D(g@2aQrX_WpSTJxKrFr5zFhJfpG~ z8@-B+-isV5PFdBcdE>%K=Wpa=N^i2IlDKbCdWyw8Tjw}kSe!2qF~`Qdw@;wmi;2=B zqTaFRvbVQu(3F{P3fFeNTC>T@V31>f#=P@ZQPp7i<)yc`D6RcAiD^5vG-Z5-l*P%G zqVC&Knd`rVr`Xq@Qlh;(W%wrdRN>DcfbZhn>;$D{gsg8LzO*X{-1R8`60Hpq)3r zh0l2!eER_B&CJ4!IxX%Zmp(~E_g<@h=BoBh;q()FUC$H8IjJnCm&?oTB^lmLQRDq? zYbNFIPn-Ktko7otsPz0js6Tp8F28e7@}oy|$I-ki4$-6b`M2%LJyYVJB2xLbj<_<# z&dH*CJ(_)CMc?g3PhLci$w>?gBkd_GKUc?JI8)1U?C`c0;WA8^*a#{glJmeVg*6Y+ zehk1!G15n$VF>vge8E5zpfe#nCh!-)xB)IP1tn>CTYGSC%81bKV(Ing|B{Nt#5F!^ zCKe?BJ7}_nWssxiN&wrDcm?VfQn>-1K*d9_4d9~qI)y_1~U0#z`p4Q^A{gvHE!QLaj7E>8qZiC%?+E$Gyr+t*dw!nEP#$fO1vH z^;=&a?FmyWR0?bUP-K2ipkS!|`OtCQbkmQQvV8*~XQTs2CZAr&M4<+4FL8^d2x`pf zGoKcA@ZGZF9qIlP{~z|=JS^tFegAK#C~YdzDw+0~X=-Ldp-3uAAxqoLG(%akl(J;B zA#Ij2BrPaXq(~@Qg^08et)h@5vXm^p^HsU;`*VLkfB5V7KEB_>`#9bmmJUtxY_7SU z*L9wclUW$pc5D8}?oKh`JyLUjC3&dD9?KJxxSKG0(ZZf^D-knZz~JU1z8mkB{!-fW z(Rlml9Sdi_2yga~`_P;CP*Ahd`SHm&8{-~qZru9l>q2sD6o2%RcW!T`Ab0$<9hf44_X~ zOgel{us~qcM#mBqE;9JFOp$>jcs^58QBFj&`d$hoYmX{NlYe!ctZx!Lx?`*$mOFT+ zNl@gVuEJljkW6-A(rk@DQvr{VjeH1Pk8uI>0{4bTWg~4H8e$lYv6mewI+*(*5FANf zR0ux6ZD1fE7Ud9uG(?swl4LOf1hWT1V{~!o%y^*r5aeYp!g{@M&;8E}m>@etN|yi3 zD{4k|^AqypYav=w{Y=_-Pjxc>EM2_0Y8OlLVnVl-N??5GV4wfKcj1b+Hhb9`ckSys z_R*748#Ai$hfu=tW5H4Eu6^bjv&ZwUixhpUpQ107E;l%JCh4lD#cqkd`d=jjZ?rEZ zmCDL|*t+}anbthD&IkN$k#yz8Myb`li<0( zT;Tpuwj(Vuc~$$&4|N5cRNvS3Wmm_(`FgCUcI+A9_4_2i4&R;$7)JgMPt&L8{xo5lim8(-~B|H=sO19T342}y85)%uw zo^Y^b&Vhy2mE0!lp&sb(LH(l##s8Co`W>ld*=ZbDbo|k_1DEO9mp#tBxjug%1HkrR zTYBcxw)i0G%pj>VVONb>-vp83lPlgf8Ax44*}>qO245#USGa*u;a0-y+6>q|2ILG- z51{FR>XZjo0w`Qa(E}Hm!DfKZL;-q_OqfP-`64Uo3TrkN9mNq5#cdA;Uk1)QSI5fkp}VrzXjc zcxVmAM?9;pJ!WRLh0&U)>AY#rCCY8MkTnBmyjf$=6kvKFd64AJj65KCPf8lZcaJ085l-T@%UuhR%l0`nSf0Bl<6c$obu$JL|riDUIGO zvFZM)CufXHRlQMS@2yPXngq}GoUysxQD;@nE@%0;R8qtDt_!XCtzmlW88*a*8>D?$ z;;<3M8w?v7X-FqQ0i!~|#N{xlY=o85ppgcRgNSvbusBp`Dmi#U5t)V`08^TPb;v@I zFRU4v{t*2)F@k(7ICu8nMwDvoeghAY;b!RV4T4*jHEOKo0!oAvkuBB z=^n`(lpn4`NlhPfP>23o@E4t5n%yFhOE3l>m%4hhH-c@ccn4JNS-q<(nQfYc8% zNhXyjd`OJL*B`Fl(BPovLoW`p5U%~ebYmz1A0p&6T&5Qt32)#6K_2PF;9xk)VPZy! zw+48Vc|c*XnbiMm#YgEM(H4{8@81%Xy5wcZtjD~(9oz2(oV83+SnQ#D=cDt)gph!s zWi!_IO7BVCeD}WpGehN~sUiGB7Cqk|J%8}Tbjf>F>AHFL;vNpWIyF}@c8mB$EYAOa z_U7lON8QN|FRr~EzjRJgz4lZISLz|9@c~T!$+_O~TW>x*-k!L0-s9Oxij~P5oM-d< z@J7)otsAnbr;NE`oK&-c(x~=g+UoP@#kXF_i!;7olvZEA@P&pewyr+o-H6aNfiiW#`yKOHM@8<;J8jpC&GtUUi1T+fJL3 zDSji}BPV+9k&XW2zl)eXmG)S1qabh8({h^JfLa?@WYd+Ms&ze{_5E9Frr)f{{iHLY zI{&QFl6sP7{CKkPtkE4dVWs>S#~EgwnIAT?zixPYVcVS_ke3ePNzY;-yk{TPH%oP(x z0)$|Wo*X?4f|HmOOejr(W*BLOu&{<@jS32nDWpHp{vfJ^LFF-EXF(td29J2`G&2-P z4zec^ZcFC^IF1kkc&EW+vF*%GOAWe>ho9o+NVe z`_d)ztHZIEmKhVWKcuwbmWbRof)V9K2S$;&9inv_-qsQkwU8Qf z&2aI8+z$#HG-*hj1KDvh6XHcoGB6*&GKEJYd?bib@&bX}6zL~kW^mvNp0lLY^G7GC z@3oh!v%;*F0?4b7hxl^9Q$l2qRDvXP)w0tPVA29z{kLKjM9q7 zgFOugk{m(FaSJ7WN*jWJiI87%=Hb`@M7<2tw2rpk`HX;~ ztYf=_l;+$B@iO6`zreZWWKIAKly(x7RobM%r=r*1XU zdVTy%Y4_A+-S%o3Db8hjfo0S?T?P|gQO3Oqxv}BOQJ0KaTQ--LS z#oUjjWt@{~8*atCw-6M%&-NDHp7yRlk+tc)zn$M%dQ7+Qs6)4wk zFKv9PrfA;^#@Xp}LrJGb8b^Djs<rA3B&uEbY7d)xJ7L~} zI>#ALcgqzvFB#Bm8bR&HI9D| z>K{EQ*FQO^-+IV#%b&=32W`EV5b(rk-@I|ZytVvbI$oH%$6%C6V10r`X-HHgiMp5H zV@wJuMp?*L`>dBBKbNS}D7WajfUaU;E{3uO93syQ<2`T*nZUqMi24jgq$wQ%Q@CiF zVR%MdB>^2Il#3je{98fJcNGPGamn2nVbah?a&1ZMB`9hBry{aAN01EC?a*g<4t?}7 zo^NC8vJ!e4&~ePX%#9$q#u$=j4%;z>=pOO*0C@~Vgivn}jUlOC#A9S(@s1@r53^ka z1S258i%i!+&>gX7!sHG8ET+>47r-T)4N*CgLQ$(Qzw{EMdKjwnZ|X#o?sxP>k`=-Y zPq}vm|JW(1@^mTtS9s4i5l0od;2TDtmYi@t`(U&|M1bR{V2iCQGn2+e4P4%u7|KbX z@uT+FtD4Ul&o=p=$?2Q5)(Uv`}r9XDVO&m~`dqOy1ZVyDHTz$Dium zks=&=T2=j&gnN5L6{}pBuZ0RUkmI_Wq1?CR)V*Bc=n2p6=+!MXK?RbVkeC-5P|BUh z^zTlkjyf!}GiY_@`-}|L1)E*RWrpy5gQZpW+#b~4JhiS+Ou4IcQ&7{ZFY=}01z&Ym z#in^q5j)=-`!mtJcjG~)g_rY0N_f$wtNYtlZGYD;v}JCEc}-^Il0Lp`ONzSnH_H=V z3Y(KwOx{Mj6e;N->sRvr^k^mHkqWfq*X|CEkiWmCxhCs=SQ=^TO6dol2ewuA931WX zHIeF|7jQ&iDze9|X4XY|;_7L}Z4*n5F4lT=C3v0Asf``E@7G51bmFJ{lJFPPmx6da><=Pka(wyuYiopi$G~H9& zp$hc(p#IT=a{H5m`rRs8Ob?TGK6^p!?VLT-dz`(#k>l20!OwZHtn(}7J61EMs4e`? zm~v_`X`jjtQh3wZ;wSleqx1u&SkVa|7}Fg61Qn1w3LyJDcq=fgHwEAt>l!bB^kF$h z{5X`o@Tp>7jK+cknH=F%HWkRt7E_+|`gZ0BZ{>YL_ql(GXzq5z>~0g}v`$hGwCS0N z@yS#WS^$zVLN|gH0S&knkXLbCy_(5`3}>6Ec7_FyB-FNlCnpKE$u%x^p-p{JYV2gn zc=|Eo+Wq%4JEI~}YBhNuzA82a+OOef;#6KLaq=I&IjVYncCgqo|Il$u&DE7rea4+8 ztt0hPr@2o|A+Ox%qLn;&Nqd$=ls-#FIMFg}UF$P8O|@lHuj1YOqB)ZK%Odrzjr}l1 zLveec2RAHPs`!$tos*TU)xz61A_Q5-PKa*Y*WbKWPXF#(bi<^% z;n6d3vb2k}d}L%{NZJeyy}mns!rm3*epjruVu zL+XCPI_88+-6wK=_Ma?5MU;5eGY#Ay2o9!0XT_(3n;dLu$@ z&@7##HS-!n-Z75m8Ac{O>3dW1JYUyHUtMO%+kkZ?9e5QuOfjwpc7=kqFl_r+)B&DE zq;F!O$DqT(kF7Bq{sAhy24?6cu$P73fJympZv$bL718WGTmtexcCO^)8)Xr|8vpkk zE%)b<7JkFOHGUcYxuP`&c8@|gLWmR+eUX00fN&2hM%>GoSPdJoAt%OyfuJ6k4i_pS z3KVo0P!3Rc;9moC8P_q`e4z$3Wg$?V3w#UOAtJp5IV2Rwpb@7vnhu{ozA3t3vBh|G zehD|_)66S|8W;G*-3!B>4CY4`JzsArX{51vD~I@+pm@Cr{49@ zkky^Kd4tp53D@@@R+>x6R@)iv8}zFAYgN)0@k#ta99aa**f?*=$wgN;=Nskvt7b1< zKl%r1)8u)Ece~GOiiq#2o8qvuDdCpI+dDhsvV)r2D$_g`)i$5dAa0zc3-wLMP|30e!s>R&M1|;D;7p8v9#a5H9m23)brXc zBKGCOCbl(%nuYoacu7DsMt(dCiaKF2?4%%43?|DxDX;3ftqxZ`-C7BHz$ z>k%$B7TgSQk3p>qt3D)ug!9x3?gA#y3yljV?+BQK(7}uWmOWVz9IB->Zuh%3O!Qk{ zj3pNZpE=ts$hk6If#19rv;F17pL!A>d`vd^;!Sfctopd!L7-xUz)YANkdcYng8>>7 zN*XqZFI)hNhbs;)R$#ot--W3jaVx}E$IKr2b(lqQkyVabL`6C<%8?h^CVUS_-=ulL ze~U{TRg_Jp{SEweU7yPzJN0ki?~dLFJ$r(}w>NNp?OpR^N9(y3)t-!>nH-}{+{2Q( zJTuy;MV(bl{u#VI#M?cc7WDF=yRXf+S?|Rsi_$~(%pf)`XJoJMj+(?%8CB?>;%)1< zYfqof&R0$|8)b~yXAf*t?|L2NC%@15gw(l5C#$sC7c`Q~Lyt-H7@IzL`~OzRcUIr!`Jre2;yzmq%6jo>@uwUdt+5iA1kwye^9fBl5K}T8Q zQbsb^#`W(D!7Tbw))?~l7lH@P0?NP~1^$ctne8?2*<@2qVtT4h@ox?)7 zBLdhEzJ`<~M2(wynHoVX40|b-e_p(ifjz;bgL)1znE+yX!SrQ{t0MTuKr7R+MWExa zAyi{vXa!*y#C-^;z%U>E9F@ZqgPei;xAOe0JO`ZR%V-b(R-QepZ;8AWzj;$tR6VTd zx}{-X&3u>SVwKI=fr%8FuYu{xW31+PHRF$$BUA?KK=RKojd6{F)b`N|{DICFEko3~ ze<3i5&+yzGvE7HV(Pz=-KtH=huykT&1s0JJ9O6Wr0|(F|9jZw|mGG?Y!lvO6aJ2sVOE)af4 zR_7;{wAw7YUbptptNShKKFxzGrgj=19h+^nOka}|W8{A4OmKbC>NCBCJ3CCn2cD^^ z>X>&~shwH5v+_=wgmK-AR~t-9=UaPoTQ$t(HvVGXnqs=KCrNA-i%w!N%|<^>a>!ZT z^~=fs@`PC?)jAG+Jr0h69$kj~FDsXCQ1~DwZo}u}WCYhw*j&D3RP#~CHcjHyF71+Z}#eW z6F*EyE@$6=N9z;z2oH#9)}-64-8D7fxd!_eBZu_rHGSUl%)#KEX6dejX_0E3ADS+_ zxpCkFKlN16?Joo3^Cq+ynR-skU|%TP5wWmVSoo!a7jNXsHBFYgr|hLcwDPBkx3;cY zsx_zO{p8+{lePDiOqf{2yB+%ao|2}1#>9M&oQWo13vTAA{k)a5h_jyiwC5{3Dc(I{ z<0hlUhP1N=N4GadeA+Hfo%iL~0nV3CKP95<8g$;-+&Z>Ztb?a)e86#H>WXFW)7Mz) z9cEv6W=S@%>-_xjOvA_FanRp``bQ7S^-m7!_mzwNnz2TUf8LSilv(K5u96bp<)e)& zS780BJ@=EcUmrt9Y%;MVV{CpiU-*`75i&2 zDDiTJ{vCWoU_8J~#7$;sG0)^Mfb<}I5MXs++l1wmm;8s(QlG{2KmJA*InS}jFzD>< zhi74PAfUWizcQ{ut}pKFV&^<>=DUtNX+u<|johzD6cN)itgOz=sJIK;Bu6l^14+lEULq7azCgqUGk#D&cZxDYB(n1J&lz?(@$ z=q#B_GesIBJOvc&&CKvw(1Rh25$X({2#+fe4zN(;|1}T(qvpZCKF|MaEV=wY^?43Y zsbAO3)cJbENlJ)nG4F)iy>oHx7iZ)B_k8TT&^diV^L3uF)2UY@&5o~?P$Tt}pF^FH zA|htM9Ww=72htm~I_Q6J*+C#ISlVW~974c(YZ{@RE&?n$uNaQ%N(B$zZg0p zLU#Z(FK(Gc--phR%N-_z_blct{Xq}0oF%7WHGyVftb6U?Q4{$`0jlReEN2AE-tjmz z$krSm9!J$EPIooaUaE8^8V@WBiPD@Hpy)9`{VXlqn(h@x{mBYGvAD(7>tlMJuS`__P+xSra(%y2 z&I-o~edFKUs>(}RDDjwrzR1MbI563l;picBeM<0=g5{qjH_w-!nKt42!;XE+21VwP z6vgKheTq5#(8(kHf@&7i=y2ZEB+=-fJ5_oPQoSz@gbP=DH_IJ&lPwuDj$Njn8*|kA zhW$or?PYy~c`Gv{e6~f7#bIUpKUB3>M1%cyQ;Hmax1J!(Q{hNA-^$mD?X3)o-QH>^&P-hi_S^ zky$ctQuzWa&8auFri&;p{dws~OZFn^t=lH1NgDWQob-6oOFFXe;OpjmA5Z;UF`J=q z6dJ%}W|+jlX@WTqc#d3z5diUzISghm6nI78SEI36WXz^<9l{Klg<&tY<#4}o$ZWyG zIAv6!XWmiI8cKnFBjgD4<2vS2AUhpv-Bs7x6wSWg*L*j1-Mb&5!DxZ{Zo zSkzUBgCGY5oCkl(@PhOaH#XDZ0nw*sp+%n04^;Y?F!NV$yC@xBTez9Ju;YrmvEgz> z?I}E86~XS@Z+Z#Z>YHC_Y)jgETk;4#$cvZ?`3{C z-Wx74XrnLnYHhAhcY3@kYRViXm4qRBsds4>d$I2eH{iN_p;#i|n+lFa{H`}aQk1rJ{Xe6&vV~m?q6g2%Q0JL)wY+4`%uD33*ZjXv#B zscr0k(sbTL*5OIIq@kkx@>No48J{J0SwG0}RWhIQVl`DUWmVI2Mw#^YPd!s8Q*>r| zzs)lFGW$qQxAx>!+!L?W7Cf11*Rg6{F?Ifl1LtjY9*e##8>Tn@J*t27sNDbPs8qB@ zco$K1A?Z;U<>cwWz)Af^}WKk-8)y7?q7bY_K|n=l5;Y2o9)UkpK56xHB_7C zVH>(qa^hkSq1Qt}Dd)YL+NQiqyteF3l(=md>kj9oyEd-xl1iY<1QHw z_<5}BPg!O0q^^FgOIYCM_rY@%s{OOiJxHut_NBFKb5lwYzwCQ%^`MQ*@=Hq_^Kw5G zmO1o@U2X{JpQF1mYr#k*vb(HO8mQ(J`E29GZ-qU9IYN`QQf`UQyXF|3_PBoFhKXdb zTd85mlP{qTwUL1Aich!`zR@Ao^v&s|3!^o*rpL-Z6ItCe{oQxo#fdR{pXhVhJM8xd zR6M_UWSRI^9e3@~KX%U3(ofM`bXIkVyBAs-yQgXJe&@O+>+hP5i!auf&Q+m_D6apiv+Pm!`1xe7BQG~)m#VB# zG*2bD8hYs?qa5L% zrc@S%Fa;yWiGu7AlrU5IF9P4X(c}5CZ%agn=hT_0`Jo`7{!h*Hvx@xo7D6KQ+Q;CF zpqa=?3Cs2v_PLmBaCC6C_BFzK6tgD`tZ^ZPfrCTEub}A0uehc(ueJL0`V5-9+& zh?&Fn%oGonN;PCa$^B z!}E7cN&(fT-mp1SW#OWEW6o_;%02h`!DRF-MQ#VLJm`=Y*s>Cj<~%u{sD5`ei*)S) zzQ6pCokTZz0nZ={pfnZ@yhB`F!377n6*?sbl?``@7D<+A8gHuG+_z{w0#*>dm&K@t8S(SdeEWma^NjQ+@$8kcOLY9)KeUcJ-za*z0_{~9kX}*l$5xcHT``;VD6y_Gc%i}6yDG-KDYF0 zUTWj`>6%?{x|*hD3dWZ#{iS2)U?voGVqR~0T=g&M4?kk7t8303ZQ60`T1Zm9+=h-z zjnM|n8q4?}a4ykB2lG7T_vv00j{TZb_&)riNjhALyJyU6(PYw&rmpFJxp?{hUE{TT zK5Y9uYtgpz+nDyp623M)jY*>GxCyOPJz_y;^`6>kv#m}eQPltAu=xA@ z+dhYv=QFx|uC7iTDd1)m^ek5d{bYwZi)oGuKa?CG>vxix^|s~Nu8G=7a~aq73!8GAFBQyjW*-UOvTXsDS@HsDJdJ zT>s>texqZllJ!%yTZBFw+IxQOtld_6w^mE&izqtmZ_1igTim_>Xu~7Xd#@Kh+`MTs zsjcB&ZhJm8SifSr%^8#*FH?lH1AKstbz~qT*NBVTACce8B^2n`_n6F*yjfg9?HI$%DW%gQUz4{V@V>OsXr5KuXiF+3w)YJ0D*DHrovi@*%avI39Wi zP`#-zN2Alj4HnVG;8?Rwjo38E%hA*#WpsqtKqm-Cgqwv8hanG;C6FbEAQTw7v3(#y z0Ks^qOvFFR;y{{;ksBgb;QvN1i5oZcV#7Ds68$Atbh$$3bA&yL4sx6fn;tc|} z?wIBLBI4bE+@>8-%xKzf8vCC2dELr*Q`?fEbf!?<U%%A59L$X|+PBqz zdsh90nNBHA_a9zMzVNDBZpFc*f?u}lRA2E!MEUz-M?H{P5kDw*KdR%&(Ft zJQX3eHq2o8nrI2Vbq0?Z((1nL6<<|m@eZEfdS$SE ztYQ9?X-+5aP%rh)v!BD?aq4_h_rV9vy;>h~9#jnGC=Mts(6Ox4@Om|TgZ+C{|L9S< z{n1hVE<95ysb>#1R=us!Szu-BI$P-GmB7hhR*$jLeVVRq*&;tBd~NMO_i3JPD(OXY za%D&UmmB(Pm92A7c$nr)tP~jtK;oiTGo#ReiGfHG{udCWSWu#4f)CjwniC=z7}r?5 z+*yc=M~VP+5i9}ugsL+C-_+Q)!K?rOQ)6}hgc>Ul7SmVf=W@O^6q$agAZ2J=FSk7C z*N|3v`ct3G;R56Do$D5*K2IpmE4MAF4$8L|3l};k_L9>t#k%Xx9}~SUbc6h^GV)D< zGUv_mn*ONpC8<$*wwjkSLmZe(?z}OvGSzc#2tLK+?Fu*8AIZLUzdh8EbbHIDisbl$ zkMYwe;iW&{%k=UMP=TJ_uVC-3@H^`>!^9%~4I{y+Y>O)T9M7@M;RveUFYU^v;E zVd=X4l;>0jUU0JG;FR+E8(kCcJF5Iz>e*{I`uan^M>k3xGW~mx>Nmt)dFm>^eRGPC z?Af-ZdKo%BPd}9OzU=f0R45NyBR)U+4mD17ApY!_&(qDq$1e637fsmvY^d+~dr<%A zLAm|OL8(X#a~X6rJuJ>7FyyA3>*q*?mPr-`ty+$s^E02bwQEc6jxRe=yN$G`TJG7I z&$6UP2Z|m%&-bX;cb1%+hx%iRASMn}4yZ1WiSuxg1-9DU6p^100+_PRz-FLA-^l_d z4{0B`!4T>OHo+u-R}P^c^1Xt^UgC=U$o=1Chi|S8X<@ZcGz|AXzXV^ShPZ=1g?@)s z#L}&n4{5B1$qcJE`T4Mo&{%oFLu_sYF)BAW4((?m%qCgkgtHEGB@pLLxH3&1>Cy zL)YFvcR`%UckR~gpLTxQXso}pq;02-QI6u_dRjY_r5b; zs?#D4&t33C?yQAJ&c%D_H9uAy>Yw>4<<6s%=RUMmf3s`lWcTR^MW59@;l5(t=|kg; zD}I#s*HYTuE6%Sh-88vh`dgerY$a>arRRJNoJ~}#TtoPVLH8Bi%3e3WZdfZISB@vT zpqsvRvZ+h0wp#0#o^=S&*G_-BVP@;nwI2D*%YiRQ!Q4nAMM{mx_&J})e-(4#*#HUq zcE;p!nbY?q)?Mp9vxK8EA>*moy$F@gDLLxhI@O9jRSQg1itCFO2sX~We>-UX5gieJ zK)xB6)t5Pmp5E6xq?q?YB!_NYy)<|wH6?*_v4jv61{1rmvfM38A!y!>n#o#NZLp{nf=TV^- zBiIRMxM6}IgSaL($|>49_FtH%ue~cjmiiawdDUV^M@v(4q^-;${`#6d)niuC#%&AL zFp&}%R%(Q`zwy|qacIr4u^0Gn@RB-Y*`>C|vT40x>a>t!Eu6^@H)l;D3U|@rwz&?y zNBi6EPfeOL=jSwTNL}m&uT0Pq^uS){;%hoP46= z|3R4Ni1lSs;*mb-w_A7AY>~fBCks*16%wC_D7q=>){m8_S@b%`Cv)AezLnd1u3sU= zw7v&J%6Me`lXg2;E7SfG)=7=hTlGKO=1w2xQ$oLg@3gB6>sZ5xAF&H9L4oEnI6$kiG za|XI51{oRN*h+#6g&C19hD8W!N45~^D5R_K^AHgvpy~0ToCj{f3t~_+Gl5o)gj)W_ zvMH&;`7VjZH--NNX3m_yv#B=M#-L7DRNegD?Jdq(N(ANZyk2PCBj%-JzO_59e}2<- zHu~K2FQnTA_zv?2bqqL?Lkn7nz3}CTd|yC~nTR(6p_@X(Ljjo^kY?~i%@G8E?+_Mi zL<1lK$4w(VQ7jr4nqMF( zAi5PiikPLw|EogyM^y-a1tb60XnWlLOfWK1(A9l!K7M<CM4g+Uu96U4>r6 zLve+D;Q8&o{Mo_N@3vVhA3n`5Bi-pw`rMiCYHYApayC*$u#V%=fq!CQmxz`T$UHiY z4r!Q|nK@D_c{IeK0P}~fETsN$?x4{#<3h{|(kM89@@<0Rb4rT*JD>Ix$U%@O{&Dl+ zs)LfvMA`sS<=}UsVb=G1myM@myDkE$vWy?u4lYGN#))sVQ*)9)87Yy1~eV5{brWV%eaQQb0b%q87k>8j7ExYz<5(A2p0q9Cvwd5~yHA*?wMl;c%0nB-luSUyyR75f$h^Ee=#a4kKU< z$}>nfJY+KC?Ew@*HUo+f1bG;NJIy0Nt7sQT>JD^#RP?uK2u%UdHub_#5|4$4bYytu zp`C#zg9AJeMx5q|y2qaYeu0i2G4XV9GQ*2Q`D=0bYjODNM)`9$ipQT?9EN9-c@Y_+ zkv(d!G#bnW>L+V8vS(a*hT;=d-_z{0RPvLzTEH3I;Iq@}T;Crg<$p$L$d{jGkSR7~ zKu42fiX?dCGU56UzOxs&V94u*2n13QF7_i3A0uOrOW43Mltj}+M!q;53Sg8c3m3?R2DU8}R%q_P^?=DCm}R8Sul3wq_im@CAgVT+Y?z=O7`L&uWmMhOqlXnET&PQGWUAtG_AwVre#_maG1qCp+427K zzW$tdx2LUDivN+Hy;UqLYqiwJA40d7>64{>W#`z`BV)McwXg4)Q48aEXCoVZlH+H+ zA6kw)JicpxXm0y*?(EcpO-tm}m6IfhP`#P8k4=7^b)S8Ok!sfE8oxPX<<>iuiyP-F z9~yms-^aH}inAombZ!6~_JMZ+FkD`vzrsC7;))_82qAFXVT{_{DnOeYY ztxazZoVLC5-7;7(T`qo!$NGbvA5`{#T zc=h0%0%hm?C7mZ8{L-!48za$_{jK@xmZf#1k2ek0i0taRHYGE6W|f_vrdM_a$5C}a zCt`k7UV0b)3f&c5K%NyNa&uNzh3*tCGSDIcx@&CP)dCR^ELR^d%rgY$UnE9E-p=5Dzg|B3(~{Mm2T#kL*gaM$Mc2fjcSc}M>a|^4&Ud_RuB;N#KdE6~=O7Ki0&a}*|d5r$Ee#@U89DAf;#dgKhPYU@*gN{>W)>3zWR%ULvl}0~u zafW3!cxuY+lR=i}Z}i6&?W$gu z^G5sm;MCaZ5zfJKbs78K8G5?Ne0^SMFm=&$F|Fh6UsOX}=2q|It{oNhqWIJ|_O6H3 zI$PzeuH73=wcH~z_UXvR_wQl-qle}3XNQH-HgvO-CB1#V((_VW-b3T=R|(?Aoa$2n zXs(Xy?ucK$eBsHJ1x^`V6U`mUyEc3wWj`OtZpzncGuR?2oGXZbV_+lvPViy(T=o3^ zHwH>#{NGFDoN&8+i*}Z6yW-@vO5?t4O0Q|70{+cImEnb(MU!duwr5|ac4`~ymCsBg z{rDJquFgP;$kGIV6$%hYL%e9Fm{`#f@ri8$$naj~WG0VBgGChjSQ;^|Cp@0m(lT+C zV_=JeI88FkOk^bUh~LrKjKS5FCUNqa7oG3_m%&xvOF{d846ed{53Y!*AH~m%w#AmS znq~wu7ZL1^7&bItSlxhQ&%%;`7-V5@z=K!cbfnB-xkN=qCL&R=sX$T*6=e=1NQhhz z1Bq{lf<+Dzm0(yU@<|aeMaio!-bJs#e;9% z9gL9GdrJGJBQtnTZv7V4xGkH+zQjo_7<96;@Y*STs{BW)XI|${KOKv%#Wj;JJZ9zg zYP8Ja(A$0-Ybqt{bNy@AggrLg_V{dwW?_V5c-1)}SCVAtig0jY+U6HBx%PjI+@Tbob{`@;P1HOHK~Y>zLS;7-0B@N+|(Q*fd2s%IH9bW?TQassp? zpUCH`kn|(nR9^{O%3s?t`m3Y2vY%h@4)$7kzd2WQ29x+J=02Zv`E;k!LOE03Ejj-! z85w;m`oA4GcGTwk?;>|Bn1xO3MC^?jTPf4G! zpq=qA4~w|nd2-|vf7_OTrHS?jC%zNO+462j@VK5eH#heys4+gh-tp<_{4Z*0hetJb z`2|AU>e@4|c3hoF3dL;uCYneCO?;b>dh&;7O=jCnDzb4^6fo|!Zt*TEBbnz*ug zYGKxCx9{X+_1C%b$M|n?9Hr8$U(9sPawrfEpSfU8??>SdZzf95s3~vuUG(ws+@@;r zx@b*Tr-z@N=^%H>**RS$#WjQXFD@zzP+#+1nYnmUW#ZU0dE3+N#XV&a7kmdo5|=cl ztJAvE&0k+hFZy**?p3vu_5pI;hAxA&(BR8$=bma?zp#m%H!)L;f9Uv)cz5@`uM;;m zTi=VDHcF*Y_}k1F>R~bE$%jX7joMG5{6Bu1(eusi;%e9PiDSl%J?9quc3CNT(MkH{ z_S+|oR9;@mvHwh2KB+3ay{aqPy1IlHFm_^?YoK)D0@Bz`+d^-A51CoNy)1_N+M?*Z zK|5zu_Lp(dy;p0Wdg^?ic&5&*;eFlmnI~AT&zIg`B1iXWi5R<~Z0fOssFYb%BCLno zhi;9359%L1DAzwZsNdK!>-xjjn|C_KN8DXb>Pdmtb>Hpo60!dL7~193+KN;7+B%w~3$}WQudym^mZu z5rR2*LRqG`Eb(ydqXQm~MhJFQDpUR)0@;-mC$(lKL%BlC%rAZW`V$#G!*xgS<5yVS z;npamuzPU=wgn=a2dhgjaDI&7dPn#*kB7uZ=pc+Rpu#IPnEp(OEC&i`a(H8f z>N9q`7bV1Q4TB@`P^A2>OYXZhZ$lG%S30V!+dOfdHEr-6DVvvGIbb{fqe5Sy^}A&o zK3Wc36uQAs=F8}%Nm}nJ_IAt(aukXPyrjN){N62}cF8^Rq-YyD6lzVss@a*EFg5hX zYW~gcXU(jfnMld9k2+i{0uT;Yrg+s?V;OFAbAD?4(O$mRm_Kvhuyx z_H?dQw{e02Q@Qlm_sR^Jj|O_Ti>}$z=4S5PDRxogbxfDlO-k6W(HES=!p_aq=}i>6 zsUMfN-pu95%kZ=^pQu-A2QR4U` z`M|JwvU{UWMTb754^^MP2lbC0l>46?l#1wxls772OlV58uxgUm5z&Pd+4<~C%dX=G zy=)ixZg}gV{+%TFx?NSCKA8T^m2^#z(A|*F`EC$0#hR!;2zaF-dls=_P!s^mjOCp< zp^XJ*kcFs67%UMe#)7hdi5ETG>A=ktxf8G@2Rhw*%2&4GIjGQ(}po4hlr zK$H!apZ6!;bPUaySk$Ft{S4C)0WZ-X`A`Vd8E$&m*1=T?O90Qv9GYERzaYLqiDDR0 zkUEL@Saaz1Msye08-gDTCI`QnFuPnQ&z#CGYe` zN=c~8?6&?Zf8&Lgl$zBi?01gjmt0@GuTrrbZ}L{>J7y%{7u9q3|Xoy z47=HgY$eh;VE-N&0D;bE4oE8@$K?=ES)^ltUu|wiBJlKN1~L#i@DP}pk(qQ3P>;}^ z5;rj@mk>W@%B9mde9mL$31NtEfZRQ^8UR>X#Q*SqQ-) zWafmQ2%}us85x*M;~GZC-3CN5I+zU~_tpWI(b zKF|}~(emG9pva$*IYVTi?-?Iz9N8N&ktWDMyGx&#_!w`t+lW?&&4z{(jhPW0Ulcl2 znkf)LOlSwd%ZDx!3p4tNkbno@75Xt#gmrrXh5^VK-P{llAJK=ffkGGsydxeuT5K$NZ=~-FJ`zHtRXsyDKW;~U)^D>^#57E>yDBinONpjBotJ^KxADrYjEzcmmBV~PEw&C|Y zx@_UB@x_`G+S4r~*Sv0+?0;dFw5S!Sh4C$ELWk|@u7+nezPp4KbWT3suqv{~aEeIu z{)rW&v@niBP5FU}AnWJ;JB}-N%otbnz-Ug)>h)D>AG_1f%XpukROY*VN?de#BCl%O z0sEI0Mf)T7_Jy4rNC@{mrQEikSHDnxYj*%$;FmZ&kN$g9|L9S<{n1hVZi*&1e7(QY zy!CedO{dd0N2`Bh$h?;mQMBa0ET+B+X$}`U{XN@k{igRp*WQrgPK9)|6^?0Tnb{WLOYLLuCXvlj#tAf`zDOD9a%VK;AsOLl|7M0S$vY#*0P8>zyk& zSUX3F-}^XGhSVlVmn0u}zUR`vu4SPGkpg7-5P#u06e>?)EL!3)&tbWPkr!eyn8-oQ7q0j4*D{|5L&0$B}~83gBSsPf4av^u6# zRUTqkspyp;ks`RQbRLBDY$U;Q%|O@EHCN{sAGh-0sQ++qxUR9n?bd~sHM^c1m3}bx z%;NK_W4gX3k9jY@n4wm;Zkk8GrrXJhHn()oa2`2VyHMTd6ff{`IINWa&Ue|Ol5bqH zjL{B}pVN=Zef&{#Og26HcqJH`JWdv6|AbHBE6Yd}#N z%^E0LXw{krbEPsx(IiEyH6W5uDY8OI%2ZfIN)aU`DocYT6f#7SDUys|wd;W^O z@BKW_AD-iQkHh=CM|w8`2`8fmoWx1GtaT_v;GhwE}FaThfrY|MM zdvqJZ$1U16Kly3%WKEHa=iQ+$Tdr?PwDxc_Ng~O(%rDC^Iy_XDUZ1Uanw8TSH#TK*qRw|)ea}Tf z;|IFzl-%tIj$O7qy6gQ@oi0`x{bK)n74@{%ufba%Xyy0Z#rh8FuO5{1pB&WhakOts zf5X(8laJ3YD^LyDtSOBUNbkvNj_;CJza{_b@~{CLAK1_P>0|V;Xb~y0=-{hLsU6?2 zU3qpYdLRK8w-11S_B2_k^qjRWQc1vY71hj>ijB;cq+STYB9G2q{c8ym=4 zZaf}eqVil8a6gGZDe!InAj+QDi^X93{U;l-F*&e|*oEN*uEY=Sl-V|O?CoaTLSO{8 zA!rdWZ^LPWzeQ>)=1ycxeF*mlWJTOxUT!Lzisd>X8^=H#KnduO;PGR_;0ZqcSmOVX zi-Eof@zi`6UjUfK2a2YS(0_t_FEN>e)YPK2%?(MqS^5p_frg|5tzAE2J${bJPM6&E zIFp#LT=Q6H_rrdLwOTGIAz!#tChRgf_F2)^GWN;}NyECi<||fvx*UJ=acE86zPRtA z;FJ9ievbNP7B~BqbMlr$6At^#)cM?ffARYIb~~$C-)9Zc-j!w)c6x%M+fI%Ai@wz* z8#~^KSEKmc+@nyvCcE`*!lSCsFAVo*F6sEHh2mp!@Ut4N^3~>zY!3^=B{k&HjEPDu zo6b*~ys`9@XK*u9bK&dTKW-N@vfkVpPg*52r?klSgG|PVa&L=%Q^o}G^))VLJ_($u z7e9d1oiI%I2~Qzl^0OJaS%E@NpF6HAPhb9V)c?KMA)3KWjh`1w(;VZX+HIb3i)zUW zjlC|bSgzFgtN8dIozBq;zjR6esZ>iY@WP^_k8>>B_6)RAUVYiEpVyWXL0zJf%P)2H zG781phlL-gvs@UL&H7d)`$obR8YorgZy9*aQruas+`F}~ zKz}CBY>@P@)_~k_exZk@?1sIy=~Qa-&aLBaT-1xcWHL&-dh)=Y;?s9ffAyf8|Ky;4 z_dLGO;tb-p+&w#R>!=GmGfz*LPi5xGY7URq@qRws`kM9Nn+5@IuCLLaS>H+eSc>W( zo$F<|T5%O|0mHNw0X=YJL+r?dtqrY(8$5Sl!SfM2ffYO^&RifFaN&XqNC1&H%)kg$ z#rhqLJsMm6v%;V*`AHqWBYZ4Ptr6i<`t6A-=|l&MQk)l){;oGrEceu>lpYf@(tGe9 zBsk**uK3H{*nOSPnzio8$G{~I0rq4zQcuB_08xU6+;}qK5CN|h2pfP~d-@>IzyOZG z+=okexv>g^2OWDmK8KB31OFXiO~D2YCT`GX3AeI{I~W@@SWA%K%%n)JH5qA(gBR4V zyufYRv!+bCbaB-6rR#0J75nb#mQNf0B_L*7k-El+S!Z05Zg*9-%;mLyA9f~m?$D(! z!!Kp4^G)9b=6xGKJl1Deqt$Z1fQFB>(XXd$pQ~}oyy%JlZ6rC%ZBd6wnrTkp=^GZSP#60R6PY~+^ zeP+HeTPMBueeS}!z7}cQs>T@&8@gzn-Px!2K6<+O7k%-)BWQMBH{Q`Jo+0j(aY}JD zoifGfkmUBv%=O_-7H)YuvhR%)#T?U5|&`7wFS2 ziXZt~f1F(DF}*C>TPMr5RUtBI^kJp``~M|PiLPcK3lM`>MJ=m z^udGFT2S2MZuxIMdepEbFXMMeWq(Ncn)il4C9nOb#tWiwS6mo7RkxxH*7j^Ym zV`D?)33krPC94jP5UoBA@CH@sibK?J|xv za69pVMwt<_;^zYe@uDPKQgIh*!{k8(AsSXSSZmpkJyVE~Eeb}=U@`DvYU8k|dQ3Qw zQEu4SO%W<)bR|e|hFTG;aTW{WXJDBn>kDOxD8rMv?;!;ug5-~%{C47n#AG!r$!{LU z5;nG9^aDbVh!_0gLbKdfx%;jogCxjB3I>%7VG(S|48WlQHs)hZ4{p8)W@22YdP@%z zDXK`D2abz^$&`Qq?O;ETR8{aAFr3B=3EhtXS|AaWlZd=`qbeanjEIoOwTvm0*jz&w z)R&LJ5w!8U=J3X?`idI)I_okl@MGWGcAVdDFFAF&Q=X&X!l{vP)Z%IMyOd~w5` z=RJDp-o}SWEyOO~m3X1M(PEp$=5xqU0b%>jr4WF1aBM`NtaAYJB5?I0fQsovrRgm{ z5E!yxo&`ILEn>hp3nwk6(=0le$>YJo!4(n+2crDA(EuQTn++`xmjL~N6$myDhu1rO zjy>laI+DM+@Qjjc$BWr|r?x&jL{mv=$Sw1`{%wGr`XZ|juS^R9UNwhZ+FtNRJ+gIc z(2+~Y;&=hM^V?ERl7PIZ52xK6iZm7Hk3y_!Pt1@SZVJ}oRCk9Xk4$o$ z&)aZzi@&F^+>SjRzQ8!jA^(N*f*-W$A(@lg0+J_88EyD``h0{L)HKy2d5UzrjjQSu zd0`-F*08(}d+jETmkIE?lgwqUX&N#iX45>0`K-3!qfV22o{hV{=iY=QqoQrY?@y7l z4v3Vk`w{9BtNDgMtp- zqP;Gy(VN%##ZY*6h@%0W$!JG&(ppsDZ!)WYI;~%i}`?TT6gQ zRiPY@LzkG)thB`G2CJ@Jt@{4prP+B>o^;@#ZjH(@iBYubn61w5-;MBA*;RFF+EM$i z+4ipCmD8#ZIV6A84{=tDAjN)_S^lGRNdI)L2=86I>8wnCy2tIMhSt-JW%|O&59d|; zUOe?D;pF50D4e_(vfg)83l14o#BB282mE+*N=;OrFHjm#S9mq&{JSg1f6eNKvv*JK zjC(p_=g`NKrwrS6V$H++vcM~~M+NlRtJ+GIj$`ksqyLas%DnS%MCrthABAuK{HVl# zbX30!kLtGzR|*dDmrq^Tz?jmq=0%$1lt5N<<|JL+H)-KtLoOH;F4;Hjp_?Vx~#bAdJ{)_56K?#^W_x3&UQDK2qk_)fLXh^CiGUdfo%EBe~_H#`g z$@tBH-huLjc88GuFnI*XNI<3`)duS}KuZu@*fZ#XRFRIKL!u3rzktO*Y3i;ob? zfB4g2a>eh&G#u1-9uM+K8t8LWA!-$!g}iCOKlIQN?IyBX#{C~F0 ze4HIzcdb?E_`vSs5hdh1E?1tcQSoZ6(~GS;---5K($U}i>;WmLd3$2j(U%0hxokOfPdq zd@E(|FXqat^ZQQy4|ZY6k>o>!l=GhemX*GA?#d0Ai9vCIEFv<8PUts9z#MR3Zy`fI zfmH#Y0A>BocZ3)LN(5A5U<8ry#)FCzSOyN$0I9PqY&m&&r|9@fKpqdePgH+YZ=`Kt zx`rG${H8wjRG)gPPd)YL>Z$)FfF&_<;u?+n{`V$qtM`0dCGeVTWw{Txqm$#~kInix zy*;}-xZ_F%pOD5n^%pYEh)o zb0HyvKyVV*OOWy?e9Y82FutM613bz=mc0ntN|Z@DU1F0mRTHA}TNi_Wd0IZE*Mr#i zyOuwEeCjbVLwnJ=4X}D5Tdn5~d*p(wyAhqrq~Pib(F7B>VKS3LuvpoEvY;iTlZ6Oz z6rdZ#r4^C}!c-^39FIsb1FHbRLKOTHfCe$(=S`qv=Lw=cCTjRsqPhdK51t=~!xo{z z#T^}4W>hLoV$-DhKeJfRkyd5zT{Yc&DPza?j$HzoV_7b%@Luk?`^{_N@w zk2IKDt#|MwDXR;gpHz@$2o@_5a>QhUj}5{g_OOTLe9OK_a3@ z2g?>35gso7coxAk4PP>rw7u$&g7bIPd!L2(5AF*57wP|A&7SU8|MVhKH?477De(5& zM0UeU0=`Ek8OglF0+z-h<9Wv;BLs;KZUc{w3B<35A)Y9F0XVRDKe5yxRx^C;is3ZG zBM-Hl%V&+I;N0Di&kNZc1xkG=LBNBc;sX^hByUni_bKuFl=yv?i9fMS{I8VwBS*SN z`i&He-ILA2JRMr;N5TgmkkvFCaA>1N@`zaTCS&1E-{EJTk7+$aip@n?klvIUX2^?) z1uQ0wbS4(AumcGx=vy(kgThoqw3ZA2W+3(eR0~Q}*#1D}#CJPlJ57&eg9{cHS>hlCf;tL<4%V*}EKZpJY{~}=lpIi}s))-w_CF|*FK9bNch&N-PP4!M zsCn{uzisy%YJMcYnGjN2*)8?2b?UnQ;r^QRDRUM~dB%Sbmb*vqu&UdrVGWf@Sq@ss zrU@4&PG1?WApJb;LB+||B{~z76uc7DKNOL2A|r?Kn1j4$d|j8mRJ(qNctlXG=C-oz z0~y(m_pYc5-1J)f22Y6(DRi^v13jCbN1ouO6e~o|+VnsM^GL&I9jkwQ*v3+8pOgIpWNIKPz z$kKj3+om?aw^Bd%lK9baeXpTs(|mpMCDs~Vtxj^z=@sW)FI*1Hx^7+)FgKFd{zmRu zZOeG=@PP}BJ6QUOg^Oru9mbZEcbpF5)|zi!8MCU?rhor2KAWXC4&RRalT$!}IDEmr#{hJP|n0d^gM%5zY(v0!s)b1q;#;Bzi#VCE|lp1-(8P z@Q}U=QJm0oumQ$`@C!Hxw)!W@_WLXlu~G@RKVgxszu$TEUvU4R+=jC~?c~2#B6y7} z@j`d%YPPo*#bfh&GFH7j)J4KS4HXE3!=aIRfP^9e5?T*%@-ZPLIwN$Hh&VyP5n)FR zMF^h(^#>uF6NyM7D02l^(6IP?LePR14spI3?joiC4H1Ke!t&n(bsW&kp2l42+=` zB-7m7h{#MT!lKYJl38rnC}8&jbn#aSXA;H)1b8w92=m4G0_qtWC>ty{n5Z!1`pford;`JqQr|$@l;;mw-$V zPZ~CZLPSR3u;Gda5d?HIfV^REM^-d!?nI_2h~tnuLk>WN@01dmew0W+=;^x)jz3kU z{QPkAp{C8C*M&xKI!#Z7W%kD zi%HE}JQ>s8vsb<=xT^hS;n(~pq-XKRP!$Fx;n_xLc>oE+lo?PTDj(AlgyP}bR=}o8 zOJvEF%01yWhzaJS91?gC8WS>SFp3a<3{DzRuBk%c$@nZ9o;;$Xdpj9Bx-&L$Gv1Gmgh0f{zinjPU>m{{5bgoFSsK(7j_32U|Xe1yKMI z$3taLAO`z<4hRBF3epIG*(S6BZkYFjXu_a@Zb>M~d6I`Ay4plN7s^4GZ05pym%F6n z(TEL323udTKMpA&azUkmngtFli*%*DNm~RqnlnnBZz_^9Yw<}*PpgvGab~CEtp&)0 zi$BN>J3(NYhyV%voQ~`WjGKW-qEa!Kz;guh1*DWg^bTDY1+ZN{(i~v=LlE<|MI(lZ zckgY&n|7~x5GAwa{X5K1{%bTs+L3bnvLo>lSKt z;IX*^Ac1KVoFn`&v|n0wOq)Ofrr-*NKNcAgOg4hjxae!}FVM%R9^!R@08n5Ic4z_! z;ekKEd<&E&rvaC&yUra{+@6Ne@TjNCJ8YA5-b-{cQL z7nf~z^H9?HP%|OE>4CGyMX{2>Up*?9|J62gZI}9->&bcp@^&l^*V~(4fBPzJB<3mZ z#bZCFigNNo6jD~izXC4vQ5b1YeEqxEQuVpy&S|rW>WFCt>*;hjjFHHU)|bbHD~=e1vqea6<+Au7qX>8s8D9jpqM_W;pxk?N3G6Iweet_; z@9ZxA^a7>l-`5o=9)L5aaWS=JU;-lG6M^*znk?jEeJs&q3#AkuN0>p{{Sh-!YTH-op z>d38a9{S1;RKL>pb5nK42Us3hGGTES?IBlg#HvYYIo-y7h3h8T6)k`Nai?6`TDA9I zgZr~rtEed0f8TP}m!x-9x@6bjgNlz9ZPC4GIj#T412NIcwb#sF&rFzR9=*j(%-%J9 ze(lkR`@M88ul`ur%CX8FBO5WNWP6$0rjh%4(xNYnw=B@PPhS@q|4G-*!g$%6rN>h- zGa04mG2O}Z-Qw7hVRAy1LBli8+@N`VYFOkWc(GQ#V#|ZG`9_0cKYd*P_-4npjnMjQ7Z2;)vk}*;}87@ql&*EjzRK>mr&z-WJzkf8k zpQJNO%^ny2R6d<+c7uC6Je)E5_*7D7!NF%QrHZ@A%42%s5a4#BQIG_U9t=AeCK$Cu zf9OU*1PfS8U`C>Q04Pq5lavGlWvyaPi0+ z36G|yA+AV2^yWW1M!dk4c$GVKfrYiHiH(UhqWb{9Wm8aipk5_I76yF)`aN*x2&^SE zTW&pIOZtXch=m5-7o;ihU8sCmjA4_3f&q#$g7*ufAD0fvI;Ip*3m}};Z3w90A~q%) zh@j?E`I7Ae$7zc{e|I{YnIVk-v1veu!tkL6FErmAR*=u7c^;LWHQ|cy&cdDcw0!T& z!C!U>WA`_{fJsU3hVy)LiToVac+br6s^+#$L6sj8U-iG|J#y3gG9SH4=M=de8za46 zI81o$BDmJibl~1FrNW;hI!+|cX{tXUanc;jou{OEzF6!*=Ij#>#mS^j9a?9vwRrRX zn?=#Q)Ne~=F13fE0`Zs^w;p{tpkt!NqI+`|=Z`hX=-kurv2?HF$WK-aLUUtQzhz8K zf0Ej-JeGP%RO@#@vpgp>CfnWfu*%sXH6K;pIZ&{BOn8Y|Aw&Gf#mzDVh3 zs!e14)7?(n$x}AAYHHXgkptJTZ4#P$Dp21+{ndkV{*#0HJ+-w|&l{qBzhjVSZbjxq zcA+*oZhHpCk2O=~J@q7qvb;u``5R51I;)}6pCrk6e!5z!nLtjFx5z>7Lq~c6_R&}d zL&Jp;C%_{Z?O|SvY-KhZDFJjKw$Y_U=p@Ds5wQr-*DC2B-c(3)D&N8 zIjBr3J>9B8xfp({7$SiGdI5_~t|{yZiT+nO3-M}K;sz+N4UO7#kJxQF8VuZW_kqFsJ zm_uR7!Gwhl{$V0!2|>be&R}$pu@(vvREAKLyJ2dCE`-Ve?Vm$M0x?~^vo|gi&f*2O zX1$ATBpu%u^(L%bY)R=rW%^Id!RPN+s!^B3GB(=7+^s(EJ9*Wy01~BMuOAiJCm8V{ z+!z}nv_e4fLa$DRR7r?cH!fUcBq~EM1KKVTrrEpd#?TBK5G)7)=j0RkIVfYPFbmQk z(Q!k*6gcG&UqZ$TjVx9yG^~?^>_OO_u%T=MP_s9`{)vxjtZ#E}?&HS7?q$MHJ7ess zLXNvnKUA>jnoMx(DWzoJ0hUJ&_j_%+QDAk+-l0B_BdD>=k%%XyA9xmXJuZBC_$iWu zra}4<|M^V|n;&hsrr16}{eb}AYVox+mdGa?@eD95!ztDU_oB0vPqI8qilSITy${3ZJZhMKY#i%5OyS(Ud0>~sy~ey;G?==(W$!iHA0Wh=zX+GA(0OZR(WI5ebiPsp?U zk!r^$P1~+$V;CR)_~viFO{U$5>}io2N9xwg8;p%U5h(M0;5I*(!9z+u{qQ!ATFhN2 zlV(t=BwQ1wJjnY#qd7>?!r|GAu~xC6Ge`C3+*qNm`r@K^X3&kO!J#Mg`enQ|Zy0!O z$QR8SmtDFHR(jv@Wa?37CXe)9Q4*Mu^G5ep{d})*+fj0k#%WhN1G}fsx+`1wD`KVZ zu>R^{t@yLU0(hl|J~wp>%hHIk(%UMjXpZZLfO99Re)OQtguDk$qEIEvb(Jfoi5p-4 zu#?^-?e6-rvqIWkO6Do{(C5H=!iX9zlN)4(T=@Njh%zU{HIOBuOfgU}1kkKNR}Q8C zS|;rGz^N2L5|3FwqT)fHyAX(|y2~=XlD}_lf%`Gz|IeCqr1E)wkNvMV$bQ=|>0}QB z%GlJ?ZoY6Kc8?6WWSDG-CE1Aa#R7p0=r5BD4I~&U7-mB{(_4^mIU^WUESUaygaI0& zZ-yZ*b98Wg`3OM~!g36I4lL4aJ`0i^{7Y$k0fBU2VBp1OFr?c@PZG-Bv|e6yNBPp-7z)laf51!ha88V(#>_lzM5D^P@#wwAhcO1;tP;W3XBdQHw;jhaZ`ug zTZ9xZyl;pfC4hcZhzNN7srYpIbnkwBy7xZa`=9IH|BxE`t8Q;9-S9B3e|Tn%8FT6p zg(KFQYz3lAx!5EabZlf$)Q-0K0q+AB$hX|0pC=_=54~3{-P}&LRkS#d+C$-?hXh}N zFf4J=XJIzw20066=p1;XQ3=4M!U_P+7*uK)UMTps=kO>f;8^BD)TrJdxsWhZQ(XP^ z3dGukIw^Y3ony7XXZ-oMZ-v$pvo^o_2~>r1F~2J|p-wWwkRG^V07;3sDF#`9KzekL zLtx!tWgPe|0if0|jmBHnR{Nu=J3 zymI-pHy`XpPckgH(7m*T-k@u($}Mntmw0^;Em*= z562%xL#@VLn&2981bC!iB@25gR<@9L;`3w?a{{=)fD`1yfeNQQAa$Rs5%e2MQLN#LoESlsbK&tfu?m zeBWzRkH5AF9Pe1~$^B_9UFb`ytjA|3owUfvQE@gQlEZqK!o~WSz(UfoG=}8?&t4FS z@MILwk)wvMJO&KCcnDMQT!sM)8y^G*aq#7eQ4B{CAF55Pnu0DB;t@!E8NQO9&G;V@ z)oLDG{PjHi_u6T${d)eKB}-P4c}RH_QnA=ZtcVb6C*$1aVJ9?fiP}?fEY$|sBA9mZJ?YWD^iHBRYG}1$Ag^1r}a6( z`ksh=PsIP%pNPF-kytWW*C*R9=^uH1ulkI)%cH&x9~8DmR&$$~f5oW_#*NjnP5v&* zt?BUvI%`OWAE8i4109XzhxFWJ(1@cnqZ(1cX~P`_*BCs4L_8*fxkVJDhlq$ZHHFYD z!PCp7^QlZXs2M4cUNW#;7XIRB>+R38AQ5bAO3CM|QBdCe=T#=}+RxC}l7kgm!^K8~ ziibu9QiKn(5rs?>K}?B90*lOJ3NYjqLR^ZmEuMaa=?e=dfCKYpn|*&!pvUy}z6oFoy)%pD?5 z*gkW(cN!z$X~lE*8J@x;ei;<|iL6*ti(_Jw6@snKOZVn`E7adr@0c{T`)mIoO;J`- znYP~2tIE5^ouGw$4z({dQh5Jm8DsH_M)^`Z*URiHC%9MFd{3}_KJ?V6Wu9mHc|F~{ z{^($5gV|;?eODTMk)3$T+AH*M_Noat`b+&@iMOL+xld7=`e5{-O3RfKYUT|m^VU5cuJk5z^hE`6km>B} zF{`G`IW?(9b>7^pJlX4UKjpu-4c1oIcD?c5d~33Ud0ONZ`OoW~H?=t3iPWPXbdxl; z#BF`|Mm+Gy$M3_bHTG+cFgsXjXTl7dhlyj0Q$Nkgy4(4CsuCX`ls)xb>$tGlX^q1p z-dpCa9(nN4)3)|f)yY-9H3wuHD{|QQFAHAE&C*mp6f!^dzSgi!_Ac*T_Osj? z8Vqczx6I@X*ifbPDAc2uV$ye1fAy$b{^+QFub{YQL2KkAOk8%GmTP`XDp`Hd=ccNx zX8&vJ^`=k%N@XOj(Vw)k^Xnqt4|hp7zBH!1mhKQ4r7DXP&=Y~h25&xC0YVYX{Se+z zAa=+7n`lzNa)djcf$cKdaY$yER9NNVm5G+!N6gFTCB!KC)2emk;P zPx=r(Q+C#s!mR6?H`1>7+)Q=fxI9m>&U~nK<>FW$dEIEM$4Uk+^2)NQt?erhoopT= zK8~6csd9yBdG_%Ddq(pa!*{Exv>ZuKns)NsgToT&%i63bTyxzXP*vsW?lm;;?kL@L zd+X*JYb`Dca$NW*BHZX&TU4&cym6T$cUt{CrO_SMU6L`YQRE)4ctywHd7V}2(oao` zW<0bgW!RjacKd#N3FnRQ%KUz;&JUwT>=9eui`qXRoS)vVt`lbvFxFV>xXlC4 z%(*3xj&xnrs@qfB<(^m|U$T>{Ls58Cx7D9#fg1t+8mZ-d}8LjFpqip0Usmc z(#MeMOOhW}Ny(dx&dT@nM_AWTDEQ8Xo1FMg=it%{7bV=3nEb)lf=pKi1@i%zJCKj& zMgv&{UKt+X9!z3_%3`5EGFfM#0fT(_dA6r&I9u$k z!*V`RErVAaemKnI^6J#L9hLc7N81fGl#gG4zleEja`B?W>cth$ha1>0?>BgpD|v6Z z4Vj|uKQB(j-Xp^C`Q6z~qk2$pA*Vj>ZvN!sP}BKiY)O|{(fLWQHlj)L|D=>aiu4*^ z8J6ViH)DIF)!O!V&vymx$|+g2!>*h^)-ahAuOWE3&x^TcU*VWe_jIkahUUzDwOBg#g z$*HwdqIvh*J3kZKyG~^t%VgKg&}?;k_}SOf@5SCxLmOs%Sr+=3HujC~+q=_>Uu-O| za2eU9b~)4jPV<O9v6!&|8B2lT0h(Zf@(29xf;|mxS?UQML4p6x)ZInEvSyDW?-5qwPiFu_1xqJ#wYjk0Va7`+m?$EIHZd4XD*l3v5i?&m3W2?ZtNbTy{`Bd_rhKRxb&a3yQc2bi-4UsQJbC`oRg?9U|t-z8_^b^^$Jd=x%}N z%l?J7Q}3={ynboo?UtWc-TJc*B|MoWI$F9|cgNCj-}miPPS7rg4_x2N+iXIe7iGDKg6$tQhASDhE4c zvVmnAZe4n5`J7htDZ-eWcWv;7Aq_uEZ|0D!O!lu2QT<<~qOrlghpHI7A$6(47kTSczu9iWJv~Wb-mRU}*F8~P+f%vw z4(hKSl*^wS6e@Sm&U^Br6LFtY56(M&I@WuI%%aU>U5;ns>pXCthmyg7r-wf>U#~u@ zt!3wUzH1C=-}dmAFQv107|rbs4(D-^jEU|Io;rfCM8*HW`NnjLVDut69C86Dg|NcM z(g8MX+(Fq;5r7s)0U{2B-d~7Lan*?(82A&wysC?*YTy0$g-O_sYc=nFHOWHJ|->o63eq?>#-w3|w?bloa;DJ?^kcvsee;L=TQ_+q3gxwWH89KG(}uU3+-! zfMc)W{zjH6|BYWnrnO)FycjE=-n>^RUCC72YN}NdI6bLc0WAo^^ z5JRGgEk6%TbEGQ3*3#3b!Z}O@36zS|8eDf_D;817fR@5!j0Gmfd=UFU{Dkc*F7iOO zV|PiX2mzOX44w!B=29h)Kxv6zJ*&95YQAyEtHg|xn_9Um@;0g(OBopt8c26}qRUE4 z59GdktP!;(Ntw3E^^WTI;L2m6lTJ_E5$fC?@6bNGD}g1Sxk|+K_F7Y#B46Zkl4VyN z>|mz6C(Kc>f^67O%^aRBcTVHO)RhlS2h=Hr-u`mS!LeiYD5K}n;p>m*)n66kg!bM} z9Q|dCj8oU6xBE-0XohoMM6V$vnR~9uM46qQn7V%2o=qF7$(!YJZUwDK?3n&CP19D^ zCB5IGlQ*<$=t_5|9dm6vmXvbvv6F%ImQlfP+b+x-YOra-T9z-}d1Rg1u*qRJSE%ss zjWi#*+fc^f);&h^!WKKLAG@Y)HaZ}FWysx9_+`r>k)^V_{i$w`Tb6oGj|_+840323 z_N%+{s@~;%@KU^Y*>;B^#p(*5S6EDonEIp4UNnrFoDs5y>AGvqmvFjoC9S2v{`B-! zJfq{uAX>=|cme!JFKK({45VX#1445(-U+F=Aj1fLhbjl&0k0mv9cG;nAFV%i7?AlRE;F>}X=??-LV%9)Fy?Ho1g zqe8|!&+^2gs;|~p)*hd`W~11CS(ZdqaBX7(v!+8pS+UlS8})R-S*2sKX}5>2D;O~P zTk_Ak5$p0ktUT?x)J4RO$XijpC2o&FY~AYvS;v+Jj(@@(zUQOrqw5QjS21n(N7i3e0X@lhfmaQRqHmP?6kb!k%ILrGQU101dOX0F7;!o-+-3y7lvv*l z{hBq+^~B|K>(Yy7pTA+6|&5huS*;ZD!R>>wUs7;ydRXXH`nlzAM zxK9`#o%Zwe@;aZlxo28FJH!SZQntO5`7m=LY2Naq<>6eVl`^~Qs&vveX^oaW7UurO zUip(!@_^N=_NHZw=bZKEjK7n|T)qCs9L+ZBvPmnx?{ZjtqiNNuEp|5sH1V8#>r8BV zmzjM>^;eI|<&Tc)cQPmWoRvpMLS}<~MO4X^rf<8;)%22OH79(To*l*fI4(@??B2B< z)1zaIjq*vk$Df?8mAd8_tyY}fqcfx;l?8()(9nnhm>a}g@YX?}fJ_7EsGu*z{0xd` z#9_KY8cxMvO$2WUAM!&2xyqDKp8&8JbF^d_sp?X*A|)j{u?W4G=cyu@V&7j1*HmBH^{;%JTgEuFziBDhPzsCPlT#N=+kkn1EU0S zz%=wsEKp;FR5v|hrbh#`miP(&D-;6Th#rX!bp}?l1j`0QU2PR@@lCPzi;LZyqMY4P z#wG_o9&s3GbUCJ=BXdZ`sVn-kXan=ct<#PU8ULz_`Q_U7mM`%MHIq!O6>m2>e2QzT zXr~tGT-UPB@-y2}!%{f)az< z-E#uIj~cI7FwA#g=meD?Byl1h6w)`}-qzU)7N4m1@)?B{LmAe2RP8f92Ov4#^5Px*q@Pi2NtYeQop~Flqg!5qX26%a(q!t$AK_ZCC5_ zCv7)gE!>!%WxKV$dtmN_0ZtKN%mSM-#_}BJQJ3YG=~$w#wy}U+yp5 zz!Yyc*4N9++*PFJJY?J@`ZE2)c^l8f=}z(W%PaEQ6O?ak5@Rt(FUi(qM#Hv5l9nLn z>)^T{J@cl%gZirn<@_fH^}GDIKJWLgF(z?iz_5X1_xa4%nitAd`bmn- z`he#nq>ZAjSSClAe-SxQ`Hua`6RG-{~ zpP&c&6~~E1B|-G=v87NJS=u7>Yr^!_AR}aW`Vod(3}a#(3NnA;P9p8R*iGL4^)ZK*bOX{$C{7prDHINiijF z24L89YJh;U+#<+4sK7ofdPL)f*CBOsUv8+_pMJ4!s`AI<(KA)I&-3KY&lp3t8ff1X zcv`T+z2xajW9QDvhs+M_Ix=6JHt6k=JDWb;e4e83IcV*Z+T0I{mkMWS?m1Q{C)v0m zc=HXN`Ok~jL}xe0DHN4?$=v3L$9nD==>K@k>*slE`-wGBfC^RxrDYb+&U=%4v%+I< zTET|TvlpQNEuhHr3suL-m-qWrHD*3zeDdS6wuE%v?fd>aJ%&v?tQ#8hR>L~VX?dH{ z{uE)CvtNp5O9HFuV(RyFuKEXeopIpuq?{4=RS$=N!w!_b-$X&g$BwnI? zW-^s_##_5he4x;lzCjc(joGfQTS0#_qrQ|okM<+t1I3K{JDHqmEvn!)o2YRPZm_C3 zF*D<&N}{vdQi@Pa!|ilm1ej4h4>FX6zg_5-BB(7#F6l( zhGtpKQNdm%YmYJ#okpVu0lLyPtk~wU@B<+ z@aMy(OX0Jbm^p)=iZ+AEBE;>0<|CpG76&XW0W={b6Y$OczL4#{5Ufe6s%+{{IT3Pb zH)z!VwFBDMasZHO6r*0Az@eFr#H)W5BazU|V(mt2@Z2a^t?>ZLC*lUcfMsIx?M8(F z9w{-9bn%ornn2`*a>Yl0IgB4n$h$DR62Nu_B^RH@fDoPrv^EktS!^^^L}|js z4ct7)CKzt4NUxh(;uDQ6A;D3D9)!I<>bUK$QnO~V1GDvJ`u@UCD;7WM-m7XF(^ONf zF@EgjdHI=>c08@y_O>Erm3Q>u;-Cf2W8b_%wXz}_)k@HO><+8#n7lRO#gI89H&sgX?co zOulW{e33j7x?61W!)4R!&>d?ALwQ zn?*`8%~fyRv;V|QsplPI@w`{b>o4D|u77h}p5gwOn{f7R-n!uf2efa|OY!^F4N)#_ z^W(XOk1DyHrJCYqSUzC>vBg0H$KQDNS*oO_mX*Fhooj#7S!?cYU7z^2xW@ToY?tLr zrAXV z4quGZY#s8GroUicedw4~^D=6ZmZsYCOJgMQM*)85JE*^UP%eLPP>kQ(w|PqE`q?!t zqs-0A>bE&@=BV2rG_T{Kd-YpQuFGbJGKavH+E-PvK?wrNJyPIy)C#Hl6O4z%Qo?S7 zI}8^56dsQS$RpP8@RFhvB8I+vSd!qj;j)nl4!1r6NT8xXF0hbAXTTNC>a7Zc z+T|yG{-?B5Mz^!%>5$%C-0KHP@s+>ZqvqrFwM6ui4Uc9=nIul8|L%{_Bw-r|#TY0g z7*<1PAff`p3HvQ#m>{$Ool}S@5r+@)7C0<4OtgEa);xj?P3S4;Kq`S`j1(;hD_9Vk z(E)#=LgF9*WgEk7{JId@3JSP&A_}Xg{K{bz)k_kn8s)N1dyQOOoa6b{$ftunoQhA( z_#PCa^>opa9V6E_Uhx-xHT`k*R#f&OjipM=%PH0L>0=*8j=Zk3ZLFa;#&YpHyl)P3 zC6AD^boUsV>gdIL7@otbE5AHtOYN(dm0^}cBE%k#c9jlZ#!&Iwsbf4yVt7Xe#z1p#hy4OADJB#e;2P9qp3?YmLy8kg&N#yC7|_xeQ= z=sT>xdRQy|?67{nSu}>j$2C zdDEv-nWXL?s1;K67e+3M;%s7c%>&X6xkXF@8-VF9Vln|Nf+>awrNizDdv#1&AQ#|Z zg9fW7;e-*wf`{lHT;MnY^)Cv8zR6Ge`nxvSEtw73`>yEhU&g@9JI}N0{(Ik)n_8MJ zuKRc6M1X|!m`@&;2!{jaoChVl=wBrYsxwgE7#LC$ zBQA&lSZ??w01!}Zp)f_g;{hWlgq;ZeAgV3;CO9<*!e-ClQkh5OC+YMGz9dz9y>vTs}wn$XUsh%+iLWeR$+jKIPFtCf>4QHbtQgqH{Qtd^JDsuF=<0i z6`w^GV9N<*E?0=YlF1>1PYi*u5WP-YzACYHVZVV|V+&(vDh6Ek*V^^ehZOM{WkG7#VPwhaal>P31)UZ$jKDHM zX9-+6SaV?Kff~Yv=iCim0jN#k^`~O&#RvGE$psb$R81xZaL9w}al?x%D<3HK!t;Jc zH+Po*_o5wMTK={CKWg^B;dxZH&uVYqwd;N0prnCF%;u3%q>+&dQUIvtL|Uo{+FZ1l zP?~`c!=xcj4@6E-OW;Gtq#r;}G`(=egZYkP1alQW3mOle0Uk^N&;Vmw3K$CPOdJGg zz*o!vYHDR=YJTRDtq6I`p|x7d3(H~+4V=H+&a_; zX&J}(p?c4h0K6+M8?X{SV1wYM1AhTO6|(IJ;RU|wp)SL(@=?E`g~d=n053WsqM*VD zd=wc`B4SGLRen+%I&nv<#lQ3P-gh~X0t+jQDQ!QWfBkR9fr9krD-R~mk*wHdIn>k0 z!eNf3Etx=vp;!nJ&`jf_?S{*Oi*zM26m>jE>DT~*5aCsn4YR1=bg`glLEU6}XF-*uuyM4;*oM;lpms)#Ky&0&gyoKZF?d zVl4%GF$6>)&$w|gxM3p(S8^d_p|&_U}+D)_bSVM!~lis*jD_PXT01YI{oTov+Iyx}s%Il#Ubc(LZB&WeC^xgToW^ch{aazbbv$@jb@L zbmj4Hvwn?u*WOR;w<}#|mDAjp3;jMcWGPj=s~hz+D&x`;!AgVcB3}f1thg8mPF_#lzvk?p@QA-~Nd&T>#BQKsmNS-uB z)E0cEEIuO2D`5hEh~3M_Dz)>z-#qZ#P}V(0@1g34F@ta0HTvG)6~wa*_5AUArpuM$ zCLQ;zxnC~5^7Ni|KF#K}d!Z@S$uy+sX!?wKO_#WB{UcppTi#nra{s2;)Rw5DKZ*4 zr=9Jk=A#Q*65l)vx;y*lLC!&WVcY|eT(yKv3!pAKeRt$DzVLfEiA@s=eMV>aUY~8J zE$yfb*m_vG=|iJ`-l*f&rM$Pszbeo&@sfpWl)bOA&1{)mmEq=Fif_`5M^B#<*ePhM zW1GZpZ?nt1X_XjW(q*{Lds4u_kH@kGlOmU`SQ=VqC|hLo9!ybdP9Ns0akPSc)#hae=;a93r);E%!r-!1k= z2qr= zfn5R*0caxNNwQfyNvRE6Q%y$qaC0u!M#S*!&5wpxuO!#}$7}RIx9bl6_Rai}8sboY zcUO>Ul-8uUYR z#0JL^OBfDs2gOWVe&pGg6*KZw3oTdc-~aet6xb#sekar8SL||g&G#wE>-L{HD0_9hoxPUV zTKBqN_x*f6ouYm7TM<@-3Zorg|+9vyq6@aFvx57%~M=NR3IPj%9d z>B+Cn>Dpy3nRoiwEFLQ<*rcWSo_hK<-LC7O#4?pf0?AFHcT1y}x7hPpv@wPoa&A~CPpn&P7?SP$ zc<+V$73|ABhHB&1ZqVJ5<+wd=$yu)6`CQfHhg#bgy4UQDJseLSF5&F{ggI6~_^-4E zkLsU2s^x!mRHJ433|Q(zO0AziV$o$QW2NK0A6qA0vm3i~1`@h^-%eL7p)7JRiai#4 z_0WTzIVD?7s&C%!e4T$H&_Z^Eh{z8D%mb|g#C*gY1@uTZR;$>6Kz~f&KeNMJzztc4g*A3ETxg$3(8jQ^0ukUshW{Xm z$!2l{D1Wf14$OtHpJpQ&7ji_vYl*8QcDjh92L_YJIf_^hr&Z_c-Yp;cLHh5@R%48%6FS>NLjudFl{47*)ad^LI_=T#153Dvh zTZQXAQL;Kue{b$=`BleGJ8JK;F|T{n^tK^8YdT-W&0jQp=9C!ygjNIjiL4>ig+})I zJ+41Ky_1*gRZM!~5FVNs?c&ntr9|CdnzggUu0p1{G3!`yOiQ$AE&A23ejSz#`HJ7R z#Bt*{4smf^S)Eu|JHkv-%*-|2i@d+OPyz!ln2tzEC%>{hA` z1KaMe%v&)o^SkoRnK!4*+WND?T=MmV$Mg->kJP7*4x+e7+TLDf_+8=aC(Wp9 zZv2Q#&5IPG9<+#y7|*Q6T%YogHNXDIa1YXikfU!)6K00QYZ^|~niL%A4F{Rx3!Vv2F6#FY+=7Te*pRZ_DV~Z+m6yj7_c|IQpnEKPSo}UwYP2R424T zs74$pdZ9t4&;a>?ECqo)m=mE@BD8^+4s$SSAy$n{E*UX}989S(N&;R@Amod0?wzC| zDm+@M+^_dbJ=J{^TI7B^#L))@N9z6%HwcX%X7DsN$mb*q1RyAkSj4gzK)u7b35z;l z>8VMf^6nxbVGBYK~g|8{V@X?@GbaMi8K z3l_C;+V0M_8#g{rNA1w^RKD{}`wtQ-Z1d%fI!gqx{+z?p#?&qtqP6&PjYU}2ilUA; zX&w^>6X^dUf!^S666pITLW2jjy!g3o)z1Kn?31dwQ6qAXM?d`%-}EiUcH8WM{?-16 z$re7>XFRhoY0mGe>KEzVD|ESM<7_CTq5RPGUB#Cts+FZ1MMNC_xV<{_^xKP?7u_j& zN7hho{>4Ep`hFMm8NaB-PB~;n|kf*Wewxuf$*t*T@XP{AjJe<@;%S@ zezG~r%B1`|szN?@tHp{@vxuA<A$|vbOd&Ox4uTPCwN%A* z6ByDWvQMZW*n2ID1c3${nP^CEj&dwS{-jm{>=5lk$9|J@KQ?yR^`vDPzK7 z&nkWB`EYVgiRyWgpyy)JmmntFT$R2wS8iYOj@s=jqvuyFRX;iO*LTY4%Z)xoXXbZw zv^`avlu)AR#dB``9^A9C;>hUo^R~tNa#hZWqR{))Mc;U`akls7$l_fyw)hV%XdOQy z1HF&&$E~NLPOVoa&))pdrfrA*gz9Yvcqd*hNy=`|qMa<04p2XzG|hhV>a4r5BZ6m1 zT1mwno4s(I!_iw&>gSU=+?g3ixF$KRP3Cj8n`AYO7q3p&oaSYGYwD7rIgt-1Jv$#8 zQF>&CC_^XfRlDh0YadgUH*a!pme?zt)em!JhFtg1Px;gPWL|%(ZePuL6to~f$~;GE zQ_P5^kB-&2S?lFx>m4^)?2vkVsL8c_InBG8(o0zDmGor49(W?29OIBxUfpF@U-bLo z%gN^LF0~$}0qeI*%4;oprknC?e*S)jj7P1Zyh%SFNVkV_dbJ1^^YEv=&z1y^w54ZMqpLA)F!9BKmDEj9ECHiC$|o< z@xn_zdHbw=1?#J-!k_1Dc;&0(6(}3|(d6fg<}LMR@_m;w1g0RI5N!f3Uw8{!(g`Ow z8Te^SLpYp>OAHKHuzjQ7V4*(nkwz$hVhJ{IB72%55L+iGYbZ5es2T{Bu8J)UZz2ZL zqUU>l|Jy>05D)(=Z_9k$8ZKiM30Or2($PS{z!i%KaV5e zp-93JLYYX&K#+09VWRZ&0cYZa?8e~pSc9P~gP|;gp)7wN%HpBdqfKs&Xjo1+8WGec z%Lz7Jd35N;<(DT;tBM$M`cuNDi?5^eKK|(()n!8@W>X&w8GA8kPx;<>j@O7PP-|Ud zRNYt5&D_rg$B&K+pR`8V$LG4qmSUf~3q0yz%L92vLO=zjl1lp9Q(9+AyZURE^~E$**5LG zG-ZRzaLSj}JEp(fC|G`HS@o`@vg=8QC$84g^;?<>)M0AJ3Se3xcJR{ zZp}umj}JRv&ZuJCSgUU+UDy(%`|W#Zp=9-$Wt&>{wN_PU9`Fz~;j{E`t-+(V-CMlx zsUF;I)DXD8EjT?GZR5JR$5QWKdTG?OsQ#Kxe9INrMDoRz%irxX_Iv5#Fud-EOVB-= zRrYlp`sdN1i*}E=qmnsCDP`-KDIc!-BriQN8TRMlxgt_xNy zunD)0Z@k$#Y{{JMJ&PPfQ`#Q?R0~+pC|4|>?VV7#z(4G0uk8)x#)|Yd+kyL#+jtv! z#UGmmGs-?mhfNq=th1&pm~>ar){fVc-q74*;~{k zdRJ}!VxPQx`sC0PSB`ajnVY___Q<@WPae{G%JkhcPS%NQQ)jn!8!SmS56SH=c=R|t zZJ6%Hno5CN`4(e#X5&VCr=$#6UM%J`x%ORL2M_8WJt&XAIjBEUhT+a<_ONt)uC5-} z8fM)wYC_?LH?nx0i-or1=EoZJMvTf3E`QP#b4>Q@J(IMrUp_YGx7@LiAJL!C$|7P8 za5E($V==)OaFKcKbeFI zMD#&bDP>=4#GG`Ns;H~7yXL=IpSg4RHa6?vS0F4<&@>P_TU=P7U_iDDG7ea{7GpLq zr2GNYVoZ<61d%<3ha5m8%)v*&WFx&1e~0lK1f}TXpmYW02r+vIedXXH42~`Elrjhp zXAOYUI-Gf~KP@Aztd+-2)1*%_H@cLNW}#9NJT~W}8!KYu1JA@>uC97aGId;G<_D+4 zEAHO2i4REpY5ZY>s&`HN*~626D7yuCv?Wb73N1TlFXtDs`QG5?>EC~z8vfPiY5#q7 z@Tgpy*NnM-#p8US{bD;arrsL)+~?#_RUD-mE@1{q4|EMef7QPCIFDQZOmKj>?e#q10mR2wNgp)Dol) zh$Zo^Wf0MGh>wK95)2_^y5Sv;ODP*kP>2s;VnENNfu_yiG2ur-M75C0!&OqHM?C*M zNljGI*`wJX{M1mAc^`8#MJMZp_Vb*q=X%btcA7K&Li+B5wL}{v|LX9Xsq*;){@4CC z$eOXh(G7DmBCY^=Pb|QZ&<0VtU>HHf$%o1m5F-rP*o4N4vTnK%N!#%3BO|l#LWXoa z#7W^{LwdynH4SPk1cp-?mhcS0--HBIXyvJV=phKHJa&+RK8t?-1wU;cDXQAcsOc}- zJAQ^GNbrYVw|}OnuXrE^rTcp79+z2u{K}+x#@FU_=FsYzJNrF5tz+jAs6k7_c4MeuB zC0+#ZLld2@kjy5$&mhq8;9_I<-(dHj3f`v4+i>gHaIddbv-FQ_sz|0PWw%{<9&)=^ z)>UPm~=|1w760TXKEO-BG zUrtSDq#zOeS?3ae{`>!J1b%Bs}xU_AEaL28zZDiZ|{ZIO?C5Gx3S>emk_%8qB(r81wH18q$~Hv zE~txo^LxjROS{t^Z8=2oU7T8KeRg( z`sbKE?xsN%<8N;-Hac#8FKpD4NmBDSCrP&a+~)txKxN!XjcAQ_>O#pZ*>N8>oj=)T zcy`K~t1QVb>Xf!-n^wjW&9m3#qmv&{Yo9ghJACV&C^#y!cv(w-86P~VfApw4|LUmF zLiX$NGqlyVH~BXgMmkTQ#G_j+y?JV`3<^%1wt<> z4jaQp8e!BIz_Uwac%#S)uqYv5D^%Rm>6omFBf{)-MBCSIKDe+k(z;BTw=wil@NujC zs?MH94?ek!*%KMOdC^q=PQ}=R>mEK1teiONhH<1QaZ<;RlKLmFXbV4#ReWJLN5*q* zShMa@R=8xqHs|~w$FF~`NLx;x+i>Zfx~t9pm*b6Pmr)Zn)PvcgL$*FCLD!$|eY4Ni zu58ABwW0&7TxN*6a7fLHMv-efZnKvt$=8@^41H5bUy*}?6qK8nZ2tI0R_Wfg4*ur* zlhl59NIYpHFTSF+TA5cm$>A$7|ijr+19 z=Lu_zs^clQUk6wFqy{wYkoYqE?vRPQC+@V`^J81+@MqU&i{uZ58K%_!_+Hy?*2)X7 z&oIc9uMe55^`@u6e#5|p)+C|uz*hP#uM$-w#g*epw~IH}jforO5}&v*W7~^!aart& zeNIz~k5Po7bi-_!YezlL?y^l?6R7?}l2M`Px#ZfFyrC6$=*r);>jaW(FK!z9qQmQD z&-&6S*YBO{Hqd;Qe_UhXOB1gYb+UxDT+^)RTcS9}sf_0AkL#AEZRXWaQeD?~p&dM^ zfApX{{^pAtW6jO{s4Z$VNDTR9oJF63wd-iBrlo^n2BZEQYpm=q2J zRRaG6+CNO(P@t?zHd@p?xHv=9J+anD!9`hS?vDC3X(c8d!$(vsAPSlt(_* zooc!F#qsOEOo!gDt)5aRsWzOh6t&i-ttz&;S21Dwu;qyP9cI$ggr}76r%84h*0&3R z_n*T=pNhyVHl0YH=OLzw2Ph5j#lT`AbBqGlC!b2?LGcSuKBRHDcO#MvBR;5sd175( zl7{H*^|F!uVPoRnU#0&FU~Npy+x?oT+x-yHbXcl7KPz|NCw0$0lyEM8O$n-0O;d_q$5Nm z1<0cabHrYROs9Y(iWU=5JHRC4H}p4>Ne90<79x+Lr8VUsl>`6^?K6fSswoW9KBpi6wm(4_66@eO6H(VyXme8H zx@auwc311AX#v0L)j#!YKCZgX#Oh0}+BVefP4~Hp6iK7$pKQiO#J4ZF6?S;-NcPnY zb$1;;wKq#k#LCU`SUtrrb#k{!M*EJSFd?^>$gViC-YFbjq_$S z#MF2yf9%}%R9x!rQg*0yP4bgI|U8mug)89xA9@Rg3RLlSB zsQy*D&n3yMnNRwBxZ)E%Prhhw??#D$5J@%uKA#J5Lne5$(Zi>i3}-@@YP?vwT4tcdIxTWKTzkDsM z-51!;oJ9ht3lm2gp9BaOF1ZA19oJ|&!SDl3iUGbcg0}i*0}%QUk+Enq!Gb{?BNo@# z8KdM20f$G>7hxCWav=A^{swI%vi+zSabU1Pmu0~nMy2-Grkc8&XEr(>y!-j>d^7t9 zv-0I#{toJ84_2$Z=%!12dNCq4P)=R$)2}=6zaHI^{kV+v%qYcgd6?4!;WimRbFa*h z6Ot}zyA385U7S1S_1bqefzFdvEoHXWudsR*v0h^2K4a4y)y5&-cS#F4(;d3}9+>&< z*cJE7QSf;77tux=(N$44YEwe&!CvXnip3;G+RfDlZxilQv>3!CcjzMH{@XEI=?aqL#Dc3pKmMLN9eHP0ZZ4KSW-SwnE7q}hs~Pk{JBl0B#lyXnZYZ`?MK}uTS=*CJJ07O!6n* zDlm7KUVbV{T`cQo_8+D-IB9W||(l~;d`gmyF%>e>D?dR zUz-Mx>K{ESkH0!9)TX{@=sESHM<}OSWxqeIqob+1$!PP{BV}liBA#q^AFU+cZOc-- zSa(2w?Bid*O}2I3sC=34YDd1I+qb5LB2It?h>bo3gFWc?gm6Zo4Z$Us4Gj&A51TnW zmyl%u-iHFk;~^dz<1Y9g_)OR<#gu7e4ejr_jhM6%OOlI)58_|{5%|@leM_%46I0@C zHAEJ6Gi}$;pM{S30N(qH4hV7t*%*(G0K{x^oLAxcZsCj(FDAxVrzRJ9aYl46Ipw+O+!UFpm3zxij!S>1x$=?m zWbIzJ%8rELW@{o-zxD)4l((Fkb0Jq`hyt{ImW#Z+e%5|V2TP;lH`I3Rv3dIx1<1y+ zZha>CqOHSlxtH0xrx|0LEaypQOkQ8#asT&=p9*_EDw^%16@QVsl@F+(#RH{A=p$d_{TH zd%_g=>)c(_u70~j=Sq0WqXo_MSD9^cTLKU7 zERgGTnsPfjT|a_su;Q5fq_4JbBd$GOH)g^iqnVjI)z*!xEgv85XzdYYz4^-G9n*$Z zrr#!AE+0{=H!d!c8nRVhcy%2XZSG@U?3ucS|DU0q!}U>uznaEu%MA($VRWmKpzH& z8UZ`SMI4Z{-4_|#l?DM zsL*lcY!Y;a_yhKR@Z*q?fzW3Jg~6qUt2D*}48kKf@Dl{}g9oSphM0)!fISUERIIu< z69{=Zs8Hy1sYG%R;R1#?4TDw+gUTS|5fcrk5Y+wt9>rUm>|$!a|A5h3xU8y8U_#Q6)cRgN=CN^c{1uE6YqJ#cu&DH`G3bhmtEVf(ZBpI zZrKw5zXXTf$r4A3{&Q+scspsR7xBf{+}E4C#t$K?h;?P*ic5hO5xJ_sKO^c{NTPy* z3A;Y}T0lqo5hLg|dFV!I@Q_d-6N17I%V!2qqo_*28-ad=j95My@DXSLEhz}E#~4wL zj_L<35lvh#R``#hrABmnl=S$S}e$!3Dp5w)R)%HZ%UHb|HUSaKm&0@WK*0es$o z&q{m{AQy#$Wf)~F*74xxVFJtq?u9V@(fQ!(K|{cxiuGhIbVP4%R4;vcv+JPWewV#F z5)?lbjK9weC|{xdSg*^TEq^;RK4#0VO4E@6^>mBtip%|Nx)OiSoFwTHeW!NScDB2U zfBT)oV>z45yT-#%ddP?TbGxZGd3GlGnG9%%^n^|+-d|lL7 z+B$Fbh^Q!b)ZX%|s`7;*2Ac72)~}P+_k1r^KC1h`B;U#}&_lE96nNd z{KGnf`ZXG<635&wos9^6lJPuy*~+U8ck)No=U9Gsm^o)%>GK7njd!OnXzd-A_B{Fc z(N#GcXWxDQE#Jf0xulpaS5sCSuKL#cMEK5(bD74Tp<;;@3G;J2eR6$8BI;_FlvBFC zBTY=h#&k$-QhgET#}C-eDCAyvG0=?bk6WaSEBTy&+=(WqrkLe1WG88hB~FgHS}NN+ zjIN+KN!Ml0eX88>Bf;ahzYa23RAi=N|I9e5_vtlV)BDFBUpSMp%(ZC!#}>y|O-fx$ zyXP;NXTsz9HKW>+8nBTBc#4ZOMfq5#T2XhUi#^km0r?HpwuI zpqWLb;RBouO*L#BJOm>lM-d(wkS3X2u|&IrhSITLchpQe4?mD0zuvlUcRj&hQ4=LL z#rGu~^-fyzWNYdE8*aom^d}tQ0|Np{OJJU%Z~BN#$j@==swS0RWD&~d_WOhft!or|ym0uv=bSB@xegB$KaDZ<}K5tjYchFh|K7~t8^^3^tM|M;kK=K9DAmt>o*7xE<40>pfV zhFXodr1L~^*;?+{wBx=vOs?HJ^r$|6csn^;de%v_JwO%XO^q!x`VBsH4H@r69|dfrbfNXz@WSnwm&L_wlO+G(N;IGWv6DbH$&= zXJ7NR%nITf+u!)$-}{`0s+K+_(I4Cq1nb(8mb;HuB82a4*_*2 zfqEd?8f_u0G6CVQ1qCU|eHyK%GY?h5ryDD>1Opoo}u+ z2u~l3F&fOA`pe8I(O0u#<(D20L3WlfWN9vkl!I*2UPQ zU^2?*Td=XY;ee0_D+wVjh75;?pa<|g;k*L?o&~K5XfBWi0f2}0NUZBIdz|P+uG)nO z>y^4swU^Dm@Y^V`s`<=9?L(e9irfU_)7`ogm>(>^WVj#w@y?nRr| @e5x@T5r$JyY*6F#|^cXz8PMg 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

1DO|NAzD9}nsFe2hpGfw3TTNq0&zBjr6DTb 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

pkaqYhatArdTN-N7m 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 delta 7494 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 Hld1m)v?o&s diff --git a/package/linux/32bitBuild.sh b/package/linux/32bitBuild.sh index 0f327160e9..1237169d32 100644 --- a/package/linux/32bitBuild.sh +++ b/package/linux/32bitBuild.sh @@ -42,7 +42,7 @@ $JAVA_HOME/bin/javapackager \ # sudo alien -r -c -k gui/deploy/bundles/bisq-$version.deb -cp "gui/deploy/bundles/bisq-$version.deb" "/home/bitsquare/Desktop/Bisq-32bit-$version.deb" +cp "gui/deploy/bundles/bisq-$version.deb" "/home/bisq-network/Desktop/Bisq-32bit-$version.deb" mv "gui/deploy/bundles/bisq-$version.deb" "/media/sf_vm_shared_ubuntu14_32bit/Bisq-32bit-$version.deb" # mv "bisq-$version-1.i386.rpm" "/media/sf_vm_shared_ubuntu14_32bit/Bisq-32bit-$version.rpm" diff --git a/package/win/Bisq.iss b/package/win/Bisq.iss index 59c1343d96..afa0670164 100755 --- a/package/win/Bisq.iss +++ b/package/win/Bisq.iss @@ -31,7 +31,7 @@ SetupIconFile=Bisq.ico UninstallDisplayIcon={app}\Bisq.ico UninstallDisplayName=Bisq WizardImageStretch=No -WizardSmallImageFile={localappdata}\Bisq-setup-icon.bmp +WizardSmallImageFile=Bisq-setup-icon.bmp ArchitecturesInstallIn64BitMode=x64 ChangesAssociations=Yes diff --git a/pom.xml b/pom.xml index faacb3fa0b..64556958e2 100644 --- a/pom.xml +++ b/pom.xml @@ -24,13 +24,13 @@ GitHub - https://github.com/bitsquare/bitsquare/issues + https://github.com/bisq-network/exchange/issues - scm:git:https://github.com/bitsquare/bitsquare - scm:git:https://github.com/bitsquare/bitsquare - scm:git:https://github.com/bitsquare/bitsquare + scm:git:https://github.com/bisq-network/exchange + scm:git:https://github.com/bisq-network/exchange + scm:git:https://github.com/bisq-network/exchange @@ -99,9 +99,9 @@ - com.github.bitsquare.libdohj + com.github.bisq-network.libdohj libdohj-core - aa0729e5 + b5c2f768 com.google.code.findbugs From 7c45fb78ccab9480ab025e3d870b49d602a4f7e2 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 17 Sep 2017 13:10:34 -0500 Subject: [PATCH 24/54] Remove never traded coins --- .../io/bisq/common/locale/CurrencyUtil.java | 104 +----------------- .../statistics/TradeStatisticsManager.java | 61 ++++++++-- 2 files changed, 58 insertions(+), 107 deletions(-) diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index 79e4cf2079..48853bc9d8 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -86,149 +86,57 @@ public class CurrencyUtil { public static List createAllSortedCryptoCurrenciesList() { final List result = new ArrayList<>(); - result.add(new CryptoCurrency("AIB", "Advanced Internet Blocks")); - result.add(new CryptoCurrency("ANC", "Anoncoin")); - result.add(new CryptoCurrency("ANTI", "Anti")); - result.add(new CryptoCurrency("ARCO", "AquariusCoin")); - result.add(new CryptoCurrency("ARG", "Argentum")); - result.add(new CryptoCurrency("REP", "Augur", true)); - result.add(new CryptoCurrency("BATL", "Battlestars")); - result.add(new CryptoCurrency("BIGUP", "BigUp")); // result.add(new CryptoCurrency("BSQ", "Bisq Token")); if (!baseCurrencyCode.equals("BTC")) result.add(new CryptoCurrency("BTC", "Bitcoin")); - result.add(new CryptoCurrency("BITAUD", "BitAUD", true)); - result.add(new CryptoCurrency("BITCHF", "BitCHF", true)); - result.add(new CryptoCurrency("BITCNY", "BitCNY", true)); - result.add(new CryptoCurrency("BITEUR", "BitEUR", true)); - result.add(new CryptoCurrency("BITGBP", "BitGBP", true)); - result.add(new CryptoCurrency("BITHKD", "BitHKD", true)); - result.add(new CryptoCurrency("BITNZD", "BitNZD", true)); - result.add(new CryptoCurrency("BITSEK", "BitSEK", true)); - result.add(new CryptoCurrency("BITSGD", "BitSGD", true)); - result.add(new CryptoCurrency("SYNQ", "BitSYNQ")); - result.add(new CryptoCurrency("BTS", "BitShares")); - result.add(new CryptoCurrency("BITUSD", "BitUSD", true)); - result.add(new CryptoCurrency("BLK", "Blackcoin")); result.add(new CryptoCurrency("BURST", "Burstcoin")); result.add(new CryptoCurrency("GBYTE", "Byte")); - result.add(new CryptoCurrency("CLAM", "Clams")); - result.add(new CryptoCurrency("CLOAK", "CloakCoin")); - result.add(new CryptoCurrency("CMT", "Comet")); result.add(new CryptoCurrency("XCP", "Counterparty")); - result.add(new CryptoCurrency("CRBIT", "Creditbit")); - result.add(new CryptoCurrency("CRW", "Crown")); - result.add(new CryptoCurrency("CBX", "Crypto Bullion")); result.add(new CryptoCurrency("DNET", "DarkNet")); - result.add(new CryptoCurrency("DIBC", "DIBCOIN")); if (!baseCurrencyCode.equals("DASH")) result.add(new CryptoCurrency("DASH", "Dash")); result.add(new CryptoCurrency("DCR", "Decred")); - result.add(new CryptoCurrency("DGB", "Digibyte")); - result.add(new CryptoCurrency("DRS", "Digital Rupees")); - result.add(new CryptoCurrency("DGD", "DigixDAO Tokens", true)); if (!baseCurrencyCode.equals("DOGE")) result.add(new CryptoCurrency("DOGE", "Dogecoin")); result.add(new CryptoCurrency("DMC", "DynamicCoin")); - result.add(new CryptoCurrency("EMC", "Emercoin")); - result.add(new CryptoCurrency("EURT", "EUR Tether")); result.add(new CryptoCurrency("ESP", "Espers")); - result.add(new CryptoCurrency("ENT", "Eternity")); result.add(new CryptoCurrency("ETH", "Ether")); result.add(new CryptoCurrency("ETC", "Ether Classic")); - result.add(new CryptoCurrency("ERC", "Europecoin")); - result.add(new CryptoCurrency("EGC", "EverGreenCoin")); - result.add(new CryptoCurrency("EOS", "EOS", true)); - result.add(new CryptoCurrency("FCT", "Factom")); - result.add(new CryptoCurrency("FAIR", "FairCoin")); - result.add(new CryptoCurrency("FLO", "FlorinCoin")); - //TODO ticker? - //result.add(new CryptoCurrency("FILE", "Filecoin", true)); - result.add(new CryptoCurrency("GAME", "GameCredits")); - result.add(new CryptoCurrency("GEMZ", "Gemz")); - result.add(new CryptoCurrency("GRC", "Gridcoin")); - result.add(new CryptoCurrency("GRS", "Groestlcoin")); - result.add(new CryptoCurrency("NLG", "Gulden")); - result.add(new CryptoCurrency("HODL", "HOdlcoin")); - result.add(new CryptoCurrency("HNC", "HunCoin")); - result.add(new CryptoCurrency("IOC", "I/O Coin")); - result.add(new CryptoCurrency("IOTA", "IOTA", true)); result.add(new CryptoCurrency("IOP", "Fermat")); - result.add(new CryptoCurrency("JNS", "Janus", true)); - result.add(new CryptoCurrency("JPYT", "JPY Tether")); - result.add(new CryptoCurrency("JBS", "Jumbucks")); + result.add(new CryptoCurrency("GRC", "Gridcoin")); result.add(new CryptoCurrency("LBC", "LBRY Credits")); - result.add(new CryptoCurrency("LTBC", "LTBcoin")); result.add(new CryptoCurrency("LSK", "Lisk")); if (!baseCurrencyCode.equals("LTC")) result.add(new CryptoCurrency("LTC", "Litecoin")); result.add(new CryptoCurrency("MAID", "MaidSafeCoin")); - result.add(new CryptoCurrency("MKR", "Maker", true)); - result.add(new CryptoCurrency("MXT", "MarteXcoin")); - result.add(new CryptoCurrency("MOIN", "Moin")); result.add(new CryptoCurrency("XMR", "Monero")); result.add(new CryptoCurrency("MT", "Mycelium Token", true)); - result.add(new CryptoCurrency("XMY", "Myriadcoin")); result.add(new CryptoCurrency("NAV", "Nav Coin")); - result.add(new CryptoCurrency("XEM", "NEM")); - result.add(new CryptoCurrency("NEVA", "Nevacoin")); result.add(new CryptoCurrency("NMC", "Namecoin")); result.add(new CryptoCurrency("NBT", "NuBits")); - result.add(new CryptoCurrency("NSR", "NuShares")); result.add(new CryptoCurrency("NXT", "Nxt")); result.add(new CryptoCurrency("888", "OctoCoin")); - result.add(new CryptoCurrency("OK", "OKCash")); - result.add(new CryptoCurrency("OMNI", "Omni")); - result.add(new CryptoCurrency("OPAL", "Opal")); - result.add(new CryptoCurrency("PART", "Particl")); result.add(new CryptoCurrency("PASC", "Pascal Coin", true)); - result.add(new CryptoCurrency("PPC", "Peercoin")); result.add(new CryptoCurrency("PEPECASH", "Pepe Cash")); - result.add(new CryptoCurrency("PINK", "Pinkcoin")); result.add(new CryptoCurrency("PIVX", "PIVX")); - result.add(new CryptoCurrency("XPTX", "PlatinumBar")); - result.add(new CryptoCurrency("PLU", "Plutons", true)); result.add(new CryptoCurrency("POST", "PostCoin")); - result.add(new CryptoCurrency("POT", "PotCoin")); - result.add(new CryptoCurrency("XPM", "Primecoin")); - result.add(new CryptoCurrency("RADS", "Radium")); - result.add(new CryptoCurrency("REALEST", "RealEst. Coin")); + result.add(new CryptoCurrency("PNC", "Pranacoin")); result.add(new CryptoCurrency("RDD", "ReddCoin")); - result.add(new CryptoCurrency("XRP", "Ripple")); result.add(new CryptoCurrency("SFSC", "Safe FileSystem Coin")); - result.add(new CryptoCurrency("SHIFT", "Shift")); result.add(new CryptoCurrency("SC", "Siacoin")); result.add(new CryptoCurrency("SF", "Siafund")); result.add(new CryptoCurrency("SIB", "Sibcoin")); - result.add(new CryptoCurrency("SMLY", "Smileycoin")); - result.add(new CryptoCurrency("SLR", "SolarCoin")); result.add(new CryptoCurrency("STEEM", "STEEM")); - result.add(new CryptoCurrency("STEEMUSD", "Steem Dollars", true)); - result.add(new CryptoCurrency("XLM", "Stellar Lumens")); - result.add(new CryptoCurrency("SJCX", "StorjcoinX")); - result.add(new CryptoCurrency("STRAT", "Stratis")); - result.add(new CryptoCurrency("SWT", "Swarm City Token", true)); - result.add(new CryptoCurrency("SYNX", "Syndicate")); - result.add(new CryptoCurrency("AMP", "Synereo", true)); - result.add(new CryptoCurrency("TRI", "Triangles")); - result.add(new CryptoCurrency("USDT", "USD Tether")); result.add(new CryptoCurrency("UNO", "Unobtanium")); - result.add(new CryptoCurrency("VCN", "VCoin")); - result.add(new CryptoCurrency("VPN", "VPNCoin")); - result.add(new CryptoCurrency("XVG", "Verge")); - result.add(new CryptoCurrency("VRC", "VeriCoin")); - result.add(new CryptoCurrency("WDC", "Worldcoin")); - result.add(new CryptoCurrency("WAVES", "Waves")); - result.add(new CryptoCurrency("XAUR", "Xaurum")); - result.add(new CryptoCurrency("YACC", "YACCoin")); - result.add(new CryptoCurrency("YBC", "YbCoin")); + result.add(new CryptoCurrency("WAC", "WACoins")); result.add(new CryptoCurrency("ZEC", "Zcash")); result.add(new CryptoCurrency("XZC", "Zcoin")); + result.add(new CryptoCurrency("ZEN", "ZenCash")); result.sort(TradeCurrency::compareTo); // Util for printing all altcoins for adding to FAQ page - /* + StringBuilder sb = new StringBuilder(); result.stream().forEach(e -> sb.append("

  • \"") .append(e.getCode()) @@ -237,7 +145,7 @@ public class CurrencyUtil { .append("\"
  • ") .append("\n")); log.info(sb.toString()); - */ + return result; } diff --git a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java index 4b9f64d122..a0ca249b31 100644 --- a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -133,7 +133,7 @@ public class TradeStatisticsManager implements PersistedDataHost { dump(); // print all currencies sorted by nr. of trades - // printAllCurrencyStats(); + printAllCurrencyStats(); } @@ -186,7 +186,7 @@ public class TradeStatisticsManager implements PersistedDataHost { } } - StringBuilder sb1 = new StringBuilder(); + StringBuilder sb1 = new StringBuilder("\nAll traded Fiat currencies:\n"); map1.entrySet().stream() .sorted((o1, o2) -> Integer.valueOf(o2.getValue().size()).compareTo(o1.getValue().size())) .forEach(e -> sb1.append(e.getKey()).append(": ").append(e.getValue().size()).append("\n")); @@ -194,19 +194,62 @@ public class TradeStatisticsManager implements PersistedDataHost { Map> map2 = new HashMap<>(); for (TradeStatistics tradeStatistics : tradeStatisticsSet) { - if (CurrencyUtil.isCryptoCurrency(tradeStatistics.getCounterCurrency())) { - final String counterCurrency = CurrencyUtil.getNameAndCode(tradeStatistics.getCounterCurrency()); - if (!map2.containsKey(counterCurrency)) - map2.put(counterCurrency, new HashSet<>()); + if (CurrencyUtil.isCryptoCurrency(tradeStatistics.getBaseCurrency())) { + final String code = CurrencyUtil.getNameAndCode(tradeStatistics.getBaseCurrency()); + if (!map2.containsKey(code)) + map2.put(code, new HashSet<>()); - map2.get(counterCurrency).add(tradeStatistics); + map2.get(code).add(tradeStatistics); } } - StringBuilder sb2 = new StringBuilder(); + List allCryptoCurrencies = new ArrayList<>(); + Set coinsWithValidator = new HashSet<>(); + coinsWithValidator.add("BTC"); + 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"); + coinsWithValidator.add("PNC"); + coinsWithValidator.add("ZEN"); + coinsWithValidator.add("WAC"); + + Set newlyAdded = new HashSet<>(); + newlyAdded.add("PNC"); + newlyAdded.add("WAC"); + newlyAdded.add("ZEN"); + + CurrencyUtil.getAllSortedCryptoCurrencies().stream() + .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.valueOf(o2.getValue().size()).compareTo(o1.getValue().size())) - .forEach(e -> sb2.append(e.getKey()).append(": ").append(e.getValue().size()).append("\n")); + .forEach(e -> { + final 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.stream() + .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()); } } From 9544095f8692b09d97e0301e06f90994c23a6d20 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 17 Sep 2017 13:12:12 -0500 Subject: [PATCH 25/54] Remove never traded coins --- common/src/main/java/io/bisq/common/locale/CurrencyUtil.java | 4 ++-- .../io/bisq/core/trade/statistics/TradeStatisticsManager.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index 48853bc9d8..30cc16846f 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -136,7 +136,7 @@ public class CurrencyUtil { result.sort(TradeCurrency::compareTo); // Util for printing all altcoins for adding to FAQ page - + /* StringBuilder sb = new StringBuilder(); result.stream().forEach(e -> sb.append("
  • \"") .append(e.getCode()) @@ -145,7 +145,7 @@ public class CurrencyUtil { .append("\"
  • ") .append("\n")); log.info(sb.toString()); - + */ return result; } diff --git a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java index a0ca249b31..ff8a63eeb9 100644 --- a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -133,7 +133,7 @@ public class TradeStatisticsManager implements PersistedDataHost { dump(); // print all currencies sorted by nr. of trades - printAllCurrencyStats(); + // printAllCurrencyStats(); } @@ -220,6 +220,7 @@ public class TradeStatisticsManager implements PersistedDataHost { coinsWithValidator.add("ZEN"); coinsWithValidator.add("WAC"); + // As of: 17.Sept 2017 Set newlyAdded = new HashSet<>(); newlyAdded.add("PNC"); newlyAdded.add("WAC"); From 36b266a204386788217cd50c87b090f44098d3f1 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 18 Sep 2017 08:59:53 -0500 Subject: [PATCH 26/54] Add altcoin DEC --- common/src/main/java/io/bisq/common/locale/CurrencyUtil.java | 1 + .../io/bisq/core/trade/statistics/TradeStatisticsManager.java | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index 30cc16846f..a8d221cad1 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -95,6 +95,7 @@ public class CurrencyUtil { result.add(new CryptoCurrency("DNET", "DarkNet")); if (!baseCurrencyCode.equals("DASH")) result.add(new CryptoCurrency("DASH", "Dash")); + result.add(new CryptoCurrency("DEC", "DECENT")); result.add(new CryptoCurrency("DCR", "Decred")); if (!baseCurrencyCode.equals("DOGE")) result.add(new CryptoCurrency("DOGE", "Dogecoin")); diff --git a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java index ff8a63eeb9..802488dde3 100644 --- a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -219,12 +219,14 @@ public class TradeStatisticsManager implements PersistedDataHost { coinsWithValidator.add("PNC"); coinsWithValidator.add("ZEN"); coinsWithValidator.add("WAC"); - + coinsWithValidator.add("DEC"); + // As of: 17.Sept 2017 Set newlyAdded = new HashSet<>(); newlyAdded.add("PNC"); newlyAdded.add("WAC"); newlyAdded.add("ZEN"); + newlyAdded.add("DEC"); CurrencyUtil.getAllSortedCryptoCurrencies().stream() .forEach(e -> allCryptoCurrencies.add(e.getNameAndCode())); From 23828c3e792657e6d9be6598460dc43c104363c4 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 18 Sep 2017 11:46:04 -0500 Subject: [PATCH 27/54] Remove some altcoins, add comment --- .../src/main/java/io/bisq/common/locale/CurrencyUtil.java | 6 ++---- common/src/main/java/io/bisq/common/locale/LocaleUtil.java | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index 40fa2c7cbc..959c275d46 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -139,12 +139,9 @@ public class CurrencyUtil { result.add(new CryptoCurrency("ETC", "Ether Classic")); result.add(new CryptoCurrency("ERC", "Europecoin")); result.add(new CryptoCurrency("EGC", "EverGreenCoin")); - result.add(new CryptoCurrency("EOS", "EOS", true)); result.add(new CryptoCurrency("FCT", "Factom")); result.add(new CryptoCurrency("FAIR", "FairCoin")); result.add(new CryptoCurrency("FLO", "FlorinCoin")); - //TODO ticker? - //result.add(new CryptoCurrency("FILE", "Filecoin", true)); result.add(new CryptoCurrency("GAME", "GameCredits")); result.add(new CryptoCurrency("GEMZ", "Gemz")); result.add(new CryptoCurrency("GRC", "Gridcoin")); @@ -153,7 +150,6 @@ public class CurrencyUtil { result.add(new CryptoCurrency("HODL", "HOdlcoin")); result.add(new CryptoCurrency("HNC", "HunCoin")); result.add(new CryptoCurrency("IOC", "I/O Coin")); - result.add(new CryptoCurrency("IOTA", "IOTA", true)); result.add(new CryptoCurrency("IOP", "Fermat")); result.add(new CryptoCurrency("JNS", "Janus", true)); result.add(new CryptoCurrency("JPYT", "JPY Tether")); @@ -253,6 +249,8 @@ public class CurrencyUtil { if (!baseCurrencyCode.equals("DASH")) result.add(new CryptoCurrency("DASH", "Dash")); result.add(new CryptoCurrency("DCR", "Decred")); + if (!baseCurrencyCode.equals("DOGE")) + result.add(new CryptoCurrency("DOGE", "Dogecoin")); result.add(new CryptoCurrency("ETH", "Ether")); result.add(new CryptoCurrency("ETC", "Ether Classic")); result.add(new CryptoCurrency("GRC", "Gridcoin")); diff --git a/common/src/main/java/io/bisq/common/locale/LocaleUtil.java b/common/src/main/java/io/bisq/common/locale/LocaleUtil.java index 3b5475e8b7..dce9fae5a1 100644 --- a/common/src/main/java/io/bisq/common/locale/LocaleUtil.java +++ b/common/src/main/java/io/bisq/common/locale/LocaleUtil.java @@ -160,7 +160,7 @@ public class LocaleUtil { allLocales.add(new Locale("es", "PE", "")); allLocales.add(new Locale("en", "PH", "")); allLocales.add(new Locale("pl", "PL", "")); - allLocales.add(new Locale("es", "PR", "")); + allLocales.add(new Locale("es", "PR", "")); // Puerto Rico allLocales.add(new Locale("pt", "PT", "")); allLocales.add(new Locale("es", "PY", "")); allLocales.add(new Locale("ar", "QA", "")); From 322ffab270628d8f1a0d81e45bb95a711df87df5 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 18 Sep 2017 12:21:48 -0500 Subject: [PATCH 28/54] Cleanup --- .../java/io/bisq/core/dao/blockchain/parse/BsqChainState.java | 2 +- .../java/io/bisq/core/provider/price/PriceFeedService.java | 3 +-- core/src/main/java/io/bisq/core/user/Preferences.java | 4 ++-- .../main/java/io/bisq/gui/util/validation/BsqValidator.java | 1 + statistics/pom.xml | 1 - 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java index d09ca5f99c..4fe0c0d5aa 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java @@ -513,7 +513,7 @@ public class BsqChainState implements PersistableEnvelope { " unspentTxOutputsMap.size={}\n" + " compensationRequestFees.size={}\n" + " votingFees.size={}\n" + - getChainHeadHeight(), + getChainHeadHeight(), bsqBlocks.size(), txMap.size(), unspentTxOutputsMap.size(), diff --git a/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java b/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java index 72c49e00e6..fc07c14338 100644 --- a/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java +++ b/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java @@ -140,8 +140,7 @@ public class PriceFeedService { else return null; } - - + public void setBisqMarketPrice(String currencyCode, Price price) { if (!cache.containsKey(currencyCode) || !cache.get(currencyCode).isExternallyProvidedPrice()) { cache.put(currencyCode, new MarketPrice(currencyCode, diff --git a/core/src/main/java/io/bisq/core/user/Preferences.java b/core/src/main/java/io/bisq/core/user/Preferences.java index 8087b57530..7f183cfb40 100644 --- a/core/src/main/java/io/bisq/core/user/Preferences.java +++ b/core/src/main/java/io/bisq/core/user/Preferences.java @@ -57,9 +57,9 @@ public final class Preferences implements PersistedDataHost { )); public static final BlockChainExplorer BSQ_MAIN_NET_EXPLORER = new BlockChainExplorer("BSQ", "https://explorer.bisq.io/tx.html?tx=", - "https://explorer.bisq.io/Address.html?addr="); + "https://explorer.bisq.network/Address.html?addr="); public static final BlockChainExplorer BSQ_TEST_NET_EXPLORER = new BlockChainExplorer("BSQ", "https://explorer.bisq.io/testnet/tx.html?tx=", - "https://explorer.bisq.io/testnet/Address.html?addr="); + "https://explorer.bisq.network/testnet/Address.html?addr="); private static final ArrayList LTC_MAIN_NET_EXPLORERS = new ArrayList<>(Arrays.asList( new BlockChainExplorer("CryptoID", "https://chainz.cryptoid.info/ltc/tx.dws?", "https://chainz.cryptoid.info/ltc/address.dws?"), diff --git a/gui/src/main/java/io/bisq/gui/util/validation/BsqValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/BsqValidator.java index 9e9a41489a..6725b512b6 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/BsqValidator.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/BsqValidator.java @@ -45,6 +45,7 @@ public class BsqValidator extends AltcoinValidator { @Inject public BsqValidator(BsqFormatter bsqFormatter) { this.bsqFormatter = bsqFormatter; + // TODO do we want a limit here? //setMaxValue(bsqFormatter.parseToCoin("2500000")); } diff --git a/statistics/pom.xml b/statistics/pom.xml index 9f0fdf819c..448feb74ce 100644 --- a/statistics/pom.xml +++ b/statistics/pom.xml @@ -29,7 +29,6 @@ statistics - false From c022fadfa48dc490bbbfe117595b5cc206106d1e Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 18 Sep 2017 12:28:47 -0500 Subject: [PATCH 29/54] Reduce seed nodes for LTC and DASH. Remove DOGE seed. --- .../bisq/network/p2p/seed/SeedNodesRepository.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java b/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java index a40bd7890d..fbf3e75371 100644 --- a/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java +++ b/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java @@ -20,7 +20,7 @@ public class SeedNodesRepository { private Set torSeedNodeAddresses = Sets.newHashSet( // BTC mainnet - //TODO dev + //TODO dev dont use live nodes atm! /*new NodeAddress("3f3cu2yw7u457ztq.onion:8000"), new NodeAddress("723ljisnynbtdohi.onion:8000"), new NodeAddress("rm7b56wbrcczpjvl.onion:8000"), @@ -44,18 +44,18 @@ public class SeedNodesRepository { // LTC mainnet new NodeAddress("acyvotgewx46pebw.onion:8003"), - new NodeAddress("pklgy3vdfn3obkur.onion:8003"), + // new NodeAddress("pklgy3vdfn3obkur.onion:8003"), removed in version 0.6 // keep the below but we don't run them atm /* new NodeAddress("cfciqxcowuhjdnkl.onion:8003"), new NodeAddress("bolqw3hs55uii7ku.onion:8003"),*/ - // DOGE mainnet - new NodeAddress("t6bwuj75mvxswavs.onion:8006"), + // DOGE mainnet + // new NodeAddress("t6bwuj75mvxswavs.onion:8006"), removed in version 0.6 (DOGE not supported anymore) //DASH mainnet - new NodeAddress("toeu5ikb27ydscxt.onion:8009"), - new NodeAddress("ae4yvaivhnekkhqf.onion:8009") + new NodeAddress("toeu5ikb27ydscxt.onion:8009") + //new NodeAddress("ae4yvaivhnekkhqf.onion:8009") removed in version 0.6 ); // Addresses are used if the last digit of their port match the network id: From 0bc339f916c0bdf0c0023fefefb9548926923277 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 18 Sep 2017 12:53:23 -0500 Subject: [PATCH 30/54] Remove DOGE from base currency selection --- .../main/java/io/bisq/core/btc/BaseCurrencyNetwork.java | 9 +++++++++ .../gui/main/settings/preferences/PreferencesView.java | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/core/src/main/java/io/bisq/core/btc/BaseCurrencyNetwork.java b/core/src/main/java/io/bisq/core/btc/BaseCurrencyNetwork.java index fc61a380bd..d56a432bf8 100644 --- a/core/src/main/java/io/bisq/core/btc/BaseCurrencyNetwork.java +++ b/core/src/main/java/io/bisq/core/btc/BaseCurrencyNetwork.java @@ -76,6 +76,15 @@ public enum BaseCurrencyNetwork { return "LTC".equals(currencyCode); } + public boolean isDash() { + return "DASH".equals(currencyCode); + } + + public boolean isDoge() { + return "DOGE".equals(currencyCode); + } + + public Coin getDefaultMinFee() { switch (BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode()) { case "BTC": diff --git a/gui/src/main/java/io/bisq/gui/main/settings/preferences/PreferencesView.java b/gui/src/main/java/io/bisq/gui/main/settings/preferences/PreferencesView.java index ac61fc1529..09f82c8c2b 100644 --- a/gui/src/main/java/io/bisq/gui/main/settings/preferences/PreferencesView.java +++ b/gui/src/main/java/io/bisq/gui/main/settings/preferences/PreferencesView.java @@ -448,6 +448,13 @@ public class PreferencesView extends ActivatableViewAndModel baseCurrencyNetworks = Arrays.asList(BaseCurrencyNetwork.values()); + + // We don't support DOGE anymore due lack of interest but leave it in the code in case it will get + // re-activated some day + baseCurrencyNetworks = baseCurrencyNetworks.stream() + .filter(e -> !e.isDoge()) + .collect(Collectors.toList()); + // show ony mainnet in production version if (!DevEnv.DEV_MODE) baseCurrencyNetworks = baseCurrencyNetworks.stream() From bc515475e01b0f871d329392a2ef021eaf5fac4a Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 18 Sep 2017 13:03:10 -0500 Subject: [PATCH 31/54] Add OXT blockexplorer --- core/src/main/java/io/bisq/core/user/Preferences.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/io/bisq/core/user/Preferences.java b/core/src/main/java/io/bisq/core/user/Preferences.java index 7f183cfb40..d1ba25ae4b 100644 --- a/core/src/main/java/io/bisq/core/user/Preferences.java +++ b/core/src/main/java/io/bisq/core/user/Preferences.java @@ -35,6 +35,7 @@ import static com.google.common.base.Preconditions.checkNotNull; public final class Preferences implements PersistedDataHost { private static final ArrayList BTC_MAIN_NET_EXPLORERS = new ArrayList<>(Arrays.asList( + new BlockChainExplorer("OXT", "https://oxt.me/transaction/", "https://oxt.me/address/"), new BlockChainExplorer("Tradeblock.com", "https://tradeblock.com/bitcoin/tx/", "https://tradeblock.com/bitcoin/address/"), new BlockChainExplorer("Insight", "https://insight.bitpay.com/tx/", "https://insight.bitpay.com/address/"), new BlockChainExplorer("Blockchain.info", "https://blockchain.info/tx/", "https://blockchain.info/address/"), From 4aeea11d63802be0d37bc3bd6dbe77abe4e9ef06 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 18 Sep 2017 13:04:28 -0500 Subject: [PATCH 32/54] Rearrange blockexplorers --- core/src/main/java/io/bisq/core/user/Preferences.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/io/bisq/core/user/Preferences.java b/core/src/main/java/io/bisq/core/user/Preferences.java index d1ba25ae4b..39f69053c8 100644 --- a/core/src/main/java/io/bisq/core/user/Preferences.java +++ b/core/src/main/java/io/bisq/core/user/Preferences.java @@ -35,9 +35,8 @@ import static com.google.common.base.Preconditions.checkNotNull; public final class Preferences implements PersistedDataHost { private static final ArrayList BTC_MAIN_NET_EXPLORERS = new ArrayList<>(Arrays.asList( + new BlockChainExplorer("Tradeblock", "https://tradeblock.com/bitcoin/tx/", "https://tradeblock.com/bitcoin/address/"), new BlockChainExplorer("OXT", "https://oxt.me/transaction/", "https://oxt.me/address/"), - new BlockChainExplorer("Tradeblock.com", "https://tradeblock.com/bitcoin/tx/", "https://tradeblock.com/bitcoin/address/"), - new BlockChainExplorer("Insight", "https://insight.bitpay.com/tx/", "https://insight.bitpay.com/address/"), new BlockChainExplorer("Blockchain.info", "https://blockchain.info/tx/", "https://blockchain.info/address/"), new BlockChainExplorer("Blockexplorer", "https://blockexplorer.com/tx/", "https://blockexplorer.com/address/"), new BlockChainExplorer("Biteasy", "https://www.biteasy.com/transactions/", "https://www.biteasy.com/addresses/"), @@ -46,7 +45,8 @@ public final class Preferences implements PersistedDataHost { new BlockChainExplorer("Smartbit", "https://www.smartbit.com.au/tx/", "https://www.smartbit.com.au/address/"), new BlockChainExplorer("SoChain. Wow.", "https://chain.so/tx/BTC/", "https://chain.so/address/BTC/"), new BlockChainExplorer("Bitaps", "https://bitaps.com/", "https://bitaps.com/"), - new BlockChainExplorer("Blockr.io", "https://btc.blockr.io/tx/info/", "https://btc.blockr.io/address/info/") + new BlockChainExplorer("Insight", "https://insight.bitpay.com/tx/", "https://insight.bitpay.com/address/"), + new BlockChainExplorer("Blockr", "https://btc.blockr.io/tx/info/", "https://btc.blockr.io/address/info/") )); private static final ArrayList BTC_TEST_NET_EXPLORERS = new ArrayList<>(Arrays.asList( new BlockChainExplorer("Blocktrail", "https://www.blocktrail.com/tBTC/tx/", "https://www.blocktrail.com/tBTC/address/"), From 2f459cd05e1db6a59fd621291bfcd4148fb5444e Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 19 Sep 2017 19:18:45 -0500 Subject: [PATCH 33/54] Decrease font size and width of Tac window for small screens --- .../main/java/io/bisq/gui/app/BisqApp.java | 20 +++++++++---------- .../gui/main/overlays/windows/TacWindow.java | 20 ++++++++++++++++--- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/gui/src/main/java/io/bisq/gui/app/BisqApp.java b/gui/src/main/java/io/bisq/gui/app/BisqApp.java index 8205b5b175..81623d808b 100644 --- a/gui/src/main/java/io/bisq/gui/app/BisqApp.java +++ b/gui/src/main/java/io/bisq/gui/app/BisqApp.java @@ -100,23 +100,21 @@ public class BisqApp extends Application { private static final long LOG_MEMORY_PERIOD_MIN = 10; private static BisqEnvironment bisqEnvironment; - - private BisqAppModule bisqAppModule; - private Injector injector; - private boolean popupOpened; - - private static Stage primaryStage; - private Scene scene; - private final List corruptedDatabaseFiles = new ArrayList<>(); - private MainView mainView; - public static Runnable shutDownHandler; - private boolean shutDownRequested; + private static Stage primaryStage; public static void setEnvironment(BisqEnvironment bisqEnvironment) { BisqApp.bisqEnvironment = bisqEnvironment; } + private BisqAppModule bisqAppModule; + private Injector injector; + private boolean popupOpened; + private Scene scene; + private final List corruptedDatabaseFiles = new ArrayList<>(); + private MainView mainView; + private boolean shutDownRequested; + @SuppressWarnings("PointlessBooleanExpression") @Override public void start(Stage stage) throws IOException { diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/TacWindow.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/TacWindow.java index c56265e8af..0953f59865 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/TacWindow.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/TacWindow.java @@ -6,16 +6,29 @@ import io.bisq.gui.app.BisqApp; import io.bisq.gui.components.HyperlinkWithIcon; import io.bisq.gui.main.overlays.Overlay; import javafx.geometry.Insets; +import javafx.geometry.Rectangle2D; import javafx.scene.layout.GridPane; +import javafx.stage.Screen; import static io.bisq.gui.util.FormBuilder.addHyperlinkWithIcon; public class TacWindow extends Overlay { + private final boolean smallScreen; + @Inject public TacWindow() { type = Type.Attention; - width = 900; + + Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds(); + final double primaryScreenBoundsWidth = primaryScreenBounds.getWidth(); + smallScreen = primaryScreenBoundsWidth < 1024; + if (smallScreen) { + this.width = primaryScreenBoundsWidth * 0.8; + log.warn("Very small screen: primaryScreenBounds=" + primaryScreenBounds.toString()); + } else { + width = 900; + } } @Override @@ -66,10 +79,11 @@ public class TacWindow extends Overlay { @Override protected void addMessage() { super.addMessage(); - messageLabel.setStyle("-fx-font-size: 12;"); + String fontSize = smallScreen ? "9" : "12"; + messageLabel.setStyle("-fx-font-size: " + fontSize + ";"); HyperlinkWithIcon hyperlinkWithIcon = addHyperlinkWithIcon(gridPane, ++rowIndex, Res.get("tacWindow.arbitrationSystem"), "https://bisq.io/arbitration_system.pdf"); - hyperlinkWithIcon.setStyle("-fx-font-size: 12;"); + hyperlinkWithIcon.setStyle("-fx-font-size: " + fontSize + ";"); GridPane.setMargin(hyperlinkWithIcon, new Insets(-6, 0, -20, -4)); } From 016626c745708e6975934bee436603db31d7316e Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 19 Sep 2017 20:53:51 -0500 Subject: [PATCH 34/54] Activate DAO for BTC testnet and regtest --- common/src/main/java/io/bisq/common/app/DevEnv.java | 1 - core/src/main/java/io/bisq/core/app/BisqEnvironment.java | 8 ++++++-- gui/src/main/java/io/bisq/gui/main/dao/DaoView.java | 3 ++- .../java/io/bisq/gui/main/dao/wallet/BsqWalletView.java | 6 +++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/common/src/main/java/io/bisq/common/app/DevEnv.java b/common/src/main/java/io/bisq/common/app/DevEnv.java index eecf7433e9..792bb7d9a9 100644 --- a/common/src/main/java/io/bisq/common/app/DevEnv.java +++ b/common/src/main/java/io/bisq/common/app/DevEnv.java @@ -20,6 +20,5 @@ public class DevEnv { @SuppressWarnings("PointlessBooleanExpression") public static final boolean DEV_MODE = STRESS_TEST_MODE || true; - public static final boolean DAO_ACTIVATED = true; public static final boolean DAO_PHASE2_ACTIVATED = false; } diff --git a/core/src/main/java/io/bisq/core/app/BisqEnvironment.java b/core/src/main/java/io/bisq/core/app/BisqEnvironment.java index adbe7ca354..3539d333c7 100644 --- a/core/src/main/java/io/bisq/core/app/BisqEnvironment.java +++ b/core/src/main/java/io/bisq/core/app/BisqEnvironment.java @@ -19,7 +19,6 @@ package io.bisq.core.app; import ch.qos.logback.classic.Level; import io.bisq.common.CommonOptionKeys; -import io.bisq.common.app.DevEnv; import io.bisq.common.app.Version; import io.bisq.common.crypto.KeyStorage; import io.bisq.common.storage.Storage; @@ -84,7 +83,12 @@ public class BisqEnvironment extends StandardEnvironment { public static boolean isDAOActivatedAndBaseCurrencySupportingBsq() { //noinspection ConstantConditions,PointlessBooleanExpression - return DevEnv.DAO_ACTIVATED && isBaseCurrencySupportingBsq(); + return isDAOEnabled() && isBaseCurrencySupportingBsq(); + } + + public static boolean isDAOEnabled() { + //noinspection ConstantConditions,PointlessBooleanExpression + return !getBaseCurrencyNetwork().isMainnet() && isBaseCurrencySupportingBsq(); } public static boolean isBaseCurrencySupportingBsq() { diff --git a/gui/src/main/java/io/bisq/gui/main/dao/DaoView.java b/gui/src/main/java/io/bisq/gui/main/dao/DaoView.java index 88fc044007..2b446ee032 100644 --- a/gui/src/main/java/io/bisq/gui/main/dao/DaoView.java +++ b/gui/src/main/java/io/bisq/gui/main/dao/DaoView.java @@ -19,6 +19,7 @@ package io.bisq.gui.main.dao; import io.bisq.common.app.DevEnv; import io.bisq.common.locale.Res; +import io.bisq.core.app.BisqEnvironment; import io.bisq.gui.Navigation; import io.bisq.gui.common.model.Activatable; import io.bisq.gui.common.view.*; @@ -63,7 +64,7 @@ public class DaoView extends ActivatableViewAndModel { votingTab.setClosable(false); root.getTabs().addAll(compensationTab, votingTab); - if (!DevEnv.DAO_PHASE2_ACTIVATED) { + if (BisqEnvironment.isDAOActivatedAndBaseCurrencySupportingBsq() && !DevEnv.DAO_PHASE2_ACTIVATED) { votingTab.setDisable(true); compensationTab.setDisable(true); } diff --git a/gui/src/main/java/io/bisq/gui/main/dao/wallet/BsqWalletView.java b/gui/src/main/java/io/bisq/gui/main/dao/wallet/BsqWalletView.java index 7714edfeac..5907fd9fd4 100644 --- a/gui/src/main/java/io/bisq/gui/main/dao/wallet/BsqWalletView.java +++ b/gui/src/main/java/io/bisq/gui/main/dao/wallet/BsqWalletView.java @@ -19,8 +19,8 @@ package io.bisq.gui.main.dao.wallet; import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeIcon; -import io.bisq.common.app.DevEnv; import io.bisq.common.locale.Res; +import io.bisq.core.app.BisqEnvironment; import io.bisq.gui.Navigation; import io.bisq.gui.common.view.*; import io.bisq.gui.main.MainView; @@ -83,7 +83,7 @@ public class BsqWalletView extends ActivatableViewAndModel { leftVBox.getChildren().addAll(dashboard, send, receive, transactions); // TODO just until DAO is enabled - if (!DevEnv.DAO_ACTIVATED) { + if (!BisqEnvironment.isDAOActivatedAndBaseCurrencySupportingBsq()) { dashboard.setDisable(true); send.setDisable(true); transactions.setDisable(true); @@ -105,7 +105,7 @@ public class BsqWalletView extends ActivatableViewAndModel { selectedViewClass = BsqDashboardView.class; // TODO just until DAO is enabled - if (!DevEnv.DAO_ACTIVATED) + if (!BisqEnvironment.isDAOActivatedAndBaseCurrencySupportingBsq()) selectedViewClass = BsqReceiveView.class; loadView(selectedViewClass); From 1b324631aecb72c474aaa975e86512d79d237edd Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 19 Sep 2017 20:54:36 -0500 Subject: [PATCH 35/54] Rename \'Account holder name\' to \'Account owner full name\' --- common/src/main/resources/i18n/displayStrings.properties | 6 +++--- .../main/resources/i18n/in_dev/displayStrings_ae.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_ba.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_br.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_cn.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_cz.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_dk.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_en.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_fi.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_fr.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_ge.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_gr.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_hr.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_hu.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_id.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_ie.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_in.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_is.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_jp.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_kg.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_kr.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_kz.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_lt.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_my.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_nl.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_no.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_nz.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_ph.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_pl.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_ro.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_ru.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_se.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_th.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_ua.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_uz.properties | 4 ++-- .../main/resources/i18n/in_dev/displayStrings_vn.properties | 4 ++-- 36 files changed, 73 insertions(+), 73 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index c95f1c5ea0..118425b04e 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -1476,10 +1476,10 @@ seed.restore.error=An error occurred when restoring the wallets with seed words. payment.account.no=Account no.: payment.account.name=Account name: -payment.account.owner=Account holder name +payment.account.owner=Account owner full name payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email -payment.account.name.emailAndHolderId=Account holder name / email / {0} +payment.account.name.email=Account owner full name / email +payment.account.name.emailAndHolderId=Account owner full name / email / {0} payment.bank.name=Bank name: payment.select.account=Select account type payment.select.region=Select region diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ae.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ae.properties index d7793804b9..80659f97f2 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ae.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ae.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ba.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ba.properties index cbbd37e2ce..dae7aabe06 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ba.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ba.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_br.properties b/common/src/main/resources/i18n/in_dev/displayStrings_br.properties index f98f2aaaf5..601b1e1460 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_br.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_br.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_cn.properties b/common/src/main/resources/i18n/in_dev/displayStrings_cn.properties index 9df757333a..5bb3637778 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_cn.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_cn.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_cz.properties b/common/src/main/resources/i18n/in_dev/displayStrings_cz.properties index 59b384881f..80591cd151 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_cz.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_cz.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_dk.properties b/common/src/main/resources/i18n/in_dev/displayStrings_dk.properties index 550048569a..cd892f7a31 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_dk.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_dk.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_en.properties b/common/src/main/resources/i18n/in_dev/displayStrings_en.properties index f215e61601..93f79ea775 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_en.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_en.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_fi.properties b/common/src/main/resources/i18n/in_dev/displayStrings_fi.properties index 7f29547fd8..c407b051b3 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_fi.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_fi.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties index 56ba2d2b6d..3fa2fd98b6 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Devise: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ge.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ge.properties index b878bf1916..a063a3d90c 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ge.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ge.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_gr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_gr.properties index b229d6d512..3e0d767d92 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_gr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_gr.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_hr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_hr.properties index fbdf5c157e..f1a51a4dee 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_hr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_hr.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_hu.properties b/common/src/main/resources/i18n/in_dev/displayStrings_hu.properties index dabc26a68c..6e299aa00a 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_hu.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_hu.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_id.properties b/common/src/main/resources/i18n/in_dev/displayStrings_id.properties index c7079434a0..6ef63a3613 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_id.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_id.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ie.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ie.properties index b7418235ca..32fa08c327 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ie.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ie.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_in.properties b/common/src/main/resources/i18n/in_dev/displayStrings_in.properties index f3d20aa775..5d0de8fe34 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_in.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_in.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_is.properties b/common/src/main/resources/i18n/in_dev/displayStrings_is.properties index 996773a5ac..e2cf160b2c 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_is.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_is.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_jp.properties b/common/src/main/resources/i18n/in_dev/displayStrings_jp.properties index e5aeee2285..dc5cd9a36e 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_jp.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_jp.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_kg.properties b/common/src/main/resources/i18n/in_dev/displayStrings_kg.properties index 733411dca1..bfa655055a 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_kg.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_kg.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_kr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_kr.properties index 547ac03e8d..6174381d87 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_kr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_kr.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_kz.properties b/common/src/main/resources/i18n/in_dev/displayStrings_kz.properties index f12552235a..ac38b26b6a 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_kz.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_kz.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_lt.properties b/common/src/main/resources/i18n/in_dev/displayStrings_lt.properties index 210136d579..9899c55a25 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_lt.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_lt.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_my.properties b/common/src/main/resources/i18n/in_dev/displayStrings_my.properties index b2fe231747..d127910820 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_my.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_my.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_nl.properties b/common/src/main/resources/i18n/in_dev/displayStrings_nl.properties index 5e7c36b3ed..f2e7c9b3c0 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_nl.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_nl.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_no.properties b/common/src/main/resources/i18n/in_dev/displayStrings_no.properties index 84345d1aa4..71b23455d8 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_no.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_no.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_nz.properties b/common/src/main/resources/i18n/in_dev/displayStrings_nz.properties index da4aca54ef..957111aa60 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_nz.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_nz.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ph.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ph.properties index 4d8484d75e..f94f95ff7e 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ph.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ph.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_pl.properties b/common/src/main/resources/i18n/in_dev/displayStrings_pl.properties index bc269247d8..b67a53a884 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_pl.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_pl.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ro.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ro.properties index 7b89fc5a5b..487c3094ad 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ro.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ro.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ru.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ru.properties index 2ad325678b..38917859c0 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ru.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ru.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_se.properties b/common/src/main/resources/i18n/in_dev/displayStrings_se.properties index 1a52b73f87..c7e66a6d79 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_se.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_se.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_th.properties b/common/src/main/resources/i18n/in_dev/displayStrings_th.properties index 34411126f8..6cef719a66 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_th.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_th.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ua.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ua.properties index 07aaa38655..716c61722b 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ua.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ua.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_uz.properties b/common/src/main/resources/i18n/in_dev/displayStrings_uz.properties index 8e28e47877..98cca6cc54 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_uz.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_uz.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_vn.properties b/common/src/main/resources/i18n/in_dev/displayStrings_vn.properties index 34ac52bb23..2f7c668ccc 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_vn.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_vn.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency From a5ab750ce8c7a0f695907d024890ec7eded6ff9b Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Wed, 20 Sep 2017 11:45:24 -0500 Subject: [PATCH 36/54] Fix bug with missing MultiSigKey --- .../main/java/io/bisq/core/btc/wallet/BtcWalletService.java | 4 ++-- core/src/main/java/io/bisq/core/btc/wallet/WalletService.java | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/io/bisq/core/btc/wallet/BtcWalletService.java index 1d65bf9eee..4e55114ccd 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BtcWalletService.java @@ -410,12 +410,12 @@ public class BtcWalletService extends WalletService { if (!Arrays.equals(pubKey, multiSigAddressEntry.getPubKey())) { log.error("Pub Key from AddressEntry does not match key pair from trade data. Trade ID={}\n" + "We try to find the keypair in the wallet with the pubKey we found in the trade data.", tradeId); - multiSigKeyPair = findKeyFromPubKeyHash(pubKey); + multiSigKeyPair = findKeyFromPubKey(pubKey); } } else { log.error("multiSigAddressEntry not found for trade ID={}.\n" + "We try to find the keypair in the wallet with the pubKey we found in the trade data.", tradeId); - multiSigKeyPair = findKeyFromPubKeyHash(pubKey); + multiSigKeyPair = findKeyFromPubKey(pubKey); } return multiSigKeyPair; diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletService.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletService.java index bf9478fdaf..3d63e86961 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletService.java @@ -517,6 +517,10 @@ public abstract class WalletService { public DeterministicKey findKeyFromPubKeyHash(byte[] pubKeyHash) { return wallet.getActiveKeychain().findKeyFromPubHash(pubKeyHash); } + + public DeterministicKey findKeyFromPubKey(byte[] pubKey) { + return wallet.getActiveKeychain().findKeyFromPubKey(pubKey); + } public Address freshReceiveAddress() { return wallet.freshReceiveAddress(); From 981e6814cceb7362130ba821801514e222c1bd73 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Wed, 20 Sep 2017 13:07:50 -0500 Subject: [PATCH 37/54] Add Chris Beams' gpg key 5BC5ED73 to resources --- gui/src/main/resources/keys/5BC5ED73.asc | 50 ++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 gui/src/main/resources/keys/5BC5ED73.asc diff --git a/gui/src/main/resources/keys/5BC5ED73.asc b/gui/src/main/resources/keys/5BC5ED73.asc new file mode 100644 index 0000000000..d1f70e1e0c --- /dev/null +++ b/gui/src/main/resources/keys/5BC5ED73.asc @@ -0,0 +1,50 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=AWz+ +-----END PGP PUBLIC KEY BLOCK----- From ac0bf80606d12c1efa182c56da66b021ce4491b2 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Wed, 20 Sep 2017 15:05:25 -0500 Subject: [PATCH 38/54] Support different keys for code signing in download-tool --- .../resources/i18n/displayStrings.properties | 8 +- .../windows/downloadupdate/BisqInstaller.java | 82 ++++++++++++++----- .../windows/downloadupdate/DownloadTask.java | 1 - .../windows/downloadupdate/VerifyTask.java | 80 +++++++++++++----- 4 files changed, 124 insertions(+), 47 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index 118425b04e..8b65c77e6b 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -1018,17 +1018,17 @@ displayAlertMessageWindow.update.headline=Important update information! displayAlertMessageWindow.update.download=Download: displayUpdateDownloadWindow.downloadedFiles=Downloaded files: displayUpdateDownloadWindow.downloadingFile=Downloading: {0} -displayUpdateDownloadWindow.verifiedSigs=Verified signatures: +displayUpdateDownloadWindow.verifiedSigs=Signature verified with keys: displayUpdateDownloadWindow.status.downloading=Downloading files... -displayUpdateDownloadWindow.status.verifying=Verifying signature(s)... -displayUpdateDownloadWindow.button.label=Download installer and verify signature(s) +displayUpdateDownloadWindow.status.verifying=Verifying signature... +displayUpdateDownloadWindow.button.label=Download installer and verify signature displayUpdateDownloadWindow.headline=A new Bisq update is available! displayUpdateDownloadWindow.download.failed=Download failed.\n\ Please download and verify manually at https://bisq.io/downloads displayUpdateDownloadWindow.installer.failed=Unable to determine the correct installer. Please download and verify manually at https://bisq.io/downloads displayUpdateDownloadWindow.verify.failed=Verification failed.\n\ Please download and verify manually at https://bisq.io/downloads -displayUpdateDownloadWindow.success=The new version has been successfully downloaded and the signature(s) verified.\n\n\ +displayUpdateDownloadWindow.success=The new version has been successfully downloaded and the signature verified.\n\n\ Please open the download directory, shut down the application and install the new version. displayUpdateDownloadWindow.download.openDir=Open download directory diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/BisqInstaller.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/BisqInstaller.java index c4760b30a8..8fee5fbc7e 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/BisqInstaller.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/BisqInstaller.java @@ -29,6 +29,7 @@ import org.jetbrains.annotations.NotNull; import java.io.*; import java.security.SignatureException; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Optional; @@ -37,25 +38,30 @@ import static com.google.common.base.Preconditions.checkArgument; @Slf4j public class BisqInstaller { - static final String LOCAL_FINGER_PRINT = "F379A1C6"; - private List knownKeys = Lists.newArrayList(LOCAL_FINGER_PRINT); + static final String FINGER_PRINT_MANFRED_KARRER = "F379A1C6"; + static final String FINGER_PRINT_CHRIS_BEAMS = "5BC5ED73"; + static final String PUB_KEY_HOSTING_URL = "https://bisq.network/pubkey/"; + static final String DOWNLOAD_HOST_URL = "https://github.com/bisq-network/exchange/releases/download/"; public boolean isSupportedOS() { return Utilities.isOSX() || Utilities.isWindows() || Utilities.isLinux(); } public Optional download(String version) { - String partialUrl = "https://github.com/bisq-network/exchange/releases/download/v" + version + "/"; + String partialUrl = DOWNLOAD_HOST_URL + "v" + version + "/"; // Get installer filename on all platforms FileDescriptor installerFileDescriptor = getInstallerDescriptor(version, partialUrl); + // tells us which key was used for signing + FileDescriptor signingKeyDescriptor = getSigningKeyDescriptor(partialUrl); List keyFileDescriptors = getKeyFileDescriptors(); List sigFileDescriptors = getSigFileDescriptors(installerFileDescriptor, keyFileDescriptors); List allFiles = Lists.newArrayList(); + allFiles.addAll(Lists.newArrayList(installerFileDescriptor)); + allFiles.addAll(Lists.newArrayList(signingKeyDescriptor)); allFiles.addAll(keyFileDescriptors); allFiles.addAll(sigFileDescriptors); - allFiles.addAll(Lists.newArrayList(installerFileDescriptor)); // Download keys, sigs and Installer return getDownloadTask(allFiles); @@ -147,6 +153,11 @@ public class BisqInstaller { inputStream.close(); log.debug("KeyID used in signature: %X\n", pgpSignature.getKeyID()); publicKey = pgpPublicKeyRing.getPublicKey(pgpSignature.getKeyID()); + + // If signature is not matching the key used for signing we fail + if (publicKey == null) + return VerifyStatusEnum.FAIL; + log.debug("The ID of the selected key is %X\n", publicKey.getKeyID()); pgpSignature.init(new BcPGPContentVerifierBuilderProvider(), publicKey); @@ -166,7 +177,6 @@ public class BisqInstaller { return result ? VerifyStatusEnum.OK : VerifyStatusEnum.FAIL; } - @NotNull public FileDescriptor getInstallerDescriptor(String version, String partialUrl) { String fileName; @@ -183,9 +193,24 @@ public class BisqInstaller { return FileDescriptor.builder() .type(DownloadType.INSTALLER) - .fileName(fileName).id(fileName).loadUrl(partialUrl.concat(fileName)).build(); + .fileName(fileName) + .id(fileName) + .loadUrl(partialUrl.concat(fileName)) + .build(); } + @NotNull + public FileDescriptor getSigningKeyDescriptor(String url) { + String fileName = "signingkey.asc"; + return FileDescriptor.builder() + .type(DownloadType.SIGNING_KEY) + .fileName(fileName) + .id(fileName) + .loadUrl(url.concat(fileName)) + .build(); + } + + /** * The files containing the gpg keys of the bisq signers. * Currently these are 2 hard-coded keys, one included with bisq and the same key online for maximum security. @@ -193,22 +218,34 @@ public class BisqInstaller { * @return list of keys to check agains corresponding sigs. */ public List getKeyFileDescriptors() { - String fingerprint = LOCAL_FINGER_PRINT; - String fileName = fingerprint + ".asc"; - String fixedKeyPath = "/keys/" + fileName; - return Lists.newArrayList( - FileDescriptor.builder() - .type(DownloadType.KEY) - .fileName(fileName) - .id(fingerprint) - .loadUrl("https://bisq.io/pubkey/" + fileName).build(), - FileDescriptor.builder() - .type(DownloadType.KEY) - .fileName(fileName + "-local") - .id(fingerprint) - .loadUrl(getClass().getResource(fixedKeyPath).toExternalForm()) - .build() - ); + List list = new ArrayList<>(); + + list.add(getKeyFileDescriptor(FINGER_PRINT_MANFRED_KARRER)); + list.add(getLocalKeyFileDescriptor(FINGER_PRINT_MANFRED_KARRER)); + + list.add(getKeyFileDescriptor(FINGER_PRINT_CHRIS_BEAMS)); + list.add(getLocalKeyFileDescriptor(FINGER_PRINT_CHRIS_BEAMS)); + + return list; + } + + private FileDescriptor getKeyFileDescriptor(String fingerPrint) { + final String fileName = fingerPrint + ".asc"; + return FileDescriptor.builder() + .type(DownloadType.KEY) + .fileName(fileName) + .id(fingerPrint) + .loadUrl(PUB_KEY_HOSTING_URL + fileName) + .build(); + } + + private FileDescriptor getLocalKeyFileDescriptor(String fingerPrint) { + return FileDescriptor.builder() + .type(DownloadType.KEY) + .fileName(fingerPrint + ".asc-local") + .id(fingerPrint) + .loadUrl(getClass().getResource("/keys/" + fingerPrint + ".asc").toExternalForm()) + .build(); } /** @@ -270,6 +307,7 @@ public class BisqInstaller { INSTALLER, KEY, SIG, + SIGNING_KEY, MISC } } diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DownloadTask.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DownloadTask.java index 5402f99204..b6a98a82f3 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DownloadTask.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DownloadTask.java @@ -119,7 +119,6 @@ public class DownloadTask extends Task> { copyInputStreamToFileNew(urlConnection.getInputStream(), outputFile, fileSize); } - public void copyInputStreamToFileNew(final InputStream source, final File destination, int fileSize) throws IOException { try { final FileOutputStream output = FileUtils.openOutputStream(destination); diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/VerifyTask.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/VerifyTask.java index 1584c7ea59..4a36d54c83 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/VerifyTask.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/VerifyTask.java @@ -29,9 +29,11 @@ import javafx.concurrent.Task; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import java.io.FileReader; import java.io.IOException; import java.util.List; import java.util.Optional; +import java.util.Scanner; import java.util.stream.Collectors; @Slf4j @@ -67,31 +69,69 @@ public class VerifyTask extends Task> { return Lists.newArrayList(); } - List sigs = fileDescriptors.stream().filter(fileDescriptor -> DownloadType.SIG.equals(fileDescriptor.getType())).collect(Collectors.toList()); - List verifyDescriptors = Lists.newArrayList(); + Optional signingKeyOptional = fileDescriptors.stream() + .filter(fileDescriptor -> DownloadType.SIGNING_KEY.equals(fileDescriptor.getType())) + .findAny(); - // iterate all signatures available to us - for (FileDescriptor sig : sigs) { - VerifyDescriptor.VerifyDescriptorBuilder verifyDescriptorBuilder = VerifyDescriptor.builder().sigFile(sig.getSaveFile()); - // Sigs are linked to keys, extract all keys which have the same id - List keys = fileDescriptors.stream() - .filter(keyDescriptor -> DownloadType.KEY.equals(keyDescriptor.getType())) - .filter(keyDescriptor -> sig.getId().equals(keyDescriptor.getId())) - .collect(Collectors.toList()); - // iterate all keys which have the same id - for (FileDescriptor key : keys) { - verifyDescriptorBuilder.keyFile(key.getSaveFile()); - try { - verifyDescriptorBuilder.verifyStatusEnum(BisqInstaller.verifySignature(key.getSaveFile(), sig.getSaveFile(), installer.get().getSaveFile())); - updateMessage(key.getFileName()); - } catch (Exception e) { - verifyDescriptorBuilder.verifyStatusEnum(BisqInstaller.VerifyStatusEnum.FAIL); - log.error(e.toString()); - e.printStackTrace(); + List verifyDescriptors = Lists.newArrayList(); + if (signingKeyOptional.isPresent()) { + final FileDescriptor signingKeyFD = signingKeyOptional.get(); + StringBuilder sb = new StringBuilder(); + try { + Scanner scanner = new Scanner(new FileReader(signingKeyFD.getSaveFile())); + while (scanner.hasNext()) { + sb.append(scanner.next()); } + scanner.close(); + } catch (Exception e) { + log.error(e.toString()); + e.printStackTrace(); + VerifyDescriptor.VerifyDescriptorBuilder verifyDescriptorBuilder = VerifyDescriptor.builder(); + verifyDescriptorBuilder.verifyStatusEnum(BisqInstaller.VerifyStatusEnum.FAIL); verifyDescriptors.add(verifyDescriptorBuilder.build()); + return verifyDescriptors; } + String signingKey = sb.toString(); + + List sigs = fileDescriptors.stream() + .filter(fileDescriptor -> DownloadType.SIG.equals(fileDescriptor.getType())) + .collect(Collectors.toList()); + + // iterate all signatures available to us + for (FileDescriptor sig : sigs) { + VerifyDescriptor.VerifyDescriptorBuilder verifyDescriptorBuilder = VerifyDescriptor.builder().sigFile(sig.getSaveFile()); + // Sigs are linked to keys, extract all keys which have the same id + List keys = fileDescriptors.stream() + .filter(keyDescriptor -> DownloadType.KEY.equals(keyDescriptor.getType())) + .filter(keyDescriptor -> sig.getId().equals(keyDescriptor.getId())) + .collect(Collectors.toList()); + // iterate all keys which have the same id + for (FileDescriptor key : keys) { + if (signingKey.equals(key.getId())) { + verifyDescriptorBuilder.keyFile(key.getSaveFile()); + try { + verifyDescriptorBuilder.verifyStatusEnum(BisqInstaller.verifySignature(key.getSaveFile(), + sig.getSaveFile(), + installer.get().getSaveFile())); + updateMessage(key.getFileName()); + } catch (Exception e) { + verifyDescriptorBuilder.verifyStatusEnum(BisqInstaller.VerifyStatusEnum.FAIL); + log.error(e.toString()); + e.printStackTrace(); + } + verifyDescriptors.add(verifyDescriptorBuilder.build()); + } else { + log.trace("key not matching the defined in signingKey. We try the next."); + } + } + } + } else { + log.error("signingKey is not found"); + VerifyDescriptor.VerifyDescriptorBuilder verifyDescriptorBuilder = VerifyDescriptor.builder(); + verifyDescriptorBuilder.verifyStatusEnum(BisqInstaller.VerifyStatusEnum.FAIL); + verifyDescriptors.add(verifyDescriptorBuilder.build()); } + return verifyDescriptors; } } From 80111caf3b63f2945e4f53815462b2deec97f299 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Wed, 20 Sep 2017 15:21:13 -0500 Subject: [PATCH 39/54] Rename bisq.io to bisq.network. --- README.md | 10 +++++----- .../java/io/bisq/common/locale/CurrencyUtil.java | 2 +- .../main/resources/i18n/displayStrings.properties | 12 ++++++------ .../main/resources/i18n/displayStrings_de.properties | 4 ++-- .../main/resources/i18n/displayStrings_el.properties | 4 ++-- .../main/resources/i18n/displayStrings_es.properties | 4 ++-- .../main/resources/i18n/displayStrings_pt.properties | 4 ++-- .../main/resources/i18n/displayStrings_sr.properties | 4 ++-- .../i18n/in_dev/displayStrings_ae.properties | 2 +- .../i18n/in_dev/displayStrings_ba.properties | 2 +- .../i18n/in_dev/displayStrings_br.properties | 2 +- .../i18n/in_dev/displayStrings_cn.properties | 2 +- .../i18n/in_dev/displayStrings_cz.properties | 2 +- .../i18n/in_dev/displayStrings_dk.properties | 2 +- .../i18n/in_dev/displayStrings_en.properties | 2 +- .../i18n/in_dev/displayStrings_fi.properties | 2 +- .../i18n/in_dev/displayStrings_fr.properties | 4 ++-- .../i18n/in_dev/displayStrings_ge.properties | 2 +- .../i18n/in_dev/displayStrings_gr.properties | 2 +- .../i18n/in_dev/displayStrings_hr.properties | 2 +- .../i18n/in_dev/displayStrings_hu.properties | 2 +- .../i18n/in_dev/displayStrings_id.properties | 2 +- .../i18n/in_dev/displayStrings_ie.properties | 2 +- .../i18n/in_dev/displayStrings_in.properties | 2 +- .../i18n/in_dev/displayStrings_is.properties | 2 +- .../i18n/in_dev/displayStrings_it.properties | 2 +- .../i18n/in_dev/displayStrings_jp.properties | 2 +- .../i18n/in_dev/displayStrings_kg.properties | 2 +- .../i18n/in_dev/displayStrings_kr.properties | 2 +- .../i18n/in_dev/displayStrings_kz.properties | 2 +- .../i18n/in_dev/displayStrings_lt.properties | 2 +- .../i18n/in_dev/displayStrings_my.properties | 2 +- .../i18n/in_dev/displayStrings_nl.properties | 2 +- .../i18n/in_dev/displayStrings_no.properties | 2 +- .../i18n/in_dev/displayStrings_nz.properties | 2 +- .../i18n/in_dev/displayStrings_ph.properties | 2 +- .../i18n/in_dev/displayStrings_pl.properties | 2 +- .../i18n/in_dev/displayStrings_pt.properties | 2 +- .../i18n/in_dev/displayStrings_ro.properties | 2 +- .../i18n/in_dev/displayStrings_ru.properties | 2 +- .../i18n/in_dev/displayStrings_se.properties | 2 +- .../i18n/in_dev/displayStrings_th.properties | 2 +- .../i18n/in_dev/displayStrings_ua.properties | 2 +- .../i18n/in_dev/displayStrings_uz.properties | 2 +- .../i18n/in_dev/displayStrings_vn.properties | 2 +- .../src/main/java/io/bisq/core/user/Preferences.java | 4 ++-- gui/src/main/java/io/bisq/gui/SystemTray.java | 2 +- .../gui/main/offer/createoffer/CreateOfferView.java | 2 +- .../bisq/gui/main/offer/takeoffer/TakeOfferView.java | 2 +- .../main/java/io/bisq/gui/main/overlays/Overlay.java | 2 +- .../main/overlays/windows/AddBitcoinNodesWindow.java | 4 ++-- .../overlays/windows/DisplayAlertMessageWindow.java | 2 +- .../io/bisq/gui/main/overlays/windows/TacWindow.java | 2 +- .../io/bisq/gui/main/settings/about/AboutView.java | 6 +++--- package/linux/32bitBuild.sh | 1 - package/linux/64bitBuild.sh | 1 - package/win/Bisq.iss | 4 ++-- 57 files changed, 75 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index f8fd5bd158..f2e06eef21 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - + What is Bisq? @@ -12,12 +12,12 @@ There are no central points of control or failure in the Bisq network. There are Because the national currency portion of any trade must be transferred via traditional means such as a wire transfer, Bisq incorporates first-class support for human arbitration to resolve any errors or disputes. -You can read about all of this and more in the [whitepaper](https://bisq.io/bitsquare.pdf) and [arbitration](https://bisq.io/arbitration_system.pdf) documents. Several [videos](https://bisq.io/blog/category/video) are available as well. +You can read about all of this and more in the [whitepaper](https://bisq.network/bitsquare.pdf) and [arbitration](https://bisq.network/arbitration_system.pdf) documents. Several [videos](https://bisq.network/blog/category/video) are available as well. Status ------ Bisq has released the beta version on the 27th of April 2016. It is operational since that time without any major incident. -Please follow the current development state at our [road map]( https://bisq.io/roadmap). +Please follow the current development state at our [road map]( https://bisq.network/roadmap). For the latest version checkout our [releases page](https://github.com/bisq-network/exchange/releases) at Github. Building from source @@ -33,9 +33,9 @@ Staying in Touch Contact the team and keep up to date using any of the following: - - The [Bisq Website](https://bisq.io) + - The [Bisq Website](https://bisq.network) - GitHub [Issues](https://github.com/bisq-network/exchange/issues) - - The [Bisq Forum]( https://forum.bisq.io) + - The [Bisq Forum]( https://forum.bisq.network) - The [#bitsquare](https://webchat.freenode.net/?channels=bitsquare) IRC channel on Freenode ([logs](https://botbot.me/freenode/bitsquare)) - Our [mailing list](https://groups.google.com/forum/#!forum/bitsquare) - [@Bitsquare_](https://twitter.com/bitsquare_) on Twitter diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index daeb8405de..3dcb78365b 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -82,7 +82,7 @@ public class CurrencyUtil { } // Don't make a PR for adding a coin but follow the steps described here: - // https://forum.bisq.io/t/how-to-add-your-favorite-altcoin/ + // https://forum.bisq.network/t/how-to-add-your-favorite-altcoin/ public static List createAllSortedCryptoCurrenciesList() { final List result = new ArrayList<>(); diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index 8b65c77e6b..4b6ebb2bb6 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -694,7 +694,7 @@ In cases where a user got stuck by a bug without getting displayed that \"Open s you can open a support ticket manually with a special short cut.\n\n\ Please use that only if you are sure that the software is not working like expected. \ If you have problems how to use Bisq or any questions please review the FAQ at the \ -bisq.io web page or post in the Bisq forum at the support section.\n\n\ +bisq.network web page or post in the Bisq forum at the support section.\n\n\ If you are sure that you want to open a support ticket please select the trade which causes the problem \ under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open \ the support ticket. @@ -1024,10 +1024,10 @@ displayUpdateDownloadWindow.status.verifying=Verifying signature... displayUpdateDownloadWindow.button.label=Download installer and verify signature displayUpdateDownloadWindow.headline=A new Bisq update is available! displayUpdateDownloadWindow.download.failed=Download failed.\n\ - Please download and verify manually at https://bisq.io/downloads -displayUpdateDownloadWindow.installer.failed=Unable to determine the correct installer. Please download and verify manually at https://bisq.io/downloads + Please download and verify manually at https://bisq.network/downloads +displayUpdateDownloadWindow.installer.failed=Unable to determine the correct installer. Please download and verify manually at https://bisq.network/downloads displayUpdateDownloadWindow.verify.failed=Verification failed.\n\ - Please download and verify manually at https://bisq.io/downloads + Please download and verify manually at https://bisq.network/downloads displayUpdateDownloadWindow.success=The new version has been successfully downloaded and the signature verified.\n\n\ Please open the download directory, shut down the application and install the new version. displayUpdateDownloadWindow.download.openDir=Open download directory @@ -1206,7 +1206,7 @@ The backup is located at:\n\ {1}/db/backup_of_corrupted_data.\n\n\ Please check if you have the latest version of Bisq installed.\n\ You can download it at:\n\ -https://bisq.io/downloads\n\n\ +https://bisq.network/downloads\n\n\ Please restart the application. popup.warning.cannotConnectAtStartup=You still did not get connected to the {0} network.\nIf you use Tor for Bitcoin it might be that you got an unstable Tor circuit.\nYou can wait longer or try to restart. popup.warning.unknownProblemAtStartup=There is an unknown problem at startup.\nPlease restart and if the problem continues file a bug report. @@ -1555,7 +1555,7 @@ popup.info.revertIdCheckRequirement=With this version we remove the requirement with bank transfer or Faster Payment was used.\n\n\ The email address is not exposed anymore in Sepa, Faster Payment and national bank transfer payment methods.\n\n\ Please see the discussion on the Bisq Forum for further background:\n\ - https://forum.bisq.io/t/new-requirement-for-payment-accounts-with-charge-back-risk/2376 + https://forum.bisq.network/t/new-requirement-for-payment-accounts-with-charge-back-risk/2376 # We use constants from the code so we do not use our normal naming convention # dynamic values are not recognized by IntelliJ diff --git a/common/src/main/resources/i18n/displayStrings_de.properties b/common/src/main/resources/i18n/displayStrings_de.properties index 0a272c4cb6..c95fa0dba1 100644 --- a/common/src/main/resources/i18n/displayStrings_de.properties +++ b/common/src/main/resources/i18n/displayStrings_de.properties @@ -660,7 +660,7 @@ support.buyerOfferer=BTC Käufer/Ersteller support.sellerOfferer=BTC Verkäufer/Ersteller support.buyerTaker=BTC Käufer/Abnehmer support.sellerTaker=BTC Verkäufer/Abnehmer -support.backgroundInfo=Bisq ist keine Firma und betreibt keine Form von Kundendienst.\n\nGibt es Konflikte während des Handelsprozess (z.B. ein Händler befolgt nicht das Handelsprotokoll), wird die Anwendung nach der Handelsdauer eine \"Konflikt öffnen\" Schaltfläche anzeigen, um den Vermittler zu kontaktieren.\nIm Falle von Softwarefehlern oder anderen Problemen, die von der Anwendung entdeckt werden, wird eine \"Unterstützungsticket öffnen\" Schaltfläche angezeigt, um den Vermittler zu kontaktieren, der die Probleme an die Entwickler weiterleitet.\n\nIm Falle, dass ein Nutzer durch einen Fehler fest hängt, ohne dass die \"Unterstützungsticket öffnen\" Schaltfläche angezeigt wird, können Sie ein Unterstützungsticket mit einer speziellen Tastenkombination manuell öffnen.\n\nBitte nutzen Sie diese nur, wenn Sie sicher sind, dass die Software sich nicht wie erwartet verhält. Falls Sie Probleme wie man Bisq verwendet oder andere Fragen haben, überprüfen Sie bitte das FAQ auf der bisq.io Website oder posten Sie in das Bisq Forum in die Unterstützungsabteilung.\n\nFalls Sie sicher sind, dass Sie ein Unterstützungsticket öffnen wollen, wählen Sie bitte den Handel, der Probleme bereitet, unter \"Mappe/Offene Händel\" und drücken Sie die Tastenkombination \"alt + o\" oder \"option + o\" um das Unterstützungsticket zu öffnen. +support.backgroundInfo=Bisq ist keine Firma und betreibt keine Form von Kundendienst.\n\nGibt es Konflikte während des Handelsprozess (z.B. ein Händler befolgt nicht das Handelsprotokoll), wird die Anwendung nach der Handelsdauer eine \"Konflikt öffnen\" Schaltfläche anzeigen, um den Vermittler zu kontaktieren.\nIm Falle von Softwarefehlern oder anderen Problemen, die von der Anwendung entdeckt werden, wird eine \"Unterstützungsticket öffnen\" Schaltfläche angezeigt, um den Vermittler zu kontaktieren, der die Probleme an die Entwickler weiterleitet.\n\nIm Falle, dass ein Nutzer durch einen Fehler fest hängt, ohne dass die \"Unterstützungsticket öffnen\" Schaltfläche angezeigt wird, können Sie ein Unterstützungsticket mit einer speziellen Tastenkombination manuell öffnen.\n\nBitte nutzen Sie diese nur, wenn Sie sicher sind, dass die Software sich nicht wie erwartet verhält. Falls Sie Probleme wie man Bisq verwendet oder andere Fragen haben, überprüfen Sie bitte das FAQ auf der bisq.network Website oder posten Sie in das Bisq Forum in die Unterstützungsabteilung.\n\nFalls Sie sicher sind, dass Sie ein Unterstützungsticket öffnen wollen, wählen Sie bitte den Handel, der Probleme bereitet, unter \"Mappe/Offene Händel\" und drücken Sie die Tastenkombination \"alt + o\" oder \"option + o\" um das Unterstützungsticket zu öffnen. support.initialInfo=Bitte beachten Sie die grundlegenen Regeln für den Konfliktprozess:\n1. Sie müssen innerhalb von 2 Tagen auf die Anfrage des Vermittlers reagieren.\n2. Die maximale Dauer des Konflikts ist 14 Tage.\n3. Sie müssen erfüllen, was der Vermittler von ihnen verlangt, um Beweise für ihren Fall zu liefern.\n4. Sie akzeptieren die Regeln, die im Wiki im Nutzungsvereinbarung umrissen wurden, als Sie das erste Mal die Anwendung gestartet haben.\n\nErfahren Sie mehr über den Konfliktprozess in unserem Wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Systemnachricht: {0} support.youOpenedTicket=Sie haben eine Anfrage auf Unterstützung geöffnet. @@ -1064,7 +1064,7 @@ error.spvFileCorrupted=Beim Einlesen der SPV Kettendatei ist ein Fehler aufgetre popup.warning.walletNotInitialized=Die Wallet ist noch nicht initialisiert popup.warning.wrongVersion=Sie haben vermutlich die falsche Bisq Version für diesen Computer.\nDie Architektur ihres Computers ist: {0}.\nDie installierten Bisq Binärdateien sind: {1}.\nBitte fahren Sie diese herunter und installieren die korrekte Version ({2}). -popup.warning.incompatibleDB=Wir haben nicht kompatible Datenbankdateien entdeckt!\n\nDie Datenbankdatei(en) sind mit unserer momentanen Codebasis nicht kompatibel:\n{0}\n\nWir haben ein Backup der beschädigten Datei(en) erstellt und die Standardwerte auf eine neue Datenbankversion angewendet.\n\nDas Backup befindet sich in:\n{1}/db/backup_of_corrupted_data.\n\nBitte überprüfen Sie, ob Sie die letzte Version von Bisq installiert haben.\nSie können diese hier herunterladen:\nhttps://bisq.io/downloads\n\nBitte starten Sie die Anwendung neu. +popup.warning.incompatibleDB=Wir haben nicht kompatible Datenbankdateien entdeckt!\n\nDie Datenbankdatei(en) sind mit unserer momentanen Codebasis nicht kompatibel:\n{0}\n\nWir haben ein Backup der beschädigten Datei(en) erstellt und die Standardwerte auf eine neue Datenbankversion angewendet.\n\nDas Backup befindet sich in:\n{1}/db/backup_of_corrupted_data.\n\nBitte überprüfen Sie, ob Sie die letzte Version von Bisq installiert haben.\nSie können diese hier herunterladen:\nhttps://bisq.network/downloads\n\nBitte starten Sie die Anwendung neu. popup.warning.cannotConnectAtStartup=Sie wurden immer noch nicht mit dem {0} Netzwerk verbunden.\nSollten Sie Tor für Bitcoin verwenden kann es sein, dass Sie eine labile Torverbindung haben.\nSie können versuchen länger zu warten oder neu zu starten. popup.warning.unknownProblemAtStartup=Es gibt ein unbekanntes Problem beim Starten.\nBitte starten Sie neu und wenn das Problem besteht, füllen Sie eine Bugmeldung aus. popup.warning.startupFailed.timeout=Die Anwendung konnte nicht nach 4 Minuten starten.\n\n{0} diff --git a/common/src/main/resources/i18n/displayStrings_el.properties b/common/src/main/resources/i18n/displayStrings_el.properties index b59de0de97..bd0a0933c5 100644 --- a/common/src/main/resources/i18n/displayStrings_el.properties +++ b/common/src/main/resources/i18n/displayStrings_el.properties @@ -660,7 +660,7 @@ support.buyerOfferer=Αγοραστής/Maker BTC support.sellerOfferer=Πωλητής/Maker BTC support.buyerTaker=Αγοραστής/Taker BTC support.sellerTaker=Πωλητής/Taker BTC -support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.io web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. +support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.network web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. support.initialInfo=Please note the basic rules for the dispute process:\n1. You need to respond to the arbitrators requests in between 2 days.\n2. The maximum period for the dispute is 14 days.\n3. You need to fulfill what the arbitrator is requesting from you to deliver evidence for your case.\n4. You accepted the rules outlined in the wiki in the user agreement when you first started the application.\n\nPlease read more in detail about the dispute process in our wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Μήνυμα συστήματος: {0} support.youOpenedTicket=Άνοιξες ένα αίτημα υποστήριξης. @@ -1066,7 +1066,7 @@ error.spvFileCorrupted=Προέκυψε σφάλμα κατά την ανάγν popup.warning.walletNotInitialized=Δεν έχει δημιουργηθεί το πορτοφόλι μέχρι στιγμής popup.warning.wrongVersion=You probably have the wrong Bisq version for this computer.\nYour computer''s architecture is: {0}.\nThe Bisq binary you installed is: {1}.\nPlease shut down and re-install the correct version ({2}). -popup.warning.incompatibleDB=We detected incompatible data base files!\n\nThose database file(s) are not compatible with our current code base:\n{0}\n\nWe made a backup of the corrupted file(s) and applied the default values to a new database version.\n\nThe backup is located at:\n{1}/db/backup_of_corrupted_data.\n\nPlease check if you have the latest version of Bisq installed.\nYou can download it at:\nhttps://bisq.io/downloads\n\nPlease restart the application. +popup.warning.incompatibleDB=We detected incompatible data base files!\n\nThose database file(s) are not compatible with our current code base:\n{0}\n\nWe made a backup of the corrupted file(s) and applied the default values to a new database version.\n\nThe backup is located at:\n{1}/db/backup_of_corrupted_data.\n\nPlease check if you have the latest version of Bisq installed.\nYou can download it at:\nhttps://bisq.network/downloads\n\nPlease restart the application. popup.warning.cannotConnectAtStartup=Παραμένεις αποσυνδεδεμένος από το δίκτυο {0}.\nΑν χρησιμοποιείς το Tor για το Bitcoin ίσως έχεις ασταθές κύκλωμα Tor.\nΜπορείς να περιμένεις περαιτέρω ή να επανεκκινήσεις. popup.warning.unknownProblemAtStartup=Υπάρχει άγνωστο πρόβλημα κατά την εκκίνηση.\nΕπανεκκίνησε την εφαρμογή και αν το πρόβλημα επιμείνει, κατάθεσε μια αναφορά σφάλματος. popup.warning.startupFailed.timeout=Η εφαρμογή δεν μπόρεσε να εκκινήσει εντός 4 λεπτών.\n\n{0} diff --git a/common/src/main/resources/i18n/displayStrings_es.properties b/common/src/main/resources/i18n/displayStrings_es.properties index 89bfd54e2e..e41efc385d 100644 --- a/common/src/main/resources/i18n/displayStrings_es.properties +++ b/common/src/main/resources/i18n/displayStrings_es.properties @@ -660,7 +660,7 @@ support.buyerOfferer= comprador/creador BTC support.sellerOfferer=vendedor/creador BTC support.buyerTaker=comprador/Tomador BTC support.sellerTaker=vendedor/Tomador BTC -support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.io web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. +support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.network web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. support.initialInfo=Por favor, tenga en cuenta las reglas básicas para el proceso de disputa:\n1. Necesita responder a las peticiones de los árbitros en menos de 2 días.\n2. El periodo máximo total de la disputa es de 14 días.\n3. Necesitará completar todas las peticiones del árbitro para entregar evidencia de su caso.\n4. Acepta las reglas destacadas en la wiki de acuerdo de usuario al comenzar la aplicación.\n\nPor favor lea en más detalle acerca del proceso de disputa en nuestra wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Mensaje de sistema: {0} support.youOpenedTicket=Ha abierto una solicitud de soporte. @@ -1064,7 +1064,7 @@ error.spvFileCorrupted=Ocurrió un error al leer el archivo de cadena SPV.\nPued popup.warning.walletNotInitialized=La cartera aún no sea ha iniciado popup.warning.wrongVersion=Probablemente tenga una versión de Bisq incorrecta para este ordenador.\nLa arquitectura de su ordenador es: {0}.\nLos binarios de Bisq instalados son: {1}.\nPor favor cierre y reinstale la versión correcta ({2}). -popup.warning.incompatibleDB=Hemos detectado archivos de base de datos incompatibles!\n\nEstos archivos de base de datos no son compatibles con nuestro actual código base:\n{0}\n\nHemos hecho una copia de seguridad de los archivos corruptos y aplicado los valores por defecto a la nueva versión de base de datos.\n\nLa copia de seguridad se localiza en:\n{1}/db/backup_of_corrupted_data.\n\nPor favor, compruebe si tiene la última versión de Bisq instalada.\nPuede descargarla en:\nhttps://bisq.io/downloads\n\nPor favor, reinicie la aplicación. +popup.warning.incompatibleDB=Hemos detectado archivos de base de datos incompatibles!\n\nEstos archivos de base de datos no son compatibles con nuestro actual código base:\n{0}\n\nHemos hecho una copia de seguridad de los archivos corruptos y aplicado los valores por defecto a la nueva versión de base de datos.\n\nLa copia de seguridad se localiza en:\n{1}/db/backup_of_corrupted_data.\n\nPor favor, compruebe si tiene la última versión de Bisq instalada.\nPuede descargarla en:\nhttps://bisq.network/downloads\n\nPor favor, reinicie la aplicación. popup.warning.cannotConnectAtStartup=Aún no se ha conecta a la red {0}.\nSi usa Tor para Bitcoin puede ser que tenga un circuito Tor inestable.\nPuede esperar más o probar reiniciando. popup.warning.unknownProblemAtStartup=Hay un problema desconocido al inicio.\nPor favor, reinicia y si el problema continua realice un reporte de error. popup.warning.startupFailed.timeout=La aplicación no se pudo iniciar después de 4 minutos.\n\n{0} diff --git a/common/src/main/resources/i18n/displayStrings_pt.properties b/common/src/main/resources/i18n/displayStrings_pt.properties index 854e5cef11..b079552aad 100644 --- a/common/src/main/resources/i18n/displayStrings_pt.properties +++ b/common/src/main/resources/i18n/displayStrings_pt.properties @@ -660,7 +660,7 @@ support.buyerOfferer=Comprador de BTC / Ofetante support.sellerOfferer=Vendedor de BTC / Ofertante support.buyerTaker=Comprador de BTC / Aceitador da oferta support.sellerTaker=Vendedor de BTC / Aceitador da oferta -support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.io web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. +support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.network web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. support.initialInfo=Favor note as regras básicas para o processo de disputa:\n1. Você precisa responder aos pedidos de árbitros em até 2 dias.\n2. O período máximo de uma disputa é 14 dias.\n3. Você precisa atender ao que o árbitro está pedindo e fornecer evidências necessárias para seu caso.\n4. Você aceitou as regras delineadas na wiki no acordo de usuário quando você iniciou o programa.\n\nMais detalhes sobre o processo de disputa se encontram na wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Mensagem do sistema: {0} support.youOpenedTicket=Você abriu uma solicitação para suporte. @@ -1066,7 +1066,7 @@ error.spvFileCorrupted=Um erro ocorreu ao ler o arquivo SPV chain.\nPode ser que popup.warning.walletNotInitialized=A carteira ainda não foi inicializada popup.warning.wrongVersion=You probably have the wrong Bisq version for this computer.\nYour computer''s architecture is: {0}.\nThe Bisq binary you installed is: {1}.\nPlease shut down and re-install the correct version ({2}). -popup.warning.incompatibleDB=We detected incompatible data base files!\n\nThose database file(s) are not compatible with our current code base:\n{0}\n\nWe made a backup of the corrupted file(s) and applied the default values to a new database version.\n\nThe backup is located at:\n{1}/db/backup_of_corrupted_data.\n\nPlease check if you have the latest version of Bisq installed.\nYou can download it at:\nhttps://bisq.io/downloads\n\nPlease restart the application. +popup.warning.incompatibleDB=We detected incompatible data base files!\n\nThose database file(s) are not compatible with our current code base:\n{0}\n\nWe made a backup of the corrupted file(s) and applied the default values to a new database version.\n\nThe backup is located at:\n{1}/db/backup_of_corrupted_data.\n\nPlease check if you have the latest version of Bisq installed.\nYou can download it at:\nhttps://bisq.network/downloads\n\nPlease restart the application. popup.warning.cannotConnectAtStartup=Você ainda não foi conectado à rede {0}.\nSe você utilizar Tor para acessar Bitcoin pode ser que você tem um circuito Tor instável.\nVocê pode aguardar mais ou tentar reiniciar. popup.warning.unknownProblemAtStartup=Houve um problema desconhecido ao iniciar.\nFavor reiniciar e se o problema persistir abrir um bug report (informe de problema). popup.warning.startupFailed.timeout=Falha ao iniciar aplicativo após 4 minutos.\n\n{0} diff --git a/common/src/main/resources/i18n/displayStrings_sr.properties b/common/src/main/resources/i18n/displayStrings_sr.properties index 61e3200f32..8f25d1ece4 100644 --- a/common/src/main/resources/i18n/displayStrings_sr.properties +++ b/common/src/main/resources/i18n/displayStrings_sr.properties @@ -660,7 +660,7 @@ support.buyerOfferer=BTC kupac/Tvorac support.sellerOfferer=BTC prodavac/Tvorac support.buyerTaker=BTC kupac/Uzimalac support.sellerTaker=BTC prodavac/Tvorac -support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.io web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. +support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.network web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. support.initialInfo=Imajte na umu osnovna pravila za proces rasprave:\n1. Potrebno je da odgovorite zahtevima arbitra u razmaku od 2 dana.\n2. Maksimalni period rasprave je 14 dana.\n3. Morate da ispunite što arbitar zahteva od vas radi dostavljanja dokaza za vaš slučaj.\n4. Prihvatili ste pravila navedena u wiki u korisničkom ugovoru kada ste prvi put pokrenuli aplikaciju.\n\nMolimo pročitajte detaljnije o procesu rasprave u našoj wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Poruka sistema: {0} support.youOpenedTicket=Otvorili ste zahtev za podršku. @@ -1064,7 +1064,7 @@ error.spvFileCorrupted=Došlo je do greške prilikom čitanja SPV lanac fajla.\n popup.warning.walletNotInitialized=Novčanik još nije inicijalizovan popup.warning.wrongVersion=Verovatno imate pogrešnu verziju Bisq za ovaj računar.\nArhitektura vašeg računara je: {0}.\nBisq program koji ste vi instalirali je: {1}.\nMolimo isključite i reinstalirajte tačnu verziju ({2}). -popup.warning.incompatibleDB=Otkrili smo nekompitabilne fajlove baze podataka!\n\nTaj fajl(ovi) baze podataka nisu kompitabilni sa trenutnom kod bazom:\n{0}\n\nNapravili smo rezervu iskvarenih fajlova i primenili podrazumevane vrednosti novoj verziji baze podataka.\n\nRezerva se nalazi na:\n{1}/db/backup_of_corrupted_data.\n\nMolimo proverite da li imate instaliranu najnoviju verziju Bisq.\nMožete je preuzeti na:\nhttps://bisq.io/downloads\n\nMolimo pokrenite ponovo aplikaciju. +popup.warning.incompatibleDB=Otkrili smo nekompitabilne fajlove baze podataka!\n\nTaj fajl(ovi) baze podataka nisu kompitabilni sa trenutnom kod bazom:\n{0}\n\nNapravili smo rezervu iskvarenih fajlova i primenili podrazumevane vrednosti novoj verziji baze podataka.\n\nRezerva se nalazi na:\n{1}/db/backup_of_corrupted_data.\n\nMolimo proverite da li imate instaliranu najnoviju verziju Bisq.\nMožete je preuzeti na:\nhttps://bisq.network/downloads\n\nMolimo pokrenite ponovo aplikaciju. popup.warning.cannotConnectAtStartup=Još uvek niste povezani na {0} mrežu.\nAko koristite Tor za Bitcoin moguće je da imate nestabilnu Tor rutu.\nMožete još čekati ili pokušati da ponovo pokrenete. popup.warning.unknownProblemAtStartup=Postoji nepoznat problem pri pokretanju.\nMolimo pokrenite ponovo i ako se problem nastavi podnesite prijavu o grešci. popup.warning.startupFailed.timeout=Aplikacija nije mogla da se pokrene nakon 4 minuta.\n\n{0} diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ae.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ae.properties index 80659f97f2..3a987af140 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ae.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ae.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ba.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ba.properties index dae7aabe06..6907f49977 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ba.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ba.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_br.properties b/common/src/main/resources/i18n/in_dev/displayStrings_br.properties index 601b1e1460..b6c3cc3f6d 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_br.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_br.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_cn.properties b/common/src/main/resources/i18n/in_dev/displayStrings_cn.properties index 5bb3637778..ce06656d57 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_cn.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_cn.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_cz.properties b/common/src/main/resources/i18n/in_dev/displayStrings_cz.properties index 80591cd151..2f9f08bb2a 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_cz.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_cz.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_dk.properties b/common/src/main/resources/i18n/in_dev/displayStrings_dk.properties index cd892f7a31..bb74ee19d5 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_dk.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_dk.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_en.properties b/common/src/main/resources/i18n/in_dev/displayStrings_en.properties index 93f79ea775..d0e2a37672 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_en.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_en.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_fi.properties b/common/src/main/resources/i18n/in_dev/displayStrings_fi.properties index c407b051b3..858e89e3b8 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_fi.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_fi.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties index 3fa2fd98b6..1d88efdac3 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties @@ -122,7 +122,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: @@ -1081,7 +1081,7 @@ popup.error.createTx=Erreur à la création de la transaction {0} error.spvFileCorrupted=Une erreur s'est produite lors de la lecture du fichier de la chaîne SPV. \nIl se pourrait être que le fichier de la chaîne SPV soit endommagé. \n\nMessage d''erreur: {0} \n\n Voulez-vous le supprimer et démarrer une resynchronisation? popup.warning.walletNotInitialized=Le portefeuille n'est pas encore initialisé popup.warning.wrongVersion=Vous avez probablement la mauvaise version Bisq pour cet ordinateur. \nL'architecture de votre ordinateur est: {0}. \nLa binaire Bisq que vous avez installée est: {1}. \nFermez et réinstallez la version correcte ({2}). -popup.warning.incompatibleDB=Nous avons détecté des fichiers de base de données incompatibles! \n \nCes fichiers de base de données ne sont pas compatibles avec notre base de code actuelle: \n {0} \n \nNous avons sauvegardé les fichiers corrompus et appliqué les valeurs par défaut à une nouvelle version de base de données. \n \nLa sauvegarde se trouve à: \n{1} /db/backup_of_corrupted_data.\n\nVérifiez si vous avez installé la dernière version de Bisq. \nVous pouvez le télécharger à: \n Https://bisq.io/downloads \n\nRedémarrez l'application. +popup.warning.incompatibleDB=Nous avons détecté des fichiers de base de données incompatibles! \n \nCes fichiers de base de données ne sont pas compatibles avec notre base de code actuelle: \n {0} \n \nNous avons sauvegardé les fichiers corrompus et appliqué les valeurs par défaut à une nouvelle version de base de données. \n \nLa sauvegarde se trouve à: \n{1} /db/backup_of_corrupted_data.\n\nVérifiez si vous avez installé la dernière version de Bisq. \nVous pouvez le télécharger à: \n Https://bisq.network/downloads \n\nRedémarrez l'application. popup.warning.cannotConnectAtStartup=Vous ne vous êtes toujours pas connecté au {0} réseau.\nSi vous utilisez Tor pour Bitcoin, il se pourrait que vous ayez un circuit Tor instable. \nVous pouvez attendre plus longtemps ou essayer de redémarrer. popup.warning.unknownProblemAtStartup=Il y a un problème inconnu lors du démarrage.\nVeuillez redémarrer et si le problème continue de déposer un rapport de bogue. popup.warning.startupFailed.timeout=L'application n'a pas pu démarrer après 4 minutes. \n \n {0}\ diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ge.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ge.properties index a063a3d90c..583a3c05c7 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ge.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ge.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_gr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_gr.properties index 3e0d767d92..27705ca26f 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_gr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_gr.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_hr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_hr.properties index f1a51a4dee..20e3f130ec 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_hr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_hr.properties @@ -118,7 +118,7 @@ createOffer.fundsBox.totalsNeeded=Potrebna sredstva: createOffer.fundsBox.totalsNeeded.prompt=Biti će izračunata iz iznosa bitcoina koje ste unijeli iznad createOffer.fundsBox.address=Adresa novčanika za razmjenu: createOffer.fundsBox.balance=Stanje novčanika za razmjenu: -# TODO remove createOffer.fundsBox.info=Svaka ponuda koristi zasebni novčanik za razmjenu. Morate napuniti taj novčanik potrebnim iznosom bitcoina. Ta su sredstva rezervirana te će se koristiti u slučaju da ponuda bude prihvaćena. U slučaju da otkažete svoju ponudu možete povući svoja sredstva iz tog novčanika. Jedina uplata koja će biti izvršena prilikom postavljanja ponude je sama naknada za postavljanje ponude. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=Svaka ponuda koristi zasebni novčanik za razmjenu. Morate napuniti taj novčanik potrebnim iznosom bitcoina. Ta su sredstva rezervirana te će se koristiti u slučaju da ponuda bude prihvaćena. U slučaju da otkažete svoju ponudu možete povući svoja sredstva iz tog novčanika. Jedina uplata koja će biti izvršena prilikom postavljanja ponude je sama naknada za postavljanje ponude. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Iznos razmjene: createOffer.fundsBox.securityDeposit=Sigurnosni depozit: createOffer.fundsBox.offerFee=Naknada za izradu ponude: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_hu.properties b/common/src/main/resources/i18n/in_dev/displayStrings_hu.properties index 6e299aa00a..07d498a2d6 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_hu.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_hu.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_id.properties b/common/src/main/resources/i18n/in_dev/displayStrings_id.properties index 6ef63a3613..e331c4ba17 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_id.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_id.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ie.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ie.properties index 32fa08c327..d3d6c568f1 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ie.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ie.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_in.properties b/common/src/main/resources/i18n/in_dev/displayStrings_in.properties index 5d0de8fe34..557e1567b6 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_in.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_in.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_is.properties b/common/src/main/resources/i18n/in_dev/displayStrings_is.properties index e2cf160b2c..2ac3df8883 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_is.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_is.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_it.properties b/common/src/main/resources/i18n/in_dev/displayStrings_it.properties index 2ebe372b2b..44a13d5e74 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_it.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_it.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Fondi richiesti: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Indirizzo portafoglio per lo scambio: createOffer.fundsBox.balance=Saldo portafoglio per lo scambio: -# TODO remove createOffer.fundsBox.info=Per ogni offerta c'è un portamonete di scambio dedicato. Devi finanziare il portamonete di scambio con la quantità di bitcoin necessaria. Questi fondi sono riservati e saranno utilizzati nel caso in cui la tua offerta venga eseguita. Se cancelli la tua offerta puoi ritirare i fondi da quel portamonete di scambio. L'unico pagamento eseguito quando disponi un'offerta è la commissione di pagamento per offerta. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=Per ogni offerta c'è un portamonete di scambio dedicato. Devi finanziare il portamonete di scambio con la quantità di bitcoin necessaria. Questi fondi sono riservati e saranno utilizzati nel caso in cui la tua offerta venga eseguita. Se cancelli la tua offerta puoi ritirare i fondi da quel portamonete di scambio. L'unico pagamento eseguito quando disponi un'offerta è la commissione di pagamento per offerta. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Importo per lo scambio: createOffer.fundsBox.securityDeposit=Deposito di sicurezza: createOffer.fundsBox.offerFee=Crea commissione di offerta: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_jp.properties b/common/src/main/resources/i18n/in_dev/displayStrings_jp.properties index dc5cd9a36e..e8be8f0fe3 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_jp.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_jp.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_kg.properties b/common/src/main/resources/i18n/in_dev/displayStrings_kg.properties index bfa655055a..76747de6c5 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_kg.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_kg.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_kr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_kr.properties index 6174381d87..ff50c03b62 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_kr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_kr.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_kz.properties b/common/src/main/resources/i18n/in_dev/displayStrings_kz.properties index ac38b26b6a..1f11f27a13 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_kz.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_kz.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_lt.properties b/common/src/main/resources/i18n/in_dev/displayStrings_lt.properties index 9899c55a25..9ed3f47343 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_lt.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_lt.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_my.properties b/common/src/main/resources/i18n/in_dev/displayStrings_my.properties index d127910820..49c5452d4a 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_my.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_my.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_nl.properties b/common/src/main/resources/i18n/in_dev/displayStrings_nl.properties index f2e7c9b3c0..50d63e68ad 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_nl.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_nl.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_no.properties b/common/src/main/resources/i18n/in_dev/displayStrings_no.properties index 71b23455d8..af409fa8b4 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_no.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_no.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_nz.properties b/common/src/main/resources/i18n/in_dev/displayStrings_nz.properties index 957111aa60..c0bd7dd45c 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_nz.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_nz.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ph.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ph.properties index f94f95ff7e..adc1823257 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ph.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ph.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_pl.properties b/common/src/main/resources/i18n/in_dev/displayStrings_pl.properties index b67a53a884..f3cfc455c2 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_pl.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_pl.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_pt.properties b/common/src/main/resources/i18n/in_dev/displayStrings_pt.properties index 946ca01fb4..552db85056 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_pt.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_pt.properties @@ -67,7 +67,7 @@ createOffer.fundsBox.totalsNeeded=Fundos necessários: createOffer.fundsBox.totalsNeeded.prompt=Será calculado pelos montantes de bitcoin introduzidos acima createOffer.fundsBox.address=Endereço da carteira da troca: createOffer.fundsBox.balance=Saldo da carteira da troca: -# TODO remove createOffer.fundsBox.info=Para cada oferta há uma carteira de troca dedicada. Precisa de provisionar essa carteira da troca com o montante de bitcoin necessário. Esses fundos serão reservados e usados no caso de a oferta ser executada. Se cancelar a sua oferta pode levantar os seus fundos dessa carteira de troca. O único pagamento feito ao criar uma oferta é o pagamento da taxa da oferta. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=Para cada oferta há uma carteira de troca dedicada. Precisa de provisionar essa carteira da troca com o montante de bitcoin necessário. Esses fundos serão reservados e usados no caso de a oferta ser executada. Se cancelar a sua oferta pode levantar os seus fundos dessa carteira de troca. O único pagamento feito ao criar uma oferta é o pagamento da taxa da oferta. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Montante da troca: createOffer.fundsBox.securityDeposit=Depósito de segurança: createOffer.fundsBox.offerFee=Taxa de criação de oferta: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ro.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ro.properties index 487c3094ad..c856220965 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ro.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ro.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ru.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ru.properties index 38917859c0..b066a2798c 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ru.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ru.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_se.properties b/common/src/main/resources/i18n/in_dev/displayStrings_se.properties index c7e66a6d79..3c77dbf0f5 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_se.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_se.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_th.properties b/common/src/main/resources/i18n/in_dev/displayStrings_th.properties index 6cef719a66..3c78bff2dd 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_th.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_th.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ua.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ua.properties index 716c61722b..d20b224d22 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ua.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ua.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_uz.properties b/common/src/main/resources/i18n/in_dev/displayStrings_uz.properties index 98cca6cc54..41fe1790e2 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_uz.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_uz.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_vn.properties b/common/src/main/resources/i18n/in_dev/displayStrings_vn.properties index 2f7c668ccc..384346c883 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_vn.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_vn.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/core/src/main/java/io/bisq/core/user/Preferences.java b/core/src/main/java/io/bisq/core/user/Preferences.java index 39f69053c8..e5a4cc1d8b 100644 --- a/core/src/main/java/io/bisq/core/user/Preferences.java +++ b/core/src/main/java/io/bisq/core/user/Preferences.java @@ -57,9 +57,9 @@ public final class Preferences implements PersistedDataHost { new BlockChainExplorer("Blockr.io", "https://tbtc.blockr.io/tx/info/", "https://tbtc.blockr.io/address/info/") )); - public static final BlockChainExplorer BSQ_MAIN_NET_EXPLORER = new BlockChainExplorer("BSQ", "https://explorer.bisq.io/tx.html?tx=", + public static final BlockChainExplorer BSQ_MAIN_NET_EXPLORER = new BlockChainExplorer("BSQ", "https://explorer.bisq.network/tx.html?tx=", "https://explorer.bisq.network/Address.html?addr="); - public static final BlockChainExplorer BSQ_TEST_NET_EXPLORER = new BlockChainExplorer("BSQ", "https://explorer.bisq.io/testnet/tx.html?tx=", + public static final BlockChainExplorer BSQ_TEST_NET_EXPLORER = new BlockChainExplorer("BSQ", "https://explorer.bisq.network/testnet/tx.html?tx=", "https://explorer.bisq.network/testnet/Address.html?addr="); private static final ArrayList LTC_MAIN_NET_EXPLORERS = new ArrayList<>(Arrays.asList( diff --git a/gui/src/main/java/io/bisq/gui/SystemTray.java b/gui/src/main/java/io/bisq/gui/SystemTray.java index dac1ac6da1..cc707165b0 100644 --- a/gui/src/main/java/io/bisq/gui/SystemTray.java +++ b/gui/src/main/java/io/bisq/gui/SystemTray.java @@ -123,7 +123,7 @@ public class SystemTray { aboutItem.addActionListener(e -> { try { - UserThread.execute(() -> GUIUtil.openWebPage("https://bisq.io")); + UserThread.execute(() -> GUIUtil.openWebPage("https://bisq.network")); } catch (Exception e1) { e1.printStackTrace(); } diff --git a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java index 679a45fa52..77478accca 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java @@ -372,7 +372,7 @@ public class CreateOfferView extends ActivatableViewAndModel().backgroundInfo(Res.get("popup.info.securityDepositInfo")) .actionButtonText(Res.get("shared.faq")) - .onAction(() -> GUIUtil.openWebPage("https://bisq.io/faq#6")) + .onAction(() -> GUIUtil.openWebPage("https://bisq.network/faq#6")) .useIUnderstandButton() .dontShowAgainId(key) .show(); diff --git a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java index 9794125e44..c01942d6bd 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java @@ -394,7 +394,7 @@ public class TakeOfferView extends ActivatableViewAndModel().backgroundInfo(Res.get("popup.info.securityDepositInfo")) .actionButtonText(Res.get("shared.faq")) - .onAction(() -> GUIUtil.openWebPage("https://bisq.io/faq#6")) + .onAction(() -> GUIUtil.openWebPage("https://bisq.network/faq#6")) .useIUnderstandButton() .dontShowAgainId(key) .show(); diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/Overlay.java b/gui/src/main/java/io/bisq/gui/main/overlays/Overlay.java index a4d976ca80..5c45735382 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/Overlay.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/Overlay.java @@ -746,7 +746,7 @@ public abstract class Overlay { forumButton.setOnAction(event -> { if (message != null) Utilities.copyToClipboard(message); - GUIUtil.openWebPage("http://forum.bisq.io"); + GUIUtil.openWebPage("http://forum.bisq.network"); }); } diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/AddBitcoinNodesWindow.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/AddBitcoinNodesWindow.java index 29a2ad1f3f..1a6946ccce 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/AddBitcoinNodesWindow.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/AddBitcoinNodesWindow.java @@ -87,13 +87,13 @@ public class AddBitcoinNodesWindow extends Overlay { "You can run it locally (127.0.0.1) or hosted on a VPS.\n" + "You can edit that settings in \"Settings/Network info\".\n\n" + "If you prefer to use the public " + Res.getBaseCurrencyName() + " network your Bitcoin transactions might get de-anonymized by chain analysis companies operating full nodes to spy on Bitcoin users.\n\n" + - "To learn more about that topic please read our FAQ on bisq.io."); + "To learn more about that topic please read our FAQ on bisq.network."); label.setWrapText(true); GridPane.setColumnSpan(label, 2); GridPane.setHalignment(label, HPos.LEFT); HyperlinkWithIcon hyperlinkWithIcon = new HyperlinkWithIcon("Open bisq FAQ", AwesomeIcon.EXTERNAL_LINK); - hyperlinkWithIcon.setOnAction(e -> GUIUtil.openWebPage("https://bisq.io/faq/#privacy_btc")); + hyperlinkWithIcon.setOnAction(e -> GUIUtil.openWebPage("https://bisq.network/faq/#privacy_btc")); GridPane.setRowIndex(hyperlinkWithIcon, ++rowIndex); GridPane.setColumnIndex(hyperlinkWithIcon, 0); GridPane.setMargin(hyperlinkWithIcon, new Insets(0, 0, 0, -4)); diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/DisplayAlertMessageWindow.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/DisplayAlertMessageWindow.java index 0b384bda61..92a1b8a4c0 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/DisplayAlertMessageWindow.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/DisplayAlertMessageWindow.java @@ -70,7 +70,7 @@ public class DisplayAlertMessageWindow extends Overlay { String fontSize = smallScreen ? "9" : "12"; messageLabel.setStyle("-fx-font-size: " + fontSize + ";"); HyperlinkWithIcon hyperlinkWithIcon = addHyperlinkWithIcon(gridPane, ++rowIndex, Res.get("tacWindow.arbitrationSystem"), - "https://bisq.io/arbitration_system.pdf"); + "https://bisq.network/arbitration_system.pdf"); hyperlinkWithIcon.setStyle("-fx-font-size: " + fontSize + ";"); GridPane.setMargin(hyperlinkWithIcon, new Insets(-6, 0, -20, -4)); } diff --git a/gui/src/main/java/io/bisq/gui/main/settings/about/AboutView.java b/gui/src/main/java/io/bisq/gui/main/settings/about/AboutView.java index 80bf150e7a..d9d653de83 100644 --- a/gui/src/main/java/io/bisq/gui/main/settings/about/AboutView.java +++ b/gui/src/main/java/io/bisq/gui/main/settings/about/AboutView.java @@ -51,7 +51,7 @@ public class AboutView extends ActivatableViewAndModel { label.setWrapText(true); GridPane.setColumnSpan(label, 2); GridPane.setHalignment(label, HPos.LEFT); - HyperlinkWithIcon hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.web"), "https://bisq.io"); + HyperlinkWithIcon hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.web"), "https://bisq.network"); GridPane.setColumnSpan(hyperlinkWithIcon, 2); hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.code"), "https://github.com/bisq-network/exchange"); GridPane.setColumnSpan(hyperlinkWithIcon, 2); @@ -64,9 +64,9 @@ public class AboutView extends ActivatableViewAndModel { label.setWrapText(true); GridPane.setColumnSpan(label, 2); GridPane.setHalignment(label, HPos.LEFT); - hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.contribute"), "https://bisq.io/contribute"); + hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.contribute"), "https://bisq.network/contribute"); GridPane.setColumnSpan(hyperlinkWithIcon, 2); - hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.donate"), "https://bisq.io/contribute/#donation"); + hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.donate"), "https://bisq.network/contribute/#donation"); GridPane.setColumnSpan(hyperlinkWithIcon, 2); final boolean isBtc = Res.getBaseCurrencyCode().equals("BTC"); diff --git a/package/linux/32bitBuild.sh b/package/linux/32bitBuild.sh index 1237169d32..e3122a1208 100644 --- a/package/linux/32bitBuild.sh +++ b/package/linux/32bitBuild.sh @@ -16,7 +16,6 @@ $JAVA_HOME/bin/javapackager \ -Bruntime="$JAVA_HOME/jre" \ -BappVersion=$version \ -Bcategory=Network \ - -Bemail=team@bisq.io \ -BlicenseType=GPLv3 \ -BlicenseFile=LICENSE \ -Bicon=package/linux/icon.png \ diff --git a/package/linux/64bitBuild.sh b/package/linux/64bitBuild.sh index ff24d818a7..653fb55f8a 100644 --- a/package/linux/64bitBuild.sh +++ b/package/linux/64bitBuild.sh @@ -16,7 +16,6 @@ $JAVA_HOME/bin/javapackager \ -Bruntime="$JAVA_HOME/jre" \ -BappVersion=$version \ -Bcategory=Network \ - -Bemail=team@bisq.io \ -BlicenseType=GPLv3 \ -BlicenseFile=LICENSE \ -Bicon=package/linux/icon.png \ diff --git a/package/win/Bisq.iss b/package/win/Bisq.iss index afa0670164..8f9dff0561 100755 --- a/package/win/Bisq.iss +++ b/package/win/Bisq.iss @@ -8,8 +8,8 @@ AppVerName=Bisq AppPublisher=Bisq AppComments=Bisq AppCopyright=Copyright (C) 2016 -AppPublisherURL=https://bisq.io -AppSupportURL=https://bisq.io +AppPublisherURL=https://bisq.network +AppSupportURL=https://bisq.network ;AppUpdatesURL=http://java.com/ DefaultDirName={localappdata}\Bisq DisableStartupPrompt=Yes From 96601afb9c5aa72912465ea175cf5160134cf727 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Wed, 20 Sep 2017 15:52:16 -0500 Subject: [PATCH 40/54] Fix issue #927 --- .../io/bisq/core/app/AppSetupWithP2P.java | 32 ++++++++++++++++--- .../network/p2p/seed/SeedNodesRepository.java | 6 ++-- .../network/p2p/storage/P2PDataStorage.java | 1 + 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java b/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java index 1145517733..e68e7f5aa7 100644 --- a/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java +++ b/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java @@ -17,6 +17,7 @@ package io.bisq.core.app; +import io.bisq.common.UserThread; import io.bisq.common.crypto.KeyRing; import io.bisq.common.proto.persistable.PersistedDataHost; import io.bisq.core.trade.statistics.TradeStatisticsManager; @@ -68,8 +69,15 @@ public class AppSetupWithP2P extends AppSetup { @Override protected void initBasicServices() { - p2pNetWorkReady = initP2PNetwork(); + BooleanProperty result = loadEntryMap(); + result.addListener((observable, oldValue, newValue) -> { + if (newValue) + startInitP2PNetwork(); + }); + } + private void startInitP2PNetwork() { + p2pNetWorkReady = initP2PNetwork(); p2pNetWorkReady.addListener((observable, oldValue, newValue) -> { if (newValue) onBasicServicesInitialized(); @@ -81,6 +89,22 @@ public class AppSetupWithP2P extends AppSetup { // Initialisation /////////////////////////////////////////////////////////////////////////////////////////// + private BooleanProperty loadEntryMap() { + BooleanProperty result = new SimpleBooleanProperty(); + Thread loadEntryMapThread = new Thread() { + @Override + public void run() { + Thread.currentThread().setName("loadEntryMapThread"); + // Used to load different EntryMap files per base currency (EntryMap_BTC, EntryMap_LTC,...) + final String storageFileName = "EntryMap_" + BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode(); + p2PService.readEntryMapFromResources(storageFileName); + UserThread.execute(() -> result.set(true)); + } + }; + loadEntryMapThread.start(); + return result; + } + private BooleanProperty initP2PNetwork() { log.info("initP2PNetwork"); p2PService.getNetworkNode().addConnectionListener(new ConnectionListener() { @@ -149,11 +173,9 @@ public class AppSetupWithP2P extends AppSetup { protected void onBasicServicesInitialized() { log.info("onBasicServicesInitialized"); - // Used to load different EntryMap files per base currency (EntryMap_BTC, EntryMap_LTC,...) - final String storageFileName = "EntryMap_" + BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode() + - "_" + BisqEnvironment.getBaseCurrencyNetwork().getNetwork(); - p2PService.readEntryMapFromResources(storageFileName); p2PService.onAllServicesInitialized(); + + tradeStatisticsManager.onAllServicesInitialized(); } } diff --git a/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java b/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java index fbf3e75371..7d8e2f314a 100644 --- a/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java +++ b/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java @@ -21,14 +21,14 @@ public class SeedNodesRepository { // BTC mainnet //TODO dev dont use live nodes atm! - /*new NodeAddress("3f3cu2yw7u457ztq.onion:8000"), + new NodeAddress("3f3cu2yw7u457ztq.onion:8000"), new NodeAddress("723ljisnynbtdohi.onion:8000"), new NodeAddress("rm7b56wbrcczpjvl.onion:8000"), - new NodeAddress("fl3mmribyxgrv63c.onion:8000"),*/ + new NodeAddress("fl3mmribyxgrv63c.onion:8000"), //TODO dev // local dev - new NodeAddress("joehwtpe7ijnz4df.onion:8000"), + // new NodeAddress("joehwtpe7ijnz4df.onion:8000"), // BTC testnet new NodeAddress("nbphlanpgbei4okt.onion:8001"), diff --git a/network/src/main/java/io/bisq/network/p2p/storage/P2PDataStorage.java b/network/src/main/java/io/bisq/network/p2p/storage/P2PDataStorage.java index 173991514f..d0f73a7f47 100644 --- a/network/src/main/java/io/bisq/network/p2p/storage/P2PDataStorage.java +++ b/network/src/main/java/io/bisq/network/p2p/storage/P2PDataStorage.java @@ -126,6 +126,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers if (persistedEntryMap != null) { map.putAll(persistedEntryMap.getMap()); log.info("persistedEntryMap size=" + map.size()); + log.error("map.putAll(persistedEntryMap.getMap());"); // In case another object is already listening... if (!hashMapChangedListeners.isEmpty()) From 570e2ffba0a5b36c873b2ad527636074892faa12 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 21 Sep 2017 09:02:59 -0500 Subject: [PATCH 41/54] Increase timeout --- .../bisq/network/p2p/peers/getdata/GetDataRequestHandler.java | 2 +- .../io/bisq/network/p2p/peers/getdata/RequestDataHandler.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/network/src/main/java/io/bisq/network/p2p/peers/getdata/GetDataRequestHandler.java b/network/src/main/java/io/bisq/network/p2p/peers/getdata/GetDataRequestHandler.java index 096ac59c0c..5429c0ba5c 100644 --- a/network/src/main/java/io/bisq/network/p2p/peers/getdata/GetDataRequestHandler.java +++ b/network/src/main/java/io/bisq/network/p2p/peers/getdata/GetDataRequestHandler.java @@ -30,7 +30,7 @@ import java.util.stream.Collectors; public class GetDataRequestHandler { private static final Logger log = LoggerFactory.getLogger(GetDataRequestHandler.class); - private static final long TIME_OUT_SEC = 40; + private static final long TIME_OUT_SEC = 60; /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/network/src/main/java/io/bisq/network/p2p/peers/getdata/RequestDataHandler.java b/network/src/main/java/io/bisq/network/p2p/peers/getdata/RequestDataHandler.java index 8ceeedcffd..68d0064371 100644 --- a/network/src/main/java/io/bisq/network/p2p/peers/getdata/RequestDataHandler.java +++ b/network/src/main/java/io/bisq/network/p2p/peers/getdata/RequestDataHandler.java @@ -36,7 +36,7 @@ import static com.google.common.base.Preconditions.checkArgument; public class RequestDataHandler implements MessageListener { private static final Logger log = LoggerFactory.getLogger(RequestDataHandler.class); - private static final long TIME_OUT_SEC = 40; + private static final long TIME_OUT_SEC = 60; private NodeAddress peersNodeAddress; From 86c20682e95faf6fd054e05ef6e9562b238e33ea Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 21 Sep 2017 09:03:50 -0500 Subject: [PATCH 42/54] Cleanup startup process --- .../io/bisq/core/app/AppSetupWithP2P.java | 19 +--- .../java/io/bisq/core/app/SetupUtils.java | 23 ++++ .../java/io/bisq/gui/main/MainViewModel.java | 107 ++++-------------- .../network/p2p/storage/P2PDataStorage.java | 1 - 4 files changed, 46 insertions(+), 104 deletions(-) diff --git a/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java b/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java index e68e7f5aa7..ea25061bd7 100644 --- a/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java +++ b/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java @@ -17,7 +17,6 @@ package io.bisq.core.app; -import io.bisq.common.UserThread; import io.bisq.common.crypto.KeyRing; import io.bisq.common.proto.persistable.PersistedDataHost; import io.bisq.core.trade.statistics.TradeStatisticsManager; @@ -69,7 +68,7 @@ public class AppSetupWithP2P extends AppSetup { @Override protected void initBasicServices() { - BooleanProperty result = loadEntryMap(); + BooleanProperty result = SetupUtils.loadEntryMap(p2PService); result.addListener((observable, oldValue, newValue) -> { if (newValue) startInitP2PNetwork(); @@ -89,22 +88,6 @@ public class AppSetupWithP2P extends AppSetup { // Initialisation /////////////////////////////////////////////////////////////////////////////////////////// - private BooleanProperty loadEntryMap() { - BooleanProperty result = new SimpleBooleanProperty(); - Thread loadEntryMapThread = new Thread() { - @Override - public void run() { - Thread.currentThread().setName("loadEntryMapThread"); - // Used to load different EntryMap files per base currency (EntryMap_BTC, EntryMap_LTC,...) - final String storageFileName = "EntryMap_" + BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode(); - p2PService.readEntryMapFromResources(storageFileName); - UserThread.execute(() -> result.set(true)); - } - }; - loadEntryMapThread.start(); - return result; - } - private BooleanProperty initP2PNetwork() { log.info("initP2PNetwork"); p2PService.getNetworkNode().addConnectionListener(new ConnectionListener() { diff --git a/core/src/main/java/io/bisq/core/app/SetupUtils.java b/core/src/main/java/io/bisq/core/app/SetupUtils.java index b57f98d7a5..1395521aa9 100644 --- a/core/src/main/java/io/bisq/core/app/SetupUtils.java +++ b/core/src/main/java/io/bisq/core/app/SetupUtils.java @@ -22,9 +22,13 @@ import io.bisq.common.crypto.CryptoException; import io.bisq.common.crypto.KeyRing; import io.bisq.common.crypto.SealedAndSigned; import io.bisq.common.handlers.ResultHandler; +import io.bisq.core.btc.BaseCurrencyNetwork; import io.bisq.network.crypto.DecryptedDataTuple; import io.bisq.network.crypto.EncryptionService; +import io.bisq.network.p2p.P2PService; import io.bisq.network.p2p.peers.keepalive.messages.Ping; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; import lombok.extern.slf4j.Slf4j; import java.security.Security; @@ -74,4 +78,23 @@ public class SetupUtils { }; checkCryptoThread.start(); } + + public static BooleanProperty loadEntryMap(P2PService p2PService) { + BooleanProperty result = new SimpleBooleanProperty(); + Thread loadEntryMapThread = new Thread() { + @Override + public void run() { + Thread.currentThread().setName("loadEntryMapThread"); + // Used to load different EntryMap files per base currency (EntryMap_BTC_MAINNET, EntryMap_LTC,...) + final BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); + final String storageFileName = "EntryMap_" + + baseCurrencyNetwork.getCurrencyCode() + "_" + + baseCurrencyNetwork.getNetwork(); + p2PService.readEntryMapFromResources(storageFileName); + UserThread.execute(() -> result.set(true)); + } + }; + loadEntryMapThread.start(); + return result; + } } diff --git a/gui/src/main/java/io/bisq/gui/main/MainViewModel.java b/gui/src/main/java/io/bisq/gui/main/MainViewModel.java index c5ba2cbdfb..8955da0372 100644 --- a/gui/src/main/java/io/bisq/gui/main/MainViewModel.java +++ b/gui/src/main/java/io/bisq/gui/main/MainViewModel.java @@ -37,6 +37,7 @@ import io.bisq.core.alert.PrivateNotificationManager; import io.bisq.core.alert.PrivateNotificationPayload; import io.bisq.core.app.AppOptionKeys; import io.bisq.core.app.BisqEnvironment; +import io.bisq.core.app.SetupUtils; import io.bisq.core.arbitration.ArbitratorManager; import io.bisq.core.arbitration.Dispute; import io.bisq.core.arbitration.DisputeManager; @@ -192,7 +193,6 @@ public class MainViewModel implements ViewModel { private BooleanProperty p2pNetWorkReady; private final BooleanProperty walletInitialized = new SimpleBooleanProperty(); private boolean allBasicServicesInitialized; - private BooleanProperty loadEntryMapResult, checkCryptoSetupResult; /////////////////////////////////////////////////////////////////////////////////////////// @@ -257,81 +257,36 @@ public class MainViewModel implements ViewModel { public void start() { //noinspection ConstantConditions,ConstantConditions + bisqEnvironment.saveBaseCryptoNetwork(BisqEnvironment.getBaseCurrencyNetwork()); + if (!preferences.isTacAccepted() && !DevEnv.DEV_MODE) { UserThread.runAfter(() -> { tacWindow.onAction(() -> { preferences.setTacAccepted(true); - showSelectBaseCurrencyWindow(); + checkIfLocalHostNodeIsRunning(); }).show(); }, 1); } else { - showSelectBaseCurrencyWindow(); + checkIfLocalHostNodeIsRunning(); } } - private void showSelectBaseCurrencyWindow() { - String key = "showSelectBaseCurrencyWindowAtFistStartup"; - if (preferences.showAgain(key)) { - bisqEnvironment.saveBaseCryptoNetwork(BisqEnvironment.getBaseCurrencyNetwork()); - preferences.dontShowAgain(key, true); - showRevertIdCheckRequirement(); - - /* new SelectBaseCurrencyWindow() - .onSelect(baseCurrencyNetwork -> { - preferences.dontShowAgain(key, true); - final boolean hasChanged = !BisqEnvironment.getBaseCurrencyNetwork().equals(baseCurrencyNetwork); - bisqEnvironment.saveBaseCryptoNetwork(baseCurrencyNetwork); - if (hasChanged) { - new Popup().warning(Res.get("settings.net.needRestart")) - .onAction(() -> { - UserThread.runAfter(BisqApp.shutDownHandler::run, 500, TimeUnit.MILLISECONDS); - }) - .actionButtonText(Res.get("shared.shutDown")) - .hideCloseButton() - .show(); - } - }) - .actionButtonText(Res.get("selectBaseCurrencyWindow.default", BisqEnvironment.getBaseCurrencyNetwork().getCurrencyName())) - .onAction(() -> { - bisqEnvironment.saveBaseCryptoNetwork(BisqEnvironment.getBaseCurrencyNetwork()); - preferences.dontShowAgain(key, true); - showRevertIdCheckRequirement(); - }) - .hideCloseButton() - .show();*/ - } else { - showRevertIdCheckRequirement(); - } - } + private void startLoadEntryMap() { + log.info("startLoadEntryMap"); - private void showRevertIdCheckRequirement() { - //TODO remove after v0.5.2 - String key = "revertIdCheckRequirement"; - if (preferences.showAgain(key) && - user.getPaymentAccounts() != null && - user.getPaymentAccounts().stream() - .filter(e -> e.getPaymentMethod().getId().equals(PaymentMethod.SEPA_ID) || - e.getPaymentMethod().getId().equals(PaymentMethod.FASTER_PAYMENTS_ID) || - e.getPaymentMethod().getId().equals(PaymentMethod.NATIONAL_BANK_ID) || - e.getPaymentMethod().getId().equals(PaymentMethod.SAME_BANK_ID) || - e.getPaymentMethod().getId().equals(PaymentMethod.SPECIFIC_BANKS_ID) || - e.getPaymentMethod().getId().equals(PaymentMethod.CLEAR_X_CHANGE_ID) || - e.getPaymentMethod().getId().equals(PaymentMethod.CHASE_QUICK_PAY_ID) || - e.getPaymentMethod().getId().equals(PaymentMethod.INTERAC_E_TRANSFER_ID)) - .findAny() - .isPresent()) { - new Popup<>().information(Res.get("popup.info.revertIdCheckRequirement")).show(); - } - preferences.dontShowAgain(key, true); - checkIfLocalHostNodeIsRunning(); + BooleanProperty result = SetupUtils.loadEntryMap(p2PService); + result.addListener((observable, oldValue, newValue) -> { + if (newValue) + startBasicServices(); + }); + + // TODO can be removed in jdk 9 + checkCryptoSetup(); } private void startBasicServices() { log.info("startBasicServices"); - loadEntryMapResult = loadEntryMap(); - checkCryptoSetupResult = checkCryptoSetup(); - ChangeListener walletInitializedListener = (observable, oldValue, newValue) -> { if (newValue && !p2pNetWorkReady.get()) showStartupTimeoutPopup(); @@ -353,14 +308,12 @@ public class MainViewModel implements ViewModel { initWalletService(); // need to store it to not get garbage collected - allServicesDone = EasyBind.combine(checkCryptoSetupResult, loadEntryMapResult, walletInitialized, p2pNetWorkReady, - (a, b, c, d) -> { - log.info("\ncheckCryptoSetupResult={}\n" + - "loadEntryMapResult={}\n" + - "walletInitialized={}\n" + + allServicesDone = EasyBind.combine(walletInitialized, p2pNetWorkReady, + (a, b) -> { + log.info("\nwalletInitialized={}\n" + "p2pNetWorkReady={}", - a, b, c, d); - return a && b && c && d; + a, b); + return a && b; }); allServicesDone.subscribe((observable, oldValue, newValue) -> { if (newValue) { @@ -395,22 +348,6 @@ public class MainViewModel implements ViewModel { // Initialisation /////////////////////////////////////////////////////////////////////////////////////////// - private BooleanProperty loadEntryMap() { - BooleanProperty result = new SimpleBooleanProperty(); - Thread loadEntryMapThread = new Thread() { - @Override - public void run() { - Thread.currentThread().setName("loadEntryMapThread"); - // Used to load different EntryMap files per base currency (EntryMap_BTC, EntryMap_LTC,...) - final String storageFileName = "EntryMap_" + BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode(); - p2PService.readEntryMapFromResources(storageFileName); - UserThread.execute(() -> result.set(true)); - } - }; - loadEntryMapThread.start(); - return result; - } - private BooleanProperty initP2PNetwork() { log.info("initP2PNetwork"); @@ -758,11 +695,11 @@ public class MainViewModel implements ViewModel { log.info("Localhost peer detected."); UserThread.execute(() -> { bisqEnvironment.setBitcoinLocalhostNodeRunning(true); - startBasicServices(); + startLoadEntryMap(); }); } catch (Throwable e) { log.info("Localhost peer not detected."); - UserThread.execute(MainViewModel.this::startBasicServices); + UserThread.execute(MainViewModel.this::startLoadEntryMap); } finally { if (socket != null) { try { diff --git a/network/src/main/java/io/bisq/network/p2p/storage/P2PDataStorage.java b/network/src/main/java/io/bisq/network/p2p/storage/P2PDataStorage.java index d0f73a7f47..173991514f 100644 --- a/network/src/main/java/io/bisq/network/p2p/storage/P2PDataStorage.java +++ b/network/src/main/java/io/bisq/network/p2p/storage/P2PDataStorage.java @@ -126,7 +126,6 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers if (persistedEntryMap != null) { map.putAll(persistedEntryMap.getMap()); log.info("persistedEntryMap size=" + map.size()); - log.error("map.putAll(persistedEntryMap.getMap());"); // In case another object is already listening... if (!hashMapChangedListeners.isEmpty()) From 01e0ac3f57a523b58ac842ba8f319aa0cf9f1247 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 23 Sep 2017 11:14:55 -0500 Subject: [PATCH 43/54] Add equals check and diff for contractAsJson --- .../payload/PaymentAccountPayload.java | 3 +++ .../java/io/bisq/core/trade/Contract.java | 22 +++++++++++++++++++ .../SellerAsTakerSignAndPublishDepositTx.java | 1 + .../taker/TakerVerifyAndSignContract.java | 5 ++++- 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/io/bisq/core/payment/payload/PaymentAccountPayload.java b/core/src/main/java/io/bisq/core/payment/payload/PaymentAccountPayload.java index d5bbed9c1e..00da7b28bb 100644 --- a/core/src/main/java/io/bisq/core/payment/payload/PaymentAccountPayload.java +++ b/core/src/main/java/io/bisq/core/payment/payload/PaymentAccountPayload.java @@ -24,6 +24,9 @@ import lombok.Getter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; +// That class is used in the contract for creating the contract json. Any change will break the contract. +// If a field gets added it need to be be annotated with @JsonExclude (excluded from contract). + @Getter @EqualsAndHashCode @ToString diff --git a/core/src/main/java/io/bisq/core/trade/Contract.java b/core/src/main/java/io/bisq/core/trade/Contract.java index 765294c4af..e4e86b344b 100644 --- a/core/src/main/java/io/bisq/core/trade/Contract.java +++ b/core/src/main/java/io/bisq/core/trade/Contract.java @@ -29,12 +29,15 @@ import io.bisq.core.proto.CoreProtoResolver; import io.bisq.generated.protobuffer.PB; import io.bisq.network.p2p.NodeAddress; import lombok.Value; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.bitcoinj.core.Coin; import javax.annotation.Nullable; import static com.google.common.base.Preconditions.checkArgument; +@Slf4j @Value public final class Contract implements NetworkPayload { private final OfferPayload offerPayload; @@ -210,6 +213,25 @@ public final class Contract implements NetworkPayload { return Price.valueOf(offerPayload.getCurrencyCode(), tradePrice); } + public void printDiff(@Nullable String peersContractAsJson) { + final String json = Utilities.objectToJson(this); + String diff = StringUtils.difference(json, peersContractAsJson); + if (!diff.isEmpty()) { + log.warn("Diff of both contracts: \n" + diff); + log.warn("\n\n------------------------------------------------------------\n" + + "Contract as json\n" + + json + + "\n------------------------------------------------------------\n"); + + log.warn("\n\n------------------------------------------------------------\n" + + "Peers contract as json\n" + + peersContractAsJson + + "\n------------------------------------------------------------\n"); + } else { + log.debug("Both contracts are the same"); + } + } + @Override public String toString() { return "Contract{" + diff --git a/core/src/main/java/io/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java b/core/src/main/java/io/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java index 6374d5ba62..0c063a9995 100644 --- a/core/src/main/java/io/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java +++ b/core/src/main/java/io/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java @@ -124,6 +124,7 @@ public class SellerAsTakerSignAndPublishDepositTx extends TradeTask { }); trade.setDepositTx(depositTx); } catch (Throwable t) { + trade.getContract().printDiff(processModel.getTradingPeer().getContractAsJson()); failed(t); } } diff --git a/core/src/main/java/io/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java b/core/src/main/java/io/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java index 8c79e682fa..d00fd5b47e 100644 --- a/core/src/main/java/io/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java +++ b/core/src/main/java/io/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java @@ -97,11 +97,14 @@ public class TakerVerifyAndSignContract extends TradeTask { ); String contractAsJson = Utilities.objectToJson(contract); log.trace("Contract as json:{}", contractAsJson); + + contract.printDiff(processModel.getTradingPeer().getContractAsJson()); + checkArgument(contractAsJson.equals(processModel.getTradingPeer().getContractAsJson()), "Contracts are not matching"); + String signature = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), contractAsJson); trade.setContract(contract); trade.setContractAsJson(contractAsJson); trade.setTakerContractSignature(signature); - try { checkNotNull(maker.getPubKeyRing(), "maker.getPubKeyRing() must nto be null"); Sig.verify(maker.getPubKeyRing().getSignaturePubKey(), From b06076653712f17c7779df3360263c2792159302 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 6 Oct 2017 20:45:07 -0500 Subject: [PATCH 44/54] Set phase 0 initial genesis tx --- .../io/bisq/core/dao/blockchain/parse/BsqChainState.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java index 4fe0c0d5aa..ac0dc20fe1 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java @@ -76,8 +76,9 @@ public class BsqChainState implements PersistableEnvelope { private static final int BTC_GENESIS_BLOCK_HEIGHT = 477865; // 2017-07-28 // TEST NET - private static final String BTC_TEST_NET_GENESIS_TX_ID = "34efdaed087084855bfbdad5f1f73b4586e64912153b0afa86bdeb5c03d4ad1c"; - private static final int BTC_TEST_NET_GENESIS_BLOCK_HEIGHT = 1155218; + // Phase 0 initial genesis tx 6.10.2017: 2f194230e23459a9211322c4b1c182cf3f367086e8059aca2f8f44e20dac527a + private static final String BTC_TEST_NET_GENESIS_TX_ID = "2f194230e23459a9211322c4b1c182cf3f367086e8059aca2f8f44e20dac527a"; + private static final int BTC_TEST_NET_GENESIS_BLOCK_HEIGHT = 1209140; // REG TEST private static final String BTC_REG_TEST_GENESIS_TX_ID = "321a2156d6cac631d3e574caf54a5a401e51971280c14b18b5f5877026a94d47"; From 2ac1b34996f270e6a8c10cd01973153811bbf492 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 6 Oct 2017 20:45:27 -0500 Subject: [PATCH 45/54] Disable dev mode --- common/src/main/java/io/bisq/common/app/DevEnv.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/io/bisq/common/app/DevEnv.java b/common/src/main/java/io/bisq/common/app/DevEnv.java index 792bb7d9a9..37dd87395e 100644 --- a/common/src/main/java/io/bisq/common/app/DevEnv.java +++ b/common/src/main/java/io/bisq/common/app/DevEnv.java @@ -10,7 +10,7 @@ public class DevEnv { // peer (click user icon and alt+r), filter/block offers by various data like offer ID (cmd + f). // The user can set a program argument to ignore all of those privileged network_messages. They are intended for // emergency cases only (beside update message and arbitrator registration). - public static final boolean USE_DEV_PRIVILEGE_KEYS = true; + public static final boolean USE_DEV_PRIVILEGE_KEYS = false; public static final String DEV_PRIVILEGE_PUB_KEY = "027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee"; public static final String DEV_PRIVILEGE_PRIV_KEY = "6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a"; @@ -18,7 +18,7 @@ public class DevEnv { // If set to true we ignore several UI behavior like confirmation popups as well dummy accounts are created and // offers are filled with default values. Intended to make dev testing faster. @SuppressWarnings("PointlessBooleanExpression") - public static final boolean DEV_MODE = STRESS_TEST_MODE || true; + public static final boolean DEV_MODE = STRESS_TEST_MODE || false; public static final boolean DAO_PHASE2_ACTIVATED = false; } From 6904c545b035cebc953c6cd91bf44c27ed358ba0 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 8 Oct 2017 19:53:04 -0500 Subject: [PATCH 46/54] Improve logging --- .../java/io/bisq/core/dao/blockchain/p2p/RequestManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/p2p/RequestManager.java b/core/src/main/java/io/bisq/core/dao/blockchain/p2p/RequestManager.java index 3a0ac986f1..9ea6eee085 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/p2p/RequestManager.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/p2p/RequestManager.java @@ -271,7 +271,7 @@ public class RequestManager implements MessageListener, ConnectionListener, Peer @Override public void onFault(String errorMessage, @Nullable Connection connection) { - log.trace("requestBlocksHandler with outbound connection failed.\n\tnodeAddress={}\n\t" + + log.warn("requestBlocksHandler with outbound connection failed.\n\tnodeAddress={}\n\t" + "ErrorMessage={}", peersNodeAddress, errorMessage); peerManager.handleConnectionFault(peersNodeAddress); From cb24b973ba6cfff4218bc6ec8da89a0a41d976ed Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 8 Oct 2017 19:53:52 -0500 Subject: [PATCH 47/54] Don't show fee and security deposit fields if BSQ is not enabled --- .../main/offer/takeoffer/TakeOfferView.java | 97 +++++++++++-------- 1 file changed, 59 insertions(+), 38 deletions(-) diff --git a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java index c01942d6bd..bdf7ecf845 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java @@ -89,7 +89,7 @@ public class TakeOfferView extends ActivatableViewAndModel().warning(Res.get("takeOffer.noPriceFeedAvailable")) .onClose(this::close) @@ -384,9 +390,12 @@ public class TakeOfferView extends ActivatableViewAndModel model.dataModel.getCurrencyCode() + "/" + Res.getBaseCurrencyCode())); priceAsPercentageLabel.prefWidthProperty().bind(priceCurrencyLabel.widthProperty()); nextButton.disableProperty().bind(model.isNextButtonDisabled); - takerFeeTextField.textProperty().bind(model.takerFee); - takerFeeCurrencyLabel.textProperty().bind(model.takerFeeCurrencyCode); - + if (model.dataModel.isBsqForFeeAvailable()) { + takerFeeTextField.textProperty().bind(model.takerFee); + takerFeeCurrencyLabel.textProperty().bind(model.takerFeeCurrencyCode); + } // funding fundingHBox.visibleProperty().bind(model.dataModel.isWalletFunded.not().and(model.showPayFundsScreenDisplayed)); fundingHBox.managedProperty().bind(model.dataModel.isWalletFunded.not().and(model.showPayFundsScreenDisplayed)); @@ -497,8 +507,10 @@ public class TakeOfferView extends ActivatableViewAndModel { @@ -754,17 +768,20 @@ public class TakeOfferView extends ActivatableViewAndModel onShowPayFundsScreen()); - delay -= diff; - transitions.fadeOutAndRemove(takerFeeTextLabel, delay); - transitions.fadeOutAndRemove(takerFeeRowHBox, delay); - delay -= diff; - transitions.fadeOutAndRemove(buyerSecurityDepositLabel, delay); - transitions.fadeOutAndRemove(buyerSecurityDepositValueCurrencyBox, delay); - delay -= diff; - transitions.fadeOutAndRemove(sellerSecurityDepositLabel, delay); - transitions.fadeOutAndRemove(sellerSecurityDepositValueCurrencyBox, delay); - + if (model.dataModel.isBsqForFeeAvailable()) { + transitions.fadeOutAndRemove(feeCurrencyTitledGroupBg, delay, (event) -> onShowPayFundsScreen()); + delay -= diff; + transitions.fadeOutAndRemove(takerFeeTextLabel, delay); + transitions.fadeOutAndRemove(takerFeeRowHBox, delay); + delay -= diff; + transitions.fadeOutAndRemove(buyerSecurityDepositLabel, delay); + transitions.fadeOutAndRemove(buyerSecurityDepositValueCurrencyBox, delay); + delay -= diff; + transitions.fadeOutAndRemove(sellerSecurityDepositLabel, delay); + transitions.fadeOutAndRemove(sellerSecurityDepositValueCurrencyBox, delay); + } else { + onShowPayFundsScreen(); + } }); cancelButton1 = new Button(Res.get("shared.cancel")); @@ -774,7 +791,9 @@ public class TakeOfferView extends ActivatableViewAndModel Date: Sun, 8 Oct 2017 20:00:37 -0500 Subject: [PATCH 48/54] Change Taker fee to Trade fee --- common/src/main/resources/i18n/displayStrings.properties | 6 +++--- common/src/main/resources/i18n/displayStrings_el.properties | 2 +- common/src/main/resources/i18n/displayStrings_es.properties | 2 +- common/src/main/resources/i18n/displayStrings_pt.properties | 2 +- common/src/main/resources/i18n/displayStrings_sr.properties | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index b4165d7247..6a288b8b6c 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -394,8 +394,8 @@ takeOffer.validation.amountLargerThanOfferAmountMinusFee=That input amount would takeOffer.fundsBox.title=Fund your trade takeOffer.fundsBox.isOfferAvailable=Check if offer is available ... takeOffer.fundsBox.tradeAmount=Amount to sell: -takeOffer.fundsBox.offerFee=Taker fee: -takeOffer.fundsBox.networkFee=Mining fees (3x): +takeOffer.fundsBox.offerFee=Trade fee: +takeOffer.fundsBox.networkFee=Mining fee (3x): takeOffer.fundsBox.takeOfferSpinnerInfo=Take offer in progress ... takeOffer.fundsBox.paymentLabel=Bisq trade with ID {0} takeOffer.success.headline=You have successfully taken an offer. @@ -427,7 +427,7 @@ takeOffer.error.depositPublished=\n\nThe deposit transaction is already publishe takeOffer.error.payoutPublished=\n\nThe payout transaction is already published.\nPlease try to restart your application and check your network connection to see if you can resolve the issue.\nIf the problem still remains please contact the developers for support. takeOffer.tac=With taking that offer I agree to the trade conditions as defined in that screen. -takeOffer.currencyForFee=Taker fee +takeOffer.currencyForFee=Trade fee takeOffer.feeCurrency=Set fee currency diff --git a/common/src/main/resources/i18n/displayStrings_el.properties b/common/src/main/resources/i18n/displayStrings_el.properties index bd0a0933c5..7cc73f9682 100644 --- a/common/src/main/resources/i18n/displayStrings_el.properties +++ b/common/src/main/resources/i18n/displayStrings_el.properties @@ -418,7 +418,7 @@ takeOffer.error.depositPublished=\n\nΗ κατάθεση κοινοποιήθη takeOffer.error.payoutPublished=\n\nΗ συναλλαγή αποπληρωμής κοινοποιήθηκε ήδη.\nΠροσπάθησε να επανεκκινήσεις την εφαρμογή και έλεγξε τη σύνδεσή σου στο διαδίκτυο ώστε να λυθεί το πρόβλημα.\nΑν το πρόβλημα επιμένει, επικοινώνησε με την ομάδα προγραμματιστών για υποστήριξη. takeOffer.tac=Αποδεχόμενος/η αυτή την προσφορά συμφωνώ με τους όρους συναλλαγών, όπως αυτοί ορίζονται ανωτέρω. -takeOffer.currencyForFee=Taker fee +takeOffer.currencyForFee=Trade fee takeOffer.feeCurrency=Καθόρισε νόμισμα προμήθειας diff --git a/common/src/main/resources/i18n/displayStrings_es.properties b/common/src/main/resources/i18n/displayStrings_es.properties index e41efc385d..f83d75f9e2 100644 --- a/common/src/main/resources/i18n/displayStrings_es.properties +++ b/common/src/main/resources/i18n/displayStrings_es.properties @@ -418,7 +418,7 @@ takeOffer.error.depositPublished=\n\nLa transacción de depósito ya se ha publi takeOffer.error.payoutPublished=\n\nLa transacción de pago ya se ha publicado.\nPor favor pruebe reiniciando la aplicación y compruebe su conexión a la red para ver si puede resolver el problema.\nSi el problema continúa por favor contacte con los desarrolladores para ayuda. takeOffer.tac=Al aceptar esta oferta afirmo estar de acuerdo en las condiciones de intercambio definidas anteriormente. -takeOffer.currencyForFee=Taker fee +takeOffer.currencyForFee=Trade fee takeOffer.feeCurrency=Establecer moneda de tasa diff --git a/common/src/main/resources/i18n/displayStrings_pt.properties b/common/src/main/resources/i18n/displayStrings_pt.properties index b079552aad..a0b76acc98 100644 --- a/common/src/main/resources/i18n/displayStrings_pt.properties +++ b/common/src/main/resources/i18n/displayStrings_pt.properties @@ -418,7 +418,7 @@ takeOffer.error.depositPublished=\n\nA transação do depósito já foi publicad takeOffer.error.payoutPublished=\n\nA transação de pagamento já foi publicada.\nPor gentileza reinicie o programa e verifique sua conexão de Internet para tentar resolver o problema.\nSe o problema persistir, favor entrar em contato com os desenvolvedores. takeOffer.tac=Ao aceitar essa oferta eu concordo com as condições de negociação definidas acima. -takeOffer.currencyForFee=Taker fee +takeOffer.currencyForFee=Trade fee takeOffer.feeCurrency=Definir moeda para taxa diff --git a/common/src/main/resources/i18n/displayStrings_sr.properties b/common/src/main/resources/i18n/displayStrings_sr.properties index 8f25d1ece4..3f579da6f1 100644 --- a/common/src/main/resources/i18n/displayStrings_sr.properties +++ b/common/src/main/resources/i18n/displayStrings_sr.properties @@ -418,7 +418,7 @@ takeOffer.error.depositPublished=\n\nTransakcija depozita je već objavljena.\nM takeOffer.error.payoutPublished=\n\nTransakcija isplate je već objavljena.\nMolimo vas pokušajte ponovo pokrenite aplikaciju i proverite vašu mrežnu konekciju da vidite da li možete da rešite problem.\nAko problem i dalje postoji molimo kontaktirajte programere za podršku. takeOffer.tac=Sa postavljanjem te ponude slažem se sa uslovima trgovine definisanim iznad. -takeOffer.currencyForFee=Taker fee +takeOffer.currencyForFee=Trade fee takeOffer.feeCurrency=Podesi valutu provizije From 3963924359dafa7e43a309c1b8a56511ed5876d9 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 8 Oct 2017 20:03:20 -0500 Subject: [PATCH 49/54] Change Maker fee to Trade fee --- common/src/main/resources/i18n/displayStrings.properties | 4 ++-- common/src/main/resources/i18n/displayStrings_el.properties | 2 +- common/src/main/resources/i18n/displayStrings_es.properties | 2 +- common/src/main/resources/i18n/displayStrings_pt.properties | 2 +- common/src/main/resources/i18n/displayStrings_sr.properties | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index 6a288b8b6c..c77d9bee3f 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -338,7 +338,7 @@ createOffer.amountPriceBox.minAmountDescription=Minimum amount of BTC createOffer.securityDeposit.prompt=Security deposit in BTC createOffer.fundsBox.title=Fund your offer createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above -createOffer.fundsBox.offerFee=Maker fee: +createOffer.fundsBox.offerFee=Trade fee: createOffer.fundsBox.networkFee=Mining fee: createOffer.fundsBox.placeOfferSpinnerInfo=Offer publishing is in progress ... createOffer.fundsBox.paymentLabel=Bisq trade with ID {0} @@ -374,7 +374,7 @@ createOffer.useLowerValue=Yes, use my lower value createOffer.priceOutSideOfDeviation=The price you have entered is outside the max. allowed deviation from the market price.\nThe max. allowed deviation is {0} and can be adjusted in the preferences. createOffer.changePrice=Change price createOffer.tac=With publishing that offer I agree to trade with any trader who fulfills the conditions as defined in that screen. -createOffer.currencyForFee=Maker fee +createOffer.currencyForFee=Trade fee createOffer.feeCurrencyAndDeposit=Set fee currency and security deposit diff --git a/common/src/main/resources/i18n/displayStrings_el.properties b/common/src/main/resources/i18n/displayStrings_el.properties index 7cc73f9682..adf913cfc2 100644 --- a/common/src/main/resources/i18n/displayStrings_el.properties +++ b/common/src/main/resources/i18n/displayStrings_el.properties @@ -365,7 +365,7 @@ createOffer.useLowerValue=Ναι, χρησιμοποίησε τη χαμηλή createOffer.priceOutSideOfDeviation=Η τιμή που εισήγαγες είναι εκτός της μέγιστης επιτρεπόμενης απόκλισης από την τιμή αγοράς.\nΗ μέγιστη επιτρεπόμενη απόκλιση είναι {0} και μπορεί να προσαρμοστεί στις προτιμήσεις. createOffer.changePrice=Άλλαξε την τιμή createOffer.tac=Τοποθετώντας αυτή την προσφορά συμφωνώ να συναλλαχθώ με οποιονδήποτε ικανοποιεί τις συνθήκες που καθορίζονται πιο πάνω. -createOffer.currencyForFee=Maker fee +createOffer.currencyForFee=Trade fee createOffer.feeCurrencyAndDeposit=Καθόρισε νόμισμα προμήθειας και ποσού εγγύησης diff --git a/common/src/main/resources/i18n/displayStrings_es.properties b/common/src/main/resources/i18n/displayStrings_es.properties index f83d75f9e2..10e519bcd7 100644 --- a/common/src/main/resources/i18n/displayStrings_es.properties +++ b/common/src/main/resources/i18n/displayStrings_es.properties @@ -365,7 +365,7 @@ createOffer.useLowerValue=Sí, usar mi valor más bajo createOffer.priceOutSideOfDeviation=El precio que ha introducido está fuera de la máxima desviación permitida del precio de mercado. La desviación máxima permitida es {0} y puede ajustarse en las preferencias. createOffer.changePrice=Cambiar precio createOffer.tac=Al colocar esta oferta estoy de acuerdo en comerciar con cualquier comerciante que cumpla con las condiciones definidas anteriormente. -createOffer.currencyForFee=Maker fee +createOffer.currencyForFee=Trade fee createOffer.feeCurrencyAndDeposit=Establecer moneda para las tasas y el depósito de seguridad diff --git a/common/src/main/resources/i18n/displayStrings_pt.properties b/common/src/main/resources/i18n/displayStrings_pt.properties index a0b76acc98..9eb846244d 100644 --- a/common/src/main/resources/i18n/displayStrings_pt.properties +++ b/common/src/main/resources/i18n/displayStrings_pt.properties @@ -365,7 +365,7 @@ createOffer.useLowerValue=Sim, usar meu valor mais baixo createOffer.priceOutSideOfDeviation=O preço submetido está fora do desvio máximo com relação ao preço de mercado.\nO desvio máximo é {0} e pode ser alterado nas preferências. createOffer.changePrice=Alterar preço createOffer.tac=Ao submeter esta oferta eu concordo em negociar com qualquer negociar que satisfaça as condições definidas acima. -createOffer.currencyForFee=Maker fee +createOffer.currencyForFee=Trade fee createOffer.feeCurrencyAndDeposit=Definir moeda da taxa e depósito de segurança diff --git a/common/src/main/resources/i18n/displayStrings_sr.properties b/common/src/main/resources/i18n/displayStrings_sr.properties index 3f579da6f1..39d951744e 100644 --- a/common/src/main/resources/i18n/displayStrings_sr.properties +++ b/common/src/main/resources/i18n/displayStrings_sr.properties @@ -365,7 +365,7 @@ createOffer.useLowerValue=Da, koristi moju manju vrednost createOffer.priceOutSideOfDeviation=Cena koju ste uneli je van maks. dozvoljenog odstupanja od tržišne cene.\nMaks. dozvoljeno odstupanje je {0} i može se podesiti u preferencama. createOffer.changePrice=Izmeni cenu createOffer.tac=Sa postavljanjem te ponude slažem se da trgujem sa bilo kojim trgovcem koji ispunjava uslove definisane iznad. -createOffer.currencyForFee=Maker fee +createOffer.currencyForFee=Trade fee createOffer.feeCurrencyAndDeposit=Podesi valutu provizije i bezbednosni depozit From 4522f501492f8a76a229d87654812f53d5efc10b Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 8 Oct 2017 20:39:09 -0500 Subject: [PATCH 50/54] Fix wrong fee group at Create offer screen. Improve wording --- .../resources/i18n/displayStrings.properties | 8 +- .../i18n/displayStrings_de.properties | 2 +- .../i18n/displayStrings_el.properties | 4 +- .../i18n/displayStrings_es.properties | 2 +- .../i18n/displayStrings_pt.properties | 2 +- .../i18n/displayStrings_sr.properties | 2 +- .../i18n/displayStrings_zh_cn.properties | 2 +- .../i18n/in_dev/displayStrings_fr.properties | 2 +- .../offer/createoffer/CreateOfferView.java | 90 ++++++++++++------- .../main/offer/takeoffer/TakeOfferView.java | 2 +- 10 files changed, 73 insertions(+), 43 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index c77d9bee3f..8f677d1139 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -166,6 +166,7 @@ shared.viewContractAsJson=View contract in JSON format shared.contract.title=Contract for trade with ID: {0} shared.paymentDetails=BTC {0} payment details: shared.securityDeposit=Security deposit +shared.yourSecurityDeposit=Your security deposit shared.contract=Contract shared.messageArrived=Message arrived. shared.messageStoredInMailbox=Message stored in mailbox. @@ -352,7 +353,7 @@ createOffer.alreadyFunded=You had already funded that offer.\nYour funds have be createOffer.createOfferFundWalletInfo.headline=Fund your offer # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Trade amount: {0} \n -createOffer.createOfferFundWalletInfo.msg=You need to deposit {0} to this offer.\n\nThose funds are reserved in your local wallet and will get locked into the Multisig deposit address once someone takes your offer.\n\nThe amount is the sum of:\n{1}- Security deposit: {2}\n- Trading fee: {3}\n- Mining fee: {4}\n\nYou can choose between two options when funding your trade:\n- Use your Bisq wallet (convenient, but transactions may be linkable) OR\n- Transfer from an external wallet (potentially more private)\n\nYou will see all funding options and details after closing this popup. +createOffer.createOfferFundWalletInfo.msg=You need to deposit {0} to this offer.\n\nThose funds are reserved in your local wallet and will get locked into the Multisig deposit address once someone takes your offer.\n\nThe amount is the sum of:\n{1}- Your security deposit: {2}\n- Trading fee: {3}\n- Mining fee: {4}\n\nYou can choose between two options when funding your trade:\n- Use your Bisq wallet (convenient, but transactions may be linkable) OR\n- Transfer from an external wallet (potentially more private)\n\nYou will see all funding options and details after closing this popup. # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=An error occurred when placing the offer:\n\n{0}\n\n\ @@ -376,6 +377,7 @@ createOffer.changePrice=Change price createOffer.tac=With publishing that offer I agree to trade with any trader who fulfills the conditions as defined in that screen. createOffer.currencyForFee=Trade fee createOffer.feeCurrencyAndDeposit=Set fee currency and security deposit +createOffer.setDeposit=Set buyer's security deposit #################################################################### @@ -395,7 +397,7 @@ takeOffer.fundsBox.title=Fund your trade takeOffer.fundsBox.isOfferAvailable=Check if offer is available ... takeOffer.fundsBox.tradeAmount=Amount to sell: takeOffer.fundsBox.offerFee=Trade fee: -takeOffer.fundsBox.networkFee=Mining fee (3x): +takeOffer.fundsBox.networkFee=Total mining fees: takeOffer.fundsBox.takeOfferSpinnerInfo=Take offer in progress ... takeOffer.fundsBox.paymentLabel=Bisq trade with ID {0} takeOffer.success.headline=You have successfully taken an offer. @@ -409,7 +411,7 @@ takeOffer.alreadyFunded.movedFunds=You had already funded that offer.\nYour fund takeOffer.takeOfferFundWalletInfo.headline=Fund your trade # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- Trade amount: {0} \n -takeOffer.takeOfferFundWalletInfo.msg=You need to deposit {0} for taking this offer.\n\nThe amount is the sum of:\n{1}- Security deposit: {2}\n- Trading fee: {3}\n- Mining fee (3x): {4}\n\nYou can choose between two options when funding your trade:\n- Use your Bisq wallet (convenient, but transactions may be linkable) OR\n- Transfer from an external wallet (potentially more private)\n\nYou will see all funding options and details after closing this popup. +takeOffer.takeOfferFundWalletInfo.msg=You need to deposit {0} for taking this offer.\n\nThe amount is the sum of:\n{1}- Your security deposit: {2}\n- Trading fee: {3}\n- Total mining fees: {4}\n\nYou can choose between two options when funding your trade:\n- Use your Bisq wallet (convenient, but transactions may be linkable) OR\n- Transfer from an external wallet (potentially more private)\n\nYou will see all funding options and details after closing this popup. takeOffer.alreadyPaidInFunds=If you have already paid in funds you can withdraw it in the \"Funds/Send funds\" screen. takeOffer.paymentInfo=Payment info takeOffer.setAmountPrice=Set amount diff --git a/common/src/main/resources/i18n/displayStrings_de.properties b/common/src/main/resources/i18n/displayStrings_de.properties index c95fa0dba1..b01dcedb1c 100644 --- a/common/src/main/resources/i18n/displayStrings_de.properties +++ b/common/src/main/resources/i18n/displayStrings_de.properties @@ -386,7 +386,7 @@ takeOffer.fundsBox.title=Ihren Handel finanzieren takeOffer.fundsBox.isOfferAvailable=Überprüfe ob Angebot verfügbar ist ... takeOffer.fundsBox.tradeAmount=Zu verkaufender Betrag: takeOffer.fundsBox.offerFee=Abnehmergebühr: -takeOffer.fundsBox.networkFee=Mining Gebühren (3x): +takeOffer.fundsBox.networkFee=Gesamte Mining Gebühren: takeOffer.fundsBox.takeOfferSpinnerInfo=Angebotsannahme im Gange ... takeOffer.fundsBox.paymentLabel=Bisq Handel mit Kennung {0} takeOffer.success.headline=Sie haben ein Angebot erfolgreich angenommen. diff --git a/common/src/main/resources/i18n/displayStrings_el.properties b/common/src/main/resources/i18n/displayStrings_el.properties index adf913cfc2..6c6b6e56c9 100644 --- a/common/src/main/resources/i18n/displayStrings_el.properties +++ b/common/src/main/resources/i18n/displayStrings_el.properties @@ -386,7 +386,7 @@ takeOffer.fundsBox.title=Χρηματοδότησε τη συναλλαγή σο takeOffer.fundsBox.isOfferAvailable=Έλεγχος διαθεσιμότητας προσφοράς... takeOffer.fundsBox.tradeAmount=Ποσό προς πώληση: takeOffer.fundsBox.offerFee=Προμήθεια Taker: -takeOffer.fundsBox.networkFee=Προμήθεια εξόρυξης (3x): +takeOffer.fundsBox.networkFee=Προμήθεια εξόρυξης: takeOffer.fundsBox.takeOfferSpinnerInfo=Αποδοχή προσφοράς σε εξέλιξη... takeOffer.fundsBox.paymentLabel=Συναλλαγή Bisq με ταυτότητα {0} takeOffer.success.headline=Αποδέχτηκες επιτυχώς μία προσφορά. @@ -400,7 +400,7 @@ takeOffer.alreadyFunded.movedFunds=Είχες ήδη χρηματοδοτήσε takeOffer.takeOfferFundWalletInfo.headline=Χρηματοδότησε τη συναλλαγή σου # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- Ποσό συναλλαγής: {0}\n -takeOffer.takeOfferFundWalletInfo.msg=You need to deposit {0} for taking this offer.\n\nThe amount is the sum of:\n{1}- Security deposit: {2}\n- Trading fee: {3}\n- Mining fee (3x): {4}\n\nYou can choose between two options when funding your trade:\n- Use your Bisq wallet (convenient, but transactions may be linkable) OR\n- Transfer from an external wallet (potentially more private)\n\nYou will see all funding options and details after closing this popup. +takeOffer.takeOfferFundWalletInfo.msg=You need to deposit {0} for taking this offer.\n\nThe amount is the sum of:\n{1}- Your security deposit: {2}\n- Trading fee: {3}\n- Mining fee (3x): {4}\n\nYou can choose between two options when funding your trade:\n- Use your Bisq wallet (convenient, but transactions may be linkable) OR\n- Transfer from an external wallet (potentially more private)\n\nYou will see all funding options and details after closing this popup. takeOffer.alreadyPaidInFunds=Αν έχεις ήδη κατατεθιμένα κεφάλαια, μπορείς να τα αποσύρεις στο \"Κεφάλαια/Αποστολή κεφαλαίων\". takeOffer.paymentInfo=Πληροφορίες πληρωμής takeOffer.setAmountPrice=Θέσε ποσό diff --git a/common/src/main/resources/i18n/displayStrings_es.properties b/common/src/main/resources/i18n/displayStrings_es.properties index 10e519bcd7..82304b574e 100644 --- a/common/src/main/resources/i18n/displayStrings_es.properties +++ b/common/src/main/resources/i18n/displayStrings_es.properties @@ -386,7 +386,7 @@ takeOffer.fundsBox.title=Dote de fondos su intercambio. takeOffer.fundsBox.isOfferAvailable=Comprobar si la oferta está disponible... takeOffer.fundsBox.tradeAmount=Cantidad a vender: takeOffer.fundsBox.offerFee=Tasa al tomador de oferta: -takeOffer.fundsBox.networkFee=Tasas de minería (3x): +takeOffer.fundsBox.networkFee=Total tasas de minería: takeOffer.fundsBox.takeOfferSpinnerInfo=Aceptación de oferta en espera... takeOffer.fundsBox.paymentLabel=Trato de Bitsquare con ID {0} takeOffer.success.headline=Ha aceptado la oferta con éxito. diff --git a/common/src/main/resources/i18n/displayStrings_pt.properties b/common/src/main/resources/i18n/displayStrings_pt.properties index 9eb846244d..b7d65822f6 100644 --- a/common/src/main/resources/i18n/displayStrings_pt.properties +++ b/common/src/main/resources/i18n/displayStrings_pt.properties @@ -386,7 +386,7 @@ takeOffer.fundsBox.title=Financiar sua negociação takeOffer.fundsBox.isOfferAvailable=Verificar se oferta está disponível ... takeOffer.fundsBox.tradeAmount=Quantidade a se vender: takeOffer.fundsBox.offerFee=Taxa de aceitação: -takeOffer.fundsBox.networkFee=Taxas de mineração (3x): +takeOffer.fundsBox.networkFee=Taxas de mineração: takeOffer.fundsBox.takeOfferSpinnerInfo=Aceitação da oferta em progresso ... takeOffer.fundsBox.paymentLabel=negociação Bisq com ID {0} takeOffer.success.headline=Você aceitou uma oferta com sucesso. diff --git a/common/src/main/resources/i18n/displayStrings_sr.properties b/common/src/main/resources/i18n/displayStrings_sr.properties index 39d951744e..2fe8c646db 100644 --- a/common/src/main/resources/i18n/displayStrings_sr.properties +++ b/common/src/main/resources/i18n/displayStrings_sr.properties @@ -386,7 +386,7 @@ takeOffer.fundsBox.title=Finansirajte vašu trgovinu takeOffer.fundsBox.isOfferAvailable=Proveri da li je ponuda dostupna ... takeOffer.fundsBox.tradeAmount=Iznos za prodaju: takeOffer.fundsBox.offerFee=Provizija prihvatanja: -takeOffer.fundsBox.networkFee=Provizija rudara (3x): +takeOffer.fundsBox.networkFee=Provizija rudara: takeOffer.fundsBox.takeOfferSpinnerInfo=Prihvatanje ponude je u toku ... takeOffer.fundsBox.paymentLabel=Bisq trgovina sa ID {0} takeOffer.success.headline=Uspešno se prihvatili ponudu. diff --git a/common/src/main/resources/i18n/displayStrings_zh_cn.properties b/common/src/main/resources/i18n/displayStrings_zh_cn.properties index 0bbaaa4e75..69bdab945c 100644 --- a/common/src/main/resources/i18n/displayStrings_zh_cn.properties +++ b/common/src/main/resources/i18n/displayStrings_zh_cn.properties @@ -390,7 +390,7 @@ takeOffer.fundsBox.title=为交易充值 takeOffer.fundsBox.isOfferAvailable=检查委托是否有效 ... takeOffer.fundsBox.tradeAmount=卖出数量: takeOffer.fundsBox.offerFee=买单费: -takeOffer.fundsBox.networkFee=矿工手续费 (3x): +takeOffer.fundsBox.networkFee=矿工手续费: takeOffer.fundsBox.takeOfferSpinnerInfo=正在下单 ... takeOffer.fundsBox.paymentLabel=Bisq交易ID {0} takeOffer.success.headline=你已成功下单一个委托. diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties index 1d88efdac3..5070b90afe 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties @@ -183,7 +183,7 @@ takeOffer.fundsBox.balance=Trade wallet balance: takeOffer.fundsBox.tradeAmount=Montant à vendre: takeOffer.fundsBox.securityDeposit=Security deposit: takeOffer.fundsBox.offerFee=Frais du client: -takeOffer.fundsBox.networkFee=Frais d'exploitation minière (3x): +takeOffer.fundsBox.networkFee=Frais d'exploitation minière: takeOffer.fundsBox.total=Total: # TODO remove takeOffer.fundsBox.showAdvanced=Show advanced settings # TODO remove takeOffer.fundsBox.hideAdvanced=Hide advanced settings diff --git a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java index 77478accca..131f04626f 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java @@ -194,7 +194,8 @@ public class CreateOfferView extends ActivatableViewAndModel tuple = add2ButtonsAfterGroup(gridPane, ++gridRow, Res.get("shared.nextStep"), Res.get("shared.cancel")); @@ -880,15 +894,20 @@ public class CreateOfferView extends ActivatableViewAndModel onShowPayFundsScreen()); - delay -= diff; - transitions.fadeOutAndRemove(makerFeeTextLabel, delay); - transitions.fadeOutAndRemove(makerFeeRowHBox, delay); + if (model.dataModel.isBsqForFeeAvailable()) { + delay -= diff; + transitions.fadeOutAndRemove(makerFeeTextLabel, delay); + transitions.fadeOutAndRemove(makerFeeRowHBox, delay); + } delay -= diff; transitions.fadeOutAndRemove(buyerSecurityDepositLabel, delay); transitions.fadeOutAndRemove(buyerSecurityDepositValueCurrencyBox, delay); + + // We hide that, but leave it in code as it might be reconsidered to show it + /* delay -= diff; transitions.fadeOutAndRemove(sellerSecurityDepositLabel, delay); - transitions.fadeOutAndRemove(sellerSecurityDepositValueCurrencyBox, delay); + transitions.fadeOutAndRemove(sellerSecurityDepositValueCurrencyBox, delay);*/ } }); } @@ -953,17 +972,21 @@ public class CreateOfferView extends ActivatableViewAndModel tuple = getEditableValueCurrencyBox( Res.get("createOffer.securityDeposit.prompt")); buyerSecurityDepositValueCurrencyBox = tuple.first; buyerSecurityDepositInputTextField = tuple.second; buyerSecurityDepositBtcLabel = tuple.third; - buyerSecurityDepositBtcLabel.setMinWidth(makerFeeCurrencyLabel.getMinWidth()); - buyerSecurityDepositBtcLabel.setMaxWidth(makerFeeCurrencyLabel.getMaxWidth()); + + if (makerFeeCurrencyLabel != null) { + buyerSecurityDepositBtcLabel.setMinWidth(makerFeeCurrencyLabel.getMinWidth()); + buyerSecurityDepositBtcLabel.setMaxWidth(makerFeeCurrencyLabel.getMaxWidth()); + } editOfferElements.add(buyerSecurityDepositInputTextField); editOfferElements.add(buyerSecurityDepositBtcLabel); @@ -971,6 +994,7 @@ public class CreateOfferView extends ActivatableViewAndModel Date: Sun, 8 Oct 2017 20:48:11 -0500 Subject: [PATCH 51/54] Remove sum column in market offerbook tables --- .../gui/main/market/offerbook/OfferBookChartView.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java b/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java index bcdb3afa2d..ad9f133faa 100644 --- a/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java +++ b/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java @@ -410,8 +410,9 @@ public class OfferBookChartView extends ActivatableViewAndModel accumulatedColumn = new TableColumn<>(Res.get("shared.sumWithCur", Res.getBaseCurrencyCode())); + /* TableColumn accumulatedColumn = new TableColumn<>(Res.get("shared.sumWithCur", Res.getBaseCurrencyCode())); accumulatedColumn.setMinWidth(100); accumulatedColumn.setSortable(false); accumulatedColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); @@ -431,9 +432,9 @@ public class OfferBookChartView extends ActivatableViewAndModel Date: Sun, 8 Oct 2017 21:03:08 -0500 Subject: [PATCH 52/54] Show better text in PeerInfo if 0 trades --- common/src/main/resources/i18n/displayStrings.properties | 1 + .../gui/main/overlays/editor/PeerInfoWithTagEditor.java | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index 8f677d1139..8d4670931b 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -1343,6 +1343,7 @@ confidence.invalid=Transaction is invalid peerInfo.title=Peer info peerInfo.nrOfTrades=Number of completed trades: +peerInfo.notTradedYet=You have not traded with that user so far. peerInfo.setTag=Set tag for that peer: addressTextField.openWallet=Open your default bitcoin wallet diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/editor/PeerInfoWithTagEditor.java b/gui/src/main/java/io/bisq/gui/main/overlays/editor/PeerInfoWithTagEditor.java index c0ddfbd66b..db5178903b 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/editor/PeerInfoWithTagEditor.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/editor/PeerInfoWithTagEditor.java @@ -78,6 +78,8 @@ public class PeerInfoWithTagEditor extends Overlay { public PeerInfoWithTagEditor numTrades(int numTrades) { this.numTrades = numTrades; + if (numTrades == 0) + width = 500; return this; } @@ -141,7 +143,10 @@ public class PeerInfoWithTagEditor extends Overlay { protected void addContent() { FormBuilder.addLabelTextField(gridPane, ++rowIndex, Res.getWithCol("shared.onionAddress"), hostName).second.setMouseTransparent(false); - FormBuilder.addLabelTextField(gridPane, ++rowIndex, Res.get("peerInfo.nrOfTrades"), String.valueOf(numTrades)); + FormBuilder.addLabelTextField(gridPane, ++rowIndex, + Res.get("peerInfo.nrOfTrades"), + numTrades > 0 ? String.valueOf(numTrades) : Res.get("peerInfo.notTradedYet")); + inputTextField = FormBuilder.addLabelInputTextField(gridPane, ++rowIndex, Res.get("peerInfo.setTag")).second; Map peerTagMap = preferences.getPeerTagMap(); String tag = peerTagMap.containsKey(hostName) ? peerTagMap.get(hostName) : ""; From 01cf823d7af6044e3ad9b982885022b2e8deaad6 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Wed, 11 Oct 2017 07:21:50 -0500 Subject: [PATCH 53/54] Prepare merge Dev --- README.md | 10 +- .../main/java/io/bisq/common/app/DevEnv.java | 1 - .../io/bisq/common/locale/CountryUtil.java | 1 + .../io/bisq/common/locale/CurrencyUtil.java | 104 +----------- .../io/bisq/common/locale/LocaleUtil.java | 3 +- common/src/main/proto/pb.proto | 46 +++--- .../resources/i18n/displayStrings.properties | 68 +++++--- .../i18n/displayStrings_de.properties | 6 +- .../i18n/displayStrings_el.properties | 12 +- .../i18n/displayStrings_es.properties | 10 +- .../i18n/displayStrings_pt.properties | 10 +- .../i18n/displayStrings_sr.properties | 10 +- .../i18n/displayStrings_zh_cn.properties | 2 +- .../i18n/in_dev/displayStrings_ae.properties | 6 +- .../i18n/in_dev/displayStrings_ba.properties | 6 +- .../i18n/in_dev/displayStrings_br.properties | 6 +- .../i18n/in_dev/displayStrings_cn.properties | 6 +- .../i18n/in_dev/displayStrings_cz.properties | 6 +- .../i18n/in_dev/displayStrings_dk.properties | 6 +- .../i18n/in_dev/displayStrings_en.properties | 6 +- .../i18n/in_dev/displayStrings_fi.properties | 6 +- .../i18n/in_dev/displayStrings_fr.properties | 10 +- .../i18n/in_dev/displayStrings_ge.properties | 6 +- .../i18n/in_dev/displayStrings_gr.properties | 6 +- .../i18n/in_dev/displayStrings_hr.properties | 6 +- .../i18n/in_dev/displayStrings_hu.properties | 6 +- .../i18n/in_dev/displayStrings_id.properties | 6 +- .../i18n/in_dev/displayStrings_ie.properties | 6 +- .../i18n/in_dev/displayStrings_in.properties | 6 +- .../i18n/in_dev/displayStrings_is.properties | 6 +- .../i18n/in_dev/displayStrings_it.properties | 2 +- .../i18n/in_dev/displayStrings_jp.properties | 6 +- .../i18n/in_dev/displayStrings_kg.properties | 6 +- .../i18n/in_dev/displayStrings_kr.properties | 6 +- .../i18n/in_dev/displayStrings_kz.properties | 6 +- .../i18n/in_dev/displayStrings_lt.properties | 6 +- .../i18n/in_dev/displayStrings_my.properties | 6 +- .../i18n/in_dev/displayStrings_nl.properties | 6 +- .../i18n/in_dev/displayStrings_no.properties | 6 +- .../i18n/in_dev/displayStrings_nz.properties | 6 +- .../i18n/in_dev/displayStrings_ph.properties | 6 +- .../i18n/in_dev/displayStrings_pl.properties | 6 +- .../i18n/in_dev/displayStrings_pt.properties | 2 +- .../i18n/in_dev/displayStrings_ro.properties | 6 +- .../i18n/in_dev/displayStrings_ru.properties | 6 +- .../i18n/in_dev/displayStrings_se.properties | 6 +- .../i18n/in_dev/displayStrings_th.properties | 6 +- .../i18n/in_dev/displayStrings_ua.properties | 6 +- .../i18n/in_dev/displayStrings_uz.properties | 6 +- .../i18n/in_dev/displayStrings_vn.properties | 6 +- .../io/bisq/core/app/AppSetupWithP2P.java | 20 ++- .../io/bisq/core/app/BisqEnvironment.java | 8 +- .../java/io/bisq/core/app/SetupUtils.java | 23 +++ .../io/bisq/core/btc/BaseCurrencyNetwork.java | 9 ++ .../core/btc/wallet/BtcWalletService.java | 4 +- .../io/bisq/core/btc/wallet/WalletConfig.java | 17 +- .../bisq/core/btc/wallet/WalletService.java | 4 + .../io/bisq/core/btc/wallet/WalletsSetup.java | 11 +- .../bisq/core/dao/blockchain/BsqFullNode.java | 9 +- .../bisq/core/dao/blockchain/BsqLiteNode.java | 2 +- .../dao/blockchain/p2p/RequestManager.java | 6 +- .../dao/blockchain/parse/BsqChainState.java | 95 +++++------ .../core/dao/blockchain/parse/BsqParser.java | 14 +- .../core/dao/blockchain/parse/RpcService.java | 27 +++- .../bisq/core/dao/blockchain/vo/BsqBlock.java | 38 ++--- .../core/dao/blockchain/vo/BsqBlockVo.java | 56 ------- .../bisq/core/dao/blockchain/vo/TxInput.java | 46 +++--- .../core/dao/blockchain/vo/TxInputVo.java | 49 ------ .../bisq/core/dao/blockchain/vo/TxOutput.java | 149 +++++++++++------- .../core/dao/blockchain/vo/TxOutputVo.java | 79 ---------- .../main/java/io/bisq/core/filter/Filter.java | 24 ++- .../io/bisq/core/filter/FilterManager.java | 71 ++++++++- .../main/java/io/bisq/core/offer/Offer.java | 2 +- .../payload/PaymentAccountPayload.java | 3 + .../bisq/core/provider/price/MarketPrice.java | 17 +- .../core/provider/price/PriceFeedService.java | 54 +++++-- .../core/provider/price/PriceProvider.java | 2 +- .../java/io/bisq/core/trade/Contract.java | 22 +++ .../core/trade/protocol/ProcessModel.java | 39 +---- .../protocol/tasks/CheckIfPeerIsBanned.java | 30 ++-- .../SellerAsTakerSignAndPublishDepositTx.java | 1 + .../taker/TakerVerifyAndSignContract.java | 5 +- .../statistics/TradeStatisticsManager.java | 82 +++++++++- .../java/io/bisq/core/user/Preferences.java | 19 ++- .../io/bisq/core/user/PreferencesPayload.java | 3 +- .../core/user/UserPayloadModelVOTest.java | 3 +- doc/build.md | 1 + doc/rpc_regtest/bitcoin.conf | 5 +- gui/src/main/java/io/bisq/gui/SystemTray.java | 2 +- .../main/java/io/bisq/gui/app/BisqApp.java | 20 ++- .../main/java/io/bisq/gui/main/MainView.java | 28 +++- .../java/io/bisq/gui/main/MainViewModel.java | 122 ++++---------- .../bisq/gui/main/PriceFeedComboBoxItem.java | 19 +-- .../java/io/bisq/gui/main/dao/DaoView.java | 3 +- .../gui/main/dao/wallet/BsqWalletView.java | 6 +- .../wallet/dashboard/BsqDashboardView.java | 112 ++++++++++++- .../market/offerbook/OfferBookChartView.java | 9 +- .../main/market/spread/SpreadViewModel.java | 2 +- .../createoffer/CreateOfferDataModel.java | 11 +- .../offer/createoffer/CreateOfferView.java | 92 +++++++---- .../createoffer/CreateOfferViewModel.java | 14 +- .../main/offer/offerbook/OfferBookView.java | 36 +++-- .../offer/offerbook/OfferBookViewModel.java | 22 +-- .../offer/takeoffer/TakeOfferDataModel.java | 45 ++++-- .../main/offer/takeoffer/TakeOfferView.java | 101 +++++++----- .../io/bisq/gui/main/overlays/Overlay.java | 2 +- .../editor/PeerInfoWithTagEditor.java | 7 +- .../windows/AddBitcoinNodesWindow.java | 4 +- .../windows/DisplayAlertMessageWindow.java | 2 +- .../main/overlays/windows/FilterWindow.java | 30 +++- .../gui/main/overlays/windows/TacWindow.java | 22 ++- .../windows/downloadupdate/BisqInstaller.java | 82 +++++++--- .../windows/downloadupdate/DownloadTask.java | 1 - .../windows/downloadupdate/VerifyTask.java | 80 +++++++--- .../pendingtrades/PendingTradesDataModel.java | 5 +- .../gui/main/settings/about/AboutView.java | 6 +- .../settings/preferences/PreferencesView.java | 7 + .../java/io/bisq/gui/util/BsqFormatter.java | 26 ++- .../validation/AltCoinAddressValidator.java | 22 +++ .../gui/util/validation/BsqValidator.java | 3 +- gui/src/main/resources/keys/5BC5ED73.asc | 50 ++++++ .../peers/getdata/GetDataRequestHandler.java | 2 +- .../p2p/peers/getdata/RequestDataHandler.java | 2 +- .../network/p2p/seed/SeedNodesRepository.java | 17 +- .../{EntryMap_BTC => EntryMap_BTC_MAINNET} | Bin .../network/p2p/mocks/MockMailboxPayload.java | 4 +- .../bisq/network/p2p/mocks/MockPayload.java | 4 +- .../network/p2p/storage/mocks/MockData.java | 4 +- package/linux/32bitBuild.sh | 1 - package/linux/64bitBuild.sh | 1 - package/win/Bisq.iss | 4 +- provider/pom.xml | 24 +++ seednode/pom.xml | 24 +++ statistics/pom.xml | 24 +++ 134 files changed, 1534 insertions(+), 1120 deletions(-) delete mode 100644 core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlockVo.java delete mode 100644 core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInputVo.java delete mode 100644 core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutputVo.java create mode 100644 gui/src/main/resources/keys/5BC5ED73.asc rename network/src/main/resources/{EntryMap_BTC => EntryMap_BTC_MAINNET} (100%) diff --git a/README.md b/README.md index f8fd5bd158..f2e06eef21 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - + What is Bisq? @@ -12,12 +12,12 @@ There are no central points of control or failure in the Bisq network. There are Because the national currency portion of any trade must be transferred via traditional means such as a wire transfer, Bisq incorporates first-class support for human arbitration to resolve any errors or disputes. -You can read about all of this and more in the [whitepaper](https://bisq.io/bitsquare.pdf) and [arbitration](https://bisq.io/arbitration_system.pdf) documents. Several [videos](https://bisq.io/blog/category/video) are available as well. +You can read about all of this and more in the [whitepaper](https://bisq.network/bitsquare.pdf) and [arbitration](https://bisq.network/arbitration_system.pdf) documents. Several [videos](https://bisq.network/blog/category/video) are available as well. Status ------ Bisq has released the beta version on the 27th of April 2016. It is operational since that time without any major incident. -Please follow the current development state at our [road map]( https://bisq.io/roadmap). +Please follow the current development state at our [road map]( https://bisq.network/roadmap). For the latest version checkout our [releases page](https://github.com/bisq-network/exchange/releases) at Github. Building from source @@ -33,9 +33,9 @@ Staying in Touch Contact the team and keep up to date using any of the following: - - The [Bisq Website](https://bisq.io) + - The [Bisq Website](https://bisq.network) - GitHub [Issues](https://github.com/bisq-network/exchange/issues) - - The [Bisq Forum]( https://forum.bisq.io) + - The [Bisq Forum]( https://forum.bisq.network) - The [#bitsquare](https://webchat.freenode.net/?channels=bitsquare) IRC channel on Freenode ([logs](https://botbot.me/freenode/bitsquare)) - Our [mailing list](https://groups.google.com/forum/#!forum/bitsquare) - [@Bitsquare_](https://twitter.com/bitsquare_) on Twitter diff --git a/common/src/main/java/io/bisq/common/app/DevEnv.java b/common/src/main/java/io/bisq/common/app/DevEnv.java index 0d2ea99318..37dd87395e 100644 --- a/common/src/main/java/io/bisq/common/app/DevEnv.java +++ b/common/src/main/java/io/bisq/common/app/DevEnv.java @@ -20,6 +20,5 @@ public class DevEnv { @SuppressWarnings("PointlessBooleanExpression") public static final boolean DEV_MODE = STRESS_TEST_MODE || false; - public static final boolean DAO_ACTIVATED = false; public static final boolean DAO_PHASE2_ACTIVATED = false; } diff --git a/common/src/main/java/io/bisq/common/locale/CountryUtil.java b/common/src/main/java/io/bisq/common/locale/CountryUtil.java index 4f943fa20e..1577cc0382 100644 --- a/common/src/main/java/io/bisq/common/locale/CountryUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CountryUtil.java @@ -170,6 +170,7 @@ public class CountryUtil { } private static final Map regionCodeToNameMap = new HashMap<>(); + // Key is: ISO 3166 code, value is region code as defined in regionCodeToNameMap private static final Map regionByCountryCodeMap = new HashMap<>(); static { diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index bbc1761040..3dcb78365b 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -82,150 +82,54 @@ public class CurrencyUtil { } // Don't make a PR for adding a coin but follow the steps described here: - // https://forum.bisq.io/t/how-to-add-your-favorite-altcoin/ + // https://forum.bisq.network/t/how-to-add-your-favorite-altcoin/ public static List createAllSortedCryptoCurrenciesList() { final List result = new ArrayList<>(); - result.add(new CryptoCurrency("AIB", "Advanced Internet Blocks")); - result.add(new CryptoCurrency("ANC", "Anoncoin")); - result.add(new CryptoCurrency("ANTI", "Anti")); - result.add(new CryptoCurrency("ARCO", "AquariusCoin")); - result.add(new CryptoCurrency("ARG", "Argentum")); - result.add(new CryptoCurrency("REP", "Augur", true)); - result.add(new CryptoCurrency("BATL", "Battlestars")); - result.add(new CryptoCurrency("BIGUP", "BigUp")); // result.add(new CryptoCurrency("BSQ", "Bisq Token")); if (!baseCurrencyCode.equals("BTC")) result.add(new CryptoCurrency("BTC", "Bitcoin")); - result.add(new CryptoCurrency("BITAUD", "BitAUD", true)); - result.add(new CryptoCurrency("BITCHF", "BitCHF", true)); - result.add(new CryptoCurrency("BITCNY", "BitCNY", true)); - result.add(new CryptoCurrency("BITEUR", "BitEUR", true)); - result.add(new CryptoCurrency("BITGBP", "BitGBP", true)); - result.add(new CryptoCurrency("BITHKD", "BitHKD", true)); - result.add(new CryptoCurrency("BITNZD", "BitNZD", true)); - result.add(new CryptoCurrency("BITSEK", "BitSEK", true)); - result.add(new CryptoCurrency("BITSGD", "BitSGD", true)); - result.add(new CryptoCurrency("SYNQ", "BitSYNQ")); - result.add(new CryptoCurrency("BTS", "BitShares")); - result.add(new CryptoCurrency("BITUSD", "BitUSD", true)); - result.add(new CryptoCurrency("BLK", "Blackcoin")); result.add(new CryptoCurrency("BURST", "Burstcoin")); result.add(new CryptoCurrency("GBYTE", "Byte")); - result.add(new CryptoCurrency("CLAM", "Clams")); - result.add(new CryptoCurrency("CLOAK", "CloakCoin")); - result.add(new CryptoCurrency("CMT", "Comet")); result.add(new CryptoCurrency("XCP", "Counterparty")); - result.add(new CryptoCurrency("CRBIT", "Creditbit")); - result.add(new CryptoCurrency("CRW", "Crown")); - result.add(new CryptoCurrency("CBX", "Crypto Bullion")); result.add(new CryptoCurrency("DNET", "DarkNet")); - result.add(new CryptoCurrency("DIBC", "DIBCOIN")); if (!baseCurrencyCode.equals("DASH")) result.add(new CryptoCurrency("DASH", "Dash")); result.add(new CryptoCurrency("DEC", "DECENT")); result.add(new CryptoCurrency("DCR", "Decred")); - result.add(new CryptoCurrency("DGB", "Digibyte")); - result.add(new CryptoCurrency("DRS", "Digital Rupees")); - result.add(new CryptoCurrency("DGD", "DigixDAO Tokens", true)); if (!baseCurrencyCode.equals("DOGE")) result.add(new CryptoCurrency("DOGE", "Dogecoin")); result.add(new CryptoCurrency("DMC", "DynamicCoin")); - result.add(new CryptoCurrency("EMC", "Emercoin")); - result.add(new CryptoCurrency("EURT", "EUR Tether")); result.add(new CryptoCurrency("ESP", "Espers")); - result.add(new CryptoCurrency("ENT", "Eternity")); result.add(new CryptoCurrency("ETH", "Ether")); result.add(new CryptoCurrency("ETC", "Ether Classic")); - result.add(new CryptoCurrency("ERC", "Europecoin")); - result.add(new CryptoCurrency("EGC", "EverGreenCoin")); - result.add(new CryptoCurrency("EOS", "EOS", true)); - result.add(new CryptoCurrency("FCT", "Factom")); - result.add(new CryptoCurrency("FAIR", "FairCoin")); - result.add(new CryptoCurrency("FLO", "FlorinCoin")); - //TODO ticker? - //result.add(new CryptoCurrency("FILE", "Filecoin", true)); - result.add(new CryptoCurrency("GAME", "GameCredits")); - result.add(new CryptoCurrency("GEMZ", "Gemz")); - result.add(new CryptoCurrency("GRC", "Gridcoin")); - result.add(new CryptoCurrency("GRS", "Groestlcoin")); - result.add(new CryptoCurrency("NLG", "Gulden")); - result.add(new CryptoCurrency("HODL", "HOdlcoin")); - result.add(new CryptoCurrency("HNC", "HunCoin")); - result.add(new CryptoCurrency("IOC", "I/O Coin")); - result.add(new CryptoCurrency("IOTA", "IOTA", true)); - result.add(new CryptoCurrency("ELLA", "Ellaism")); result.add(new CryptoCurrency("IOP", "Fermat")); - result.add(new CryptoCurrency("JNS", "Janus", true)); - result.add(new CryptoCurrency("JPYT", "JPY Tether")); - result.add(new CryptoCurrency("JBS", "Jumbucks")); + result.add(new CryptoCurrency("GRC", "Gridcoin")); result.add(new CryptoCurrency("LBC", "LBRY Credits")); - result.add(new CryptoCurrency("LTBC", "LTBcoin")); result.add(new CryptoCurrency("LSK", "Lisk")); if (!baseCurrencyCode.equals("LTC")) result.add(new CryptoCurrency("LTC", "Litecoin")); result.add(new CryptoCurrency("MAID", "MaidSafeCoin")); - result.add(new CryptoCurrency("MKR", "Maker", true)); - result.add(new CryptoCurrency("MXT", "MarteXcoin")); - result.add(new CryptoCurrency("MOIN", "Moin")); result.add(new CryptoCurrency("XMR", "Monero")); result.add(new CryptoCurrency("MT", "Mycelium Token", true)); - result.add(new CryptoCurrency("XMY", "Myriadcoin")); result.add(new CryptoCurrency("NAV", "Nav Coin")); - result.add(new CryptoCurrency("XEM", "NEM")); - result.add(new CryptoCurrency("NEVA", "Nevacoin")); result.add(new CryptoCurrency("NMC", "Namecoin")); result.add(new CryptoCurrency("NBT", "NuBits")); - result.add(new CryptoCurrency("NSR", "NuShares")); result.add(new CryptoCurrency("NXT", "Nxt")); result.add(new CryptoCurrency("888", "OctoCoin")); - result.add(new CryptoCurrency("OK", "OKCash")); - result.add(new CryptoCurrency("OMNI", "Omni")); - result.add(new CryptoCurrency("OPAL", "Opal")); - result.add(new CryptoCurrency("PART", "Particl")); result.add(new CryptoCurrency("PASC", "Pascal Coin", true)); - result.add(new CryptoCurrency("PPC", "Peercoin")); result.add(new CryptoCurrency("PEPECASH", "Pepe Cash")); - result.add(new CryptoCurrency("PINK", "Pinkcoin")); result.add(new CryptoCurrency("PIVX", "PIVX")); - result.add(new CryptoCurrency("XPTX", "PlatinumBar")); - result.add(new CryptoCurrency("PLU", "Plutons", true)); - result.add(new CryptoCurrency("PNC", "Pranacoin")); result.add(new CryptoCurrency("POST", "PostCoin")); - result.add(new CryptoCurrency("POT", "PotCoin")); - result.add(new CryptoCurrency("XPM", "Primecoin")); - result.add(new CryptoCurrency("RADS", "Radium")); - result.add(new CryptoCurrency("REALEST", "RealEst. Coin")); + result.add(new CryptoCurrency("PNC", "Pranacoin")); result.add(new CryptoCurrency("RDD", "ReddCoin")); - result.add(new CryptoCurrency("XRP", "Ripple")); result.add(new CryptoCurrency("SFSC", "Safe FileSystem Coin")); - result.add(new CryptoCurrency("SHIFT", "Shift")); result.add(new CryptoCurrency("SC", "Siacoin")); result.add(new CryptoCurrency("SF", "Siafund")); result.add(new CryptoCurrency("SIB", "Sibcoin")); - result.add(new CryptoCurrency("SMLY", "Smileycoin")); - result.add(new CryptoCurrency("SLR", "SolarCoin")); result.add(new CryptoCurrency("STEEM", "STEEM")); - result.add(new CryptoCurrency("STEEMUSD", "Steem Dollars", true)); - result.add(new CryptoCurrency("XLM", "Stellar Lumens")); - result.add(new CryptoCurrency("SJCX", "StorjcoinX")); - result.add(new CryptoCurrency("STRAT", "Stratis")); - result.add(new CryptoCurrency("SWT", "Swarm City Token", true)); - result.add(new CryptoCurrency("SYNX", "Syndicate")); - result.add(new CryptoCurrency("AMP", "Synereo", true)); - result.add(new CryptoCurrency("TRI", "Triangles")); - result.add(new CryptoCurrency("USDT", "USD Tether")); result.add(new CryptoCurrency("UNO", "Unobtanium")); - result.add(new CryptoCurrency("VCN", "VCoin")); - result.add(new CryptoCurrency("VPN", "VPNCoin")); - result.add(new CryptoCurrency("XVG", "Verge")); - result.add(new CryptoCurrency("VRC", "VeriCoin")); result.add(new CryptoCurrency("WAC", "WACoins")); - result.add(new CryptoCurrency("WAVES", "Waves")); - result.add(new CryptoCurrency("WDC", "Worldcoin")); - result.add(new CryptoCurrency("XAUR", "Xaurum")); - result.add(new CryptoCurrency("YACC", "YACCoin")); - result.add(new CryptoCurrency("YBC", "YbCoin")); result.add(new CryptoCurrency("ZEC", "Zcash")); result.add(new CryptoCurrency("XZC", "Zcoin")); result.add(new CryptoCurrency("ZEN", "ZenCash")); @@ -254,6 +158,8 @@ public class CurrencyUtil { if (!baseCurrencyCode.equals("DASH")) result.add(new CryptoCurrency("DASH", "Dash")); result.add(new CryptoCurrency("DCR", "Decred")); + if (!baseCurrencyCode.equals("DOGE")) + result.add(new CryptoCurrency("DOGE", "Dogecoin")); result.add(new CryptoCurrency("ETH", "Ether")); result.add(new CryptoCurrency("ETC", "Ether Classic")); result.add(new CryptoCurrency("GRC", "Gridcoin")); diff --git a/common/src/main/java/io/bisq/common/locale/LocaleUtil.java b/common/src/main/java/io/bisq/common/locale/LocaleUtil.java index 3b5475e8b7..d81a77bf94 100644 --- a/common/src/main/java/io/bisq/common/locale/LocaleUtil.java +++ b/common/src/main/java/io/bisq/common/locale/LocaleUtil.java @@ -30,6 +30,7 @@ public class LocaleUtil { public static List getAllLocales() { // derived form Locale.getAvailableLocales() and added some missing locales + // Key is ISO 639-1 code (https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes), value is ISO 3166 country code List allLocales = new ArrayList<>(); allLocales.add(new Locale("bg", "", "")); @@ -160,7 +161,7 @@ public class LocaleUtil { allLocales.add(new Locale("es", "PE", "")); allLocales.add(new Locale("en", "PH", "")); allLocales.add(new Locale("pl", "PL", "")); - allLocales.add(new Locale("es", "PR", "")); + allLocales.add(new Locale("es", "PR", "")); // Puerto Rico allLocales.add(new Locale("pt", "PT", "")); allLocales.add(new Locale("es", "PY", "")); allLocales.add(new Locale("ar", "QA", "")); diff --git a/common/src/main/proto/pb.proto b/common/src/main/proto/pb.proto index 60b0435b07..d437cbbcb3 100644 --- a/common/src/main/proto/pb.proto +++ b/common/src/main/proto/pb.proto @@ -410,24 +410,10 @@ message PubKeyScript { string hex = 5; } -message TxInputVo { +message TxInput { string tx_id = 1; int32 tx_output_index = 2; -} - -message TxInput { - TxInputVo tx_input_vo = 1; - TxOutput connected_tx_output = 2; -} - -message TxOutputVo { - int32 index = 1; - int64 value = 2; - string tx_id = 3; - PubKeyScript pub_key_script = 4; - string address = 5; - bytes op_return_data = 6; - int32 block_height= 7; + TxOutput connected_tx_output = 3; } message SpentInfo { @@ -449,11 +435,17 @@ enum TxOutputType { } message TxOutput { - TxOutputVo tx_output_vo = 1; - bool is_unspent = 2; - bool is_verified = 3; - TxOutputType tx_output_type = 4; - SpentInfo spent_info = 5; + int32 index = 1; + int64 value = 2; + string tx_id = 3; + PubKeyScript pub_key_script = 4; + string address = 5; + bytes op_return_data = 6; + int32 block_height= 7; + bool is_unspent = 8; + bool is_verified = 9; + TxOutputType tx_output_type = 10; + SpentInfo spent_info = 11; } message TxVo { @@ -486,16 +478,12 @@ message Tx { int64 burnt_fee = 4; TxType tx_type = 5; } - -message BsqBlockVo { + +message BsqBlock { int32 height = 1; string hash = 2; string previous_block_hash = 3; -} - -message BsqBlock { - BsqBlockVo bsq_block_vo = 1; - repeated Tx txs = 2; + repeated Tx txs = 4; } message TxIdIndexTuple { @@ -551,6 +539,8 @@ message Filter { string signature_as_base64 = 4; bytes owner_pub_key_bytes = 5; map extra_data = 6; + repeated string banned_currencies = 7; + repeated string banned_payment_methods = 8; } message TradeStatistics { diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index b37690892b..8d4670931b 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -166,6 +166,7 @@ shared.viewContractAsJson=View contract in JSON format shared.contract.title=Contract for trade with ID: {0} shared.paymentDetails=BTC {0} payment details: shared.securityDeposit=Security deposit +shared.yourSecurityDeposit=Your security deposit shared.contract=Contract shared.messageArrived=Message arrived. shared.messageStoredInMailbox=Message stored in mailbox. @@ -210,6 +211,9 @@ mainView.menu.account=Account mainView.menu.dao=DAO mainView.marketPrice.provider=Market price provider: +mainView.marketPrice.bisqInternalPrice=Price of latest Bisq trade +mainView.marketPrice.tooltip.bisqInternalPrice=There is no market price from external price feed providers available.\n\ + The displayed price is the latest Bisq trade price for that currency. mainView.marketPrice.tooltip=Market price is provided by {0}{1}\nLast update: {2}\nProvider node URL: {3} mainView.marketPrice.tooltip.altcoinExtra=If the altcoin is not available at Poloniex we use https://coinmarketcap.com mainView.balance.available=Available balance @@ -315,6 +319,8 @@ offerbook.warning.noMatchingAccount.msg=You don't have a trading account with th offerbook.warning.wrongTradeProtocol=That offer requires a different protocol version as the one used in your version of the software.\n\nPlease check if you have the latest version installed, otherwise the user who created the offer has used an older version.\n\nUsers cannot trade with an incompatible trade protocol version. offerbook.warning.userIgnored=You have added that user's onion address to your ignore list. offerbook.warning.offerBlocked=That offer was blocked by the Bisq developers.\nProbably there is an unhandled bug causing issues when taking that offer. +offerbook.warning.currencyBanned=The currency used in that offer was blocked by the Bisq developers.\nPlease visit the Bisq Forum for more information. +offerbook.warning.paymentMethodBanned=The payment method used in that offer was blocked by the Bisq developers.\nPlease visit the Bisq Forum for more information. offerbook.warning.nodeBlocked=The onion address of that trader was blocked by the Bisq developers.\nProbably there is an unhandled bug causing issues when taking offers from that trader. @@ -333,7 +339,7 @@ createOffer.amountPriceBox.minAmountDescription=Minimum amount of BTC createOffer.securityDeposit.prompt=Security deposit in BTC createOffer.fundsBox.title=Fund your offer createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above -createOffer.fundsBox.offerFee=Maker fee: +createOffer.fundsBox.offerFee=Trade fee: createOffer.fundsBox.networkFee=Mining fee: createOffer.fundsBox.placeOfferSpinnerInfo=Offer publishing is in progress ... createOffer.fundsBox.paymentLabel=Bisq trade with ID {0} @@ -347,7 +353,7 @@ createOffer.alreadyFunded=You had already funded that offer.\nYour funds have be createOffer.createOfferFundWalletInfo.headline=Fund your offer # suppress inspection "TrailingSpacesInProperty" createOffer.createOfferFundWalletInfo.tradeAmount=- Trade amount: {0} \n -createOffer.createOfferFundWalletInfo.msg=You need to deposit {0} to this offer.\n\nThose funds are reserved in your local wallet and will get locked into the Multisig deposit address once someone takes your offer.\n\nThe amount is the sum of:\n{1}- Security deposit: {2}\n- Trading fee: {3}\n- Mining fee: {4}\n\nYou can choose between two options when funding your trade:\n- Use your Bisq wallet (convenient, but transactions may be linkable) OR\n- Transfer from an external wallet (potentially more private)\n\nYou will see all funding options and details after closing this popup. +createOffer.createOfferFundWalletInfo.msg=You need to deposit {0} to this offer.\n\nThose funds are reserved in your local wallet and will get locked into the Multisig deposit address once someone takes your offer.\n\nThe amount is the sum of:\n{1}- Your security deposit: {2}\n- Trading fee: {3}\n- Mining fee: {4}\n\nYou can choose between two options when funding your trade:\n- Use your Bisq wallet (convenient, but transactions may be linkable) OR\n- Transfer from an external wallet (potentially more private)\n\nYou will see all funding options and details after closing this popup. # only first part "An error occurred when placing the offer:" has been used before. We added now the rest (need update in existing translations!) createOffer.amountPriceBox.error.message=An error occurred when placing the offer:\n\n{0}\n\n\ @@ -369,8 +375,9 @@ createOffer.useLowerValue=Yes, use my lower value createOffer.priceOutSideOfDeviation=The price you have entered is outside the max. allowed deviation from the market price.\nThe max. allowed deviation is {0} and can be adjusted in the preferences. createOffer.changePrice=Change price createOffer.tac=With publishing that offer I agree to trade with any trader who fulfills the conditions as defined in that screen. -createOffer.currencyForFee=Maker fee +createOffer.currencyForFee=Trade fee createOffer.feeCurrencyAndDeposit=Set fee currency and security deposit +createOffer.setDeposit=Set buyer's security deposit #################################################################### @@ -389,8 +396,8 @@ takeOffer.validation.amountLargerThanOfferAmountMinusFee=That input amount would takeOffer.fundsBox.title=Fund your trade takeOffer.fundsBox.isOfferAvailable=Check if offer is available ... takeOffer.fundsBox.tradeAmount=Amount to sell: -takeOffer.fundsBox.offerFee=Taker fee: -takeOffer.fundsBox.networkFee=Mining fees (3x): +takeOffer.fundsBox.offerFee=Trade fee: +takeOffer.fundsBox.networkFee=Total mining fees: takeOffer.fundsBox.takeOfferSpinnerInfo=Take offer in progress ... takeOffer.fundsBox.paymentLabel=Bisq trade with ID {0} takeOffer.success.headline=You have successfully taken an offer. @@ -404,7 +411,7 @@ takeOffer.alreadyFunded.movedFunds=You had already funded that offer.\nYour fund takeOffer.takeOfferFundWalletInfo.headline=Fund your trade # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- Trade amount: {0} \n -takeOffer.takeOfferFundWalletInfo.msg=You need to deposit {0} for taking this offer.\n\nThe amount is the sum of:\n{1}- Security deposit: {2}\n- Trading fee: {3}\n- Mining fee (3x): {4}\n\nYou can choose between two options when funding your trade:\n- Use your Bisq wallet (convenient, but transactions may be linkable) OR\n- Transfer from an external wallet (potentially more private)\n\nYou will see all funding options and details after closing this popup. +takeOffer.takeOfferFundWalletInfo.msg=You need to deposit {0} for taking this offer.\n\nThe amount is the sum of:\n{1}- Your security deposit: {2}\n- Trading fee: {3}\n- Total mining fees: {4}\n\nYou can choose between two options when funding your trade:\n- Use your Bisq wallet (convenient, but transactions may be linkable) OR\n- Transfer from an external wallet (potentially more private)\n\nYou will see all funding options and details after closing this popup. takeOffer.alreadyPaidInFunds=If you have already paid in funds you can withdraw it in the \"Funds/Send funds\" screen. takeOffer.paymentInfo=Payment info takeOffer.setAmountPrice=Set amount @@ -422,7 +429,7 @@ takeOffer.error.depositPublished=\n\nThe deposit transaction is already publishe takeOffer.error.payoutPublished=\n\nThe payout transaction is already published.\nPlease try to restart your application and check your network connection to see if you can resolve the issue.\nIf the problem still remains please contact the developers for support. takeOffer.tac=With taking that offer I agree to the trade conditions as defined in that screen. -takeOffer.currencyForFee=Taker fee +takeOffer.currencyForFee=Trade fee takeOffer.feeCurrency=Set fee currency @@ -689,7 +696,7 @@ In cases where a user got stuck by a bug without getting displayed that \"Open s you can open a support ticket manually with a special short cut.\n\n\ Please use that only if you are sure that the software is not working like expected. \ If you have problems how to use Bisq or any questions please review the FAQ at the \ -bisq.io web page or post in the Bisq forum at the support section.\n\n\ +bisq.network web page or post in the Bisq forum at the support section.\n\n\ If you are sure that you want to open a support ticket please select the trade which causes the problem \ under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open \ the support ticket. @@ -954,9 +961,21 @@ dao.wallet.menuItem.send=Send dao.wallet.menuItem.receive=Receive dao.wallet.menuItem.transactions=Transactions +dao.wallet.dashboard.statistics=Statistics +dao.wallet.dashboard.genesisBlockHeight=Genesis block height: +dao.wallet.dashboard.genesisTxId=Genesis transaction ID: +dao.wallet.dashboard.issuedAmount=Total issued amount: +dao.wallet.dashboard.availableAmount=Available amount: +dao.wallet.dashboard.burntAmount=Burnt amount (fees): +dao.wallet.dashboard.allTx=No. of all BSQ transactions: +dao.wallet.dashboard.utxo=No. of all unspent transaction outputs: +dao.wallet.dashboard.spentTxo=No. of all spent transaction outputs: +dao.wallet.dashboard.burntTx=No. of all fee payments transactions (burnt): +dao.wallet.dashboard.price=Price: +dao.wallet.dashboard.marketCap=Market capitalisation: + dao.wallet.receive.fundBSQWallet=Fund Bisq BSQ wallet dao.wallet.receive.fundYourWallet=Fund your BSQ wallet -dao.wallet.receive.amountOptional=Amount (optional) dao.wallet.send.sendFunds=Send funds dao.wallet.send.amount=Amount in BSQ: @@ -967,8 +986,8 @@ dao.wallet.send.send=Send BSQ funds dao.wallet.send.sendFunds.headline=Confirm withdrawal request dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequired transaction fee is: {2} ({3} Satoshis/byte)\nTransaction size: {4} Kb\n\nThe recipient will receive: {5}\n\nAre you sure you want to withdraw that amount? dao.wallet.bsqFee=BSQ fee payment -dao.wallet.chainHeightSynced=Synchronized with blockchain height. Current height: {0} / Best chain height: {1} -dao.wallet.chainHeightSyncing=Synchronizing with blockchain height. Current height: {0} / Best chain height: {1} +dao.wallet.chainHeightSynced=Synchronized up to block:{0} (latest block: {1}) +dao.wallet.chainHeightSyncing=Synchronizing block: {0} (latest block: {1}) dao.wallet.tx.type=Type dao.tx.type.enum.UNVERIFIED=Unverified BSQ transaction @@ -1001,17 +1020,17 @@ displayAlertMessageWindow.update.headline=Important update information! displayAlertMessageWindow.update.download=Download: displayUpdateDownloadWindow.downloadedFiles=Downloaded files: displayUpdateDownloadWindow.downloadingFile=Downloading: {0} -displayUpdateDownloadWindow.verifiedSigs=Verified signatures: +displayUpdateDownloadWindow.verifiedSigs=Signature verified with keys: displayUpdateDownloadWindow.status.downloading=Downloading files... -displayUpdateDownloadWindow.status.verifying=Verifying signature(s)... -displayUpdateDownloadWindow.button.label=Download installer and verify signature(s) +displayUpdateDownloadWindow.status.verifying=Verifying signature... +displayUpdateDownloadWindow.button.label=Download installer and verify signature displayUpdateDownloadWindow.headline=A new Bisq update is available! displayUpdateDownloadWindow.download.failed=Download failed.\n\ - Please download and verify manually at https://bisq.io/downloads -displayUpdateDownloadWindow.installer.failed=Unable to determine the correct installer. Please download and verify manually at https://bisq.io/downloads + Please download and verify manually at https://bisq.network/downloads +displayUpdateDownloadWindow.installer.failed=Unable to determine the correct installer. Please download and verify manually at https://bisq.network/downloads displayUpdateDownloadWindow.verify.failed=Verification failed.\n\ - Please download and verify manually at https://bisq.io/downloads -displayUpdateDownloadWindow.success=The new version has been successfully downloaded and the signature(s) verified.\n\n\ + Please download and verify manually at https://bisq.network/downloads +displayUpdateDownloadWindow.success=The new version has been successfully downloaded and the signature verified.\n\n\ Please open the download directory, shut down the application and install the new version. displayUpdateDownloadWindow.download.openDir=Open download directory @@ -1071,6 +1090,8 @@ filterWindow.headline=Edit filter list filterWindow.offers=Filtered offers (comma sep.): filterWindow.onions=Filtered onion addresses (comma sep.): filterWindow.accounts=Filtered trading account data:\nFormat: comma sep. list of [payment method id | data field | value] +filterWindow.bannedCurrencies=Filtered currency codes (comma sep.): +filterWindow.bannedPaymentMethods=Filtered payment method IDs (comma sep.): filterWindow.add=Add filter filterWindow.remove=Remove filter @@ -1187,7 +1208,7 @@ The backup is located at:\n\ {1}/db/backup_of_corrupted_data.\n\n\ Please check if you have the latest version of Bisq installed.\n\ You can download it at:\n\ -https://bisq.io/downloads\n\n\ +https://bisq.network/downloads\n\n\ Please restart the application. popup.warning.cannotConnectAtStartup=You still did not get connected to the {0} network.\nIf you use Tor for Bitcoin it might be that you got an unstable Tor circuit.\nYou can wait longer or try to restart. popup.warning.unknownProblemAtStartup=There is an unknown problem at startup.\nPlease restart and if the problem continues file a bug report. @@ -1322,6 +1343,7 @@ confidence.invalid=Transaction is invalid peerInfo.title=Peer info peerInfo.nrOfTrades=Number of completed trades: +peerInfo.notTradedYet=You have not traded with that user so far. peerInfo.setTag=Set tag for that peer: addressTextField.openWallet=Open your default bitcoin wallet @@ -1457,10 +1479,10 @@ seed.restore.error=An error occurred when restoring the wallets with seed words. payment.account.no=Account no.: payment.account.name=Account name: -payment.account.owner=Account holder name +payment.account.owner=Account owner full name payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email -payment.account.name.emailAndHolderId=Account holder name / email / {0} +payment.account.name.email=Account owner full name / email +payment.account.name.emailAndHolderId=Account owner full name / email / {0} payment.bank.name=Bank name: payment.select.account=Select account type payment.select.region=Select region @@ -1536,7 +1558,7 @@ popup.info.revertIdCheckRequirement=With this version we remove the requirement with bank transfer or Faster Payment was used.\n\n\ The email address is not exposed anymore in Sepa, Faster Payment and national bank transfer payment methods.\n\n\ Please see the discussion on the Bisq Forum for further background:\n\ - https://forum.bisq.io/t/new-requirement-for-payment-accounts-with-charge-back-risk/2376 + https://forum.bisq.network/t/new-requirement-for-payment-accounts-with-charge-back-risk/2376 # We use constants from the code so we do not use our normal naming convention # dynamic values are not recognized by IntelliJ diff --git a/common/src/main/resources/i18n/displayStrings_de.properties b/common/src/main/resources/i18n/displayStrings_de.properties index 0a272c4cb6..b01dcedb1c 100644 --- a/common/src/main/resources/i18n/displayStrings_de.properties +++ b/common/src/main/resources/i18n/displayStrings_de.properties @@ -386,7 +386,7 @@ takeOffer.fundsBox.title=Ihren Handel finanzieren takeOffer.fundsBox.isOfferAvailable=Überprüfe ob Angebot verfügbar ist ... takeOffer.fundsBox.tradeAmount=Zu verkaufender Betrag: takeOffer.fundsBox.offerFee=Abnehmergebühr: -takeOffer.fundsBox.networkFee=Mining Gebühren (3x): +takeOffer.fundsBox.networkFee=Gesamte Mining Gebühren: takeOffer.fundsBox.takeOfferSpinnerInfo=Angebotsannahme im Gange ... takeOffer.fundsBox.paymentLabel=Bisq Handel mit Kennung {0} takeOffer.success.headline=Sie haben ein Angebot erfolgreich angenommen. @@ -660,7 +660,7 @@ support.buyerOfferer=BTC Käufer/Ersteller support.sellerOfferer=BTC Verkäufer/Ersteller support.buyerTaker=BTC Käufer/Abnehmer support.sellerTaker=BTC Verkäufer/Abnehmer -support.backgroundInfo=Bisq ist keine Firma und betreibt keine Form von Kundendienst.\n\nGibt es Konflikte während des Handelsprozess (z.B. ein Händler befolgt nicht das Handelsprotokoll), wird die Anwendung nach der Handelsdauer eine \"Konflikt öffnen\" Schaltfläche anzeigen, um den Vermittler zu kontaktieren.\nIm Falle von Softwarefehlern oder anderen Problemen, die von der Anwendung entdeckt werden, wird eine \"Unterstützungsticket öffnen\" Schaltfläche angezeigt, um den Vermittler zu kontaktieren, der die Probleme an die Entwickler weiterleitet.\n\nIm Falle, dass ein Nutzer durch einen Fehler fest hängt, ohne dass die \"Unterstützungsticket öffnen\" Schaltfläche angezeigt wird, können Sie ein Unterstützungsticket mit einer speziellen Tastenkombination manuell öffnen.\n\nBitte nutzen Sie diese nur, wenn Sie sicher sind, dass die Software sich nicht wie erwartet verhält. Falls Sie Probleme wie man Bisq verwendet oder andere Fragen haben, überprüfen Sie bitte das FAQ auf der bisq.io Website oder posten Sie in das Bisq Forum in die Unterstützungsabteilung.\n\nFalls Sie sicher sind, dass Sie ein Unterstützungsticket öffnen wollen, wählen Sie bitte den Handel, der Probleme bereitet, unter \"Mappe/Offene Händel\" und drücken Sie die Tastenkombination \"alt + o\" oder \"option + o\" um das Unterstützungsticket zu öffnen. +support.backgroundInfo=Bisq ist keine Firma und betreibt keine Form von Kundendienst.\n\nGibt es Konflikte während des Handelsprozess (z.B. ein Händler befolgt nicht das Handelsprotokoll), wird die Anwendung nach der Handelsdauer eine \"Konflikt öffnen\" Schaltfläche anzeigen, um den Vermittler zu kontaktieren.\nIm Falle von Softwarefehlern oder anderen Problemen, die von der Anwendung entdeckt werden, wird eine \"Unterstützungsticket öffnen\" Schaltfläche angezeigt, um den Vermittler zu kontaktieren, der die Probleme an die Entwickler weiterleitet.\n\nIm Falle, dass ein Nutzer durch einen Fehler fest hängt, ohne dass die \"Unterstützungsticket öffnen\" Schaltfläche angezeigt wird, können Sie ein Unterstützungsticket mit einer speziellen Tastenkombination manuell öffnen.\n\nBitte nutzen Sie diese nur, wenn Sie sicher sind, dass die Software sich nicht wie erwartet verhält. Falls Sie Probleme wie man Bisq verwendet oder andere Fragen haben, überprüfen Sie bitte das FAQ auf der bisq.network Website oder posten Sie in das Bisq Forum in die Unterstützungsabteilung.\n\nFalls Sie sicher sind, dass Sie ein Unterstützungsticket öffnen wollen, wählen Sie bitte den Handel, der Probleme bereitet, unter \"Mappe/Offene Händel\" und drücken Sie die Tastenkombination \"alt + o\" oder \"option + o\" um das Unterstützungsticket zu öffnen. support.initialInfo=Bitte beachten Sie die grundlegenen Regeln für den Konfliktprozess:\n1. Sie müssen innerhalb von 2 Tagen auf die Anfrage des Vermittlers reagieren.\n2. Die maximale Dauer des Konflikts ist 14 Tage.\n3. Sie müssen erfüllen, was der Vermittler von ihnen verlangt, um Beweise für ihren Fall zu liefern.\n4. Sie akzeptieren die Regeln, die im Wiki im Nutzungsvereinbarung umrissen wurden, als Sie das erste Mal die Anwendung gestartet haben.\n\nErfahren Sie mehr über den Konfliktprozess in unserem Wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Systemnachricht: {0} support.youOpenedTicket=Sie haben eine Anfrage auf Unterstützung geöffnet. @@ -1064,7 +1064,7 @@ error.spvFileCorrupted=Beim Einlesen der SPV Kettendatei ist ein Fehler aufgetre popup.warning.walletNotInitialized=Die Wallet ist noch nicht initialisiert popup.warning.wrongVersion=Sie haben vermutlich die falsche Bisq Version für diesen Computer.\nDie Architektur ihres Computers ist: {0}.\nDie installierten Bisq Binärdateien sind: {1}.\nBitte fahren Sie diese herunter und installieren die korrekte Version ({2}). -popup.warning.incompatibleDB=Wir haben nicht kompatible Datenbankdateien entdeckt!\n\nDie Datenbankdatei(en) sind mit unserer momentanen Codebasis nicht kompatibel:\n{0}\n\nWir haben ein Backup der beschädigten Datei(en) erstellt und die Standardwerte auf eine neue Datenbankversion angewendet.\n\nDas Backup befindet sich in:\n{1}/db/backup_of_corrupted_data.\n\nBitte überprüfen Sie, ob Sie die letzte Version von Bisq installiert haben.\nSie können diese hier herunterladen:\nhttps://bisq.io/downloads\n\nBitte starten Sie die Anwendung neu. +popup.warning.incompatibleDB=Wir haben nicht kompatible Datenbankdateien entdeckt!\n\nDie Datenbankdatei(en) sind mit unserer momentanen Codebasis nicht kompatibel:\n{0}\n\nWir haben ein Backup der beschädigten Datei(en) erstellt und die Standardwerte auf eine neue Datenbankversion angewendet.\n\nDas Backup befindet sich in:\n{1}/db/backup_of_corrupted_data.\n\nBitte überprüfen Sie, ob Sie die letzte Version von Bisq installiert haben.\nSie können diese hier herunterladen:\nhttps://bisq.network/downloads\n\nBitte starten Sie die Anwendung neu. popup.warning.cannotConnectAtStartup=Sie wurden immer noch nicht mit dem {0} Netzwerk verbunden.\nSollten Sie Tor für Bitcoin verwenden kann es sein, dass Sie eine labile Torverbindung haben.\nSie können versuchen länger zu warten oder neu zu starten. popup.warning.unknownProblemAtStartup=Es gibt ein unbekanntes Problem beim Starten.\nBitte starten Sie neu und wenn das Problem besteht, füllen Sie eine Bugmeldung aus. popup.warning.startupFailed.timeout=Die Anwendung konnte nicht nach 4 Minuten starten.\n\n{0} diff --git a/common/src/main/resources/i18n/displayStrings_el.properties b/common/src/main/resources/i18n/displayStrings_el.properties index b59de0de97..6c6b6e56c9 100644 --- a/common/src/main/resources/i18n/displayStrings_el.properties +++ b/common/src/main/resources/i18n/displayStrings_el.properties @@ -365,7 +365,7 @@ createOffer.useLowerValue=Ναι, χρησιμοποίησε τη χαμηλή createOffer.priceOutSideOfDeviation=Η τιμή που εισήγαγες είναι εκτός της μέγιστης επιτρεπόμενης απόκλισης από την τιμή αγοράς.\nΗ μέγιστη επιτρεπόμενη απόκλιση είναι {0} και μπορεί να προσαρμοστεί στις προτιμήσεις. createOffer.changePrice=Άλλαξε την τιμή createOffer.tac=Τοποθετώντας αυτή την προσφορά συμφωνώ να συναλλαχθώ με οποιονδήποτε ικανοποιεί τις συνθήκες που καθορίζονται πιο πάνω. -createOffer.currencyForFee=Maker fee +createOffer.currencyForFee=Trade fee createOffer.feeCurrencyAndDeposit=Καθόρισε νόμισμα προμήθειας και ποσού εγγύησης @@ -386,7 +386,7 @@ takeOffer.fundsBox.title=Χρηματοδότησε τη συναλλαγή σο takeOffer.fundsBox.isOfferAvailable=Έλεγχος διαθεσιμότητας προσφοράς... takeOffer.fundsBox.tradeAmount=Ποσό προς πώληση: takeOffer.fundsBox.offerFee=Προμήθεια Taker: -takeOffer.fundsBox.networkFee=Προμήθεια εξόρυξης (3x): +takeOffer.fundsBox.networkFee=Προμήθεια εξόρυξης: takeOffer.fundsBox.takeOfferSpinnerInfo=Αποδοχή προσφοράς σε εξέλιξη... takeOffer.fundsBox.paymentLabel=Συναλλαγή Bisq με ταυτότητα {0} takeOffer.success.headline=Αποδέχτηκες επιτυχώς μία προσφορά. @@ -400,7 +400,7 @@ takeOffer.alreadyFunded.movedFunds=Είχες ήδη χρηματοδοτήσε takeOffer.takeOfferFundWalletInfo.headline=Χρηματοδότησε τη συναλλαγή σου # suppress inspection "TrailingSpacesInProperty" takeOffer.takeOfferFundWalletInfo.tradeAmount=- Ποσό συναλλαγής: {0}\n -takeOffer.takeOfferFundWalletInfo.msg=You need to deposit {0} for taking this offer.\n\nThe amount is the sum of:\n{1}- Security deposit: {2}\n- Trading fee: {3}\n- Mining fee (3x): {4}\n\nYou can choose between two options when funding your trade:\n- Use your Bisq wallet (convenient, but transactions may be linkable) OR\n- Transfer from an external wallet (potentially more private)\n\nYou will see all funding options and details after closing this popup. +takeOffer.takeOfferFundWalletInfo.msg=You need to deposit {0} for taking this offer.\n\nThe amount is the sum of:\n{1}- Your security deposit: {2}\n- Trading fee: {3}\n- Mining fee (3x): {4}\n\nYou can choose between two options when funding your trade:\n- Use your Bisq wallet (convenient, but transactions may be linkable) OR\n- Transfer from an external wallet (potentially more private)\n\nYou will see all funding options and details after closing this popup. takeOffer.alreadyPaidInFunds=Αν έχεις ήδη κατατεθιμένα κεφάλαια, μπορείς να τα αποσύρεις στο \"Κεφάλαια/Αποστολή κεφαλαίων\". takeOffer.paymentInfo=Πληροφορίες πληρωμής takeOffer.setAmountPrice=Θέσε ποσό @@ -418,7 +418,7 @@ takeOffer.error.depositPublished=\n\nΗ κατάθεση κοινοποιήθη takeOffer.error.payoutPublished=\n\nΗ συναλλαγή αποπληρωμής κοινοποιήθηκε ήδη.\nΠροσπάθησε να επανεκκινήσεις την εφαρμογή και έλεγξε τη σύνδεσή σου στο διαδίκτυο ώστε να λυθεί το πρόβλημα.\nΑν το πρόβλημα επιμένει, επικοινώνησε με την ομάδα προγραμματιστών για υποστήριξη. takeOffer.tac=Αποδεχόμενος/η αυτή την προσφορά συμφωνώ με τους όρους συναλλαγών, όπως αυτοί ορίζονται ανωτέρω. -takeOffer.currencyForFee=Taker fee +takeOffer.currencyForFee=Trade fee takeOffer.feeCurrency=Καθόρισε νόμισμα προμήθειας @@ -660,7 +660,7 @@ support.buyerOfferer=Αγοραστής/Maker BTC support.sellerOfferer=Πωλητής/Maker BTC support.buyerTaker=Αγοραστής/Taker BTC support.sellerTaker=Πωλητής/Taker BTC -support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.io web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. +support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.network web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. support.initialInfo=Please note the basic rules for the dispute process:\n1. You need to respond to the arbitrators requests in between 2 days.\n2. The maximum period for the dispute is 14 days.\n3. You need to fulfill what the arbitrator is requesting from you to deliver evidence for your case.\n4. You accepted the rules outlined in the wiki in the user agreement when you first started the application.\n\nPlease read more in detail about the dispute process in our wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Μήνυμα συστήματος: {0} support.youOpenedTicket=Άνοιξες ένα αίτημα υποστήριξης. @@ -1066,7 +1066,7 @@ error.spvFileCorrupted=Προέκυψε σφάλμα κατά την ανάγν popup.warning.walletNotInitialized=Δεν έχει δημιουργηθεί το πορτοφόλι μέχρι στιγμής popup.warning.wrongVersion=You probably have the wrong Bisq version for this computer.\nYour computer''s architecture is: {0}.\nThe Bisq binary you installed is: {1}.\nPlease shut down and re-install the correct version ({2}). -popup.warning.incompatibleDB=We detected incompatible data base files!\n\nThose database file(s) are not compatible with our current code base:\n{0}\n\nWe made a backup of the corrupted file(s) and applied the default values to a new database version.\n\nThe backup is located at:\n{1}/db/backup_of_corrupted_data.\n\nPlease check if you have the latest version of Bisq installed.\nYou can download it at:\nhttps://bisq.io/downloads\n\nPlease restart the application. +popup.warning.incompatibleDB=We detected incompatible data base files!\n\nThose database file(s) are not compatible with our current code base:\n{0}\n\nWe made a backup of the corrupted file(s) and applied the default values to a new database version.\n\nThe backup is located at:\n{1}/db/backup_of_corrupted_data.\n\nPlease check if you have the latest version of Bisq installed.\nYou can download it at:\nhttps://bisq.network/downloads\n\nPlease restart the application. popup.warning.cannotConnectAtStartup=Παραμένεις αποσυνδεδεμένος από το δίκτυο {0}.\nΑν χρησιμοποιείς το Tor για το Bitcoin ίσως έχεις ασταθές κύκλωμα Tor.\nΜπορείς να περιμένεις περαιτέρω ή να επανεκκινήσεις. popup.warning.unknownProblemAtStartup=Υπάρχει άγνωστο πρόβλημα κατά την εκκίνηση.\nΕπανεκκίνησε την εφαρμογή και αν το πρόβλημα επιμείνει, κατάθεσε μια αναφορά σφάλματος. popup.warning.startupFailed.timeout=Η εφαρμογή δεν μπόρεσε να εκκινήσει εντός 4 λεπτών.\n\n{0} diff --git a/common/src/main/resources/i18n/displayStrings_es.properties b/common/src/main/resources/i18n/displayStrings_es.properties index 89bfd54e2e..82304b574e 100644 --- a/common/src/main/resources/i18n/displayStrings_es.properties +++ b/common/src/main/resources/i18n/displayStrings_es.properties @@ -365,7 +365,7 @@ createOffer.useLowerValue=Sí, usar mi valor más bajo createOffer.priceOutSideOfDeviation=El precio que ha introducido está fuera de la máxima desviación permitida del precio de mercado. La desviación máxima permitida es {0} y puede ajustarse en las preferencias. createOffer.changePrice=Cambiar precio createOffer.tac=Al colocar esta oferta estoy de acuerdo en comerciar con cualquier comerciante que cumpla con las condiciones definidas anteriormente. -createOffer.currencyForFee=Maker fee +createOffer.currencyForFee=Trade fee createOffer.feeCurrencyAndDeposit=Establecer moneda para las tasas y el depósito de seguridad @@ -386,7 +386,7 @@ takeOffer.fundsBox.title=Dote de fondos su intercambio. takeOffer.fundsBox.isOfferAvailable=Comprobar si la oferta está disponible... takeOffer.fundsBox.tradeAmount=Cantidad a vender: takeOffer.fundsBox.offerFee=Tasa al tomador de oferta: -takeOffer.fundsBox.networkFee=Tasas de minería (3x): +takeOffer.fundsBox.networkFee=Total tasas de minería: takeOffer.fundsBox.takeOfferSpinnerInfo=Aceptación de oferta en espera... takeOffer.fundsBox.paymentLabel=Trato de Bitsquare con ID {0} takeOffer.success.headline=Ha aceptado la oferta con éxito. @@ -418,7 +418,7 @@ takeOffer.error.depositPublished=\n\nLa transacción de depósito ya se ha publi takeOffer.error.payoutPublished=\n\nLa transacción de pago ya se ha publicado.\nPor favor pruebe reiniciando la aplicación y compruebe su conexión a la red para ver si puede resolver el problema.\nSi el problema continúa por favor contacte con los desarrolladores para ayuda. takeOffer.tac=Al aceptar esta oferta afirmo estar de acuerdo en las condiciones de intercambio definidas anteriormente. -takeOffer.currencyForFee=Taker fee +takeOffer.currencyForFee=Trade fee takeOffer.feeCurrency=Establecer moneda de tasa @@ -660,7 +660,7 @@ support.buyerOfferer= comprador/creador BTC support.sellerOfferer=vendedor/creador BTC support.buyerTaker=comprador/Tomador BTC support.sellerTaker=vendedor/Tomador BTC -support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.io web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. +support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.network web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. support.initialInfo=Por favor, tenga en cuenta las reglas básicas para el proceso de disputa:\n1. Necesita responder a las peticiones de los árbitros en menos de 2 días.\n2. El periodo máximo total de la disputa es de 14 días.\n3. Necesitará completar todas las peticiones del árbitro para entregar evidencia de su caso.\n4. Acepta las reglas destacadas en la wiki de acuerdo de usuario al comenzar la aplicación.\n\nPor favor lea en más detalle acerca del proceso de disputa en nuestra wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Mensaje de sistema: {0} support.youOpenedTicket=Ha abierto una solicitud de soporte. @@ -1064,7 +1064,7 @@ error.spvFileCorrupted=Ocurrió un error al leer el archivo de cadena SPV.\nPued popup.warning.walletNotInitialized=La cartera aún no sea ha iniciado popup.warning.wrongVersion=Probablemente tenga una versión de Bisq incorrecta para este ordenador.\nLa arquitectura de su ordenador es: {0}.\nLos binarios de Bisq instalados son: {1}.\nPor favor cierre y reinstale la versión correcta ({2}). -popup.warning.incompatibleDB=Hemos detectado archivos de base de datos incompatibles!\n\nEstos archivos de base de datos no son compatibles con nuestro actual código base:\n{0}\n\nHemos hecho una copia de seguridad de los archivos corruptos y aplicado los valores por defecto a la nueva versión de base de datos.\n\nLa copia de seguridad se localiza en:\n{1}/db/backup_of_corrupted_data.\n\nPor favor, compruebe si tiene la última versión de Bisq instalada.\nPuede descargarla en:\nhttps://bisq.io/downloads\n\nPor favor, reinicie la aplicación. +popup.warning.incompatibleDB=Hemos detectado archivos de base de datos incompatibles!\n\nEstos archivos de base de datos no son compatibles con nuestro actual código base:\n{0}\n\nHemos hecho una copia de seguridad de los archivos corruptos y aplicado los valores por defecto a la nueva versión de base de datos.\n\nLa copia de seguridad se localiza en:\n{1}/db/backup_of_corrupted_data.\n\nPor favor, compruebe si tiene la última versión de Bisq instalada.\nPuede descargarla en:\nhttps://bisq.network/downloads\n\nPor favor, reinicie la aplicación. popup.warning.cannotConnectAtStartup=Aún no se ha conecta a la red {0}.\nSi usa Tor para Bitcoin puede ser que tenga un circuito Tor inestable.\nPuede esperar más o probar reiniciando. popup.warning.unknownProblemAtStartup=Hay un problema desconocido al inicio.\nPor favor, reinicia y si el problema continua realice un reporte de error. popup.warning.startupFailed.timeout=La aplicación no se pudo iniciar después de 4 minutos.\n\n{0} diff --git a/common/src/main/resources/i18n/displayStrings_pt.properties b/common/src/main/resources/i18n/displayStrings_pt.properties index 854e5cef11..b7d65822f6 100644 --- a/common/src/main/resources/i18n/displayStrings_pt.properties +++ b/common/src/main/resources/i18n/displayStrings_pt.properties @@ -365,7 +365,7 @@ createOffer.useLowerValue=Sim, usar meu valor mais baixo createOffer.priceOutSideOfDeviation=O preço submetido está fora do desvio máximo com relação ao preço de mercado.\nO desvio máximo é {0} e pode ser alterado nas preferências. createOffer.changePrice=Alterar preço createOffer.tac=Ao submeter esta oferta eu concordo em negociar com qualquer negociar que satisfaça as condições definidas acima. -createOffer.currencyForFee=Maker fee +createOffer.currencyForFee=Trade fee createOffer.feeCurrencyAndDeposit=Definir moeda da taxa e depósito de segurança @@ -386,7 +386,7 @@ takeOffer.fundsBox.title=Financiar sua negociação takeOffer.fundsBox.isOfferAvailable=Verificar se oferta está disponível ... takeOffer.fundsBox.tradeAmount=Quantidade a se vender: takeOffer.fundsBox.offerFee=Taxa de aceitação: -takeOffer.fundsBox.networkFee=Taxas de mineração (3x): +takeOffer.fundsBox.networkFee=Taxas de mineração: takeOffer.fundsBox.takeOfferSpinnerInfo=Aceitação da oferta em progresso ... takeOffer.fundsBox.paymentLabel=negociação Bisq com ID {0} takeOffer.success.headline=Você aceitou uma oferta com sucesso. @@ -418,7 +418,7 @@ takeOffer.error.depositPublished=\n\nA transação do depósito já foi publicad takeOffer.error.payoutPublished=\n\nA transação de pagamento já foi publicada.\nPor gentileza reinicie o programa e verifique sua conexão de Internet para tentar resolver o problema.\nSe o problema persistir, favor entrar em contato com os desenvolvedores. takeOffer.tac=Ao aceitar essa oferta eu concordo com as condições de negociação definidas acima. -takeOffer.currencyForFee=Taker fee +takeOffer.currencyForFee=Trade fee takeOffer.feeCurrency=Definir moeda para taxa @@ -660,7 +660,7 @@ support.buyerOfferer=Comprador de BTC / Ofetante support.sellerOfferer=Vendedor de BTC / Ofertante support.buyerTaker=Comprador de BTC / Aceitador da oferta support.sellerTaker=Vendedor de BTC / Aceitador da oferta -support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.io web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. +support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.network web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. support.initialInfo=Favor note as regras básicas para o processo de disputa:\n1. Você precisa responder aos pedidos de árbitros em até 2 dias.\n2. O período máximo de uma disputa é 14 dias.\n3. Você precisa atender ao que o árbitro está pedindo e fornecer evidências necessárias para seu caso.\n4. Você aceitou as regras delineadas na wiki no acordo de usuário quando você iniciou o programa.\n\nMais detalhes sobre o processo de disputa se encontram na wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Mensagem do sistema: {0} support.youOpenedTicket=Você abriu uma solicitação para suporte. @@ -1066,7 +1066,7 @@ error.spvFileCorrupted=Um erro ocorreu ao ler o arquivo SPV chain.\nPode ser que popup.warning.walletNotInitialized=A carteira ainda não foi inicializada popup.warning.wrongVersion=You probably have the wrong Bisq version for this computer.\nYour computer''s architecture is: {0}.\nThe Bisq binary you installed is: {1}.\nPlease shut down and re-install the correct version ({2}). -popup.warning.incompatibleDB=We detected incompatible data base files!\n\nThose database file(s) are not compatible with our current code base:\n{0}\n\nWe made a backup of the corrupted file(s) and applied the default values to a new database version.\n\nThe backup is located at:\n{1}/db/backup_of_corrupted_data.\n\nPlease check if you have the latest version of Bisq installed.\nYou can download it at:\nhttps://bisq.io/downloads\n\nPlease restart the application. +popup.warning.incompatibleDB=We detected incompatible data base files!\n\nThose database file(s) are not compatible with our current code base:\n{0}\n\nWe made a backup of the corrupted file(s) and applied the default values to a new database version.\n\nThe backup is located at:\n{1}/db/backup_of_corrupted_data.\n\nPlease check if you have the latest version of Bisq installed.\nYou can download it at:\nhttps://bisq.network/downloads\n\nPlease restart the application. popup.warning.cannotConnectAtStartup=Você ainda não foi conectado à rede {0}.\nSe você utilizar Tor para acessar Bitcoin pode ser que você tem um circuito Tor instável.\nVocê pode aguardar mais ou tentar reiniciar. popup.warning.unknownProblemAtStartup=Houve um problema desconhecido ao iniciar.\nFavor reiniciar e se o problema persistir abrir um bug report (informe de problema). popup.warning.startupFailed.timeout=Falha ao iniciar aplicativo após 4 minutos.\n\n{0} diff --git a/common/src/main/resources/i18n/displayStrings_sr.properties b/common/src/main/resources/i18n/displayStrings_sr.properties index 61e3200f32..2fe8c646db 100644 --- a/common/src/main/resources/i18n/displayStrings_sr.properties +++ b/common/src/main/resources/i18n/displayStrings_sr.properties @@ -365,7 +365,7 @@ createOffer.useLowerValue=Da, koristi moju manju vrednost createOffer.priceOutSideOfDeviation=Cena koju ste uneli je van maks. dozvoljenog odstupanja od tržišne cene.\nMaks. dozvoljeno odstupanje je {0} i može se podesiti u preferencama. createOffer.changePrice=Izmeni cenu createOffer.tac=Sa postavljanjem te ponude slažem se da trgujem sa bilo kojim trgovcem koji ispunjava uslove definisane iznad. -createOffer.currencyForFee=Maker fee +createOffer.currencyForFee=Trade fee createOffer.feeCurrencyAndDeposit=Podesi valutu provizije i bezbednosni depozit @@ -386,7 +386,7 @@ takeOffer.fundsBox.title=Finansirajte vašu trgovinu takeOffer.fundsBox.isOfferAvailable=Proveri da li je ponuda dostupna ... takeOffer.fundsBox.tradeAmount=Iznos za prodaju: takeOffer.fundsBox.offerFee=Provizija prihvatanja: -takeOffer.fundsBox.networkFee=Provizija rudara (3x): +takeOffer.fundsBox.networkFee=Provizija rudara: takeOffer.fundsBox.takeOfferSpinnerInfo=Prihvatanje ponude je u toku ... takeOffer.fundsBox.paymentLabel=Bisq trgovina sa ID {0} takeOffer.success.headline=Uspešno se prihvatili ponudu. @@ -418,7 +418,7 @@ takeOffer.error.depositPublished=\n\nTransakcija depozita je već objavljena.\nM takeOffer.error.payoutPublished=\n\nTransakcija isplate je već objavljena.\nMolimo vas pokušajte ponovo pokrenite aplikaciju i proverite vašu mrežnu konekciju da vidite da li možete da rešite problem.\nAko problem i dalje postoji molimo kontaktirajte programere za podršku. takeOffer.tac=Sa postavljanjem te ponude slažem se sa uslovima trgovine definisanim iznad. -takeOffer.currencyForFee=Taker fee +takeOffer.currencyForFee=Trade fee takeOffer.feeCurrency=Podesi valutu provizije @@ -660,7 +660,7 @@ support.buyerOfferer=BTC kupac/Tvorac support.sellerOfferer=BTC prodavac/Tvorac support.buyerTaker=BTC kupac/Uzimalac support.sellerTaker=BTC prodavac/Tvorac -support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.io web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. +support.backgroundInfo=Bisq is not a company and not operating any kind of customer support.\n\nIf there are disputes in the trade process (e.g. one trader does not follow the trade protocol) the application will display a \"Open dispute\" button after the trade period is over for contacting the arbitrator.\nIn cases of software bugs or other problems, which are detected by the application there will be displayed a \"Open support ticket\" button to contact the arbitrator who will forward the issue to the developers.\n\nIn cases where a user got stuck by a bug without getting displayed that \"Open support ticket\" button, you can open a support ticket manually with a special short cut.\n\nPlease use that only if you are sure that the software is not working like expected. If you have problems how to use Bisq or any questions please review the FAQ at the bisq.network web page or post in the Bisq forum at the support section.\n\nIf you are sure that you want to open a support ticket please select the trade which causes the problem under \"Portfolio/Open trades\" and type the key combination \"alt + o\" or \"option + o\" to open the support ticket. support.initialInfo=Imajte na umu osnovna pravila za proces rasprave:\n1. Potrebno je da odgovorite zahtevima arbitra u razmaku od 2 dana.\n2. Maksimalni period rasprave je 14 dana.\n3. Morate da ispunite što arbitar zahteva od vas radi dostavljanja dokaza za vaš slučaj.\n4. Prihvatili ste pravila navedena u wiki u korisničkom ugovoru kada ste prvi put pokrenuli aplikaciju.\n\nMolimo pročitajte detaljnije o procesu rasprave u našoj wiki:\nhttps://github.com/bisq-network/exchange/wiki/Arbitration-system support.systemMsg=Poruka sistema: {0} support.youOpenedTicket=Otvorili ste zahtev za podršku. @@ -1064,7 +1064,7 @@ error.spvFileCorrupted=Došlo je do greške prilikom čitanja SPV lanac fajla.\n popup.warning.walletNotInitialized=Novčanik još nije inicijalizovan popup.warning.wrongVersion=Verovatno imate pogrešnu verziju Bisq za ovaj računar.\nArhitektura vašeg računara je: {0}.\nBisq program koji ste vi instalirali je: {1}.\nMolimo isključite i reinstalirajte tačnu verziju ({2}). -popup.warning.incompatibleDB=Otkrili smo nekompitabilne fajlove baze podataka!\n\nTaj fajl(ovi) baze podataka nisu kompitabilni sa trenutnom kod bazom:\n{0}\n\nNapravili smo rezervu iskvarenih fajlova i primenili podrazumevane vrednosti novoj verziji baze podataka.\n\nRezerva se nalazi na:\n{1}/db/backup_of_corrupted_data.\n\nMolimo proverite da li imate instaliranu najnoviju verziju Bisq.\nMožete je preuzeti na:\nhttps://bisq.io/downloads\n\nMolimo pokrenite ponovo aplikaciju. +popup.warning.incompatibleDB=Otkrili smo nekompitabilne fajlove baze podataka!\n\nTaj fajl(ovi) baze podataka nisu kompitabilni sa trenutnom kod bazom:\n{0}\n\nNapravili smo rezervu iskvarenih fajlova i primenili podrazumevane vrednosti novoj verziji baze podataka.\n\nRezerva se nalazi na:\n{1}/db/backup_of_corrupted_data.\n\nMolimo proverite da li imate instaliranu najnoviju verziju Bisq.\nMožete je preuzeti na:\nhttps://bisq.network/downloads\n\nMolimo pokrenite ponovo aplikaciju. popup.warning.cannotConnectAtStartup=Još uvek niste povezani na {0} mrežu.\nAko koristite Tor za Bitcoin moguće je da imate nestabilnu Tor rutu.\nMožete još čekati ili pokušati da ponovo pokrenete. popup.warning.unknownProblemAtStartup=Postoji nepoznat problem pri pokretanju.\nMolimo pokrenite ponovo i ako se problem nastavi podnesite prijavu o grešci. popup.warning.startupFailed.timeout=Aplikacija nije mogla da se pokrene nakon 4 minuta.\n\n{0} diff --git a/common/src/main/resources/i18n/displayStrings_zh_cn.properties b/common/src/main/resources/i18n/displayStrings_zh_cn.properties index 0bbaaa4e75..69bdab945c 100644 --- a/common/src/main/resources/i18n/displayStrings_zh_cn.properties +++ b/common/src/main/resources/i18n/displayStrings_zh_cn.properties @@ -390,7 +390,7 @@ takeOffer.fundsBox.title=为交易充值 takeOffer.fundsBox.isOfferAvailable=检查委托是否有效 ... takeOffer.fundsBox.tradeAmount=卖出数量: takeOffer.fundsBox.offerFee=买单费: -takeOffer.fundsBox.networkFee=矿工手续费 (3x): +takeOffer.fundsBox.networkFee=矿工手续费: takeOffer.fundsBox.takeOfferSpinnerInfo=正在下单 ... takeOffer.fundsBox.paymentLabel=Bisq交易ID {0} takeOffer.success.headline=你已成功下单一个委托. diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ae.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ae.properties index d7793804b9..3a987af140 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ae.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ae.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ba.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ba.properties index cbbd37e2ce..6907f49977 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ba.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ba.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_br.properties b/common/src/main/resources/i18n/in_dev/displayStrings_br.properties index f98f2aaaf5..b6c3cc3f6d 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_br.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_br.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_cn.properties b/common/src/main/resources/i18n/in_dev/displayStrings_cn.properties index 9df757333a..ce06656d57 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_cn.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_cn.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_cz.properties b/common/src/main/resources/i18n/in_dev/displayStrings_cz.properties index 59b384881f..2f9f08bb2a 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_cz.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_cz.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_dk.properties b/common/src/main/resources/i18n/in_dev/displayStrings_dk.properties index 550048569a..bb74ee19d5 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_dk.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_dk.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_en.properties b/common/src/main/resources/i18n/in_dev/displayStrings_en.properties index f215e61601..d0e2a37672 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_en.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_en.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_fi.properties b/common/src/main/resources/i18n/in_dev/displayStrings_fi.properties index 7f29547fd8..858e89e3b8 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_fi.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_fi.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties index 56ba2d2b6d..5070b90afe 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_fr.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Devise: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -122,7 +122,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: @@ -183,7 +183,7 @@ takeOffer.fundsBox.balance=Trade wallet balance: takeOffer.fundsBox.tradeAmount=Montant à vendre: takeOffer.fundsBox.securityDeposit=Security deposit: takeOffer.fundsBox.offerFee=Frais du client: -takeOffer.fundsBox.networkFee=Frais d'exploitation minière (3x): +takeOffer.fundsBox.networkFee=Frais d'exploitation minière: takeOffer.fundsBox.total=Total: # TODO remove takeOffer.fundsBox.showAdvanced=Show advanced settings # TODO remove takeOffer.fundsBox.hideAdvanced=Hide advanced settings @@ -1081,7 +1081,7 @@ popup.error.createTx=Erreur à la création de la transaction {0} error.spvFileCorrupted=Une erreur s'est produite lors de la lecture du fichier de la chaîne SPV. \nIl se pourrait être que le fichier de la chaîne SPV soit endommagé. \n\nMessage d''erreur: {0} \n\n Voulez-vous le supprimer et démarrer une resynchronisation? popup.warning.walletNotInitialized=Le portefeuille n'est pas encore initialisé popup.warning.wrongVersion=Vous avez probablement la mauvaise version Bisq pour cet ordinateur. \nL'architecture de votre ordinateur est: {0}. \nLa binaire Bisq que vous avez installée est: {1}. \nFermez et réinstallez la version correcte ({2}). -popup.warning.incompatibleDB=Nous avons détecté des fichiers de base de données incompatibles! \n \nCes fichiers de base de données ne sont pas compatibles avec notre base de code actuelle: \n {0} \n \nNous avons sauvegardé les fichiers corrompus et appliqué les valeurs par défaut à une nouvelle version de base de données. \n \nLa sauvegarde se trouve à: \n{1} /db/backup_of_corrupted_data.\n\nVérifiez si vous avez installé la dernière version de Bisq. \nVous pouvez le télécharger à: \n Https://bisq.io/downloads \n\nRedémarrez l'application. +popup.warning.incompatibleDB=Nous avons détecté des fichiers de base de données incompatibles! \n \nCes fichiers de base de données ne sont pas compatibles avec notre base de code actuelle: \n {0} \n \nNous avons sauvegardé les fichiers corrompus et appliqué les valeurs par défaut à une nouvelle version de base de données. \n \nLa sauvegarde se trouve à: \n{1} /db/backup_of_corrupted_data.\n\nVérifiez si vous avez installé la dernière version de Bisq. \nVous pouvez le télécharger à: \n Https://bisq.network/downloads \n\nRedémarrez l'application. popup.warning.cannotConnectAtStartup=Vous ne vous êtes toujours pas connecté au {0} réseau.\nSi vous utilisez Tor pour Bitcoin, il se pourrait que vous ayez un circuit Tor instable. \nVous pouvez attendre plus longtemps ou essayer de redémarrer. popup.warning.unknownProblemAtStartup=Il y a un problème inconnu lors du démarrage.\nVeuillez redémarrer et si le problème continue de déposer un rapport de bogue. popup.warning.startupFailed.timeout=L'application n'a pas pu démarrer après 4 minutes. \n \n {0}\ diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ge.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ge.properties index b878bf1916..583a3c05c7 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ge.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ge.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_gr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_gr.properties index b229d6d512..27705ca26f 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_gr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_gr.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_hr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_hr.properties index fbdf5c157e..20e3f130ec 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_hr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_hr.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -118,7 +118,7 @@ createOffer.fundsBox.totalsNeeded=Potrebna sredstva: createOffer.fundsBox.totalsNeeded.prompt=Biti će izračunata iz iznosa bitcoina koje ste unijeli iznad createOffer.fundsBox.address=Adresa novčanika za razmjenu: createOffer.fundsBox.balance=Stanje novčanika za razmjenu: -# TODO remove createOffer.fundsBox.info=Svaka ponuda koristi zasebni novčanik za razmjenu. Morate napuniti taj novčanik potrebnim iznosom bitcoina. Ta su sredstva rezervirana te će se koristiti u slučaju da ponuda bude prihvaćena. U slučaju da otkažete svoju ponudu možete povući svoja sredstva iz tog novčanika. Jedina uplata koja će biti izvršena prilikom postavljanja ponude je sama naknada za postavljanje ponude. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=Svaka ponuda koristi zasebni novčanik za razmjenu. Morate napuniti taj novčanik potrebnim iznosom bitcoina. Ta su sredstva rezervirana te će se koristiti u slučaju da ponuda bude prihvaćena. U slučaju da otkažete svoju ponudu možete povući svoja sredstva iz tog novčanika. Jedina uplata koja će biti izvršena prilikom postavljanja ponude je sama naknada za postavljanje ponude. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Iznos razmjene: createOffer.fundsBox.securityDeposit=Sigurnosni depozit: createOffer.fundsBox.offerFee=Naknada za izradu ponude: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_hu.properties b/common/src/main/resources/i18n/in_dev/displayStrings_hu.properties index dabc26a68c..07d498a2d6 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_hu.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_hu.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_id.properties b/common/src/main/resources/i18n/in_dev/displayStrings_id.properties index c7079434a0..e331c4ba17 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_id.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_id.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ie.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ie.properties index b7418235ca..d3d6c568f1 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ie.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ie.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_in.properties b/common/src/main/resources/i18n/in_dev/displayStrings_in.properties index f3d20aa775..557e1567b6 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_in.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_in.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_is.properties b/common/src/main/resources/i18n/in_dev/displayStrings_is.properties index 996773a5ac..2ac3df8883 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_is.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_is.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_it.properties b/common/src/main/resources/i18n/in_dev/displayStrings_it.properties index 2ebe372b2b..44a13d5e74 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_it.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_it.properties @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Fondi richiesti: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Indirizzo portafoglio per lo scambio: createOffer.fundsBox.balance=Saldo portafoglio per lo scambio: -# TODO remove createOffer.fundsBox.info=Per ogni offerta c'è un portamonete di scambio dedicato. Devi finanziare il portamonete di scambio con la quantità di bitcoin necessaria. Questi fondi sono riservati e saranno utilizzati nel caso in cui la tua offerta venga eseguita. Se cancelli la tua offerta puoi ritirare i fondi da quel portamonete di scambio. L'unico pagamento eseguito quando disponi un'offerta è la commissione di pagamento per offerta. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=Per ogni offerta c'è un portamonete di scambio dedicato. Devi finanziare il portamonete di scambio con la quantità di bitcoin necessaria. Questi fondi sono riservati e saranno utilizzati nel caso in cui la tua offerta venga eseguita. Se cancelli la tua offerta puoi ritirare i fondi da quel portamonete di scambio. L'unico pagamento eseguito quando disponi un'offerta è la commissione di pagamento per offerta. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Importo per lo scambio: createOffer.fundsBox.securityDeposit=Deposito di sicurezza: createOffer.fundsBox.offerFee=Crea commissione di offerta: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_jp.properties b/common/src/main/resources/i18n/in_dev/displayStrings_jp.properties index e5aeee2285..e8be8f0fe3 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_jp.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_jp.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_kg.properties b/common/src/main/resources/i18n/in_dev/displayStrings_kg.properties index 733411dca1..76747de6c5 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_kg.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_kg.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_kr.properties b/common/src/main/resources/i18n/in_dev/displayStrings_kr.properties index 547ac03e8d..ff50c03b62 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_kr.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_kr.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_kz.properties b/common/src/main/resources/i18n/in_dev/displayStrings_kz.properties index f12552235a..1f11f27a13 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_kz.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_kz.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_lt.properties b/common/src/main/resources/i18n/in_dev/displayStrings_lt.properties index 210136d579..9ed3f47343 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_lt.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_lt.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_my.properties b/common/src/main/resources/i18n/in_dev/displayStrings_my.properties index b2fe231747..49c5452d4a 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_my.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_my.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_nl.properties b/common/src/main/resources/i18n/in_dev/displayStrings_nl.properties index 5e7c36b3ed..50d63e68ad 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_nl.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_nl.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_no.properties b/common/src/main/resources/i18n/in_dev/displayStrings_no.properties index 84345d1aa4..af409fa8b4 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_no.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_no.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_nz.properties b/common/src/main/resources/i18n/in_dev/displayStrings_nz.properties index da4aca54ef..c0bd7dd45c 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_nz.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_nz.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ph.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ph.properties index 4d8484d75e..adc1823257 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ph.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ph.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_pl.properties b/common/src/main/resources/i18n/in_dev/displayStrings_pl.properties index bc269247d8..f3cfc455c2 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_pl.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_pl.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_pt.properties b/common/src/main/resources/i18n/in_dev/displayStrings_pt.properties index 946ca01fb4..552db85056 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_pt.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_pt.properties @@ -67,7 +67,7 @@ createOffer.fundsBox.totalsNeeded=Fundos necessários: createOffer.fundsBox.totalsNeeded.prompt=Será calculado pelos montantes de bitcoin introduzidos acima createOffer.fundsBox.address=Endereço da carteira da troca: createOffer.fundsBox.balance=Saldo da carteira da troca: -# TODO remove createOffer.fundsBox.info=Para cada oferta há uma carteira de troca dedicada. Precisa de provisionar essa carteira da troca com o montante de bitcoin necessário. Esses fundos serão reservados e usados no caso de a oferta ser executada. Se cancelar a sua oferta pode levantar os seus fundos dessa carteira de troca. O único pagamento feito ao criar uma oferta é o pagamento da taxa da oferta. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=Para cada oferta há uma carteira de troca dedicada. Precisa de provisionar essa carteira da troca com o montante de bitcoin necessário. Esses fundos serão reservados e usados no caso de a oferta ser executada. Se cancelar a sua oferta pode levantar os seus fundos dessa carteira de troca. O único pagamento feito ao criar uma oferta é o pagamento da taxa da oferta. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Montante da troca: createOffer.fundsBox.securityDeposit=Depósito de segurança: createOffer.fundsBox.offerFee=Taxa de criação de oferta: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ro.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ro.properties index 7b89fc5a5b..c856220965 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ro.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ro.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ru.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ru.properties index 2ad325678b..b066a2798c 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ru.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ru.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_se.properties b/common/src/main/resources/i18n/in_dev/displayStrings_se.properties index 1a52b73f87..3c77dbf0f5 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_se.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_se.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_th.properties b/common/src/main/resources/i18n/in_dev/displayStrings_th.properties index 34411126f8..3c78bff2dd 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_th.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_th.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_ua.properties b/common/src/main/resources/i18n/in_dev/displayStrings_ua.properties index 07aaa38655..d20b224d22 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_ua.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_ua.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_uz.properties b/common/src/main/resources/i18n/in_dev/displayStrings_uz.properties index 8e28e47877..41fe1790e2 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_uz.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_uz.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/common/src/main/resources/i18n/in_dev/displayStrings_vn.properties b/common/src/main/resources/i18n/in_dev/displayStrings_vn.properties index 34ac52bb23..384346c883 100644 --- a/common/src/main/resources/i18n/in_dev/displayStrings_vn.properties +++ b/common/src/main/resources/i18n/in_dev/displayStrings_vn.properties @@ -30,9 +30,9 @@ payment.account.no=Account no.: shared.currency=Currency: payment.account.name=Account name: # TODO remove payment.payment.method=Payment method: -payment.account.owner=Account holder name: +payment.account.owner=Account owner full name: payment.bank.country=Country of bank: -payment.account.name.email=Account holder name / email +payment.account.name.email=Account owner full name / email payment.bank.name=Bank name: payment.select.account=Select account type list.currency.select=Select currency @@ -120,7 +120,7 @@ createOffer.fundsBox.totalsNeeded=Funds needed: createOffer.fundsBox.totalsNeeded.prompt=Will be calculated from the bitcoin amount entered above createOffer.fundsBox.address=Trade wallet address: createOffer.fundsBox.balance=Trade wallet balance: -# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.io/faq/#6 +# TODO remove createOffer.fundsBox.info=For every offer there is a dedicated trade wallet. You need to fund that trade wallet with the necessary bitcoin amount. Those funds are reserved and will be used in the case that your offer gets executed. If you cancel your offer you can withdraw your funds from that trading wallet. The only payment made when placing an offer is the offer fee payment. https://bisq.network/faq/#6 createOffer.fundsBox.tradeAmount=Trade amount: createOffer.fundsBox.securityDeposit=Security deposit: createOffer.fundsBox.offerFee=Create offer fee: diff --git a/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java b/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java index d983f92dad..ea25061bd7 100644 --- a/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java +++ b/core/src/main/java/io/bisq/core/app/AppSetupWithP2P.java @@ -68,10 +68,17 @@ public class AppSetupWithP2P extends AppSetup { @Override protected void initBasicServices() { - p2pNetWorkReady = initP2PNetwork(); - - p2pNetWorkReady.addListener((observable, oldValue, newValue) -> { + BooleanProperty result = SetupUtils.loadEntryMap(p2PService); + result.addListener((observable, oldValue, newValue) -> { if (newValue) + startInitP2PNetwork(); + }); + } + + private void startInitP2PNetwork() { + p2pNetWorkReady = initP2PNetwork(); + p2pNetWorkReady.addListener((observable, oldValue, newValue) -> { + if (newValue) onBasicServicesInitialized(); }); } @@ -149,10 +156,9 @@ public class AppSetupWithP2P extends AppSetup { protected void onBasicServicesInitialized() { log.info("onBasicServicesInitialized"); - // Used to load different EntryMap files per base currency (EntryMap_BTC, EntryMap_LTC,...) - final String storageFileName = "EntryMap_" + BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode(); - p2PService.readEntryMapFromResources(storageFileName); - + p2PService.onAllServicesInitialized(); + + tradeStatisticsManager.onAllServicesInitialized(); } } diff --git a/core/src/main/java/io/bisq/core/app/BisqEnvironment.java b/core/src/main/java/io/bisq/core/app/BisqEnvironment.java index adbe7ca354..3539d333c7 100644 --- a/core/src/main/java/io/bisq/core/app/BisqEnvironment.java +++ b/core/src/main/java/io/bisq/core/app/BisqEnvironment.java @@ -19,7 +19,6 @@ package io.bisq.core.app; import ch.qos.logback.classic.Level; import io.bisq.common.CommonOptionKeys; -import io.bisq.common.app.DevEnv; import io.bisq.common.app.Version; import io.bisq.common.crypto.KeyStorage; import io.bisq.common.storage.Storage; @@ -84,7 +83,12 @@ public class BisqEnvironment extends StandardEnvironment { public static boolean isDAOActivatedAndBaseCurrencySupportingBsq() { //noinspection ConstantConditions,PointlessBooleanExpression - return DevEnv.DAO_ACTIVATED && isBaseCurrencySupportingBsq(); + return isDAOEnabled() && isBaseCurrencySupportingBsq(); + } + + public static boolean isDAOEnabled() { + //noinspection ConstantConditions,PointlessBooleanExpression + return !getBaseCurrencyNetwork().isMainnet() && isBaseCurrencySupportingBsq(); } public static boolean isBaseCurrencySupportingBsq() { diff --git a/core/src/main/java/io/bisq/core/app/SetupUtils.java b/core/src/main/java/io/bisq/core/app/SetupUtils.java index b57f98d7a5..1395521aa9 100644 --- a/core/src/main/java/io/bisq/core/app/SetupUtils.java +++ b/core/src/main/java/io/bisq/core/app/SetupUtils.java @@ -22,9 +22,13 @@ import io.bisq.common.crypto.CryptoException; import io.bisq.common.crypto.KeyRing; import io.bisq.common.crypto.SealedAndSigned; import io.bisq.common.handlers.ResultHandler; +import io.bisq.core.btc.BaseCurrencyNetwork; import io.bisq.network.crypto.DecryptedDataTuple; import io.bisq.network.crypto.EncryptionService; +import io.bisq.network.p2p.P2PService; import io.bisq.network.p2p.peers.keepalive.messages.Ping; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; import lombok.extern.slf4j.Slf4j; import java.security.Security; @@ -74,4 +78,23 @@ public class SetupUtils { }; checkCryptoThread.start(); } + + public static BooleanProperty loadEntryMap(P2PService p2PService) { + BooleanProperty result = new SimpleBooleanProperty(); + Thread loadEntryMapThread = new Thread() { + @Override + public void run() { + Thread.currentThread().setName("loadEntryMapThread"); + // Used to load different EntryMap files per base currency (EntryMap_BTC_MAINNET, EntryMap_LTC,...) + final BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); + final String storageFileName = "EntryMap_" + + baseCurrencyNetwork.getCurrencyCode() + "_" + + baseCurrencyNetwork.getNetwork(); + p2PService.readEntryMapFromResources(storageFileName); + UserThread.execute(() -> result.set(true)); + } + }; + loadEntryMapThread.start(); + return result; + } } diff --git a/core/src/main/java/io/bisq/core/btc/BaseCurrencyNetwork.java b/core/src/main/java/io/bisq/core/btc/BaseCurrencyNetwork.java index fc61a380bd..d56a432bf8 100644 --- a/core/src/main/java/io/bisq/core/btc/BaseCurrencyNetwork.java +++ b/core/src/main/java/io/bisq/core/btc/BaseCurrencyNetwork.java @@ -76,6 +76,15 @@ public enum BaseCurrencyNetwork { return "LTC".equals(currencyCode); } + public boolean isDash() { + return "DASH".equals(currencyCode); + } + + public boolean isDoge() { + return "DOGE".equals(currencyCode); + } + + public Coin getDefaultMinFee() { switch (BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode()) { case "BTC": diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/io/bisq/core/btc/wallet/BtcWalletService.java index 1d65bf9eee..4e55114ccd 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BtcWalletService.java @@ -410,12 +410,12 @@ public class BtcWalletService extends WalletService { if (!Arrays.equals(pubKey, multiSigAddressEntry.getPubKey())) { log.error("Pub Key from AddressEntry does not match key pair from trade data. Trade ID={}\n" + "We try to find the keypair in the wallet with the pubKey we found in the trade data.", tradeId); - multiSigKeyPair = findKeyFromPubKeyHash(pubKey); + multiSigKeyPair = findKeyFromPubKey(pubKey); } } else { log.error("multiSigAddressEntry not found for trade ID={}.\n" + "We try to find the keypair in the wallet with the pubKey we found in the trade data.", tradeId); - multiSigKeyPair = findKeyFromPubKeyHash(pubKey); + multiSigKeyPair = findKeyFromPubKey(pubKey); } return multiSigKeyPair; diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletConfig.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletConfig.java index b21f80800c..15d321d6a4 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletConfig.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletConfig.java @@ -101,8 +101,6 @@ public class WalletConfig extends AbstractIdleService { private InputStream checkpoints; private boolean blockingStartup = true; - private String userAgent; - private String version; @Nullable private PeerDiscovery discovery; @@ -265,18 +263,6 @@ public class WalletConfig extends AbstractIdleService { return this; } - /** - * Sets the string that will appear in the subver field of the version message. - * - * @param userAgent A short string that should be the name of your app, e.g. "My Wallet" - * @param version A short string that contains the version number, e.g. "1.0-BETA" - */ - public WalletConfig setUserAgent(String userAgent, String version) { - this.userAgent = checkNotNull(userAgent); - this.version = checkNotNull(version); - return this; - } - /** * If a seed is set here then any existing wallet that matches the file name will be renamed to a backup name, * the chain file will be deleted, and the wallet object will be instantiated with the given seed instead of @@ -440,7 +426,9 @@ public class WalletConfig extends AbstractIdleService { vPeerGroup.addWallet(vBtcWallet); if (vBsqWallet != null) { + //noinspection ConstantConditions vChain.addWallet(vBsqWallet); + //noinspection ConstantConditions vPeerGroup.addWallet(vBsqWallet); } @@ -560,6 +548,7 @@ public class WalletConfig extends AbstractIdleService { vPeerGroup.stop(); vBtcWallet.saveToFile(vBtcWalletFile); if (vBsqWallet != null && vBsqWalletFile != null) + //noinspection ConstantConditions,ConstantConditions vBsqWallet.saveToFile(vBsqWalletFile); vStore.close(); diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletService.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletService.java index bf9478fdaf..3d63e86961 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletService.java @@ -517,6 +517,10 @@ public abstract class WalletService { public DeterministicKey findKeyFromPubKeyHash(byte[] pubKeyHash) { return wallet.getActiveKeychain().findKeyFromPubHash(pubKeyHash); } + + public DeterministicKey findKeyFromPubKey(byte[] pubKey) { + return wallet.getActiveKeychain().findKeyFromPubKey(pubKey); + } public Address freshReceiveAddress() { return wallet.freshReceiveAddress(); diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java index 103f42a625..dae0895428 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java @@ -29,7 +29,10 @@ import io.bisq.common.handlers.ExceptionHandler; import io.bisq.common.handlers.ResultHandler; import io.bisq.common.storage.FileUtil; import io.bisq.core.app.BisqEnvironment; -import io.bisq.core.btc.*; +import io.bisq.core.btc.AddressEntry; +import io.bisq.core.btc.AddressEntryList; +import io.bisq.core.btc.BtcOptionKeys; +import io.bisq.core.btc.RegTestHost; import io.bisq.core.user.Preferences; import io.bisq.network.DnsLookupTor; import io.bisq.network.Socks5MultiDiscovery; @@ -74,7 +77,6 @@ public class WalletsSetup { private final RegTestHost regTestHost; private final AddressEntryList addressEntryList; - private final UserAgent userAgent; private final Preferences preferences; private final Socks5ProxyProvider socks5ProxyProvider; private final BisqEnvironment bisqEnvironment; @@ -96,7 +98,6 @@ public class WalletsSetup { @Inject public WalletsSetup(RegTestHost regTestHost, AddressEntryList addressEntryList, - UserAgent userAgent, Preferences preferences, Socks5ProxyProvider socks5ProxyProvider, BisqEnvironment bisqEnvironment, @@ -104,7 +105,6 @@ public class WalletsSetup { @Named(BtcOptionKeys.SOCKS5_DISCOVER_MODE) String socks5DiscoverModeString) { this.regTestHost = regTestHost; this.addressEntryList = addressEntryList; - this.userAgent = userAgent; this.preferences = preferences; this.socks5ProxyProvider = socks5ProxyProvider; this.bisqEnvironment = bisqEnvironment; @@ -179,8 +179,7 @@ public class WalletsSetup { configPeerNodes(socks5Proxy); walletConfig.setDownloadListener(downloadListener) - .setBlockingStartup(false) - .setUserAgent(userAgent.getName(), userAgent.getVersion()); + .setBlockingStartup(false); // If seed is non-null it means we are restoring from backup. walletConfig.setSeed(seed); diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/BsqFullNode.java b/core/src/main/java/io/bisq/core/dao/blockchain/BsqFullNode.java index d98e5972ad..dffa28414a 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/BsqFullNode.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/BsqFullNode.java @@ -89,10 +89,11 @@ public class BsqFullNode extends BsqNode { @Override protected void parseBlocksWithChainHeadHeight(int startBlockHeight, int genesisBlockHeight, String genesisTxId) { log.info("parseBlocksWithChainHeadHeight startBlockHeight={}", startBlockHeight); - bsqFullNodeExecutor.requestChainHeadHeight(chainHeadHeight -> parseBlocks(startBlockHeight, genesisBlockHeight, genesisTxId, chainHeadHeight), throwable -> { - log.error(throwable.toString()); - throwable.printStackTrace(); - }); + bsqFullNodeExecutor.requestChainHeadHeight(chainHeadHeight -> parseBlocks(startBlockHeight, genesisBlockHeight, genesisTxId, chainHeadHeight), + throwable -> { + log.error(throwable.toString()); + throwable.printStackTrace(); + }); } @Override diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/BsqLiteNode.java b/core/src/main/java/io/bisq/core/dao/blockchain/BsqLiteNode.java index f4df369f87..da17081fc6 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/BsqLiteNode.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/BsqLiteNode.java @@ -85,7 +85,7 @@ public class BsqLiteNode extends BsqNode { @Override public void onBlockReceived(GetBsqBlocksResponse getBsqBlocksResponse) { List bsqBlockList = new ArrayList<>(getBsqBlocksResponse.getBsqBlocks()); - log.info("received msg with {} items", bsqBlockList.size(), bsqBlockList.get(bsqBlockList.size() - 1).getHeight()); + log.info("received msg with {} items", bsqBlockList.size()); if (bsqBlockList.size() > 0) log.info("block height of last item: {}", bsqBlockList.get(bsqBlockList.size() - 1).getHeight()); // Be safe and reset all mutable data in case the provider would not have done it diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/p2p/RequestManager.java b/core/src/main/java/io/bisq/core/dao/blockchain/p2p/RequestManager.java index fa5199d966..9ea6eee085 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/p2p/RequestManager.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/p2p/RequestManager.java @@ -174,7 +174,7 @@ public class RequestManager implements MessageListener, ConnectionListener, Peer @Override public void onAwakeFromStandby() { - Log.traceCall(); + log.info("onAwakeFromStandby"); closeAllHandlers(); stopped = false; if (!networkNode.getAllConnections().isEmpty()) @@ -271,7 +271,7 @@ public class RequestManager implements MessageListener, ConnectionListener, Peer @Override public void onFault(String errorMessage, @Nullable Connection connection) { - log.trace("requestBlocksHandler with outbound connection failed.\n\tnodeAddress={}\n\t" + + log.warn("requestBlocksHandler with outbound connection failed.\n\tnodeAddress={}\n\t" + "ErrorMessage={}", peersNodeAddress, errorMessage); peerManager.handleConnectionFault(peersNodeAddress); @@ -327,9 +327,11 @@ public class RequestManager implements MessageListener, ConnectionListener, Peer List list = seedNodeAddresses.stream() .filter(e -> peerManager.isSeedNode(e) && !peerManager.isSelf(e)) .collect(Collectors.toList()); + Collections.shuffle(list); if (!list.isEmpty()) { NodeAddress nextCandidate = list.get(0); + seedNodeAddresses.remove(nextCandidate); log.info("We try requestBlocks with {}", nextCandidate); requestBlocks(nextCandidate, startBlockHeight); } else { diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java index 1e531e67c2..8149e58da3 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java @@ -29,6 +29,7 @@ import io.bisq.core.dao.blockchain.exceptions.BlockNotConnectingException; import io.bisq.core.dao.blockchain.vo.*; import io.bisq.generated.protobuffer.PB; import lombok.extern.slf4j.Slf4j; +import org.bitcoinj.core.Coin; import javax.annotation.Nullable; import javax.inject.Inject; @@ -61,50 +62,27 @@ public class BsqChainState implements PersistableEnvelope { private static final int SNAPSHOT_GRID = 100; // set high to deactivate private static final int ISSUANCE_MATURITY = 144 * 30; // 30 days + public static final Coin GENESIS_TOTAL_SUPPLY = Coin.COIN.multiply(25); //mainnet // this tx has a lot of outputs // https://blockchain.info/de/tx/ee921650ab3f978881b8fe291e0c025e0da2b7dc684003d7a03d9649dfee2e15 // BLOCK_HEIGHT 411779 // 411812 has 693 recursions + // block 376078 has 2843 recursions and caused once a StackOverflowError, a second run worked. Took 1,2 sec. // BTC MAIN NET - // private static final String BTC_GENESIS_TX_ID = "b26371e2145f52c94b3d30713a9e38305bfc665fc27cd554e794b5e369d99ef5"; - //private static final int BTC_GENESIS_BLOCK_HEIGHT = 461718; // 2017-04-13 - private static final String BTC_GENESIS_TX_ID = "4371a1579bccc672231178cc5fe9fbb9366774d3bcbf21545a82f637f4b61a06"; - private static final int BTC_GENESIS_BLOCK_HEIGHT = 473000; // 2017-06-26 + private static final String BTC_GENESIS_TX_ID = "e5c8313c4144d219b5f6b2dacf1d36f2d43a9039bb2fcd1bd57f8352a9c9809a"; + private static final int BTC_GENESIS_BLOCK_HEIGHT = 477865; // 2017-07-28 - // LTC MAIN NET - private static final String LTC_GENESIS_TX_ID = "44074e68c1168d67871b3e9af0e65d6d7c820b03ba15445df2c4089729985fb6"; - private static final int LTC_GENESIS_BLOCK_HEIGHT = 1220170; // 2017-06-11 - // 1186935 - - //1220127 - // block 300000 2014-05-10 - // block 350000 2015-03-30 - // block 400000 2016-02-25 - // block 450000 2017-01-25 - - // REG TEST - private static final String BTC_REG_TEST_GENESIS_TX_ID = "da216721fb915da499fe0400d08362f44b672096f37c74501c2f9bcaa7760656"; - private static final int BTC_REG_TEST_GENESIS_BLOCK_HEIGHT = 363; - - // LTC REG TEST - private static final String LTC_REG_TEST_GENESIS_TX_ID = "3551aa22fbf2e237df3d96d94f286aecc4f3109a7dcd873c5c51e30a6398172c"; - private static final int LTC_REG_TEST_GENESIS_BLOCK_HEIGHT = 105; - - - // TEST NET // TEST NET // Phase 0 initial genesis tx 6.10.2017: 2f194230e23459a9211322c4b1c182cf3f367086e8059aca2f8f44e20dac527a private static final String BTC_TEST_NET_GENESIS_TX_ID = "2f194230e23459a9211322c4b1c182cf3f367086e8059aca2f8f44e20dac527a"; private static final int BTC_TEST_NET_GENESIS_BLOCK_HEIGHT = 1209140; - private static final String LTC_TEST_NET_GENESIS_TX_ID = "not set"; - private static final int LTC_TEST_NET_GENESIS_BLOCK_HEIGHT = 1; - - - // block 376078 has 2843 recursions and caused once a StackOverflowError, a second run worked. Took 1,2 sec. + // REG TEST + private static final String BTC_REG_TEST_GENESIS_TX_ID = "321a2156d6cac631d3e574caf54a5a401e51971280c14b18b5f5877026a94d47"; + private static final int BTC_REG_TEST_GENESIS_BLOCK_HEIGHT = 111; /////////////////////////////////////////////////////////////////////////////////////////// @@ -118,6 +96,7 @@ public class BsqChainState implements PersistableEnvelope { private final String genesisTxId; private final int genesisBlockHeight; private int chainHeadHeight = 0; + @Nullable private Tx genesisTx; // not impl in PB yet @@ -150,10 +129,6 @@ public class BsqChainState implements PersistableEnvelope { storage = new Storage<>(storageDir, persistenceProtoResolver); switch (BisqEnvironment.getBaseCurrencyNetwork()) { - case BTC_MAINNET: - genesisTxId = BTC_GENESIS_TX_ID; - genesisBlockHeight = BTC_GENESIS_BLOCK_HEIGHT; - break; case BTC_TESTNET: genesisTxId = BTC_TEST_NET_GENESIS_TX_ID; genesisBlockHeight = BTC_TEST_NET_GENESIS_BLOCK_HEIGHT; @@ -162,19 +137,10 @@ public class BsqChainState implements PersistableEnvelope { genesisTxId = BTC_REG_TEST_GENESIS_TX_ID; genesisBlockHeight = BTC_REG_TEST_GENESIS_BLOCK_HEIGHT; break; - - case LTC_TESTNET: - genesisTxId = LTC_TEST_NET_GENESIS_TX_ID; - genesisBlockHeight = LTC_TEST_NET_GENESIS_BLOCK_HEIGHT; - break; - case LTC_REGTEST: - genesisTxId = LTC_REG_TEST_GENESIS_TX_ID; - genesisBlockHeight = LTC_REG_TEST_GENESIS_BLOCK_HEIGHT; - break; - case LTC_MAINNET: + case BTC_MAINNET: default: - genesisTxId = LTC_GENESIS_TX_ID; - genesisBlockHeight = LTC_GENESIS_BLOCK_HEIGHT; + genesisTxId = BTC_GENESIS_TX_ID; + genesisBlockHeight = BTC_GENESIS_BLOCK_HEIGHT; break; } @@ -191,7 +157,7 @@ public class BsqChainState implements PersistableEnvelope { String genesisTxId, int genesisBlockHeight, int chainHeadHeight, - Tx genesisTx) { + @Nullable Tx genesisTx) { this.bsqBlocks = bsqBlocks; this.txMap = txMap; this.unspentTxOutputsMap = unspentTxOutputsMap; @@ -213,7 +179,7 @@ public class BsqChainState implements PersistableEnvelope { } private PB.BsqChainState.Builder getBsqChainStateBuilder() { - return PB.BsqChainState.newBuilder() + final PB.BsqChainState.Builder builder = PB.BsqChainState.newBuilder() .addAllBsqBlocks(bsqBlocks.stream() .map(BsqBlock::toProtoMessage) .collect(Collectors.toList())) @@ -225,8 +191,11 @@ public class BsqChainState implements PersistableEnvelope { v -> v.getValue().toProtoMessage()))) .setGenesisTxId(genesisTxId) .setGenesisBlockHeight(genesisBlockHeight) - .setChainHeadHeight(chainHeadHeight) - .setGenesisTx(genesisTx.toProtoMessage()); + .setChainHeadHeight(chainHeadHeight); + + Optional.ofNullable(genesisTx).ifPresent(e -> builder.setGenesisTx(genesisTx.toProtoMessage())); + + return builder; } public static PersistableEnvelope fromProto(PB.BsqChainState proto) { @@ -240,8 +209,7 @@ public class BsqChainState implements PersistableEnvelope { proto.getGenesisTxId(), proto.getGenesisBlockHeight(), proto.getChainHeadHeight(), - Tx.fromProto(proto.getGenesisTx()) - ); + proto.hasGenesisTx() ? Tx.fromProto(proto.getGenesisTx()) : null); } @@ -404,6 +372,29 @@ public class BsqChainState implements PersistableEnvelope { }); } + public Coin getTotalBurntFee() { + return lock.read(() -> Coin.valueOf(getTxMap().entrySet().stream().mapToLong(e -> e.getValue().getBurntFee()).sum())); + } + + public Set getFeeTransactions() { + return lock.read(() -> getTxMap().entrySet().stream().filter(e -> e.getValue().getBurntFee() > 0).map(Map.Entry::getValue).collect(Collectors.toSet())); + } + + public Coin getIssuedAmount() { + return lock.read(() -> BsqChainState.GENESIS_TOTAL_SUPPLY); + } + + public Set getUnspentTxOutputs() { + return lock.read(() -> getAllTxOutputs().stream().filter(e -> e.isVerified() && e.isUnspent()).collect(Collectors.toSet())); + } + + public Set getSpentTxOutputs() { + return lock.read(() -> getAllTxOutputs().stream().filter(e -> e.isVerified() && !e.isUnspent()).collect(Collectors.toSet())); + } + + public Set getTransactions() { + return lock.read(() -> getTxMap().entrySet().stream().map(Map.Entry::getValue).collect(Collectors.toSet())); + } /////////////////////////////////////////////////////////////////////////////////////////// // Package scope read access diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqParser.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqParser.java index 0fd0585a81..c17f524137 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqParser.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqParser.java @@ -108,10 +108,9 @@ public class BsqParser { List bsqTxsInBlock = findBsqTxsInBlock(btcdBlock, genesisBlockHeight, genesisTxId); - final BsqBlockVo bsqBlockVo = new BsqBlockVo(btcdBlock.getHeight(), + final BsqBlock bsqBlock = new BsqBlock(btcdBlock.getHeight(), btcdBlock.getHash(), - btcdBlock.getPreviousBlockHash()); - final BsqBlock bsqBlock = new BsqBlock(bsqBlockVo, + btcdBlock.getPreviousBlockHash(), ImmutableList.copyOf(bsqTxsInBlock)); bsqChainState.addBlock(bsqBlock); @@ -173,10 +172,9 @@ public class BsqParser { List bsqTxsInBlock = findBsqTxsInBlock(btcdBlock, genesisBlockHeight, genesisTxId); - final BsqBlockVo bsqBlockVo = new BsqBlockVo(btcdBlock.getHeight(), + final BsqBlock bsqBlock = new BsqBlock(btcdBlock.getHeight(), btcdBlock.getHash(), - btcdBlock.getPreviousBlockHash()); - final BsqBlock bsqBlock = new BsqBlock(bsqBlockVo, + btcdBlock.getPreviousBlockHash(), ImmutableList.copyOf(bsqTxsInBlock)); bsqChainState.addBlock(bsqBlock); return bsqBlock; @@ -252,7 +250,7 @@ public class BsqParser { // we check if we have any valid BSQ from that tx set bsqTxsInBlock.addAll(txsWithoutInputsFromSameBlock.stream() - .filter(tx -> isValidBsqTx(blockHeight, tx)) + .filter(tx -> isBsqTx(blockHeight, tx)) .collect(Collectors.toList())); log.debug("Parsing of all txsWithoutInputsFromSameBlock is done."); @@ -277,7 +275,7 @@ public class BsqParser { } } - private boolean isValidBsqTx(int blockHeight, Tx tx) { + private boolean isBsqTx(int blockHeight, Tx tx) { boolean isBsqTx = false; long availableValue = 0; for (int inputIndex = 0; inputIndex < tx.getInputs().size(); inputIndex++) { diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java index 1b40433f5b..2130576141 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/RpcService.java @@ -33,7 +33,10 @@ import com.neemre.btcdcli4j.daemon.event.BlockListener; import io.bisq.core.dao.DaoOptionKeys; import io.bisq.core.dao.blockchain.btcd.PubKeyScript; import io.bisq.core.dao.blockchain.exceptions.BsqBlockchainException; -import io.bisq.core.dao.blockchain.vo.*; +import io.bisq.core.dao.blockchain.vo.Tx; +import io.bisq.core.dao.blockchain.vo.TxInput; +import io.bisq.core.dao.blockchain.vo.TxOutput; +import io.bisq.core.dao.blockchain.vo.TxVo; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; @@ -96,6 +99,9 @@ public class RpcService { nodeConfig.setProperty("node.bitcoind.rpc.password", rpcPassword); nodeConfig.setProperty("node.bitcoind.rpc.port", rpcPort); nodeConfig.setProperty("node.bitcoind.notification.block.port", rpcBlockPort); + nodeConfig.setProperty("node.bitcoind.notification.alert.port", "64647"); + nodeConfig.setProperty("node.bitcoind.notification.wallet.port", "64648"); + nodeConfig.setProperty("node.bitcoind.http.auth_scheme", "Basic"); BtcdClientImpl client = new BtcdClientImpl(httpProvider, nodeConfig); daemon = new BtcdDaemonImpl(client); log.info("Setup took {} ms", System.currentTimeMillis() - startTs); @@ -120,9 +126,13 @@ public class RpcService { public void blockDetected(Block block) { if (block != null) { log.info("New block received: height={}, id={}", block.getHeight(), block.getHash()); - blockHandler.accept(block); + if (block.getHeight() != null && block.getHash() != null) { + blockHandler.accept(block); + } else { + log.warn("We received a block with block.getHeight()=null or block.getHash()=null. That should not happen."); + } } else { - log.error("We received a block with value null. That should not happen."); + log.warn("We received a block with value null. That should not happen."); } } }); @@ -157,7 +167,7 @@ public class RpcService { final List txInputs = rawTransaction.getVIn() .stream() .filter(rawInput -> rawInput != null && rawInput.getVOut() != null && rawInput.getTxId() != null) - .map(rawInput -> new TxInput(new TxInputVo(rawInput.getTxId(), rawInput.getVOut()))) + .map(rawInput -> new TxInput(rawInput.getTxId(), rawInput.getVOut())) .collect(Collectors.toList()); final List txOutputs = rawTransaction.getVOut() @@ -169,14 +179,16 @@ public class RpcService { if (scriptPubKey.getType().equals(ScriptTypes.NULL_DATA)) { String[] chunks = scriptPubKey.getAsm().split(" "); // TODO only store BSQ OP_RETURN date filtered by type byte - if (chunks.length == 2 && chunks[0].equals("OP_RETURN")) { + + // We get on testnet a lot of "OP_RETURN 0" data, so we filter those away + if (chunks.length == 2 && chunks[0].equals("OP_RETURN") && !"0".equals(chunks[1])) { try { opReturnData = Utils.HEX.decode(chunks[1]); } catch (Throwable t) { // We get sometimes exceptions, seems BitcoinJ // cannot handle all existing OP_RETURN data, but we ignore them // anyway as our OP_RETURN data is valid in BitcoinJ - log.warn(t.toString()); + log.warn("Error at Utils.HEX.decode(chunks[1]): " + t.toString() + " / chunks[1]=" + chunks[1]); } } } @@ -184,14 +196,13 @@ public class RpcService { String address = scriptPubKey.getAddresses() != null && scriptPubKey.getAddresses().size() == 1 ? scriptPubKey.getAddresses().get(0) : null; final PubKeyScript pubKeyScript = dumpBlockchainData ? new PubKeyScript(scriptPubKey) : null; - final TxOutputVo txOutputVo = new TxOutputVo(rawOutput.getN(), + return new TxOutput(rawOutput.getN(), rawOutput.getValue().movePointRight(8).longValue(), rawTransaction.getTxId(), pubKeyScript, address, opReturnData, blockHeight); - return new TxOutput(txOutputVo); } ) .collect(Collectors.toList()); diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlock.java b/core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlock.java index 9886bb3945..c1001e8ab3 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlock.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlock.java @@ -27,11 +27,15 @@ import java.util.stream.Collectors; @Data public class BsqBlock implements PersistablePayload { - private final BsqBlockVo bsqBlockVo; + private final int height; + private final String hash; + private final String previousBlockHash; private final List txs; - public BsqBlock(BsqBlockVo bsqBlockVo, List txs) { - this.bsqBlockVo = bsqBlockVo; + public BsqBlock(int height, String hash, String previousBlockHash, List txs) { + this.height = height; + this.hash = hash; + this.previousBlockHash = previousBlockHash; this.txs = txs; } @@ -42,7 +46,9 @@ public class BsqBlock implements PersistablePayload { public PB.BsqBlock toProtoMessage() { return PB.BsqBlock.newBuilder() - .setBsqBlockVo(bsqBlockVo.toProtoMessage()) + .setHeight(height) + .setHash(hash) + .setPreviousBlockHash(previousBlockHash) .addAllTxs(txs.stream() .map(Tx::toProtoMessage) .collect(Collectors.toList())) @@ -50,7 +56,9 @@ public class BsqBlock implements PersistablePayload { } public static BsqBlock fromProto(PB.BsqBlock proto) { - return new BsqBlock(BsqBlockVo.fromProto(proto.getBsqBlockVo()), + return new BsqBlock(proto.getHeight(), + proto.getHash(), + proto.getPreviousBlockHash(), proto.getTxsList().isEmpty() ? new ArrayList<>() : proto.getTxsList().stream() @@ -67,7 +75,6 @@ public class BsqBlock implements PersistablePayload { txs.stream().forEach(Tx::reset); } - @Override public String toString() { return "BsqBlock{" + @@ -77,23 +84,4 @@ public class BsqBlock implements PersistablePayload { ",\n txs='" + txs + '\'' + "\n}"; } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Delegates - /////////////////////////////////////////////////////////////////////////////////////////// - - public int getHeight() { - return bsqBlockVo.getHeight(); - } - - public String getHash() { - return bsqBlockVo.getHash(); - } - - public String getPreviousBlockHash() { - return bsqBlockVo.getPreviousBlockHash(); - } - - } diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlockVo.java b/core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlockVo.java deleted file mode 100644 index 1608e8b84d..0000000000 --- a/core/src/main/java/io/bisq/core/dao/blockchain/vo/BsqBlockVo.java +++ /dev/null @@ -1,56 +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 io.bisq.core.dao.blockchain.vo; - -import io.bisq.common.proto.persistable.PersistablePayload; -import io.bisq.generated.protobuffer.PB; -import lombok.Getter; -import lombok.Value; - -@Value -@Getter -public class BsqBlockVo implements PersistablePayload { - private final int height; - private final String hash; - private final String previousBlockHash; - - public BsqBlockVo(int height, String hash, String previousBlockHash) { - this.height = height; - this.hash = hash; - this.previousBlockHash = previousBlockHash; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - - public PB.BsqBlockVo toProtoMessage() { - return PB.BsqBlockVo.newBuilder() - .setHeight(height) - .setHash(hash) - .setPreviousBlockHash(previousBlockHash).build(); - } - - public static BsqBlockVo fromProto(PB.BsqBlockVo proto) { - return new BsqBlockVo(proto.getHeight(), - proto.getHash(), - proto.getPreviousBlockHash()); - } -} diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInput.java b/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInput.java index d7b1ff4cda..e8e41c22d9 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInput.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInput.java @@ -26,12 +26,13 @@ import java.util.Optional; @Data public class TxInput implements PersistablePayload { - private final TxInputVo txInputVo; + private final String txId; + private final int txOutputIndex; @Nullable private TxOutput connectedTxOutput; - public TxInput(TxInputVo txInputVo) { - this(txInputVo, null); + public TxInput(String txId, int txOutputIndex) { + this(txId, txOutputIndex, null); } @@ -39,20 +40,25 @@ public class TxInput implements PersistablePayload { // PROTO BUFFER /////////////////////////////////////////////////////////////////////////////////////////// - private TxInput(TxInputVo txInputVo, @Nullable TxOutput connectedTxOutput) { - this.txInputVo = txInputVo; + private TxInput(String txId, int txOutputIndex, @Nullable TxOutput connectedTxOutput) { + this.txId = txId; + this.txOutputIndex = txOutputIndex; this.connectedTxOutput = connectedTxOutput; } public PB.TxInput toProtoMessage() { final PB.TxInput.Builder builder = PB.TxInput.newBuilder() - .setTxInputVo(txInputVo.toProtoMessage()); + .setTxId(txId) + .setTxOutputIndex(txOutputIndex); + Optional.ofNullable(connectedTxOutput).ifPresent(e -> builder.setConnectedTxOutput(e.toProtoMessage())); + return builder.build(); } public static TxInput fromProto(PB.TxInput proto) { - return new TxInput(TxInputVo.fromProto(proto.getTxInputVo()), + return new TxInput(proto.getTxId(), + proto.getTxOutputIndex(), proto.hasConnectedTxOutput() ? TxOutput.fromProto(proto.getConnectedTxOutput()) : null); } @@ -65,28 +71,16 @@ public class TxInput implements PersistablePayload { connectedTxOutput = null; } + public TxIdIndexTuple getTxIdIndexTuple() { + return new TxIdIndexTuple(txId, txOutputIndex); + } + @Override public String toString() { return "TxInput{" + - "\n txId=" + getTxId() + - ",\n txOutputIndex=" + getTxOutputIndex() + - ",\n txOutput='" + connectedTxOutput + '\'' + + "\n txId=" + txId + + ",\n txOutputIndex=" + txOutputIndex + + ",\n connectedTxOutput='" + connectedTxOutput + '\'' + "\n}"; } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Delegates - /////////////////////////////////////////////////////////////////////////////////////////// - - public String getTxId() { - return txInputVo.getTxId(); - } - - public int getTxOutputIndex() { - return txInputVo.getTxOutputIndex(); - } - - public TxIdIndexTuple getTxIdIndexTuple() { - return txInputVo.getTxIdIndexTuple(); - } } diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInputVo.java b/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInputVo.java deleted file mode 100644 index a632673fc8..0000000000 --- a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxInputVo.java +++ /dev/null @@ -1,49 +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 io.bisq.core.dao.blockchain.vo; - -import io.bisq.common.proto.persistable.PersistablePayload; -import io.bisq.generated.protobuffer.PB; -import lombok.Value; - -@Value -public class TxInputVo implements PersistablePayload { - private final String txId; - private final int txOutputIndex; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public PB.TxInputVo toProtoMessage() { - return PB.TxInputVo.newBuilder() - .setTxId(txId) - .setTxOutputIndex(txOutputIndex) - .build(); - } - - public static TxInputVo fromProto(PB.TxInputVo proto) { - return new TxInputVo(proto.getTxId(), - proto.getTxOutputIndex()); - } - - public TxIdIndexTuple getTxIdIndexTuple() { - return new TxIdIndexTuple(txId, txOutputIndex); - } -} diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutput.java b/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutput.java index 3f98ccdee6..fce16a6927 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutput.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutput.java @@ -17,10 +17,11 @@ package io.bisq.core.dao.blockchain.vo; +import com.google.protobuf.ByteString; import io.bisq.common.proto.persistable.PersistablePayload; +import io.bisq.common.util.JsonExclude; import io.bisq.core.dao.blockchain.btcd.PubKeyScript; import io.bisq.generated.protobuffer.PB; -import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.bitcoinj.core.Utils; @@ -29,18 +30,43 @@ import javax.annotation.Nullable; import java.util.Optional; @Data -@AllArgsConstructor @Slf4j public class TxOutput implements PersistablePayload { - private final TxOutputVo txOutputVo; + private final int index; + private final long value; + private final String txId; + @Nullable + private final PubKeyScript pubKeyScript; + @Nullable + private final String address; + @Nullable + @JsonExclude + private final byte[] opReturnData; + private final int blockHeight; private boolean isUnspent; private boolean isVerified; private TxOutputType txOutputType = TxOutputType.UNDEFINED; @Nullable private SpentInfo spentInfo; - public TxOutput(TxOutputVo txOutputVo) { - this.txOutputVo = txOutputVo; + public TxOutput(int index, + long value, + String txId, + @Nullable PubKeyScript pubKeyScript, + @Nullable String address, + @Nullable byte[] opReturnData, + int blockHeight) { + this(index, + value, + txId, + pubKeyScript, + address, + opReturnData, + blockHeight, + false, + false, + TxOutputType.UNDEFINED, + null); } @@ -48,20 +74,56 @@ public class TxOutput implements PersistablePayload { // PROTO BUFFER /////////////////////////////////////////////////////////////////////////////////////////// + private TxOutput(int index, + long value, + String txId, + @Nullable PubKeyScript pubKeyScript, + @Nullable String address, + @Nullable byte[] opReturnData, + int blockHeight, + boolean isUnspent, + boolean isVerified, + TxOutputType txOutputType, + @Nullable SpentInfo spentInfo) { + this.index = index; + this.value = value; + this.txId = txId; + this.pubKeyScript = pubKeyScript; + this.address = address; + this.opReturnData = opReturnData; + this.blockHeight = blockHeight; + this.isUnspent = isUnspent; + this.isVerified = isVerified; + this.txOutputType = txOutputType; + this.spentInfo = spentInfo; + } + public PB.TxOutput toProtoMessage() { final PB.TxOutput.Builder builder = PB.TxOutput.newBuilder() - .setTxOutputVo(txOutputVo.toProtoMessage()) + .setIndex(index) + .setValue(value) + .setTxId(txId) + .setBlockHeight(blockHeight) .setIsUnspent(isUnspent) .setIsVerified(isVerified) .setTxOutputType(txOutputType.toProtoMessage()); + Optional.ofNullable(pubKeyScript).ifPresent(e -> builder.setPubKeyScript(pubKeyScript.toProtoMessage())); + Optional.ofNullable(address).ifPresent(e -> builder.setAddress(address)); + Optional.ofNullable(opReturnData).ifPresent(e -> builder.setOpReturnData(ByteString.copyFrom(opReturnData))); Optional.ofNullable(spentInfo).ifPresent(e -> builder.setSpentInfo(e.toProtoMessage())); return builder.build(); } public static TxOutput fromProto(PB.TxOutput proto) { - return new TxOutput(TxOutputVo.fromProto(proto.getTxOutputVo()), + return new TxOutput(proto.getIndex(), + proto.getValue(), + proto.getTxId(), + proto.hasPubKeyScript() ? PubKeyScript.fromProto(proto.getPubKeyScript()) : null, + proto.getAddress().isEmpty() ? null : proto.getAddress(), + proto.getOpReturnData().isEmpty() ? null : proto.getOpReturnData().toByteArray(), + proto.getBlockHeight(), proto.getIsUnspent(), proto.getIsVerified(), TxOutputType.fromProto(proto.getTxOutputType()), @@ -80,23 +142,6 @@ public class TxOutput implements PersistablePayload { spentInfo = null; } - @Override - public String toString() { - return "TxOutput{" + - "\n index=" + getIndex() + - ",\n value=" + getValue() + - ",\n txId='" + getId() + '\'' + - ",\n pubKeyScript=" + getPubKeyScript() + - ",\n address='" + getAddress() + '\'' + - ",\n opReturnData=" + (getOpReturnData() != null ? Utils.HEX.encode(getOpReturnData()) : "null") + - ",\n blockHeight=" + getBlockHeight() + - ",\n isUnspent=" + isUnspent + - ",\n isVerified=" + isVerified + - ",\n txOutputType=" + txOutputType + - ",\n spentInfo=" + (spentInfo != null ? spentInfo.toString() : "null") + - "\n}"; - } - public boolean isCompensationRequestBtcOutput() { return txOutputType == TxOutputType.COMPENSATION_REQUEST_BTC_OUTPUT; } @@ -105,46 +150,28 @@ public class TxOutput implements PersistablePayload { return txOutputType == TxOutputType.SPONSORING_BTC_OUTPUT; } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Delegates - /////////////////////////////////////////////////////////////////////////////////////////// - - public int getIndex() { - return txOutputVo.getIndex(); - } - - public long getValue() { - return txOutputVo.getValue(); - } - - public String getTxId() { - return txOutputVo.getTxId(); - } - - public PubKeyScript getPubKeyScript() { - return txOutputVo.getPubKeyScript(); - } - - @Nullable - public String getAddress() { - return txOutputVo.getAddress(); - } - - @Nullable - public byte[] getOpReturnData() { - return txOutputVo.getOpReturnData(); - } - - public int getBlockHeight() { - return txOutputVo.getBlockHeight(); - } - public String getId() { - return txOutputVo.getId(); + return txId + ":" + index; } public TxIdIndexTuple getTxIdIndexTuple() { - return txOutputVo.getTxIdIndexTuple(); + return new TxIdIndexTuple(txId, index); + } + + @Override + public String toString() { + return "TxOutput{" + + "\n index=" + index + + ",\n value=" + value + + ",\n txId='" + getId() + '\'' + + ",\n pubKeyScript=" + pubKeyScript + + ",\n address='" + address + '\'' + + ",\n opReturnData=" + (opReturnData != null ? Utils.HEX.encode(opReturnData) : "null") + + ",\n blockHeight=" + blockHeight + + ",\n isUnspent=" + isUnspent + + ",\n isVerified=" + isVerified + + ",\n txOutputType=" + txOutputType + + ",\n spentInfo=" + (spentInfo != null ? spentInfo.toString() : "null") + + "\n}"; } } diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutputVo.java b/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutputVo.java deleted file mode 100644 index 462c473454..0000000000 --- a/core/src/main/java/io/bisq/core/dao/blockchain/vo/TxOutputVo.java +++ /dev/null @@ -1,79 +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 io.bisq.core.dao.blockchain.vo; - -import com.google.protobuf.ByteString; -import io.bisq.common.proto.persistable.PersistablePayload; -import io.bisq.common.util.JsonExclude; -import io.bisq.core.dao.blockchain.btcd.PubKeyScript; -import io.bisq.generated.protobuffer.PB; -import lombok.Value; - -import javax.annotation.Nullable; -import java.util.Optional; - -@Value -public class TxOutputVo implements PersistablePayload { - private final int index; - private final long value; - private final String txId; - @Nullable - private final PubKeyScript pubKeyScript; - @Nullable - private final String address; - @Nullable - @JsonExclude - private final byte[] opReturnData; - private final int blockHeight; - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public PB.TxOutputVo toProtoMessage() { - final PB.TxOutputVo.Builder builder = PB.TxOutputVo.newBuilder() - .setIndex(index) - .setValue(value) - .setTxId(txId) - .setBlockHeight(blockHeight); - - Optional.ofNullable(pubKeyScript).ifPresent(e -> builder.setPubKeyScript(pubKeyScript.toProtoMessage())); - Optional.ofNullable(address).ifPresent(e -> builder.setAddress(address)); - Optional.ofNullable(opReturnData).ifPresent(e -> builder.setOpReturnData(ByteString.copyFrom(opReturnData))); - - return builder.build(); - } - - public static TxOutputVo fromProto(PB.TxOutputVo proto) { - return new TxOutputVo(proto.getIndex(), - proto.getValue(), - proto.getTxId(), - proto.hasPubKeyScript() ? PubKeyScript.fromProto(proto.getPubKeyScript()) : null, - proto.getAddress().isEmpty() ? null : proto.getAddress(), - proto.getOpReturnData().isEmpty() ? null : proto.getOpReturnData().toByteArray(), - proto.getBlockHeight()); - } - - public String getId() { - return txId + ":" + index; - } - - public TxIdIndexTuple getTxIdIndexTuple() { - return new TxIdIndexTuple(txId, index); - } -} diff --git a/core/src/main/java/io/bisq/core/filter/Filter.java b/core/src/main/java/io/bisq/core/filter/Filter.java index e340ca1758..4a6e20d1a1 100644 --- a/core/src/main/java/io/bisq/core/filter/Filter.java +++ b/core/src/main/java/io/bisq/core/filter/Filter.java @@ -47,6 +47,12 @@ public final class Filter implements StoragePayload { private final List bannedNodeAddress; private final List bannedPaymentAccounts; + // Because we added those fields in v 0.5.4 and old versions do not have it we annotate it with @Nullable + @Nullable + private final List bannedCurrencies; + @Nullable + private final List bannedPaymentMethods; + private String signatureAsBase64; private byte[] ownerPubKeyBytes; // Should be only used in emergency case if we need to add data but do not want to break backward compatibility @@ -58,10 +64,14 @@ public final class Filter implements StoragePayload { public Filter(List bannedOfferIds, List bannedNodeAddress, - List bannedPaymentAccounts) { + List bannedPaymentAccounts, + @Nullable List bannedCurrencies, + @Nullable List bannedPaymentMethods) { this.bannedOfferIds = bannedOfferIds; this.bannedNodeAddress = bannedNodeAddress; this.bannedPaymentAccounts = bannedPaymentAccounts; + this.bannedCurrencies = bannedCurrencies; + this.bannedPaymentMethods = bannedPaymentMethods; } @@ -73,12 +83,16 @@ public final class Filter implements StoragePayload { public Filter(List bannedOfferIds, List bannedNodeAddress, List bannedPaymentAccounts, + @Nullable List bannedCurrencies, + @Nullable List bannedPaymentMethods, String signatureAsBase64, byte[] ownerPubKeyBytes, @Nullable Map extraDataMap) { this(bannedOfferIds, bannedNodeAddress, - bannedPaymentAccounts); + bannedPaymentAccounts, + bannedCurrencies, + bannedPaymentMethods); this.signatureAsBase64 = signatureAsBase64; this.ownerPubKeyBytes = ownerPubKeyBytes; this.extraDataMap = extraDataMap; @@ -99,7 +113,11 @@ public final class Filter implements StoragePayload { .addAllBannedPaymentAccounts(paymentAccountFilterList) .setSignatureAsBase64(signatureAsBase64) .setOwnerPubKeyBytes(ByteString.copyFrom(ownerPubKeyBytes)); + + Optional.ofNullable(bannedCurrencies).ifPresent(builder::addAllBannedCurrencies); + Optional.ofNullable(bannedPaymentMethods).ifPresent(builder::addAllBannedPaymentMethods); Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData); + return PB.StoragePayload.newBuilder().setFilter(builder).build(); } @@ -109,6 +127,8 @@ public final class Filter implements StoragePayload { proto.getBannedPaymentAccountsList().stream() .map(PaymentAccountFilter::fromProto) .collect(Collectors.toList()), + CollectionUtils.isEmpty(proto.getBannedCurrenciesList()) ? null : proto.getBannedCurrenciesList().stream().collect(Collectors.toList()), + CollectionUtils.isEmpty(proto.getBannedPaymentMethodsList()) ? null : proto.getBannedPaymentMethodsList().stream().collect(Collectors.toList()), proto.getSignatureAsBase64(), proto.getOwnerPubKeyBytes().toByteArray(), CollectionUtils.isEmpty(proto.getExtraDataMap()) ? null : proto.getExtraDataMap()); diff --git a/core/src/main/java/io/bisq/core/filter/FilterManager.java b/core/src/main/java/io/bisq/core/filter/FilterManager.java index 044fb0b7bb..d48807af1e 100644 --- a/core/src/main/java/io/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/io/bisq/core/filter/FilterManager.java @@ -22,6 +22,8 @@ import com.google.inject.name.Named; import io.bisq.common.app.DevEnv; import io.bisq.common.crypto.KeyRing; import io.bisq.core.app.AppOptionKeys; +import io.bisq.core.payment.payload.PaymentAccountPayload; +import io.bisq.core.payment.payload.PaymentMethod; import io.bisq.core.user.User; import io.bisq.generated.protobuffer.PB; import io.bisq.network.p2p.P2PService; @@ -36,8 +38,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nullable; +import java.lang.reflect.Method; import java.math.BigInteger; import java.security.SignatureException; +import java.util.Optional; import java.util.stream.Collectors; import static org.bitcoinj.core.Utils.HEX; @@ -162,12 +166,18 @@ public class FilterManager { } } + // We dont use full data from Filter as we are only interested in the filter data not the sig and keys private String getHexFromData(Filter filter) { - PB.Filter.Builder builder = PB.Filter.newBuilder().addAllBannedNodeAddress(filter.getBannedNodeAddress()) + PB.Filter.Builder builder = PB.Filter.newBuilder() .addAllBannedOfferIds(filter.getBannedOfferIds()) + .addAllBannedNodeAddress(filter.getBannedNodeAddress()) .addAllBannedPaymentAccounts(filter.getBannedPaymentAccounts().stream() .map(PaymentAccountFilter::toProtoMessage) .collect(Collectors.toList())); + + Optional.ofNullable(filter.getBannedCurrencies()).ifPresent(builder::addAllBannedCurrencies); + Optional.ofNullable(filter.getBannedPaymentMethods()).ifPresent(builder::addAllBannedPaymentMethods); + return Utils.HEX.encode(builder.build().toByteArray()); } @@ -175,4 +185,63 @@ public class FilterManager { public Filter getDevelopersFilter() { return user.getDevelopersFilter(); } + + public boolean isCurrencyBanned(String currencyCode) { + return getFilter() != null && + getFilter().getBannedCurrencies() != null && + getFilter().getBannedCurrencies().stream() + .filter(e -> e.equals(currencyCode)) + .findAny() + .isPresent(); + } + + public boolean isPaymentMethodBanned(PaymentMethod paymentMethod) { + return getFilter() != null && + getFilter().getBannedPaymentMethods() != null && + getFilter().getBannedPaymentMethods().stream() + .filter(e -> e.equals(paymentMethod.getId())) + .findAny() + .isPresent(); + } + + public boolean isOfferIdBanned(String offerId) { + return getFilter() != null && + getFilter().getBannedOfferIds().stream() + .filter(e -> e.equals(offerId)) + .findAny() + .isPresent(); + } + + public boolean isNodeAddressBanned(String nodeAddress) { + return getFilter() != null && + getFilter().getBannedNodeAddress().stream() + .filter(e -> e.equals(nodeAddress)) + .findAny() + .isPresent(); + } + + public boolean isPeersPaymentAccountDataAreBanned(PaymentAccountPayload paymentAccountPayload, + PaymentAccountFilter[] appliedPaymentAccountFilter) { + return getFilter() != null && + getFilter().getBannedPaymentAccounts().stream() + .filter(paymentAccountFilter -> { + final boolean samePaymentMethodId = paymentAccountFilter.getPaymentMethodId().equals( + paymentAccountPayload.getPaymentMethodId()); + if (samePaymentMethodId) { + try { + Method method = paymentAccountPayload.getClass().getMethod(paymentAccountFilter.getGetMethodName()); + String result = (String) method.invoke(paymentAccountPayload); + appliedPaymentAccountFilter[0] = paymentAccountFilter; + return result.equals(paymentAccountFilter.getValue()); + } catch (Throwable e) { + log.error(e.getMessage()); + return false; + } + } else { + return false; + } + }) + .findAny() + .isPresent(); + } } diff --git a/core/src/main/java/io/bisq/core/offer/Offer.java b/core/src/main/java/io/bisq/core/offer/Offer.java index 82be39aeec..8bf4f0896d 100644 --- a/core/src/main/java/io/bisq/core/offer/Offer.java +++ b/core/src/main/java/io/bisq/core/offer/Offer.java @@ -128,7 +128,7 @@ public class Offer implements NetworkPayload, PersistablePayload { if (offerPayload.isUseMarketBasedPrice()) { checkNotNull(priceFeedService, "priceFeed must not be null"); MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode); - if (marketPrice != null && marketPrice.isValid()) { + if (marketPrice != null && marketPrice.isRecentExternalPriceAvailable()) { double factor; double marketPriceMargin = offerPayload.getMarketPriceMargin(); if (CurrencyUtil.isCryptoCurrency(currencyCode)) { diff --git a/core/src/main/java/io/bisq/core/payment/payload/PaymentAccountPayload.java b/core/src/main/java/io/bisq/core/payment/payload/PaymentAccountPayload.java index d5bbed9c1e..00da7b28bb 100644 --- a/core/src/main/java/io/bisq/core/payment/payload/PaymentAccountPayload.java +++ b/core/src/main/java/io/bisq/core/payment/payload/PaymentAccountPayload.java @@ -24,6 +24,9 @@ import lombok.Getter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; +// That class is used in the contract for creating the contract json. Any change will break the contract. +// If a field gets added it need to be be annotated with @JsonExclude (excluded from contract). + @Getter @EqualsAndHashCode @ToString diff --git a/core/src/main/java/io/bisq/core/provider/price/MarketPrice.java b/core/src/main/java/io/bisq/core/provider/price/MarketPrice.java index 0a2a6c7461..271fdac836 100644 --- a/core/src/main/java/io/bisq/core/provider/price/MarketPrice.java +++ b/core/src/main/java/io/bisq/core/provider/price/MarketPrice.java @@ -27,15 +27,24 @@ public class MarketPrice { private final String currencyCode; private final double price; private final long timestampSec; + private final boolean isExternallyProvidedPrice; // if we get it from btc average or others. - public MarketPrice(String currencyCode, double price, long timestampSec) { + public MarketPrice(String currencyCode, double price, long timestampSec, boolean isExternallyProvidedPrice) { this.currencyCode = currencyCode; this.price = price; this.timestampSec = timestampSec; + this.isExternallyProvidedPrice = isExternallyProvidedPrice; } - public boolean isValid() { - long limit = Instant.now().getEpochSecond() - MARKET_PRICE_MAX_AGE_SEC; - return timestampSec > limit && price > 0; + public boolean isPriceAvailable() { + return price > 0; + } + + private boolean isRecentPriceAvailable() { + return timestampSec > (Instant.now().getEpochSecond() - MARKET_PRICE_MAX_AGE_SEC) && isPriceAvailable(); + } + + public boolean isRecentExternalPriceAvailable() { + return isExternallyProvidedPrice && isRecentPriceAvailable(); } } diff --git a/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java b/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java index 747acf47e2..fc07c14338 100644 --- a/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java +++ b/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java @@ -25,6 +25,8 @@ import io.bisq.common.app.Log; import io.bisq.common.handlers.FaultHandler; import io.bisq.common.locale.CurrencyUtil; import io.bisq.common.locale.TradeCurrency; +import io.bisq.common.monetary.Price; +import io.bisq.common.util.MathUtils; import io.bisq.common.util.Tuple2; import io.bisq.core.app.BisqEnvironment; import io.bisq.core.provider.ProvidersRepository; @@ -138,6 +140,16 @@ public class PriceFeedService { else return null; } + + public void setBisqMarketPrice(String currencyCode, Price price) { + if (!cache.containsKey(currencyCode) || !cache.get(currencyCode).isExternallyProvidedPrice()) { + cache.put(currencyCode, new MarketPrice(currencyCode, + MathUtils.scaleDownByPowerOf10(price.getValue(), CurrencyUtil.isCryptoCurrency(currencyCode) ? 8 : 4), + 0, + false)); + updateCounter.set(updateCounter.get() + 1); + } + } /////////////////////////////////////////////////////////////////////////////////////////// // Setter @@ -198,7 +210,7 @@ public class PriceFeedService { if (cache.containsKey(currencyCode)) { try { MarketPrice marketPrice = cache.get(currencyCode); - if (marketPrice.isValid()) + if (marketPrice.isRecentExternalPriceAvailable()) priceConsumer.accept(marketPrice.getPrice()); } catch (Throwable t) { log.warn("Error at applyPriceToConsumer " + t.getMessage()); @@ -235,20 +247,36 @@ public class PriceFeedService { case "DASH": // apply conversion of btc based price to baseCurrencyCode based with btc/baseCurrencyCode price MarketPrice baseCurrencyPrice = priceMap.get(baseCurrencyCode); - Map convertedPriceMap = new HashMap<>(); - priceMap.entrySet().stream().forEach(e -> { - final MarketPrice value = e.getValue(); - double convertedPrice; - if (CurrencyUtil.isCryptoCurrency(e.getKey())) - convertedPrice = value.getPrice() / baseCurrencyPrice.getPrice(); - else - convertedPrice = value.getPrice() * baseCurrencyPrice.getPrice(); - convertedPriceMap.put(e.getKey(), new MarketPrice(value.getCurrencyCode(), convertedPrice, value.getTimestampSec())); - }); - cache.putAll(convertedPriceMap); + if (baseCurrencyPrice != null) { + Map convertedPriceMap = new HashMap<>(); + priceMap.entrySet().stream().forEach(e -> { + final MarketPrice marketPrice = e.getValue(); + if (marketPrice != null) { + double convertedPrice; + final double marketPriceAsDouble = marketPrice.getPrice(); + final double baseCurrencyPriceAsDouble = baseCurrencyPrice.getPrice(); + if (marketPriceAsDouble > 0 && baseCurrencyPriceAsDouble > 0) { + if (CurrencyUtil.isCryptoCurrency(e.getKey())) + convertedPrice = marketPriceAsDouble / baseCurrencyPriceAsDouble; + else + convertedPrice = marketPriceAsDouble * baseCurrencyPriceAsDouble; + convertedPriceMap.put(e.getKey(), + new MarketPrice(marketPrice.getCurrencyCode(), convertedPrice, marketPrice.getTimestampSec(), true)); + } else { + log.warn("marketPriceAsDouble or baseCurrencyPriceAsDouble is 0: marketPriceAsDouble={}, " + + "baseCurrencyPriceAsDouble={}", marketPriceAsDouble, baseCurrencyPriceAsDouble); + } + } else { + log.warn("marketPrice is null"); + } + }); + cache.putAll(convertedPriceMap); + } else { + log.warn("baseCurrencyPrice is null"); + } break; default: - throw new RuntimeException("baseCurrencyCode not dfined. baseCurrencyCode=" + baseCurrencyCode); + throw new RuntimeException("baseCurrencyCode not defined. baseCurrencyCode=" + baseCurrencyCode); } resultHandler.run(); diff --git a/core/src/main/java/io/bisq/core/provider/price/PriceProvider.java b/core/src/main/java/io/bisq/core/provider/price/PriceProvider.java index 622bec803f..fe2260d689 100644 --- a/core/src/main/java/io/bisq/core/provider/price/PriceProvider.java +++ b/core/src/main/java/io/bisq/core/provider/price/PriceProvider.java @@ -58,7 +58,7 @@ public class PriceProvider extends HttpClientProvider { final double price = (double) treeMap.get("price"); // json uses double for our timestampSec long value... final long timestampSec = MathUtils.doubleToLong((double) treeMap.get("timestampSec")); - marketPriceMap.put(currencyCode, new MarketPrice(currencyCode, price, timestampSec)); + marketPriceMap.put(currencyCode, new MarketPrice(currencyCode, price, timestampSec, true)); } catch (Throwable t) { log.error(t.toString()); t.printStackTrace(); diff --git a/core/src/main/java/io/bisq/core/trade/Contract.java b/core/src/main/java/io/bisq/core/trade/Contract.java index 765294c4af..e4e86b344b 100644 --- a/core/src/main/java/io/bisq/core/trade/Contract.java +++ b/core/src/main/java/io/bisq/core/trade/Contract.java @@ -29,12 +29,15 @@ import io.bisq.core.proto.CoreProtoResolver; import io.bisq.generated.protobuffer.PB; import io.bisq.network.p2p.NodeAddress; import lombok.Value; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.bitcoinj.core.Coin; import javax.annotation.Nullable; import static com.google.common.base.Preconditions.checkArgument; +@Slf4j @Value public final class Contract implements NetworkPayload { private final OfferPayload offerPayload; @@ -210,6 +213,25 @@ public final class Contract implements NetworkPayload { return Price.valueOf(offerPayload.getCurrencyCode(), tradePrice); } + public void printDiff(@Nullable String peersContractAsJson) { + final String json = Utilities.objectToJson(this); + String diff = StringUtils.difference(json, peersContractAsJson); + if (!diff.isEmpty()) { + log.warn("Diff of both contracts: \n" + diff); + log.warn("\n\n------------------------------------------------------------\n" + + "Contract as json\n" + + json + + "\n------------------------------------------------------------\n"); + + log.warn("\n\n------------------------------------------------------------\n" + + "Peers contract as json\n" + + peersContractAsJson + + "\n------------------------------------------------------------\n"); + } else { + log.debug("Both contracts are the same"); + } + } + @Override public String toString() { return "Contract{" + diff --git a/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java b/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java index 25c82d611a..0fe3e5e9e3 100644 --- a/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java +++ b/core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java @@ -29,7 +29,6 @@ import io.bisq.core.btc.wallet.BsqWalletService; import io.bisq.core.btc.wallet.BtcWalletService; import io.bisq.core.btc.wallet.TradeWalletService; import io.bisq.core.filter.FilterManager; -import io.bisq.core.filter.PaymentAccountFilter; import io.bisq.core.offer.Offer; import io.bisq.core.offer.OpenOfferManager; import io.bisq.core.payment.PaymentAccount; @@ -48,13 +47,11 @@ import io.bisq.network.p2p.P2PService; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.NotImplementedException; import org.bitcoinj.core.Coin; import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Transaction; import javax.annotation.Nullable; -import java.lang.reflect.Method; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -239,12 +236,11 @@ public class ProcessModel implements Model, PersistablePayload { @Override public void persist() { - throw new NotImplementedException("persist is not implemented in that class"); + log.warn("persist is not implemented in that class"); } @Override public void onComplete() { - throw new NotImplementedException("persist is not implemented in that class"); } public void setTakeOfferFeeTx(Transaction takeOfferFeeTx) { @@ -262,39 +258,6 @@ public class ProcessModel implements Model, PersistablePayload { return paymentAccount != null ? paymentAccount.getPaymentAccountPayload() : null; } - public boolean isPeersPaymentAccountDataAreBanned(PaymentAccountPayload paymentAccountPayload, - PaymentAccountFilter[] appliedPaymentAccountFilter) { - return filterManager.getFilter() != null && - filterManager.getFilter().getBannedPaymentAccounts().stream() - .filter(paymentAccountFilter -> { - final boolean samePaymentMethodId = paymentAccountFilter.getPaymentMethodId().equals( - paymentAccountPayload.getPaymentMethodId()); - if (samePaymentMethodId) { - try { - Method method = paymentAccountPayload.getClass().getMethod(paymentAccountFilter.getGetMethodName()); - String result = (String) method.invoke(paymentAccountPayload); - appliedPaymentAccountFilter[0] = paymentAccountFilter; - return result.equals(paymentAccountFilter.getValue()); - } catch (Throwable e) { - log.error(e.getMessage()); - return false; - } - } else { - return false; - } - }) - .findAny() - .isPresent(); - } - - public boolean isNodeBanned(NodeAddress nodeAddress) { - return filterManager.getFilter() != null && - filterManager.getFilter().getBannedNodeAddress().stream() - .filter(e -> e.equals(nodeAddress.getHostNameWithoutPostFix())) - .findAny() - .isPresent(); - } - public Coin getFundsNeededForTradeAsLong() { return Coin.valueOf(fundsNeededForTradeAsLong); } diff --git a/core/src/main/java/io/bisq/core/trade/protocol/tasks/CheckIfPeerIsBanned.java b/core/src/main/java/io/bisq/core/trade/protocol/tasks/CheckIfPeerIsBanned.java index ab091fd8f8..c1624f6014 100644 --- a/core/src/main/java/io/bisq/core/trade/protocol/tasks/CheckIfPeerIsBanned.java +++ b/core/src/main/java/io/bisq/core/trade/protocol/tasks/CheckIfPeerIsBanned.java @@ -38,18 +38,26 @@ public class CheckIfPeerIsBanned extends TradeTask { try { runInterceptHook(); - final NodeAddress tempTradingPeerNodeAddress = processModel.getTempTradingPeerNodeAddress(); - if (tempTradingPeerNodeAddress != null && processModel.isNodeBanned(tempTradingPeerNodeAddress)) { - failed("Other trader is banned by his node address.\n" + - "tradingPeerNodeAddress=" + tempTradingPeerNodeAddress); + final NodeAddress nodeAddress = processModel.getTempTradingPeerNodeAddress(); + PaymentAccountPayload paymentAccountPayload = checkNotNull(processModel.getTradingPeer().getPaymentAccountPayload()); + final PaymentAccountFilter[] appliedPaymentAccountFilter = new PaymentAccountFilter[1]; - PaymentAccountPayload paymentAccountPayload = checkNotNull(processModel.getTradingPeer().getPaymentAccountPayload()); - final PaymentAccountFilter[] appliedPaymentAccountFilter = new PaymentAccountFilter[1]; - if (processModel.isPeersPaymentAccountDataAreBanned(paymentAccountPayload, appliedPaymentAccountFilter)) { - failed("Other trader is banned by his trading account data.\n" + - "paymentAccountPayload=" + paymentAccountPayload.getPaymentDetails() + "\n" + - "banFilter=" + appliedPaymentAccountFilter[0].toString()); - } + if (nodeAddress != null && processModel.getFilterManager().isNodeAddressBanned(nodeAddress.getHostNameWithoutPostFix())) { + failed("Other trader is banned by his node address.\n" + + "tradingPeerNodeAddress=" + nodeAddress); + } else if (processModel.getFilterManager().isOfferIdBanned(trade.getId())) { + failed("Offer ID is banned.\n" + + "Offer ID=" + trade.getId()); + } else if (processModel.getFilterManager().isCurrencyBanned(trade.getOffer().getCurrencyCode())) { + failed("Currency is banned.\n" + + "Currency code=" + trade.getOffer().getCurrencyCode()); + } else if (processModel.getFilterManager().isPaymentMethodBanned(trade.getOffer().getPaymentMethod())) { + failed("Payment method is banned.\n" + + "Payment method=" + trade.getOffer().getPaymentMethod().getId()); + } else if (processModel.getFilterManager().isPeersPaymentAccountDataAreBanned(paymentAccountPayload, appliedPaymentAccountFilter)) { + failed("Other trader is banned by his trading account data.\n" + + "paymentAccountPayload=" + paymentAccountPayload.getPaymentDetails() + "\n" + + "banFilter=" + appliedPaymentAccountFilter[0].toString()); } else { complete(); } diff --git a/core/src/main/java/io/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java b/core/src/main/java/io/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java index 6374d5ba62..0c063a9995 100644 --- a/core/src/main/java/io/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java +++ b/core/src/main/java/io/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java @@ -124,6 +124,7 @@ public class SellerAsTakerSignAndPublishDepositTx extends TradeTask { }); trade.setDepositTx(depositTx); } catch (Throwable t) { + trade.getContract().printDiff(processModel.getTradingPeer().getContractAsJson()); failed(t); } } diff --git a/core/src/main/java/io/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java b/core/src/main/java/io/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java index 8c79e682fa..d00fd5b47e 100644 --- a/core/src/main/java/io/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java +++ b/core/src/main/java/io/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java @@ -97,11 +97,14 @@ public class TakerVerifyAndSignContract extends TradeTask { ); String contractAsJson = Utilities.objectToJson(contract); log.trace("Contract as json:{}", contractAsJson); + + contract.printDiff(processModel.getTradingPeer().getContractAsJson()); + checkArgument(contractAsJson.equals(processModel.getTradingPeer().getContractAsJson()), "Contracts are not matching"); + String signature = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), contractAsJson); trade.setContract(contract); trade.setContractAsJson(contractAsJson); trade.setTakerContractSignature(signature); - try { checkNotNull(maker.getPubKeyRing(), "maker.getPubKeyRing() must nto be null"); Sig.verify(maker.getPubKeyRing().getSignaturePubKey(), diff --git a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java index 4b9f64d122..6f70151c3f 100644 --- a/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/io/bisq/core/trade/statistics/TradeStatisticsManager.java @@ -13,6 +13,7 @@ import io.bisq.common.storage.Storage; import io.bisq.common.util.Utilities; import io.bisq.core.app.AppOptionKeys; import io.bisq.core.app.BisqEnvironment; +import io.bisq.core.provider.price.PriceFeedService; import io.bisq.network.p2p.P2PService; import io.bisq.network.p2p.storage.HashMapChangedListener; import io.bisq.network.p2p.storage.payload.ProtectedStorageEntry; @@ -32,6 +33,7 @@ public class TradeStatisticsManager implements PersistedDataHost { private final Storage statisticsStorage; private final JsonFileManager jsonFileManager; private final P2PService p2PService; + private final PriceFeedService priceFeedService; private final boolean dumpStatistics; private final ObservableSet observableTradeStatisticsSet = FXCollections.observableSet(); private final HashSet tradeStatisticsSet = new HashSet<>(); @@ -40,10 +42,12 @@ public class TradeStatisticsManager implements PersistedDataHost { @Inject public TradeStatisticsManager(Storage statisticsStorage, P2PService p2PService, + PriceFeedService priceFeedService, @Named(Storage.STORAGE_DIR) File storageDir, @Named(AppOptionKeys.DUMP_STATISTICS) boolean dumpStatistics) { this.statisticsStorage = statisticsStorage; this.p2PService = p2PService; + this.priceFeedService = priceFeedService; this.dumpStatistics = dumpStatistics; jsonFileManager = new JsonFileManager(storageDir); @@ -129,6 +133,8 @@ public class TradeStatisticsManager implements PersistedDataHost { }); + applyBisqMarketPrice(); + statisticsStorage.queueUpForSave(new TradeStatisticsList(new ArrayList<>(tradeStatisticsSet)), 2000); dump(); @@ -137,6 +143,16 @@ public class TradeStatisticsManager implements PersistedDataHost { } + private void applyBisqMarketPrice() { + List sortedList = new ArrayList<>(tradeStatisticsSet); + // sort by date so we have most recent as last entry which we use for displaying the latest price + sortedList.sort((o1, o2) -> o1.getTradeDate().compareTo(o2.getTradeDate())); + if (!sortedList.isEmpty()) { + TradeStatistics tradeStatistics = sortedList.get(sortedList.size() - 1); + priceFeedService.setBisqMarketPrice(tradeStatistics.getCurrencyCode(), tradeStatistics.getTradePrice()); + } + } + public void add(TradeStatistics tradeStatistics, boolean storeLocally) { if (!tradeStatisticsSet.contains(tradeStatistics)) { boolean itemAlreadyAdded = tradeStatisticsSet.stream().filter(e -> (e.getOfferId().equals(tradeStatistics.getOfferId()))).findAny().isPresent(); @@ -144,7 +160,11 @@ public class TradeStatisticsManager implements PersistedDataHost { tradeStatisticsSet.add(tradeStatistics); observableTradeStatisticsSet.add(tradeStatistics); + tradeStatistics.getTradePrice().getValue(); + if (storeLocally) { + applyBisqMarketPrice(); + statisticsStorage.queueUpForSave(new TradeStatisticsList(new ArrayList<>(tradeStatisticsSet)), 2000); dump(); } @@ -186,7 +206,7 @@ public class TradeStatisticsManager implements PersistedDataHost { } } - StringBuilder sb1 = new StringBuilder(); + StringBuilder sb1 = new StringBuilder("\nAll traded Fiat currencies:\n"); map1.entrySet().stream() .sorted((o1, o2) -> Integer.valueOf(o2.getValue().size()).compareTo(o1.getValue().size())) .forEach(e -> sb1.append(e.getKey()).append(": ").append(e.getValue().size()).append("\n")); @@ -194,19 +214,65 @@ public class TradeStatisticsManager implements PersistedDataHost { Map> map2 = new HashMap<>(); for (TradeStatistics tradeStatistics : tradeStatisticsSet) { - if (CurrencyUtil.isCryptoCurrency(tradeStatistics.getCounterCurrency())) { - final String counterCurrency = CurrencyUtil.getNameAndCode(tradeStatistics.getCounterCurrency()); - if (!map2.containsKey(counterCurrency)) - map2.put(counterCurrency, new HashSet<>()); + if (CurrencyUtil.isCryptoCurrency(tradeStatistics.getBaseCurrency())) { + final String code = CurrencyUtil.getNameAndCode(tradeStatistics.getBaseCurrency()); + if (!map2.containsKey(code)) + map2.put(code, new HashSet<>()); - map2.get(counterCurrency).add(tradeStatistics); + map2.get(code).add(tradeStatistics); } } - StringBuilder sb2 = new StringBuilder(); + List allCryptoCurrencies = new ArrayList<>(); + Set coinsWithValidator = new HashSet<>(); + coinsWithValidator.add("BTC"); + 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"); + coinsWithValidator.add("PNC"); + coinsWithValidator.add("ZEN"); + coinsWithValidator.add("WAC"); + coinsWithValidator.add("DEC"); + + // As of: 17.Sept 2017 + Set newlyAdded = new HashSet<>(); + newlyAdded.add("PNC"); + newlyAdded.add("WAC"); + newlyAdded.add("ZEN"); + newlyAdded.add("DEC"); + + CurrencyUtil.getAllSortedCryptoCurrencies().stream() + .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.valueOf(o2.getValue().size()).compareTo(o1.getValue().size())) - .forEach(e -> sb2.append(e.getKey()).append(": ").append(e.getValue().size()).append("\n")); + .forEach(e -> { + final 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.stream() + .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()); } } diff --git a/core/src/main/java/io/bisq/core/user/Preferences.java b/core/src/main/java/io/bisq/core/user/Preferences.java index d5f4e09d14..7f97153386 100644 --- a/core/src/main/java/io/bisq/core/user/Preferences.java +++ b/core/src/main/java/io/bisq/core/user/Preferences.java @@ -35,8 +35,8 @@ import static com.google.common.base.Preconditions.checkNotNull; public final class Preferences implements PersistedDataHost { private static final ArrayList BTC_MAIN_NET_EXPLORERS = new ArrayList<>(Arrays.asList( - new BlockChainExplorer("Tradeblock.com", "https://tradeblock.com/bitcoin/tx/", "https://tradeblock.com/bitcoin/address/"), - new BlockChainExplorer("Insight", "https://insight.bitpay.com/tx/", "https://insight.bitpay.com/address/"), + new BlockChainExplorer("Tradeblock", "https://tradeblock.com/bitcoin/tx/", "https://tradeblock.com/bitcoin/address/"), + new BlockChainExplorer("OXT", "https://oxt.me/transaction/", "https://oxt.me/address/"), new BlockChainExplorer("Blockchain.info", "https://blockchain.info/tx/", "https://blockchain.info/address/"), new BlockChainExplorer("Blockexplorer", "https://blockexplorer.com/tx/", "https://blockexplorer.com/address/"), new BlockChainExplorer("Biteasy", "https://www.biteasy.com/transactions/", "https://www.biteasy.com/addresses/"), @@ -44,6 +44,8 @@ public final class Preferences implements PersistedDataHost { new BlockChainExplorer("Chainflyer", "http://chainflyer.bitflyer.jp/Transaction/", "http://chainflyer.bitflyer.jp/Address/"), new BlockChainExplorer("Smartbit", "https://www.smartbit.com.au/tx/", "https://www.smartbit.com.au/address/"), new BlockChainExplorer("SoChain. Wow.", "https://chain.so/tx/BTC/", "https://chain.so/address/BTC/"), + new BlockChainExplorer("Bitaps", "https://bitaps.com/", "https://bitaps.com/"), + new BlockChainExplorer("Insight", "https://insight.bitpay.com/tx/", "https://insight.bitpay.com/address/"), new BlockChainExplorer("Bitaps", "https://bitaps.com/", "https://bitaps.com/") )); private static final ArrayList BTC_TEST_NET_EXPLORERS = new ArrayList<>(Arrays.asList( @@ -54,6 +56,11 @@ public final class Preferences implements PersistedDataHost { new BlockChainExplorer("SoChain. Wow.", "https://chain.so/tx/BTCTEST/", "https://chain.so/address/BTCTEST/") )); + public static final BlockChainExplorer BSQ_MAIN_NET_EXPLORER = new BlockChainExplorer("BSQ", "https://explorer.bisq.network/tx.html?tx=", + "https://explorer.bisq.network/Address.html?addr="); + public static final BlockChainExplorer BSQ_TEST_NET_EXPLORER = new BlockChainExplorer("BSQ", "https://explorer.bisq.network/testnet/tx.html?tx=", + "https://explorer.bisq.network/testnet/Address.html?addr="); + private static final ArrayList LTC_MAIN_NET_EXPLORERS = new ArrayList<>(Arrays.asList( new BlockChainExplorer("CryptoID", "https://chainz.cryptoid.info/ltc/tx.dws?", "https://chainz.cryptoid.info/ltc/address.dws?"), new BlockChainExplorer("Abe Search", "http://explorer.litecoin.net/tx/", "http://explorer.litecoin.net/address/"), @@ -158,6 +165,7 @@ public final class Preferences implements PersistedDataHost { @Override public void readPersisted() { PreferencesPayload persisted = storage.initAndGetPersistedWithFileName("PreferencesPayload"); + final BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); TradeCurrency preferredTradeCurrency; if (persisted != null) { prefPayload = persisted; @@ -179,7 +187,6 @@ public final class Preferences implements PersistedDataHost { setFiatCurrencies(CurrencyUtil.getMainFiatCurrencies()); setCryptoCurrencies(CurrencyUtil.getMainCryptoCurrencies()); - final BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); switch (baseCurrencyNetwork.getCurrencyCode()) { case "BTC": setBlockChainExplorerMainNet(BTC_MAIN_NET_EXPLORERS.get(0)); @@ -209,6 +216,7 @@ public final class Preferences implements PersistedDataHost { prefPayload.setSellScreenCurrencyCode(preferredTradeCurrency.getCode()); } + prefPayload.setBsqBlockChainExplorer(baseCurrencyNetwork.isMainnet() ? BSQ_MAIN_NET_EXPLORER : BSQ_TEST_NET_EXPLORER); // We don't want to pass Preferences to all popups where the dont show again checkbox is used, so we use // that static lookup class to avoid static access to the Preferences directly. @@ -437,11 +445,6 @@ public final class Preferences implements PersistedDataHost { persist(); } - public void setBsqBlockChainExplorer(BlockChainExplorer bsqBlockChainExplorer) { - prefPayload.setBsqBlockChainExplorer(bsqBlockChainExplorer); - persist(); - } - public void setPayFeeInBtc(boolean payFeeInBtc) { prefPayload.setPayFeeInBtc(payFeeInBtc); persist(); diff --git a/core/src/main/java/io/bisq/core/user/PreferencesPayload.java b/core/src/main/java/io/bisq/core/user/PreferencesPayload.java index 73ae914e9d..3933d51226 100644 --- a/core/src/main/java/io/bisq/core/user/PreferencesPayload.java +++ b/core/src/main/java/io/bisq/core/user/PreferencesPayload.java @@ -32,8 +32,7 @@ public final class PreferencesPayload implements PersistableEnvelope { private List cryptoCurrencies = new ArrayList<>(); private BlockChainExplorer blockChainExplorerMainNet; private BlockChainExplorer blockChainExplorerTestNet; - private BlockChainExplorer bsqBlockChainExplorer = new BlockChainExplorer("BSQ", "https://explorer.bisq.io/tx.html?tx=", - "https://explorer.bisq.io/Address.html?addr="); + private BlockChainExplorer bsqBlockChainExplorer = Preferences.BSQ_MAIN_NET_EXPLORER; @Nullable private String backupDirectory; private boolean autoSelectArbitrators = true; diff --git a/core/src/test/java/io/bisq/core/user/UserPayloadModelVOTest.java b/core/src/test/java/io/bisq/core/user/UserPayloadModelVOTest.java index 6b9d26f796..01023b37ac 100644 --- a/core/src/test/java/io/bisq/core/user/UserPayloadModelVOTest.java +++ b/core/src/test/java/io/bisq/core/user/UserPayloadModelVOTest.java @@ -41,7 +41,8 @@ public class UserPayloadModelVOTest { UserPayload vo = new UserPayload(); vo.setAccountId("accountId"); vo.setDisplayedAlert(new Alert("message", true, "version", new byte[]{12, -64, 12}, "string", null)); - vo.setDevelopersFilter(new Filter(Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), "string", new byte[]{10, 0, 0}, null)); + vo.setDevelopersFilter(new Filter(Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), + Lists.newArrayList(), Lists.newArrayList(), "string", new byte[]{10, 0, 0}, null)); vo.setRegisteredArbitrator(ArbitratorTest.getArbitratorMock()); vo.setRegisteredMediator(MediatorTest.getMediatorMock()); vo.setAcceptedArbitrators(Lists.newArrayList(ArbitratorTest.getArbitratorMock())); diff --git a/doc/build.md b/doc/build.md index 977c82c65c..dca9cc5d4a 100644 --- a/doc/build.md +++ b/doc/build.md @@ -95,6 +95,7 @@ Build bisq Now we have all prepared to build the correct bisq jar. + $ git clone https://github.com/bitsquare/bitsquare.git bisq $ cd bisq $ mvn clean package verify -DskipTests -Dmaven.javadoc.skip=true diff --git a/doc/rpc_regtest/bitcoin.conf b/doc/rpc_regtest/bitcoin.conf index 676861adaa..72c845ed97 100644 --- a/doc/rpc_regtest/bitcoin.conf +++ b/doc/rpc_regtest/bitcoin.conf @@ -4,10 +4,13 @@ regtest=1 txindex=1 #rpc +whitelist=127.0.0.1 +rpcallowip=127.0.0.1 + server=1 rpcuser=bisq rpcpassword=bisqPW # we want to test with 2 local apps so we need the output at 2 diff ports. # Unix process substitution does not work from Bitcoin Core, so we use a bash script -blocknotify=bash /Users/dev/Library/Application\ Support/Bitcoin-rt/blocknotify %s \ No newline at end of file +blocknotify=bash [PATH_TO_BTC_APP_DIR]/blocknotify %s \ No newline at end of file diff --git a/gui/src/main/java/io/bisq/gui/SystemTray.java b/gui/src/main/java/io/bisq/gui/SystemTray.java index dac1ac6da1..cc707165b0 100644 --- a/gui/src/main/java/io/bisq/gui/SystemTray.java +++ b/gui/src/main/java/io/bisq/gui/SystemTray.java @@ -123,7 +123,7 @@ public class SystemTray { aboutItem.addActionListener(e -> { try { - UserThread.execute(() -> GUIUtil.openWebPage("https://bisq.io")); + UserThread.execute(() -> GUIUtil.openWebPage("https://bisq.network")); } catch (Exception e1) { e1.printStackTrace(); } diff --git a/gui/src/main/java/io/bisq/gui/app/BisqApp.java b/gui/src/main/java/io/bisq/gui/app/BisqApp.java index b33a15f021..e4a04d8e3d 100644 --- a/gui/src/main/java/io/bisq/gui/app/BisqApp.java +++ b/gui/src/main/java/io/bisq/gui/app/BisqApp.java @@ -100,23 +100,21 @@ public class BisqApp extends Application { private static final long LOG_MEMORY_PERIOD_MIN = 10; private static BisqEnvironment bisqEnvironment; - - private BisqAppModule bisqAppModule; - private Injector injector; - private boolean popupOpened; - - private static Stage primaryStage; - private Scene scene; - private final List corruptedDatabaseFiles = new ArrayList<>(); - private MainView mainView; - public static Runnable shutDownHandler; - private boolean shutDownRequested; + private static Stage primaryStage; public static void setEnvironment(BisqEnvironment bisqEnvironment) { BisqApp.bisqEnvironment = bisqEnvironment; } + private BisqAppModule bisqAppModule; + private Injector injector; + private boolean popupOpened; + private Scene scene; + private final List corruptedDatabaseFiles = new ArrayList<>(); + private MainView mainView; + private boolean shutDownRequested; + @SuppressWarnings("PointlessBooleanExpression") @Override public void start(Stage stage) throws IOException { diff --git a/gui/src/main/java/io/bisq/gui/main/MainView.java b/gui/src/main/java/io/bisq/gui/main/MainView.java index 0c095f3f97..7284f0cd3c 100644 --- a/gui/src/main/java/io/bisq/gui/main/MainView.java +++ b/gui/src/main/java/io/bisq/gui/main/MainView.java @@ -159,7 +159,7 @@ public class MainView extends InitializableView { }); }, 1); } - + HBox leftNavPane = new HBox(marketButton, buyButton, sellButton, portfolioButtonHolder, fundsButton, disputesButtonHolder) {{ setLeftAnchor(this, 10d); setTopAnchor(this, 0d); @@ -330,7 +330,7 @@ public class MainView extends InitializableView { HBox.setMargin(btcAverageIconButton, new Insets(0, 5, 0, 0)); btcAverageIconButton.setOnAction(e -> GUIUtil.openWebPage("https://bitcoinaverage.com")); btcAverageIconButton.setVisible(model.isFiatCurrencyPriceFeedSelected.get()); - btcAverageIconButton.setManaged(model.isFiatCurrencyPriceFeedSelected.get()); + btcAverageIconButton.setManaged(btcAverageIconButton.isVisible()); btcAverageIconButton.visibleProperty().bind(model.isFiatCurrencyPriceFeedSelected); btcAverageIconButton.managedProperty().bind(model.isFiatCurrencyPriceFeedSelected); btcAverageIconButton.setOnMouseEntered(e -> { @@ -354,7 +354,7 @@ public class MainView extends InitializableView { HBox.setMargin(poloniexIconButton, new Insets(2, 3, 0, 0)); poloniexIconButton.setOnAction(e -> GUIUtil.openWebPage("https://poloniex.com")); poloniexIconButton.setVisible(model.isCryptoCurrencyPriceFeedSelected.get()); - poloniexIconButton.setManaged(model.isCryptoCurrencyPriceFeedSelected.get()); + poloniexIconButton.setManaged(poloniexIconButton.isVisible()); poloniexIconButton.visibleProperty().bind(model.isCryptoCurrencyPriceFeedSelected); poloniexIconButton.managedProperty().bind(model.isCryptoCurrencyPriceFeedSelected); poloniexIconButton.setOnMouseEntered(e -> { @@ -372,10 +372,28 @@ public class MainView extends InitializableView { Label label = new Label(Res.get("mainView.marketPrice.provider")); label.setId("nav-balance-label"); label.setPadding(new Insets(0, 5, 0, 2)); - label.visibleProperty().bind(createBooleanBinding(() -> model.isCryptoCurrencyPriceFeedSelected.get() || model.isFiatCurrencyPriceFeedSelected.get(), - model.isCryptoCurrencyPriceFeedSelected, model.isFiatCurrencyPriceFeedSelected)); + label.visibleProperty().bind(createBooleanBinding(() -> model.isPriceAvailable.get() && + (model.isCryptoCurrencyPriceFeedSelected.get() || + model.isFiatCurrencyPriceFeedSelected.get() || + !model.isExternallyProvidedPrice.get()), + model.isPriceAvailable, + model.isCryptoCurrencyPriceFeedSelected, + model.isFiatCurrencyPriceFeedSelected, + model.isExternallyProvidedPrice)); label.managedProperty().bind(label.visibleProperty()); + model.isExternallyProvidedPrice.addListener((observable, oldValue, newValue) -> { + if (newValue) { + label.setText(Res.get("mainView.marketPrice.provider")); + label.setTooltip(null); + } else { + label.setText(Res.get("mainView.marketPrice.bisqInternalPrice")); + final Tooltip tooltip = new Tooltip(Res.get("mainView.marketPrice.tooltip.bisqInternalPrice")); + tooltip.setStyle("-fx-font-size: 12"); + label.setTooltip(tooltip); + } + }); + HBox hBox2 = new HBox(); hBox2.getChildren().setAll(label, btcAverageIconButton, poloniexIconButton); diff --git a/gui/src/main/java/io/bisq/gui/main/MainViewModel.java b/gui/src/main/java/io/bisq/gui/main/MainViewModel.java index e499ebe710..8955da0372 100644 --- a/gui/src/main/java/io/bisq/gui/main/MainViewModel.java +++ b/gui/src/main/java/io/bisq/gui/main/MainViewModel.java @@ -37,6 +37,7 @@ import io.bisq.core.alert.PrivateNotificationManager; import io.bisq.core.alert.PrivateNotificationPayload; import io.bisq.core.app.AppOptionKeys; import io.bisq.core.app.BisqEnvironment; +import io.bisq.core.app.SetupUtils; import io.bisq.core.arbitration.ArbitratorManager; import io.bisq.core.arbitration.Dispute; import io.bisq.core.arbitration.DisputeManager; @@ -148,6 +149,8 @@ public class MainViewModel implements ViewModel { final ObjectProperty selectedPriceFeedComboBoxItemProperty = new SimpleObjectProperty<>(); final BooleanProperty isFiatCurrencyPriceFeedSelected = new SimpleBooleanProperty(true); final BooleanProperty isCryptoCurrencyPriceFeedSelected = new SimpleBooleanProperty(false); + final BooleanProperty isExternallyProvidedPrice = new SimpleBooleanProperty(true); + final BooleanProperty isPriceAvailable = new SimpleBooleanProperty(false); final StringProperty availableBalance = new SimpleStringProperty(); final StringProperty reservedBalance = new SimpleStringProperty(); final StringProperty lockedBalance = new SimpleStringProperty(); @@ -190,7 +193,6 @@ public class MainViewModel implements ViewModel { private BooleanProperty p2pNetWorkReady; private final BooleanProperty walletInitialized = new SimpleBooleanProperty(); private boolean allBasicServicesInitialized; - private BooleanProperty loadEntryMapResult, checkCryptoSetupResult; /////////////////////////////////////////////////////////////////////////////////////////// @@ -255,81 +257,36 @@ public class MainViewModel implements ViewModel { public void start() { //noinspection ConstantConditions,ConstantConditions + bisqEnvironment.saveBaseCryptoNetwork(BisqEnvironment.getBaseCurrencyNetwork()); + if (!preferences.isTacAccepted() && !DevEnv.DEV_MODE) { UserThread.runAfter(() -> { tacWindow.onAction(() -> { preferences.setTacAccepted(true); - showSelectBaseCurrencyWindow(); + checkIfLocalHostNodeIsRunning(); }).show(); }, 1); } else { - showSelectBaseCurrencyWindow(); + checkIfLocalHostNodeIsRunning(); } } - private void showSelectBaseCurrencyWindow() { - String key = "showSelectBaseCurrencyWindowAtFistStartup"; - if (preferences.showAgain(key)) { - bisqEnvironment.saveBaseCryptoNetwork(BisqEnvironment.getBaseCurrencyNetwork()); - preferences.dontShowAgain(key, true); - showRevertIdCheckRequirement(); - - /* new SelectBaseCurrencyWindow() - .onSelect(baseCurrencyNetwork -> { - preferences.dontShowAgain(key, true); - final boolean hasChanged = !BisqEnvironment.getBaseCurrencyNetwork().equals(baseCurrencyNetwork); - bisqEnvironment.saveBaseCryptoNetwork(baseCurrencyNetwork); - if (hasChanged) { - new Popup().warning(Res.get("settings.net.needRestart")) - .onAction(() -> { - UserThread.runAfter(BisqApp.shutDownHandler::run, 500, TimeUnit.MILLISECONDS); - }) - .actionButtonText(Res.get("shared.shutDown")) - .hideCloseButton() - .show(); - } - }) - .actionButtonText(Res.get("selectBaseCurrencyWindow.default", BisqEnvironment.getBaseCurrencyNetwork().getCurrencyName())) - .onAction(() -> { - bisqEnvironment.saveBaseCryptoNetwork(BisqEnvironment.getBaseCurrencyNetwork()); - preferences.dontShowAgain(key, true); - showRevertIdCheckRequirement(); - }) - .hideCloseButton() - .show();*/ - } else { - showRevertIdCheckRequirement(); - } - } + private void startLoadEntryMap() { + log.info("startLoadEntryMap"); - private void showRevertIdCheckRequirement() { - //TODO remove after v0.5.2 - String key = "revertIdCheckRequirement"; - if (preferences.showAgain(key) && - user.getPaymentAccounts() != null && - user.getPaymentAccounts().stream() - .filter(e -> e.getPaymentMethod().getId().equals(PaymentMethod.SEPA_ID) || - e.getPaymentMethod().getId().equals(PaymentMethod.FASTER_PAYMENTS_ID) || - e.getPaymentMethod().getId().equals(PaymentMethod.NATIONAL_BANK_ID) || - e.getPaymentMethod().getId().equals(PaymentMethod.SAME_BANK_ID) || - e.getPaymentMethod().getId().equals(PaymentMethod.SPECIFIC_BANKS_ID) || - e.getPaymentMethod().getId().equals(PaymentMethod.CLEAR_X_CHANGE_ID) || - e.getPaymentMethod().getId().equals(PaymentMethod.CHASE_QUICK_PAY_ID) || - e.getPaymentMethod().getId().equals(PaymentMethod.INTERAC_E_TRANSFER_ID)) - .findAny() - .isPresent()) { - new Popup<>().information(Res.get("popup.info.revertIdCheckRequirement")).show(); - } - preferences.dontShowAgain(key, true); - checkIfLocalHostNodeIsRunning(); + BooleanProperty result = SetupUtils.loadEntryMap(p2PService); + result.addListener((observable, oldValue, newValue) -> { + if (newValue) + startBasicServices(); + }); + + // TODO can be removed in jdk 9 + checkCryptoSetup(); } private void startBasicServices() { log.info("startBasicServices"); - loadEntryMapResult = loadEntryMap(); - checkCryptoSetupResult = checkCryptoSetup(); - ChangeListener walletInitializedListener = (observable, oldValue, newValue) -> { if (newValue && !p2pNetWorkReady.get()) showStartupTimeoutPopup(); @@ -351,14 +308,12 @@ public class MainViewModel implements ViewModel { initWalletService(); // need to store it to not get garbage collected - allServicesDone = EasyBind.combine(checkCryptoSetupResult, loadEntryMapResult, walletInitialized, p2pNetWorkReady, - (a, b, c, d) -> { - log.info("\ncheckCryptoSetupResult={}\n" + - "loadEntryMapResult={}\n" + - "walletInitialized={}\n" + + allServicesDone = EasyBind.combine(walletInitialized, p2pNetWorkReady, + (a, b) -> { + log.info("\nwalletInitialized={}\n" + "p2pNetWorkReady={}", - a, b, c, d); - return a && b && c && d; + a, b); + return a && b; }); allServicesDone.subscribe((observable, oldValue, newValue) -> { if (newValue) { @@ -393,22 +348,6 @@ public class MainViewModel implements ViewModel { // Initialisation /////////////////////////////////////////////////////////////////////////////////////////// - private BooleanProperty loadEntryMap() { - BooleanProperty result = new SimpleBooleanProperty(); - Thread loadEntryMapThread = new Thread() { - @Override - public void run() { - Thread.currentThread().setName("loadEntryMapThread"); - // Used to load different EntryMap files per base currency (EntryMap_BTC, EntryMap_LTC,...) - final String storageFileName = "EntryMap_" + BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode(); - p2PService.readEntryMapFromResources(storageFileName); - UserThread.execute(() -> result.set(true)); - } - }; - loadEntryMapThread.start(); - return result; - } - private BooleanProperty initP2PNetwork() { log.info("initP2PNetwork"); @@ -756,11 +695,11 @@ public class MainViewModel implements ViewModel { log.info("Localhost peer detected."); UserThread.execute(() -> { bisqEnvironment.setBitcoinLocalhostNodeRunning(true); - startBasicServices(); + startLoadEntryMap(); }); } catch (Throwable e) { log.info("Localhost peer not detected."); - UserThread.execute(MainViewModel.this::startBasicServices); + UserThread.execute(MainViewModel.this::startLoadEntryMap); } finally { if (socket != null) { try { @@ -1026,12 +965,13 @@ public class MainViewModel implements ViewModel { String currencyCode = item.currencyCode; MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode); String priceString; - if (marketPrice != null && marketPrice.isValid()) { + if (marketPrice != null && marketPrice.isPriceAvailable()) { priceString = formatter.formatMarketPrice(marketPrice.getPrice(), currencyCode); - item.setIsPriceAvailable(true); + item.setPriceAvailable(true); + item.setExternallyProvidedPrice(marketPrice.isExternallyProvidedPrice()); } else { priceString = Res.get("shared.na"); - item.setIsPriceAvailable(false); + item.setPriceAvailable(false); } item.setDisplayString(formatter.getCurrencyPair(currencyCode) + ": " + priceString); }); @@ -1057,8 +997,10 @@ public class MainViewModel implements ViewModel { UserThread.runAfter(() -> { if (item != null) { String code = item.currencyCode; - isFiatCurrencyPriceFeedSelected.set(CurrencyUtil.isFiatCurrency(code) && CurrencyUtil.getFiatCurrency(code).isPresent() && item.isPriceAvailable()); - isCryptoCurrencyPriceFeedSelected.set(CurrencyUtil.isCryptoCurrency(code) && CurrencyUtil.getCryptoCurrency(code).isPresent() && item.isPriceAvailable()); + isFiatCurrencyPriceFeedSelected.set(CurrencyUtil.isFiatCurrency(code) && CurrencyUtil.getFiatCurrency(code).isPresent() && item.isPriceAvailable() && item.isExternallyProvidedPrice()); + isCryptoCurrencyPriceFeedSelected.set(CurrencyUtil.isCryptoCurrency(code) && CurrencyUtil.getCryptoCurrency(code).isPresent() && item.isPriceAvailable() && item.isExternallyProvidedPrice()); + isExternallyProvidedPrice.set(item.isExternallyProvidedPrice()); + isPriceAvailable.set(item.isPriceAvailable()); } }, 100, TimeUnit.MILLISECONDS); } diff --git a/gui/src/main/java/io/bisq/gui/main/PriceFeedComboBoxItem.java b/gui/src/main/java/io/bisq/gui/main/PriceFeedComboBoxItem.java index 19d5737e48..638fadd485 100644 --- a/gui/src/main/java/io/bisq/gui/main/PriceFeedComboBoxItem.java +++ b/gui/src/main/java/io/bisq/gui/main/PriceFeedComboBoxItem.java @@ -2,15 +2,18 @@ package io.bisq.gui.main; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.Getter; +import lombok.Setter; public class PriceFeedComboBoxItem { - private static final Logger log = LoggerFactory.getLogger(PriceFeedComboBoxItem.class); - public final String currencyCode; public final StringProperty displayStringProperty = new SimpleStringProperty(); + @Setter + @Getter private boolean isPriceAvailable; + @Setter + @Getter + private boolean isExternallyProvidedPrice; public PriceFeedComboBoxItem(String currencyCode) { this.currencyCode = currencyCode; @@ -19,12 +22,4 @@ public class PriceFeedComboBoxItem { public void setDisplayString(String displayString) { this.displayStringProperty.set(displayString); } - - public void setIsPriceAvailable(boolean isPriceAvailable) { - this.isPriceAvailable = isPriceAvailable; - } - - public boolean isPriceAvailable() { - return isPriceAvailable; - } } diff --git a/gui/src/main/java/io/bisq/gui/main/dao/DaoView.java b/gui/src/main/java/io/bisq/gui/main/dao/DaoView.java index 88fc044007..2b446ee032 100644 --- a/gui/src/main/java/io/bisq/gui/main/dao/DaoView.java +++ b/gui/src/main/java/io/bisq/gui/main/dao/DaoView.java @@ -19,6 +19,7 @@ package io.bisq.gui.main.dao; import io.bisq.common.app.DevEnv; import io.bisq.common.locale.Res; +import io.bisq.core.app.BisqEnvironment; import io.bisq.gui.Navigation; import io.bisq.gui.common.model.Activatable; import io.bisq.gui.common.view.*; @@ -63,7 +64,7 @@ public class DaoView extends ActivatableViewAndModel { votingTab.setClosable(false); root.getTabs().addAll(compensationTab, votingTab); - if (!DevEnv.DAO_PHASE2_ACTIVATED) { + if (BisqEnvironment.isDAOActivatedAndBaseCurrencySupportingBsq() && !DevEnv.DAO_PHASE2_ACTIVATED) { votingTab.setDisable(true); compensationTab.setDisable(true); } diff --git a/gui/src/main/java/io/bisq/gui/main/dao/wallet/BsqWalletView.java b/gui/src/main/java/io/bisq/gui/main/dao/wallet/BsqWalletView.java index 7714edfeac..5907fd9fd4 100644 --- a/gui/src/main/java/io/bisq/gui/main/dao/wallet/BsqWalletView.java +++ b/gui/src/main/java/io/bisq/gui/main/dao/wallet/BsqWalletView.java @@ -19,8 +19,8 @@ package io.bisq.gui.main.dao.wallet; import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeIcon; -import io.bisq.common.app.DevEnv; import io.bisq.common.locale.Res; +import io.bisq.core.app.BisqEnvironment; import io.bisq.gui.Navigation; import io.bisq.gui.common.view.*; import io.bisq.gui.main.MainView; @@ -83,7 +83,7 @@ public class BsqWalletView extends ActivatableViewAndModel { leftVBox.getChildren().addAll(dashboard, send, receive, transactions); // TODO just until DAO is enabled - if (!DevEnv.DAO_ACTIVATED) { + if (!BisqEnvironment.isDAOActivatedAndBaseCurrencySupportingBsq()) { dashboard.setDisable(true); send.setDisable(true); transactions.setDisable(true); @@ -105,7 +105,7 @@ public class BsqWalletView extends ActivatableViewAndModel { selectedViewClass = BsqDashboardView.class; // TODO just until DAO is enabled - if (!DevEnv.DAO_ACTIVATED) + if (!BisqEnvironment.isDAOActivatedAndBaseCurrencySupportingBsq()) selectedViewClass = BsqReceiveView.class; loadView(selectedViewClass); diff --git a/gui/src/main/java/io/bisq/gui/main/dao/wallet/dashboard/BsqDashboardView.java b/gui/src/main/java/io/bisq/gui/main/dao/wallet/dashboard/BsqDashboardView.java index 18cbe3ee08..1f547f3f07 100644 --- a/gui/src/main/java/io/bisq/gui/main/dao/wallet/dashboard/BsqDashboardView.java +++ b/gui/src/main/java/io/bisq/gui/main/dao/wallet/dashboard/BsqDashboardView.java @@ -17,45 +17,149 @@ package io.bisq.gui.main.dao.wallet.dashboard; +import de.jensd.fx.fontawesome.AwesomeIcon; +import io.bisq.common.locale.Res; +import io.bisq.common.monetary.Altcoin; +import io.bisq.common.monetary.Price; +import io.bisq.common.util.MathUtils; +import io.bisq.core.dao.blockchain.BsqBlockchainManager; +import io.bisq.core.dao.blockchain.BsqChainStateListener; +import io.bisq.core.dao.blockchain.parse.BsqChainState; +import io.bisq.core.provider.price.MarketPrice; +import io.bisq.core.provider.price.PriceFeedService; +import io.bisq.core.user.Preferences; import io.bisq.gui.common.view.ActivatableView; import io.bisq.gui.common.view.FxmlView; +import io.bisq.gui.components.HyperlinkWithIcon; import io.bisq.gui.main.dao.wallet.BsqBalanceUtil; +import io.bisq.gui.util.BsqFormatter; +import io.bisq.gui.util.GUIUtil; +import io.bisq.gui.util.Layout; +import javafx.beans.value.ChangeListener; +import javafx.geometry.Insets; +import javafx.scene.control.Label; import javafx.scene.control.TextField; +import javafx.scene.control.Tooltip; import javafx.scene.layout.GridPane; +import org.bitcoinj.core.Coin; import javax.inject.Inject; -@FxmlView -public class BsqDashboardView extends ActivatableView { +import static io.bisq.gui.util.FormBuilder.addLabelTextField; +import static io.bisq.gui.util.FormBuilder.addTitledGroupBg; - private TextField balanceTextField; +@FxmlView +public class BsqDashboardView extends ActivatableView implements BsqChainStateListener { private final BsqBalanceUtil bsqBalanceUtil; + private final BsqBlockchainManager bsqBlockchainManager; + private final BsqChainState bsqChainState; + private final PriceFeedService priceFeedService; + private final Preferences preferences; + private final BsqFormatter bsqFormatter; private int gridRow = 0; + private TextField issuedAmountTextField, availableAmountTextField, burntAmountTextField, allTxTextField, + burntTxTextField, spentTxTextField, + utxoTextField, priceTextField, marketCapTextField; + private ChangeListener priceChangeListener; + private HyperlinkWithIcon hyperlinkWithIcon; + /////////////////////////////////////////////////////////////////////////////////////////// // Constructor, lifecycle /////////////////////////////////////////////////////////////////////////////////////////// @Inject - private BsqDashboardView(BsqBalanceUtil bsqBalanceUtil) { + private BsqDashboardView(BsqBalanceUtil bsqBalanceUtil, BsqBlockchainManager bsqBlockchainManager, + BsqChainState bsqChainState, PriceFeedService priceFeedService, + Preferences preferences, BsqFormatter bsqFormatter) { this.bsqBalanceUtil = bsqBalanceUtil; + this.bsqBlockchainManager = bsqBlockchainManager; + this.bsqChainState = bsqChainState; + this.priceFeedService = priceFeedService; + this.preferences = preferences; + this.bsqFormatter = bsqFormatter; } @Override public void initialize() { gridRow = bsqBalanceUtil.addGroup(root, gridRow); + + addTitledGroupBg(root, ++gridRow, 12, Res.get("dao.wallet.dashboard.statistics"), Layout.GROUP_DISTANCE); + + addLabelTextField(root, gridRow, Res.get("dao.wallet.dashboard.genesisBlockHeight"), + String.valueOf(bsqChainState.getGenesisBlockHeight()), Layout.FIRST_ROW_AND_GROUP_DISTANCE); + + Label label = new Label(Res.get("dao.wallet.dashboard.genesisTxId")); + GridPane.setRowIndex(label, ++gridRow); + root.getChildren().add(label); + hyperlinkWithIcon = new HyperlinkWithIcon(bsqChainState.getGenesisTxId(), AwesomeIcon.EXTERNAL_LINK); + hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", bsqChainState.getGenesisTxId()))); + GridPane.setRowIndex(hyperlinkWithIcon, gridRow); + GridPane.setColumnIndex(hyperlinkWithIcon, 1); + GridPane.setMargin(hyperlinkWithIcon, new Insets(0, 0, 0, -4)); + root.getChildren().add(hyperlinkWithIcon); + + issuedAmountTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.issuedAmount")).second; + availableAmountTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.availableAmount")).second; + burntAmountTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.burntAmount")).second; + + allTxTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.allTx")).second; + utxoTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.utxo")).second; + spentTxTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.spentTxo")).second; + burntTxTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.burntTx")).second; + + priceTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.price")).second; + marketCapTextField = addLabelTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.marketCap")).second; + + priceChangeListener = (observable, oldValue, newValue) -> updatePrice(); } @Override protected void activate() { bsqBalanceUtil.activate(); + + bsqBlockchainManager.addBsqChainStateListener(this); + priceFeedService.updateCounterProperty().addListener(priceChangeListener); + + hyperlinkWithIcon.setOnAction(event -> GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + bsqChainState.getGenesisTxId())); + + onBsqChainStateChanged(); + updatePrice(); } @Override protected void deactivate() { bsqBalanceUtil.deactivate(); + bsqBlockchainManager.removeBsqChainStateListener(this); + priceFeedService.updateCounterProperty().removeListener(priceChangeListener); + hyperlinkWithIcon.setOnAction(null); + } + + + @Override + public void onBsqChainStateChanged() { + issuedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(bsqChainState.getIssuedAmount())); + final Coin burntFee = bsqChainState.getTotalBurntFee(); + final Coin availableAmount = bsqChainState.getIssuedAmount().subtract(burntFee); + availableAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(availableAmount)); + burntAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(burntFee)); + allTxTextField.setText(String.valueOf(bsqChainState.getTransactions().size())); + utxoTextField.setText(String.valueOf(bsqChainState.getUnspentTxOutputs().size())); + spentTxTextField.setText(String.valueOf(bsqChainState.getSpentTxOutputs().size())); + burntTxTextField.setText(String.valueOf(bsqChainState.getFeeTransactions().size())); + } + + private void updatePrice() { + final Coin issuedAmount = bsqChainState.getIssuedAmount(); + final MarketPrice bsqMarketPrice = priceFeedService.getMarketPrice("BSQ"); + if (bsqMarketPrice != null) { + long bsqPrice = MathUtils.roundDoubleToLong(MathUtils.scaleUpByPowerOf10(bsqMarketPrice.getPrice(), Altcoin.SMALLEST_UNIT_EXPONENT)); + priceTextField.setText(bsqFormatter.formatPrice(Price.valueOf("BSQ", bsqPrice)) + " BSQ/BTC"); + + marketCapTextField.setText(bsqFormatter.formatMarketCap(bsqMarketPrice, priceFeedService.getMarketPrice("USD"), issuedAmount)); + } } } diff --git a/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java b/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java index bcdb3afa2d..ad9f133faa 100644 --- a/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java +++ b/gui/src/main/java/io/bisq/gui/main/market/offerbook/OfferBookChartView.java @@ -410,8 +410,9 @@ public class OfferBookChartView extends ActivatableViewAndModel accumulatedColumn = new TableColumn<>(Res.get("shared.sumWithCur", Res.getBaseCurrencyCode())); + /* TableColumn accumulatedColumn = new TableColumn<>(Res.get("shared.sumWithCur", Res.getBaseCurrencyCode())); accumulatedColumn.setMinWidth(100); accumulatedColumn.setSortable(false); accumulatedColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); @@ -431,9 +432,9 @@ public class OfferBookChartView extends ActivatableViewAndModel= 0, "securityDeposit must be not be less than " + Restrictions.getMinBuyerSecurityDeposit().toFriendlyString()); + + checkArgument(!filterManager.isCurrencyBanned(currencyCode), + Res.get("offerbook.warning.currencyBanned")); + checkArgument(!filterManager.isPaymentMethodBanned(paymentAccount.getPaymentMethod()), + Res.get("offerbook.warning.paymentMethodBanned")); + OfferPayload offerPayload = new OfferPayload(offerId, new Date().getTime(), p2PService.getAddress(), diff --git a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java index 679a45fa52..131f04626f 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/createoffer/CreateOfferView.java @@ -194,7 +194,8 @@ public class CreateOfferView extends ActivatableViewAndModel().backgroundInfo(Res.get("popup.info.securityDepositInfo")) .actionButtonText(Res.get("shared.faq")) - .onAction(() -> GUIUtil.openWebPage("https://bisq.io/faq#6")) + .onAction(() -> GUIUtil.openWebPage("https://bisq.network/faq#6")) .useIUnderstandButton() .dontShowAgainId(key) .show(); @@ -495,8 +496,11 @@ public class CreateOfferView extends ActivatableViewAndModel tuple = add2ButtonsAfterGroup(gridPane, ++gridRow, Res.get("shared.nextStep"), Res.get("shared.cancel")); @@ -880,15 +894,20 @@ public class CreateOfferView extends ActivatableViewAndModel onShowPayFundsScreen()); - delay -= diff; - transitions.fadeOutAndRemove(makerFeeTextLabel, delay); - transitions.fadeOutAndRemove(makerFeeRowHBox, delay); + if (model.dataModel.isBsqForFeeAvailable()) { + delay -= diff; + transitions.fadeOutAndRemove(makerFeeTextLabel, delay); + transitions.fadeOutAndRemove(makerFeeRowHBox, delay); + } delay -= diff; transitions.fadeOutAndRemove(buyerSecurityDepositLabel, delay); transitions.fadeOutAndRemove(buyerSecurityDepositValueCurrencyBox, delay); + + // We hide that, but leave it in code as it might be reconsidered to show it + /* delay -= diff; transitions.fadeOutAndRemove(sellerSecurityDepositLabel, delay); - transitions.fadeOutAndRemove(sellerSecurityDepositValueCurrencyBox, delay); + transitions.fadeOutAndRemove(sellerSecurityDepositValueCurrencyBox, delay);*/ } }); } @@ -953,17 +972,21 @@ public class CreateOfferView extends ActivatableViewAndModel tuple = getEditableValueCurrencyBox( Res.get("createOffer.securityDeposit.prompt")); buyerSecurityDepositValueCurrencyBox = tuple.first; buyerSecurityDepositInputTextField = tuple.second; buyerSecurityDepositBtcLabel = tuple.third; - buyerSecurityDepositBtcLabel.setMinWidth(makerFeeCurrencyLabel.getMinWidth()); - buyerSecurityDepositBtcLabel.setMaxWidth(makerFeeCurrencyLabel.getMaxWidth()); + + if (makerFeeCurrencyLabel != null) { + buyerSecurityDepositBtcLabel.setMinWidth(makerFeeCurrencyLabel.getMinWidth()); + buyerSecurityDepositBtcLabel.setMaxWidth(makerFeeCurrencyLabel.getMaxWidth()); + } editOfferElements.add(buyerSecurityDepositInputTextField); editOfferElements.add(buyerSecurityDepositBtcLabel); @@ -971,6 +994,7 @@ public class CreateOfferView extends ActivatableViewAndModel { switch (BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode()) { case "BTC": - amount.set("0.01"); - price.set("2000"); + amount.set("0.0101"); + price.set("3500"); break; case "LTC": amount.set("50"); @@ -301,7 +301,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel priceColumn.sortTypeProperty().bind(model.priceSortTypeProperty)); + model.priceSortTypeProperty.addListener((observable, oldValue, newValue) -> { + priceColumn.setSortType(newValue); + }); paymentMethodComboBox.setItems(model.getPaymentMethods()); paymentMethodComboBox.setOnAction(e -> model.onSetPaymentMethod(paymentMethodComboBox.getSelectionModel().getSelectedItem())); @@ -268,9 +268,11 @@ public class OfferBookView extends ActivatableViewAndModel().warning(Res.get("offerbook.warning.userIgnored")).show(); } else if (isOfferBanned) { new Popup<>().warning(Res.get("offerbook.warning.offerBlocked")).show(); - } else if (isNodeBanned) { + } else if (isCurrencyBanned) { + new Popup<>().warning(Res.get("offerbook.warning.currencyBanned")).show(); + } else if (isPaymentMethodBanned) { + new Popup<>().warning(Res.get("offerbook.warning.paymentMethodBanned")).show(); + } else if (isNodeAddressBanned) { new Popup<>().warning(Res.get("offerbook.warning.nodeBlocked")).show(); } } @@ -672,7 +679,8 @@ public class OfferBookView extends ActivatableViewAndModel onShowInfo(isPaymentAccountValidForOffer, hasMatchingArbitrator, hasSameProtocolVersion, - isIgnored, isOfferBanned, isNodeBanned)); + isIgnored, isOfferBanned, isCurrencyBanned, isPaymentMethodBanned, + isNodeAddressBanned)); button.setText(title); setGraphic(button); diff --git a/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookViewModel.java b/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookViewModel.java index e3f9a7869e..8eb070b8a2 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookViewModel.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/offerbook/OfferBookViewModel.java @@ -448,19 +448,19 @@ class OfferBookViewModel extends ActivatableViewModel { } boolean isOfferBanned(Offer offer) { - return filterManager.getFilter() != null && - filterManager.getFilter().getBannedOfferIds().stream() - .filter(e -> e.equals(offer.getId())) - .findAny() - .isPresent(); + return filterManager.isOfferIdBanned(offer.getId()); } - boolean isNodeBanned(Offer offer) { - return filterManager.getFilter() != null && - filterManager.getFilter().getBannedNodeAddress().stream() - .filter(e -> e.equals(offer.getMakerNodeAddress().getHostNameWithoutPostFix())) - .findAny() - .isPresent(); + boolean isCurrencyBanned(Offer offer) { + return filterManager.isCurrencyBanned(offer.getCurrencyCode()); + } + + boolean isPaymentMethodBanned(Offer offer) { + return filterManager.isPaymentMethodBanned(offer.getPaymentMethod()); + } + + boolean isNodeAddressBanned(Offer offer) { + return filterManager.isNodeAddressBanned(offer.getMakerNodeAddress().getHostNameWithoutPostFix()); } boolean hasSameProtocolVersion(Offer offer) { diff --git a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferDataModel.java b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferDataModel.java index 3477c3f230..153edc6ca8 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferDataModel.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferDataModel.java @@ -29,6 +29,7 @@ import io.bisq.core.btc.Restrictions; import io.bisq.core.btc.listeners.BalanceListener; import io.bisq.core.btc.wallet.BsqWalletService; import io.bisq.core.btc.wallet.BtcWalletService; +import io.bisq.core.filter.FilterManager; import io.bisq.core.offer.Offer; import io.bisq.core.offer.OfferPayload; import io.bisq.core.payment.PaymentAccount; @@ -66,6 +67,7 @@ class TakeOfferDataModel extends ActivatableDataModel { private final BsqWalletService bsqWalletService; private final User user; private final FeeService feeService; + private final FilterManager filterManager; private final Preferences preferences; private final PriceFeedService priceFeedService; private final BSFormatter formatter; @@ -103,7 +105,7 @@ class TakeOfferDataModel extends ActivatableDataModel { @Inject TakeOfferDataModel(TradeManager tradeManager, BtcWalletService btcWalletService, BsqWalletService bsqWalletService, - User user, FeeService feeService, + User user, FeeService feeService, FilterManager filterManager, Preferences preferences, PriceFeedService priceFeedService, BSFormatter formatter) { this.tradeManager = tradeManager; @@ -111,6 +113,7 @@ class TakeOfferDataModel extends ActivatableDataModel { this.bsqWalletService = bsqWalletService; this.user = user; this.feeService = feeService; + this.filterManager = filterManager; this.preferences = preferences; this.priceFeedService = priceFeedService; this.formatter = formatter; @@ -274,21 +277,31 @@ class TakeOfferDataModel extends ActivatableDataModel { if (isBuyOffer()) fundsNeededForTrade = fundsNeededForTrade.add(amount.get()); - tradeManager.onTakeOffer(amount.get(), - txFeeFromFeeService, - getTakerFee(), - isCurrencyForTakerFeeBtc(), - tradePrice.getValue(), - fundsNeededForTrade, - offer, - paymentAccount.getId(), - useSavingsWallet, - tradeResultHandler, - errorMessage -> { - log.warn(errorMessage); - new Popup<>().warning(errorMessage).show(); - } - ); + if (filterManager.isCurrencyBanned(offer.getCurrencyCode())) { + new Popup<>().warning(Res.get("offerbook.warning.currencyBanned")).show(); + } else if (filterManager.isPaymentMethodBanned(offer.getPaymentMethod())) { + new Popup<>().warning(Res.get("offerbook.warning.paymentMethodBanned")).show(); + } else if (filterManager.isOfferIdBanned(offer.getId())) { + new Popup<>().warning(Res.get("offerbook.warning.offerBlocked")).show(); + } else if (filterManager.isNodeAddressBanned(offer.getMakerNodeAddress().getHostNameWithoutPostFix())) { + new Popup<>().warning(Res.get("offerbook.warning.nodeBlocked")).show(); + } else { + tradeManager.onTakeOffer(amount.get(), + txFeeFromFeeService, + getTakerFee(), + isCurrencyForTakerFeeBtc(), + tradePrice.getValue(), + fundsNeededForTrade, + offer, + paymentAccount.getId(), + useSavingsWallet, + tradeResultHandler, + errorMessage -> { + log.warn(errorMessage); + new Popup<>().warning(errorMessage).show(); + } + ); + } } public void onPaymentAccountSelected(PaymentAccount paymentAccount) { diff --git a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java index 9794125e44..375480673d 100644 --- a/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java +++ b/gui/src/main/java/io/bisq/gui/main/offer/takeoffer/TakeOfferView.java @@ -89,7 +89,7 @@ public class TakeOfferView extends ActivatableViewAndModel().warning(Res.get("takeOffer.noPriceFeedAvailable")) .onClose(this::close) @@ -384,9 +390,12 @@ public class TakeOfferView extends ActivatableViewAndModel().backgroundInfo(Res.get("popup.info.securityDepositInfo")) .actionButtonText(Res.get("shared.faq")) - .onAction(() -> GUIUtil.openWebPage("https://bisq.io/faq#6")) + .onAction(() -> GUIUtil.openWebPage("https://bisq.network/faq#6")) .useIUnderstandButton() .dontShowAgainId(key) .show(); @@ -476,9 +485,10 @@ public class TakeOfferView extends ActivatableViewAndModel model.dataModel.getCurrencyCode() + "/" + Res.getBaseCurrencyCode())); priceAsPercentageLabel.prefWidthProperty().bind(priceCurrencyLabel.widthProperty()); nextButton.disableProperty().bind(model.isNextButtonDisabled); - takerFeeTextField.textProperty().bind(model.takerFee); - takerFeeCurrencyLabel.textProperty().bind(model.takerFeeCurrencyCode); - + if (model.dataModel.isBsqForFeeAvailable()) { + takerFeeTextField.textProperty().bind(model.takerFee); + takerFeeCurrencyLabel.textProperty().bind(model.takerFeeCurrencyCode); + } // funding fundingHBox.visibleProperty().bind(model.dataModel.isWalletFunded.not().and(model.showPayFundsScreenDisplayed)); fundingHBox.managedProperty().bind(model.dataModel.isWalletFunded.not().and(model.showPayFundsScreenDisplayed)); @@ -497,8 +507,10 @@ public class TakeOfferView extends ActivatableViewAndModel { @@ -754,17 +768,20 @@ public class TakeOfferView extends ActivatableViewAndModel onShowPayFundsScreen()); - delay -= diff; - transitions.fadeOutAndRemove(takerFeeTextLabel, delay); - transitions.fadeOutAndRemove(takerFeeRowHBox, delay); - delay -= diff; - transitions.fadeOutAndRemove(buyerSecurityDepositLabel, delay); - transitions.fadeOutAndRemove(buyerSecurityDepositValueCurrencyBox, delay); - delay -= diff; - transitions.fadeOutAndRemove(sellerSecurityDepositLabel, delay); - transitions.fadeOutAndRemove(sellerSecurityDepositValueCurrencyBox, delay); - + if (model.dataModel.isBsqForFeeAvailable()) { + transitions.fadeOutAndRemove(feeCurrencyTitledGroupBg, delay, (event) -> onShowPayFundsScreen()); + delay -= diff; + transitions.fadeOutAndRemove(takerFeeTextLabel, delay); + transitions.fadeOutAndRemove(takerFeeRowHBox, delay); + delay -= diff; + transitions.fadeOutAndRemove(buyerSecurityDepositLabel, delay); + transitions.fadeOutAndRemove(buyerSecurityDepositValueCurrencyBox, delay); + delay -= diff; + transitions.fadeOutAndRemove(sellerSecurityDepositLabel, delay); + transitions.fadeOutAndRemove(sellerSecurityDepositValueCurrencyBox, delay); + } else { + onShowPayFundsScreen(); + } }); cancelButton1 = new Button(Res.get("shared.cancel")); @@ -774,7 +791,9 @@ public class TakeOfferView extends ActivatableViewAndModel { forumButton.setOnAction(event -> { if (message != null) Utilities.copyToClipboard(message); - GUIUtil.openWebPage("http://forum.bisq.io"); + GUIUtil.openWebPage("http://forum.bisq.network"); }); } diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/editor/PeerInfoWithTagEditor.java b/gui/src/main/java/io/bisq/gui/main/overlays/editor/PeerInfoWithTagEditor.java index c0ddfbd66b..db5178903b 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/editor/PeerInfoWithTagEditor.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/editor/PeerInfoWithTagEditor.java @@ -78,6 +78,8 @@ public class PeerInfoWithTagEditor extends Overlay { public PeerInfoWithTagEditor numTrades(int numTrades) { this.numTrades = numTrades; + if (numTrades == 0) + width = 500; return this; } @@ -141,7 +143,10 @@ public class PeerInfoWithTagEditor extends Overlay { protected void addContent() { FormBuilder.addLabelTextField(gridPane, ++rowIndex, Res.getWithCol("shared.onionAddress"), hostName).second.setMouseTransparent(false); - FormBuilder.addLabelTextField(gridPane, ++rowIndex, Res.get("peerInfo.nrOfTrades"), String.valueOf(numTrades)); + FormBuilder.addLabelTextField(gridPane, ++rowIndex, + Res.get("peerInfo.nrOfTrades"), + numTrades > 0 ? String.valueOf(numTrades) : Res.get("peerInfo.notTradedYet")); + inputTextField = FormBuilder.addLabelInputTextField(gridPane, ++rowIndex, Res.get("peerInfo.setTag")).second; Map peerTagMap = preferences.getPeerTagMap(); String tag = peerTagMap.containsKey(hostName) ? peerTagMap.get(hostName) : ""; diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/AddBitcoinNodesWindow.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/AddBitcoinNodesWindow.java index 29a2ad1f3f..1a6946ccce 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/AddBitcoinNodesWindow.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/AddBitcoinNodesWindow.java @@ -87,13 +87,13 @@ public class AddBitcoinNodesWindow extends Overlay { "You can run it locally (127.0.0.1) or hosted on a VPS.\n" + "You can edit that settings in \"Settings/Network info\".\n\n" + "If you prefer to use the public " + Res.getBaseCurrencyName() + " network your Bitcoin transactions might get de-anonymized by chain analysis companies operating full nodes to spy on Bitcoin users.\n\n" + - "To learn more about that topic please read our FAQ on bisq.io."); + "To learn more about that topic please read our FAQ on bisq.network."); label.setWrapText(true); GridPane.setColumnSpan(label, 2); GridPane.setHalignment(label, HPos.LEFT); HyperlinkWithIcon hyperlinkWithIcon = new HyperlinkWithIcon("Open bisq FAQ", AwesomeIcon.EXTERNAL_LINK); - hyperlinkWithIcon.setOnAction(e -> GUIUtil.openWebPage("https://bisq.io/faq/#privacy_btc")); + hyperlinkWithIcon.setOnAction(e -> GUIUtil.openWebPage("https://bisq.network/faq/#privacy_btc")); GridPane.setRowIndex(hyperlinkWithIcon, ++rowIndex); GridPane.setColumnIndex(hyperlinkWithIcon, 0); GridPane.setMargin(hyperlinkWithIcon, new Insets(0, 0, 0, -4)); diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/DisplayAlertMessageWindow.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/DisplayAlertMessageWindow.java index 0b384bda61..92a1b8a4c0 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/DisplayAlertMessageWindow.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/DisplayAlertMessageWindow.java @@ -70,7 +70,7 @@ public class DisplayAlertMessageWindow extends Overlay { InputTextField offerIdsInputTextField = addLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.offers")).second; InputTextField nodesInputTextField = addLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.onions")).second; InputTextField paymentAccountFilterInputTextField = addLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.accounts")).second; + InputTextField bannedCurrenciesInputTextField = addLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.bannedCurrencies")).second; + InputTextField bannedPaymentMethodsInputTextField = addLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.bannedPaymentMethods")).second; GridPane.setHalignment(paymentAccountFilterInputTextField, HPos.RIGHT); final Filter filter = filterManager.getDevelopersFilter(); @@ -135,22 +137,33 @@ public class FilterWindow extends Overlay { }); paymentAccountFilterInputTextField.setText(sb.toString()); } + + if (filter.getBannedCurrencies() != null) + bannedCurrenciesInputTextField.setText(filter.getBannedCurrencies().stream().collect(Collectors.joining(", "))); + + if (filter.getBannedPaymentMethods() != null) + bannedPaymentMethodsInputTextField.setText(filter.getBannedPaymentMethods().stream().collect(Collectors.joining(", "))); } Button sendButton = new Button(Res.get("filterWindow.add")); sendButton.setOnAction(e -> { ArrayList offerIds = new ArrayList<>(); ArrayList nodes = new ArrayList<>(); ArrayList paymentAccountFilters = new ArrayList<>(); + ArrayList bannedCurrencies = new ArrayList<>(); + ArrayList bannedPaymentMethods = new ArrayList<>(); if (!offerIdsInputTextField.getText().isEmpty()) { offerIds = new ArrayList<>(Arrays.asList(StringUtils.deleteWhitespace(offerIdsInputTextField.getText()) .split(","))); } - if (!nodesInputTextField.getText().isEmpty()) + + if (!nodesInputTextField.getText().isEmpty()) { nodes = new ArrayList<>(Arrays.asList(StringUtils.deleteWhitespace(nodesInputTextField.getText()).replace(":9999", "") .replace(".onion", "") .split(","))); - if (!paymentAccountFilterInputTextField.getText().isEmpty()) + } + + if (!paymentAccountFilterInputTextField.getText().isEmpty()) { paymentAccountFilters = new ArrayList<>(Arrays.asList(paymentAccountFilterInputTextField.getText() .replace(", ", ",") .split(",")) @@ -162,8 +175,19 @@ public class FilterWindow extends Overlay { return new PaymentAccountFilter("", "", ""); }) .collect(Collectors.toList())); + } - if (sendFilterMessageHandler.handle(new Filter(offerIds, nodes, paymentAccountFilters), keyInputTextField.getText())) + if (!bannedCurrenciesInputTextField.getText().isEmpty()) { + bannedCurrencies = new ArrayList<>(Arrays.asList(StringUtils.deleteWhitespace(bannedCurrenciesInputTextField.getText()) + .split(","))); + } + + if (!bannedPaymentMethodsInputTextField.getText().isEmpty()) { + bannedPaymentMethods = new ArrayList<>(Arrays.asList(StringUtils.deleteWhitespace(bannedPaymentMethodsInputTextField.getText()) + .split(","))); + } + + if (sendFilterMessageHandler.handle(new Filter(offerIds, nodes, paymentAccountFilters, bannedCurrencies, bannedPaymentMethods), keyInputTextField.getText())) hide(); else new Popup<>().warning(Res.get("shared.invalidKey")).width(300).onClose(this::blurAgain).show(); diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/TacWindow.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/TacWindow.java index c56265e8af..a8c161e5d8 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/TacWindow.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/TacWindow.java @@ -6,16 +6,29 @@ import io.bisq.gui.app.BisqApp; import io.bisq.gui.components.HyperlinkWithIcon; import io.bisq.gui.main.overlays.Overlay; import javafx.geometry.Insets; +import javafx.geometry.Rectangle2D; import javafx.scene.layout.GridPane; +import javafx.stage.Screen; import static io.bisq.gui.util.FormBuilder.addHyperlinkWithIcon; public class TacWindow extends Overlay { + private final boolean smallScreen; + @Inject public TacWindow() { type = Type.Attention; - width = 900; + + Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds(); + final double primaryScreenBoundsWidth = primaryScreenBounds.getWidth(); + smallScreen = primaryScreenBoundsWidth < 1024; + if (smallScreen) { + this.width = primaryScreenBoundsWidth * 0.8; + log.warn("Very small screen: primaryScreenBounds=" + primaryScreenBounds.toString()); + } else { + width = 900; + } } @Override @@ -66,10 +79,11 @@ public class TacWindow extends Overlay { @Override protected void addMessage() { super.addMessage(); - messageLabel.setStyle("-fx-font-size: 12;"); + String fontSize = smallScreen ? "9" : "12"; + messageLabel.setStyle("-fx-font-size: " + fontSize + ";"); HyperlinkWithIcon hyperlinkWithIcon = addHyperlinkWithIcon(gridPane, ++rowIndex, Res.get("tacWindow.arbitrationSystem"), - "https://bisq.io/arbitration_system.pdf"); - hyperlinkWithIcon.setStyle("-fx-font-size: 12;"); + "https://bisq.network/arbitration_system.pdf"); + hyperlinkWithIcon.setStyle("-fx-font-size: " + fontSize + ";"); GridPane.setMargin(hyperlinkWithIcon, new Insets(-6, 0, -20, -4)); } diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/BisqInstaller.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/BisqInstaller.java index c4760b30a8..8fee5fbc7e 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/BisqInstaller.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/BisqInstaller.java @@ -29,6 +29,7 @@ import org.jetbrains.annotations.NotNull; import java.io.*; import java.security.SignatureException; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Optional; @@ -37,25 +38,30 @@ import static com.google.common.base.Preconditions.checkArgument; @Slf4j public class BisqInstaller { - static final String LOCAL_FINGER_PRINT = "F379A1C6"; - private List knownKeys = Lists.newArrayList(LOCAL_FINGER_PRINT); + static final String FINGER_PRINT_MANFRED_KARRER = "F379A1C6"; + static final String FINGER_PRINT_CHRIS_BEAMS = "5BC5ED73"; + static final String PUB_KEY_HOSTING_URL = "https://bisq.network/pubkey/"; + static final String DOWNLOAD_HOST_URL = "https://github.com/bisq-network/exchange/releases/download/"; public boolean isSupportedOS() { return Utilities.isOSX() || Utilities.isWindows() || Utilities.isLinux(); } public Optional download(String version) { - String partialUrl = "https://github.com/bisq-network/exchange/releases/download/v" + version + "/"; + String partialUrl = DOWNLOAD_HOST_URL + "v" + version + "/"; // Get installer filename on all platforms FileDescriptor installerFileDescriptor = getInstallerDescriptor(version, partialUrl); + // tells us which key was used for signing + FileDescriptor signingKeyDescriptor = getSigningKeyDescriptor(partialUrl); List keyFileDescriptors = getKeyFileDescriptors(); List sigFileDescriptors = getSigFileDescriptors(installerFileDescriptor, keyFileDescriptors); List allFiles = Lists.newArrayList(); + allFiles.addAll(Lists.newArrayList(installerFileDescriptor)); + allFiles.addAll(Lists.newArrayList(signingKeyDescriptor)); allFiles.addAll(keyFileDescriptors); allFiles.addAll(sigFileDescriptors); - allFiles.addAll(Lists.newArrayList(installerFileDescriptor)); // Download keys, sigs and Installer return getDownloadTask(allFiles); @@ -147,6 +153,11 @@ public class BisqInstaller { inputStream.close(); log.debug("KeyID used in signature: %X\n", pgpSignature.getKeyID()); publicKey = pgpPublicKeyRing.getPublicKey(pgpSignature.getKeyID()); + + // If signature is not matching the key used for signing we fail + if (publicKey == null) + return VerifyStatusEnum.FAIL; + log.debug("The ID of the selected key is %X\n", publicKey.getKeyID()); pgpSignature.init(new BcPGPContentVerifierBuilderProvider(), publicKey); @@ -166,7 +177,6 @@ public class BisqInstaller { return result ? VerifyStatusEnum.OK : VerifyStatusEnum.FAIL; } - @NotNull public FileDescriptor getInstallerDescriptor(String version, String partialUrl) { String fileName; @@ -183,9 +193,24 @@ public class BisqInstaller { return FileDescriptor.builder() .type(DownloadType.INSTALLER) - .fileName(fileName).id(fileName).loadUrl(partialUrl.concat(fileName)).build(); + .fileName(fileName) + .id(fileName) + .loadUrl(partialUrl.concat(fileName)) + .build(); } + @NotNull + public FileDescriptor getSigningKeyDescriptor(String url) { + String fileName = "signingkey.asc"; + return FileDescriptor.builder() + .type(DownloadType.SIGNING_KEY) + .fileName(fileName) + .id(fileName) + .loadUrl(url.concat(fileName)) + .build(); + } + + /** * The files containing the gpg keys of the bisq signers. * Currently these are 2 hard-coded keys, one included with bisq and the same key online for maximum security. @@ -193,22 +218,34 @@ public class BisqInstaller { * @return list of keys to check agains corresponding sigs. */ public List getKeyFileDescriptors() { - String fingerprint = LOCAL_FINGER_PRINT; - String fileName = fingerprint + ".asc"; - String fixedKeyPath = "/keys/" + fileName; - return Lists.newArrayList( - FileDescriptor.builder() - .type(DownloadType.KEY) - .fileName(fileName) - .id(fingerprint) - .loadUrl("https://bisq.io/pubkey/" + fileName).build(), - FileDescriptor.builder() - .type(DownloadType.KEY) - .fileName(fileName + "-local") - .id(fingerprint) - .loadUrl(getClass().getResource(fixedKeyPath).toExternalForm()) - .build() - ); + List list = new ArrayList<>(); + + list.add(getKeyFileDescriptor(FINGER_PRINT_MANFRED_KARRER)); + list.add(getLocalKeyFileDescriptor(FINGER_PRINT_MANFRED_KARRER)); + + list.add(getKeyFileDescriptor(FINGER_PRINT_CHRIS_BEAMS)); + list.add(getLocalKeyFileDescriptor(FINGER_PRINT_CHRIS_BEAMS)); + + return list; + } + + private FileDescriptor getKeyFileDescriptor(String fingerPrint) { + final String fileName = fingerPrint + ".asc"; + return FileDescriptor.builder() + .type(DownloadType.KEY) + .fileName(fileName) + .id(fingerPrint) + .loadUrl(PUB_KEY_HOSTING_URL + fileName) + .build(); + } + + private FileDescriptor getLocalKeyFileDescriptor(String fingerPrint) { + return FileDescriptor.builder() + .type(DownloadType.KEY) + .fileName(fingerPrint + ".asc-local") + .id(fingerPrint) + .loadUrl(getClass().getResource("/keys/" + fingerPrint + ".asc").toExternalForm()) + .build(); } /** @@ -270,6 +307,7 @@ public class BisqInstaller { INSTALLER, KEY, SIG, + SIGNING_KEY, MISC } } diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DownloadTask.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DownloadTask.java index 5402f99204..b6a98a82f3 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DownloadTask.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/DownloadTask.java @@ -119,7 +119,6 @@ public class DownloadTask extends Task> { copyInputStreamToFileNew(urlConnection.getInputStream(), outputFile, fileSize); } - public void copyInputStreamToFileNew(final InputStream source, final File destination, int fileSize) throws IOException { try { final FileOutputStream output = FileUtils.openOutputStream(destination); diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/VerifyTask.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/VerifyTask.java index 1584c7ea59..4a36d54c83 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/VerifyTask.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/downloadupdate/VerifyTask.java @@ -29,9 +29,11 @@ import javafx.concurrent.Task; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import java.io.FileReader; import java.io.IOException; import java.util.List; import java.util.Optional; +import java.util.Scanner; import java.util.stream.Collectors; @Slf4j @@ -67,31 +69,69 @@ public class VerifyTask extends Task> { return Lists.newArrayList(); } - List sigs = fileDescriptors.stream().filter(fileDescriptor -> DownloadType.SIG.equals(fileDescriptor.getType())).collect(Collectors.toList()); - List verifyDescriptors = Lists.newArrayList(); + Optional signingKeyOptional = fileDescriptors.stream() + .filter(fileDescriptor -> DownloadType.SIGNING_KEY.equals(fileDescriptor.getType())) + .findAny(); - // iterate all signatures available to us - for (FileDescriptor sig : sigs) { - VerifyDescriptor.VerifyDescriptorBuilder verifyDescriptorBuilder = VerifyDescriptor.builder().sigFile(sig.getSaveFile()); - // Sigs are linked to keys, extract all keys which have the same id - List keys = fileDescriptors.stream() - .filter(keyDescriptor -> DownloadType.KEY.equals(keyDescriptor.getType())) - .filter(keyDescriptor -> sig.getId().equals(keyDescriptor.getId())) - .collect(Collectors.toList()); - // iterate all keys which have the same id - for (FileDescriptor key : keys) { - verifyDescriptorBuilder.keyFile(key.getSaveFile()); - try { - verifyDescriptorBuilder.verifyStatusEnum(BisqInstaller.verifySignature(key.getSaveFile(), sig.getSaveFile(), installer.get().getSaveFile())); - updateMessage(key.getFileName()); - } catch (Exception e) { - verifyDescriptorBuilder.verifyStatusEnum(BisqInstaller.VerifyStatusEnum.FAIL); - log.error(e.toString()); - e.printStackTrace(); + List verifyDescriptors = Lists.newArrayList(); + if (signingKeyOptional.isPresent()) { + final FileDescriptor signingKeyFD = signingKeyOptional.get(); + StringBuilder sb = new StringBuilder(); + try { + Scanner scanner = new Scanner(new FileReader(signingKeyFD.getSaveFile())); + while (scanner.hasNext()) { + sb.append(scanner.next()); } + scanner.close(); + } catch (Exception e) { + log.error(e.toString()); + e.printStackTrace(); + VerifyDescriptor.VerifyDescriptorBuilder verifyDescriptorBuilder = VerifyDescriptor.builder(); + verifyDescriptorBuilder.verifyStatusEnum(BisqInstaller.VerifyStatusEnum.FAIL); verifyDescriptors.add(verifyDescriptorBuilder.build()); + return verifyDescriptors; } + String signingKey = sb.toString(); + + List sigs = fileDescriptors.stream() + .filter(fileDescriptor -> DownloadType.SIG.equals(fileDescriptor.getType())) + .collect(Collectors.toList()); + + // iterate all signatures available to us + for (FileDescriptor sig : sigs) { + VerifyDescriptor.VerifyDescriptorBuilder verifyDescriptorBuilder = VerifyDescriptor.builder().sigFile(sig.getSaveFile()); + // Sigs are linked to keys, extract all keys which have the same id + List keys = fileDescriptors.stream() + .filter(keyDescriptor -> DownloadType.KEY.equals(keyDescriptor.getType())) + .filter(keyDescriptor -> sig.getId().equals(keyDescriptor.getId())) + .collect(Collectors.toList()); + // iterate all keys which have the same id + for (FileDescriptor key : keys) { + if (signingKey.equals(key.getId())) { + verifyDescriptorBuilder.keyFile(key.getSaveFile()); + try { + verifyDescriptorBuilder.verifyStatusEnum(BisqInstaller.verifySignature(key.getSaveFile(), + sig.getSaveFile(), + installer.get().getSaveFile())); + updateMessage(key.getFileName()); + } catch (Exception e) { + verifyDescriptorBuilder.verifyStatusEnum(BisqInstaller.VerifyStatusEnum.FAIL); + log.error(e.toString()); + e.printStackTrace(); + } + verifyDescriptors.add(verifyDescriptorBuilder.build()); + } else { + log.trace("key not matching the defined in signingKey. We try the next."); + } + } + } + } else { + log.error("signingKey is not found"); + VerifyDescriptor.VerifyDescriptorBuilder verifyDescriptorBuilder = VerifyDescriptor.builder(); + verifyDescriptorBuilder.verifyStatusEnum(BisqInstaller.VerifyStatusEnum.FAIL); + verifyDescriptors.add(verifyDescriptorBuilder.build()); } + return verifyDescriptors; } } diff --git a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java index c401619530..9219056907 100644 --- a/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java +++ b/gui/src/main/java/io/bisq/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java @@ -37,7 +37,6 @@ import io.bisq.core.trade.SellerTrade; import io.bisq.core.trade.Trade; import io.bisq.core.trade.TradeManager; import io.bisq.core.user.Preferences; -import io.bisq.core.user.User; import io.bisq.gui.Navigation; import io.bisq.gui.common.model.ActivatableDataModel; import io.bisq.gui.main.MainView; @@ -70,7 +69,6 @@ import static com.google.common.base.Preconditions.checkNotNull; public class PendingTradesDataModel extends ActivatableDataModel { public final TradeManager tradeManager; public final BtcWalletService btcWalletService; - private final User user; private final KeyRing keyRing; public final DisputeManager disputeManager; private final P2PService p2PService; @@ -96,13 +94,12 @@ public class PendingTradesDataModel extends ActivatableDataModel { @Inject public PendingTradesDataModel(TradeManager tradeManager, BtcWalletService btcWalletService, - User user, KeyRing keyRing, DisputeManager disputeManager, + KeyRing keyRing, DisputeManager disputeManager, Preferences preferences, P2PService p2PService, Navigation navigation, WalletPasswordWindow walletPasswordWindow, NotificationCenter notificationCenter) { this.tradeManager = tradeManager; this.btcWalletService = btcWalletService; - this.user = user; this.keyRing = keyRing; this.disputeManager = disputeManager; this.preferences = preferences; diff --git a/gui/src/main/java/io/bisq/gui/main/settings/about/AboutView.java b/gui/src/main/java/io/bisq/gui/main/settings/about/AboutView.java index 80bf150e7a..d9d653de83 100644 --- a/gui/src/main/java/io/bisq/gui/main/settings/about/AboutView.java +++ b/gui/src/main/java/io/bisq/gui/main/settings/about/AboutView.java @@ -51,7 +51,7 @@ public class AboutView extends ActivatableViewAndModel { label.setWrapText(true); GridPane.setColumnSpan(label, 2); GridPane.setHalignment(label, HPos.LEFT); - HyperlinkWithIcon hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.web"), "https://bisq.io"); + HyperlinkWithIcon hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.web"), "https://bisq.network"); GridPane.setColumnSpan(hyperlinkWithIcon, 2); hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.code"), "https://github.com/bisq-network/exchange"); GridPane.setColumnSpan(hyperlinkWithIcon, 2); @@ -64,9 +64,9 @@ public class AboutView extends ActivatableViewAndModel { label.setWrapText(true); GridPane.setColumnSpan(label, 2); GridPane.setHalignment(label, HPos.LEFT); - hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.contribute"), "https://bisq.io/contribute"); + hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.contribute"), "https://bisq.network/contribute"); GridPane.setColumnSpan(hyperlinkWithIcon, 2); - hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.donate"), "https://bisq.io/contribute/#donation"); + hyperlinkWithIcon = addHyperlinkWithIcon(root, ++gridRow, Res.get("setting.about.donate"), "https://bisq.network/contribute/#donation"); GridPane.setColumnSpan(hyperlinkWithIcon, 2); final boolean isBtc = Res.getBaseCurrencyCode().equals("BTC"); diff --git a/gui/src/main/java/io/bisq/gui/main/settings/preferences/PreferencesView.java b/gui/src/main/java/io/bisq/gui/main/settings/preferences/PreferencesView.java index ac61fc1529..09f82c8c2b 100644 --- a/gui/src/main/java/io/bisq/gui/main/settings/preferences/PreferencesView.java +++ b/gui/src/main/java/io/bisq/gui/main/settings/preferences/PreferencesView.java @@ -448,6 +448,13 @@ public class PreferencesView extends ActivatableViewAndModel baseCurrencyNetworks = Arrays.asList(BaseCurrencyNetwork.values()); + + // We don't support DOGE anymore due lack of interest but leave it in the code in case it will get + // re-activated some day + baseCurrencyNetworks = baseCurrencyNetworks.stream() + .filter(e -> !e.isDoge()) + .collect(Collectors.toList()); + // show ony mainnet in production version if (!DevEnv.DEV_MODE) baseCurrencyNetworks = baseCurrencyNetworks.stream() diff --git a/gui/src/main/java/io/bisq/gui/util/BsqFormatter.java b/gui/src/main/java/io/bisq/gui/util/BsqFormatter.java index 9034e89757..5eb322c72d 100644 --- a/gui/src/main/java/io/bisq/gui/util/BsqFormatter.java +++ b/gui/src/main/java/io/bisq/gui/util/BsqFormatter.java @@ -18,20 +18,25 @@ package io.bisq.gui.util; import io.bisq.common.app.DevEnv; +import io.bisq.common.util.MathUtils; import io.bisq.core.app.BisqEnvironment; +import io.bisq.core.provider.price.MarketPrice; +import lombok.extern.slf4j.Slf4j; import org.bitcoinj.core.Address; import org.bitcoinj.core.AddressFormatException; +import org.bitcoinj.core.Coin; import org.bitcoinj.utils.MonetaryFormat; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import javax.inject.Inject; +import java.text.DecimalFormat; +@Slf4j public class BsqFormatter extends BSFormatter { - private static final Logger log = LoggerFactory.getLogger(BsqFormatter.class); @SuppressWarnings("PointlessBooleanExpression") private static final boolean useBsqAddressFormat = true || !DevEnv.DEV_MODE; private final String prefix = "B"; + private final DecimalFormat amountFormat = new DecimalFormat("###,###,###.###"); + private final DecimalFormat marketCapFormat = new DecimalFormat("###,###,###"); @Inject private BsqFormatter() { @@ -56,6 +61,8 @@ public class BsqFormatter extends BSFormatter { default: throw new RuntimeException("baseCurrencyCode not defined. baseCurrencyCode=" + baseCurrencyCode); } + + amountFormat.setMinimumFractionDigits(3); } /** @@ -83,4 +90,17 @@ public class BsqFormatter extends BSFormatter { throw new RuntimeException(e); } } + + public String formatAmountWithGroupSeparatorAndCode(Coin amount) { + return amountFormat.format(MathUtils.scaleDownByPowerOf10(amount.value, 3)) + " BSQ"; + } + + public String formatMarketCap(MarketPrice bsqPriceMarketPrice, MarketPrice fiatMarketPrice, Coin issuedAmount) { + if (bsqPriceMarketPrice != null && fiatMarketPrice != null) { + double marketCap = bsqPriceMarketPrice.getPrice() * fiatMarketPrice.getPrice() * (MathUtils.scaleDownByPowerOf10(issuedAmount.value, 3)); + return marketCapFormat.format(MathUtils.doubleToLong(marketCap)) + " " + fiatMarketPrice.getCurrencyCode(); + } else { + return ""; + } + } } diff --git a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java index e7cab304e7..5982731c24 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/AltCoinAddressValidator.java @@ -177,6 +177,28 @@ public final class AltCoinAddressValidator extends InputValidator { } catch (AddressFormatException e) { return new ValidationResult(false, getErrorMessage(e)); } + case "BSQ": + if (!input.startsWith("B")) + return new ValidationResult(false, Res.get("validation.altcoin.invalidAddress", + currencyCode, "BSQ address must start with \"B\"")); + + String addressAsBtc = input.substring(1, input.length()); + try { + switch (BisqEnvironment.getBaseCurrencyNetwork()) { + case BTC_MAINNET: + Address.fromBase58(MainNetParams.get(), addressAsBtc); + break; + case BTC_TESTNET: + Address.fromBase58(TestNet3Params.get(), addressAsBtc); + break; + case BTC_REGTEST: + Address.fromBase58(RegTestParams.get(), addressAsBtc); + break; + } + return new ValidationResult(true); + } catch (AddressFormatException e) { + return new ValidationResult(false, getErrorMessage(e)); + } case "ETH": // https://github.com/ethereum/web3.js/blob/master/lib/utils/utils.js#L403 if (!input.matches("^(0x)?[0-9a-fA-F]{40}$")) diff --git a/gui/src/main/java/io/bisq/gui/util/validation/BsqValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/BsqValidator.java index 1e06ca407a..6725b512b6 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/BsqValidator.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/BsqValidator.java @@ -45,7 +45,8 @@ public class BsqValidator extends AltcoinValidator { @Inject public BsqValidator(BsqFormatter bsqFormatter) { this.bsqFormatter = bsqFormatter; - setMaxValue(bsqFormatter.parseToCoin("2300000")); // TODO make it lower + // TODO do we want a limit here? + //setMaxValue(bsqFormatter.parseToCoin("2500000")); } public void setMaxValue(@NotNull Coin maxValue) { diff --git a/gui/src/main/resources/keys/5BC5ED73.asc b/gui/src/main/resources/keys/5BC5ED73.asc new file mode 100644 index 0000000000..d1f70e1e0c --- /dev/null +++ b/gui/src/main/resources/keys/5BC5ED73.asc @@ -0,0 +1,50 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=AWz+ +-----END PGP PUBLIC KEY BLOCK----- diff --git a/network/src/main/java/io/bisq/network/p2p/peers/getdata/GetDataRequestHandler.java b/network/src/main/java/io/bisq/network/p2p/peers/getdata/GetDataRequestHandler.java index 096ac59c0c..5429c0ba5c 100644 --- a/network/src/main/java/io/bisq/network/p2p/peers/getdata/GetDataRequestHandler.java +++ b/network/src/main/java/io/bisq/network/p2p/peers/getdata/GetDataRequestHandler.java @@ -30,7 +30,7 @@ import java.util.stream.Collectors; public class GetDataRequestHandler { private static final Logger log = LoggerFactory.getLogger(GetDataRequestHandler.class); - private static final long TIME_OUT_SEC = 40; + private static final long TIME_OUT_SEC = 60; /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/network/src/main/java/io/bisq/network/p2p/peers/getdata/RequestDataHandler.java b/network/src/main/java/io/bisq/network/p2p/peers/getdata/RequestDataHandler.java index 8ceeedcffd..68d0064371 100644 --- a/network/src/main/java/io/bisq/network/p2p/peers/getdata/RequestDataHandler.java +++ b/network/src/main/java/io/bisq/network/p2p/peers/getdata/RequestDataHandler.java @@ -36,7 +36,7 @@ import static com.google.common.base.Preconditions.checkArgument; public class RequestDataHandler implements MessageListener { private static final Logger log = LoggerFactory.getLogger(RequestDataHandler.class); - private static final long TIME_OUT_SEC = 40; + private static final long TIME_OUT_SEC = 60; private NodeAddress peersNodeAddress; diff --git a/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java b/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java index 6c2a078d2e..7d8e2f314a 100644 --- a/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java +++ b/network/src/main/java/io/bisq/network/p2p/seed/SeedNodesRepository.java @@ -19,13 +19,16 @@ public class SeedNodesRepository { @SuppressWarnings("ConstantConditions") private Set torSeedNodeAddresses = Sets.newHashSet( // BTC mainnet + + //TODO dev dont use live nodes atm! new NodeAddress("3f3cu2yw7u457ztq.onion:8000"), new NodeAddress("723ljisnynbtdohi.onion:8000"), new NodeAddress("rm7b56wbrcczpjvl.onion:8000"), new NodeAddress("fl3mmribyxgrv63c.onion:8000"), + //TODO dev // local dev - //new NodeAddress("joehwtpe7ijnz4df.onion:8000"), + // new NodeAddress("joehwtpe7ijnz4df.onion:8000"), // BTC testnet new NodeAddress("nbphlanpgbei4okt.onion:8001"), @@ -41,18 +44,18 @@ public class SeedNodesRepository { // LTC mainnet new NodeAddress("acyvotgewx46pebw.onion:8003"), - new NodeAddress("pklgy3vdfn3obkur.onion:8003"), + // new NodeAddress("pklgy3vdfn3obkur.onion:8003"), removed in version 0.6 // keep the below but we don't run them atm /* new NodeAddress("cfciqxcowuhjdnkl.onion:8003"), new NodeAddress("bolqw3hs55uii7ku.onion:8003"),*/ - // DOGE mainnet - new NodeAddress("t6bwuj75mvxswavs.onion:8006"), + // DOGE mainnet + // new NodeAddress("t6bwuj75mvxswavs.onion:8006"), removed in version 0.6 (DOGE not supported anymore) //DASH mainnet - new NodeAddress("toeu5ikb27ydscxt.onion:8009"), - new NodeAddress("ae4yvaivhnekkhqf.onion:8009") + new NodeAddress("toeu5ikb27ydscxt.onion:8009") + //new NodeAddress("ae4yvaivhnekkhqf.onion:8009") removed in version 0.6 ); // Addresses are used if the last digit of their port match the network id: @@ -95,7 +98,7 @@ public class SeedNodesRepository { String networkIdAsString = String.valueOf(networkId); Set nodeAddresses = useLocalhostForP2P ? localhostSeedNodeAddresses : torSeedNodeAddresses; Set filtered = nodeAddresses.stream() - .filter(e -> String.valueOf(e.getPort()).endsWith(networkIdAsString)) + .filter(e -> String.valueOf(e.getPort()).endsWith("0" + networkIdAsString)) .filter(e -> !e.equals(nodeAddressToExclude)) .collect(Collectors.toSet()); log.debug("SeedNodeAddresses (useLocalhostForP2P={}) for networkId {}:\nnetworkId={}", diff --git a/network/src/main/resources/EntryMap_BTC b/network/src/main/resources/EntryMap_BTC_MAINNET similarity index 100% rename from network/src/main/resources/EntryMap_BTC rename to network/src/main/resources/EntryMap_BTC_MAINNET diff --git a/network/src/test/java/io/bisq/network/p2p/mocks/MockMailboxPayload.java b/network/src/test/java/io/bisq/network/p2p/mocks/MockMailboxPayload.java index f651bfe73b..1427f775e3 100644 --- a/network/src/test/java/io/bisq/network/p2p/mocks/MockMailboxPayload.java +++ b/network/src/test/java/io/bisq/network/p2p/mocks/MockMailboxPayload.java @@ -8,7 +8,7 @@ import io.bisq.network.p2p.NodeAddress; import io.bisq.network.p2p.storage.payload.ExpirablePayload; import lombok.EqualsAndHashCode; import lombok.Value; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; +import org.apache.commons.lang3.NotImplementedException; import java.util.UUID; @@ -31,7 +31,7 @@ public final class MockMailboxPayload extends NetworkEnvelope implements Mailbox @Override public PB.NetworkEnvelope toProtoNetworkEnvelope() { - throw new NotImplementedException(); + throw new NotImplementedException("toProtoNetworkEnvelope not impl."); } diff --git a/network/src/test/java/io/bisq/network/p2p/mocks/MockPayload.java b/network/src/test/java/io/bisq/network/p2p/mocks/MockPayload.java index 9fc9e3c660..f75518d120 100644 --- a/network/src/test/java/io/bisq/network/p2p/mocks/MockPayload.java +++ b/network/src/test/java/io/bisq/network/p2p/mocks/MockPayload.java @@ -4,7 +4,7 @@ import io.bisq.common.app.Version; import io.bisq.common.proto.network.NetworkEnvelope; import io.bisq.generated.protobuffer.PB; import io.bisq.network.p2p.storage.payload.ExpirablePayload; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; +import org.apache.commons.lang3.NotImplementedException; @SuppressWarnings("ALL") public final class MockPayload extends NetworkEnvelope implements ExpirablePayload { @@ -24,7 +24,7 @@ public final class MockPayload extends NetworkEnvelope implements ExpirablePaylo @Override public PB.NetworkEnvelope toProtoNetworkEnvelope() { - throw new NotImplementedException(); + throw new NotImplementedException("toProtoNetworkEnvelope not impl."); } @Override diff --git a/network/src/test/java/io/bisq/network/p2p/storage/mocks/MockData.java b/network/src/test/java/io/bisq/network/p2p/storage/mocks/MockData.java index ffd06cd525..92baea4b25 100644 --- a/network/src/test/java/io/bisq/network/p2p/storage/mocks/MockData.java +++ b/network/src/test/java/io/bisq/network/p2p/storage/mocks/MockData.java @@ -2,7 +2,7 @@ package io.bisq.network.p2p.storage.mocks; import io.bisq.generated.protobuffer.PB; import io.bisq.network.p2p.storage.payload.StoragePayload; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; +import org.apache.commons.lang3.NotImplementedException; import javax.annotation.Nullable; import java.security.PublicKey; @@ -63,6 +63,6 @@ public class MockData implements StoragePayload { @Override public PB.ProtectedMailboxStorageEntry toProtoMessage() { - throw new NotImplementedException(); + throw new NotImplementedException("toProtoMessage not impl."); } } diff --git a/package/linux/32bitBuild.sh b/package/linux/32bitBuild.sh index 1237169d32..e3122a1208 100644 --- a/package/linux/32bitBuild.sh +++ b/package/linux/32bitBuild.sh @@ -16,7 +16,6 @@ $JAVA_HOME/bin/javapackager \ -Bruntime="$JAVA_HOME/jre" \ -BappVersion=$version \ -Bcategory=Network \ - -Bemail=team@bisq.io \ -BlicenseType=GPLv3 \ -BlicenseFile=LICENSE \ -Bicon=package/linux/icon.png \ diff --git a/package/linux/64bitBuild.sh b/package/linux/64bitBuild.sh index ff24d818a7..653fb55f8a 100644 --- a/package/linux/64bitBuild.sh +++ b/package/linux/64bitBuild.sh @@ -16,7 +16,6 @@ $JAVA_HOME/bin/javapackager \ -Bruntime="$JAVA_HOME/jre" \ -BappVersion=$version \ -Bcategory=Network \ - -Bemail=team@bisq.io \ -BlicenseType=GPLv3 \ -BlicenseFile=LICENSE \ -Bicon=package/linux/icon.png \ diff --git a/package/win/Bisq.iss b/package/win/Bisq.iss index afa0670164..8f9dff0561 100755 --- a/package/win/Bisq.iss +++ b/package/win/Bisq.iss @@ -8,8 +8,8 @@ AppVerName=Bisq AppPublisher=Bisq AppComments=Bisq AppCopyright=Copyright (C) 2016 -AppPublisherURL=https://bisq.io -AppSupportURL=https://bisq.io +AppPublisherURL=https://bisq.network +AppSupportURL=https://bisq.network ;AppUpdatesURL=http://java.com/ DefaultDirName={localappdata}\Bisq DisableStartupPrompt=Yes diff --git a/provider/pom.xml b/provider/pom.xml index f76a47cbe6..96b01af881 100644 --- a/provider/pom.xml +++ b/provider/pom.xml @@ -1,4 +1,21 @@ + + @@ -71,6 +88,11 @@ maven-shade-plugin 2.3 + + + org.bouncycastle:*:*:* + + false @@ -83,11 +105,13 @@ + true *:* + META-INF/maven/**/pom.properties META-INF/*.SF META-INF/*.DSA META-INF/*.RSA diff --git a/seednode/pom.xml b/seednode/pom.xml index b655386873..82af1c578b 100644 --- a/seednode/pom.xml +++ b/seednode/pom.xml @@ -1,4 +1,21 @@ + + @@ -71,6 +88,11 @@ maven-shade-plugin 2.3 + + + org.bouncycastle:*:*:* + + false @@ -83,11 +105,13 @@ + true *:* + META-INF/maven/**/pom.properties META-INF/*.SF META-INF/*.DSA META-INF/*.RSA diff --git a/statistics/pom.xml b/statistics/pom.xml index 81d614a82a..448feb74ce 100644 --- a/statistics/pom.xml +++ b/statistics/pom.xml @@ -1,4 +1,21 @@ + + @@ -70,6 +87,11 @@ maven-shade-plugin 2.3 + + + org.bouncycastle:*:*:* + + false @@ -82,11 +104,13 @@ + true *:* + META-INF/maven/**/pom.properties META-INF/*.SF META-INF/*.DSA META-INF/*.RSA From 0c86ae9354245295cf9452dddcc5df7bd7cf3373 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Wed, 11 Oct 2017 07:28:58 -0500 Subject: [PATCH 54/54] Add Altcoin Ellaism --- common/src/main/java/io/bisq/common/locale/CurrencyUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java index 3dcb78365b..7bbadc7d15 100644 --- a/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java +++ b/common/src/main/java/io/bisq/common/locale/CurrencyUtil.java @@ -100,6 +100,7 @@ public class CurrencyUtil { if (!baseCurrencyCode.equals("DOGE")) result.add(new CryptoCurrency("DOGE", "Dogecoin")); result.add(new CryptoCurrency("DMC", "DynamicCoin")); + result.add(new CryptoCurrency("ELLA", "Ellaism")); result.add(new CryptoCurrency("ESP", "Espers")); result.add(new CryptoCurrency("ETH", "Ether")); result.add(new CryptoCurrency("ETC", "Ether Classic"));