Merge branch 'master' into remove_awt_dependencies

This commit is contained in:
James Cox 2021-01-07 17:54:40 -06:00 committed by GitHub
commit e50a182883
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
221 changed files with 5559 additions and 3097 deletions

View File

@ -59,7 +59,7 @@ public class BitcoinDaemon extends AbstractLinuxProcess implements LinuxProcess
+ " -rpcport=" + config.bitcoinRpcPort
+ " -rpcuser=" + config.bitcoinRpcUser
+ " -rpcpassword=" + config.bitcoinRpcPassword
+ " -blocknotify=" + config.bitcoinDatadir + "/blocknotify";
+ " -blocknotify=" + "\"" + config.bitcoinDatadir + "/blocknotify" + " %s\"";
BashCommand cmd = new BashCommand(bitcoindCmd).run();
log.info("Starting ...\n$ {}", cmd.getCommand());

View File

@ -19,7 +19,6 @@ package bisq.apitest;
import java.net.InetAddress;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
@ -73,16 +72,6 @@ public class ApiTestCase {
// gRPC service stubs are used by method & scenario tests, but not e2e tests.
private static final Map<BisqAppConfig, GrpcStubs> grpcStubsCache = new HashMap<>();
public static void setUpScaffold(File callRateMeteringConfigFile,
Enum<?>... supportingApps)
throws InterruptedException, ExecutionException, IOException {
scaffold = new Scaffold(stream(supportingApps).map(Enum::name)
.collect(Collectors.joining(",")))
.setUp();
config = scaffold.config;
bitcoinCli = new BitcoinCliHelper((config));
}
public static void setUpScaffold(Enum<?>... supportingApps)
throws InterruptedException, ExecutionException, IOException {
scaffold = new Scaffold(stream(supportingApps).map(Enum::name)
@ -107,6 +96,10 @@ public class ApiTestCase {
scaffold.tearDown();
}
protected static String getEnumArrayAsString(Enum<?>[] supportingApps) {
return stream(supportingApps).map(Enum::name).collect(Collectors.joining(","));
}
protected static GrpcStubs grpcStubs(BisqAppConfig bisqAppConfig) {
if (grpcStubsCache.containsKey(bisqAppConfig)) {
return grpcStubsCache.get(bisqAppConfig);

View File

@ -19,6 +19,7 @@ package bisq.apitest.method;
import bisq.core.api.model.PaymentAccountForm;
import bisq.core.api.model.TxFeeRateInfo;
import bisq.core.payment.F2FAccount;
import bisq.core.proto.CoreProtoResolver;
import bisq.common.util.Utilities;
@ -34,6 +35,7 @@ import bisq.proto.grpc.CreatePaymentAccountRequest;
import bisq.proto.grpc.GetAddressBalanceRequest;
import bisq.proto.grpc.GetBalancesRequest;
import bisq.proto.grpc.GetFundingAddressesRequest;
import bisq.proto.grpc.GetMyOfferRequest;
import bisq.proto.grpc.GetOfferRequest;
import bisq.proto.grpc.GetPaymentAccountFormRequest;
import bisq.proto.grpc.GetPaymentAccountsRequest;
@ -133,9 +135,9 @@ public class MethodTest extends ApiTestCase {
}
}
private static void doPostStartup(boolean registerDisputeAgents,
boolean generateBtcBlock,
Enum<?>... supportingApps) {
protected static void doPostStartup(boolean registerDisputeAgents,
boolean generateBtcBlock,
Enum<?>... supportingApps) {
if (registerDisputeAgents) {
registerDisputeAgents(arbdaemon);
}
@ -223,6 +225,10 @@ public class MethodTest extends ApiTestCase {
return GetOfferRequest.newBuilder().setId(offerId).build();
}
protected final GetMyOfferRequest createGetMyOfferRequest(String offerId) {
return GetMyOfferRequest.newBuilder().setId(offerId).build();
}
protected final CancelOfferRequest createCancelOfferRequest(String offerId) {
return CancelOfferRequest.newBuilder().setId(offerId).build();
}
@ -401,6 +407,11 @@ public class MethodTest extends ApiTestCase {
return grpcStubs(bisqAppConfig).offersService.getOffer(req).getOffer();
}
protected final OfferInfo getMyOffer(BisqAppConfig bisqAppConfig, String offerId) {
var req = createGetMyOfferRequest(offerId);
return grpcStubs(bisqAppConfig).offersService.getMyOffer(req).getOffer();
}
@SuppressWarnings("ResultOfMethodCallIgnored")
protected final void cancelOffer(BisqAppConfig bisqAppConfig, String offerId) {
var req = createCancelOfferRequest(offerId);
@ -464,6 +475,21 @@ public class MethodTest extends ApiTestCase {
return grpcStubs(bisqAppConfig).walletsService.getTransaction(req).getTxInfo();
}
public bisq.core.payment.PaymentAccount createDummyF2FAccount(BisqAppConfig bisqAppConfig,
String countryCode) {
String f2fAccountJsonString = "{\n" +
" \"_COMMENTS_\": \"This is a dummy account.\",\n" +
" \"paymentMethodId\": \"F2F\",\n" +
" \"accountName\": \"Dummy " + countryCode.toUpperCase() + " F2F Account\",\n" +
" \"city\": \"Anytown\",\n" +
" \"contact\": \"Morse Code\",\n" +
" \"country\": \"" + countryCode.toUpperCase() + "\",\n" +
" \"extraInfo\": \"Salt Lick #213\"\n" +
"}\n";
F2FAccount f2FAccount = (F2FAccount) createPaymentAccount(bisqAppConfig, f2fAccountJsonString);
return f2FAccount;
}
// Static conveniences for test methods and test case fixture setups.
protected static RegisterDisputeAgentRequest createRegisterDisputeAgentRequest(String disputeAgentType) {

View File

@ -18,13 +18,13 @@
package bisq.apitest.method.offer;
import bisq.core.monetary.Altcoin;
import bisq.core.payment.PaymentAccount;
import bisq.proto.grpc.CreateOfferRequest;
import bisq.proto.grpc.GetMyOffersRequest;
import bisq.proto.grpc.GetOffersRequest;
import bisq.proto.grpc.OfferInfo;
import protobuf.PaymentAccount;
import org.bitcoinj.utils.Fiat;
import java.math.BigDecimal;
@ -121,6 +121,10 @@ public abstract class AbstractOfferTest extends MethodTest {
return aliceStubs.offersService.getOffer(createGetOfferRequest(offerId)).getOffer();
}
protected final OfferInfo getMyOffer(String offerId) {
return aliceStubs.offersService.getMyOffer(createGetMyOfferRequest(offerId)).getOffer();
}
@SuppressWarnings("ResultOfMethodCallIgnored")
protected final void cancelOffer(GrpcStubs grpcStubs, String offerId) {
grpcStubs.offersService.cancelOffer(createCancelOfferRequest(offerId));
@ -134,11 +138,9 @@ public abstract class AbstractOfferTest extends MethodTest {
return offerInfoList.get(offerInfoList.size() - 1);
}
protected final int getOpenOffersCount(GrpcStubs grpcStubs, String direction, String currencyCode) {
return getOffersSortedByDate(grpcStubs, direction, currencyCode).size();
}
protected final List<OfferInfo> getOffersSortedByDate(GrpcStubs grpcStubs, String direction, String currencyCode) {
protected final List<OfferInfo> getOffersSortedByDate(GrpcStubs grpcStubs,
String direction,
String currencyCode) {
var req = GetOffersRequest.newBuilder()
.setDirection(direction)
.setCurrencyCode(currencyCode).build();
@ -146,6 +148,16 @@ public abstract class AbstractOfferTest extends MethodTest {
return sortOffersByDate(reply.getOffersList());
}
protected final List<OfferInfo> getMyOffersSortedByDate(GrpcStubs grpcStubs,
String direction,
String currencyCode) {
var req = GetMyOffersRequest.newBuilder()
.setDirection(direction)
.setCurrencyCode(currencyCode).build();
var reply = grpcStubs.offersService.getMyOffers(req);
return sortOffersByDate(reply.getOffersList());
}
protected final List<OfferInfo> sortOffersByDate(List<OfferInfo> offerInfoList) {
return offerInfoList.stream()
.sorted(comparing(OfferInfo::getDate))

View File

@ -18,6 +18,7 @@
package bisq.apitest.method.offer;
import bisq.core.btc.wallet.Restrictions;
import bisq.core.payment.PaymentAccount;
import bisq.proto.grpc.CreateOfferRequest;
import bisq.proto.grpc.OfferInfo;
@ -32,6 +33,7 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Disabled
@ -39,15 +41,18 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class CancelOfferTest extends AbstractOfferTest {
private static final String DIRECTION = "buy";
private static final String CURRENCY_CODE = "cad";
private static final int MAX_OFFERS = 3;
@Test
@Order(1)
public void testCancelOffer() {
PaymentAccount cadAccount = createDummyF2FAccount(alicedaemon, "CA");
var req = CreateOfferRequest.newBuilder()
.setPaymentAccountId(alicesDummyAcct.getId())
.setDirection("buy")
.setCurrencyCode("cad")
.setPaymentAccountId(cadAccount.getId())
.setDirection(DIRECTION)
.setCurrencyCode(CURRENCY_CODE)
.setAmount(10000000)
.setMinAmount(10000000)
.setUseMarketBasedPrice(true)
@ -66,18 +71,19 @@ public class CancelOfferTest extends AbstractOfferTest {
sleep(2500);
}
List<OfferInfo> offers = getOffersSortedByDate(aliceStubs, "buy", "cad");
List<OfferInfo> offers = getMyOffersSortedByDate(aliceStubs, DIRECTION, CURRENCY_CODE);
assertEquals(MAX_OFFERS, offers.size());
// Cancel the offers, checking the open offer count after each offer removal.
for (int i = 1; i <= MAX_OFFERS; i++) {
cancelOffer(aliceStubs, offers.remove(0).getId());
assertEquals(MAX_OFFERS - i, getOpenOffersCount(aliceStubs, "buy", "cad"));
offers = getMyOffersSortedByDate(aliceStubs, DIRECTION, CURRENCY_CODE);
assertEquals(MAX_OFFERS - i, offers.size());
}
sleep(1000); // wait for offer removal
offers = getOffersSortedByDate(aliceStubs, "buy", "cad");
offers = getMyOffersSortedByDate(aliceStubs, DIRECTION, CURRENCY_CODE);
assertEquals(0, offers.size());
}
}

View File

@ -17,7 +17,7 @@
package bisq.apitest.method.offer;
import bisq.core.btc.wallet.Restrictions;
import bisq.core.payment.PaymentAccount;
import bisq.proto.grpc.CreateOfferRequest;
@ -29,6 +29,8 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
@ -43,16 +45,17 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {
@Test
@Order(1)
public void testCreateAUDBTCBuyOfferUsingFixedPrice16000() {
PaymentAccount audAccount = createDummyF2FAccount(alicedaemon, "AU");
var req = CreateOfferRequest.newBuilder()
.setPaymentAccountId(alicesDummyAcct.getId())
.setPaymentAccountId(audAccount.getId())
.setDirection("buy")
.setCurrencyCode("aud")
.setAmount(10000000)
.setMinAmount(10000000)
.setUseMarketBasedPrice(false)
.setMarketPriceMargin(0.00)
.setPrice("16000")
.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent())
.setPrice("36000")
.setBuyerSecurityDeposit(getDefaultBuyerSecurityDepositAsPercent())
.setMakerFeeCurrencyCode(MAKER_FEE_CURRENCY_CODE)
.build();
var newOffer = aliceStubs.offersService.createOffer(req).getOffer();
@ -60,24 +63,24 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {
assertNotEquals("", newOfferId);
assertEquals("BUY", newOffer.getDirection());
assertFalse(newOffer.getUseMarketBasedPrice());
assertEquals(160000000, newOffer.getPrice());
assertEquals(360000000, newOffer.getPrice());
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesDummyAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(audAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("AUD", newOffer.getCounterCurrencyCode());
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
newOffer = getOffer(newOfferId);
newOffer = getMyOffer(newOfferId);
assertEquals(newOfferId, newOffer.getId());
assertEquals("BUY", newOffer.getDirection());
assertFalse(newOffer.getUseMarketBasedPrice());
assertEquals(160000000, newOffer.getPrice());
assertEquals(360000000, newOffer.getPrice());
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesDummyAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(audAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("AUD", newOffer.getCounterCurrencyCode());
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
@ -86,16 +89,17 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {
@Test
@Order(2)
public void testCreateUSDBTCBuyOfferUsingFixedPrice100001234() {
PaymentAccount usdAccount = createDummyF2FAccount(alicedaemon, "US");
var req = CreateOfferRequest.newBuilder()
.setPaymentAccountId(alicesDummyAcct.getId())
.setPaymentAccountId(usdAccount.getId())
.setDirection("buy")
.setCurrencyCode("usd")
.setAmount(10000000)
.setMinAmount(10000000)
.setUseMarketBasedPrice(false)
.setMarketPriceMargin(0.00)
.setPrice("10000.1234")
.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent())
.setPrice("30000.1234")
.setBuyerSecurityDeposit(getDefaultBuyerSecurityDepositAsPercent())
.setMakerFeeCurrencyCode(MAKER_FEE_CURRENCY_CODE)
.build();
var newOffer = aliceStubs.offersService.createOffer(req).getOffer();
@ -103,24 +107,24 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {
assertNotEquals("", newOfferId);
assertEquals("BUY", newOffer.getDirection());
assertFalse(newOffer.getUseMarketBasedPrice());
assertEquals(100001234, newOffer.getPrice());
assertEquals(300001234, newOffer.getPrice());
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesDummyAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("USD", newOffer.getCounterCurrencyCode());
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
newOffer = getOffer(newOfferId);
newOffer = getMyOffer(newOfferId);
assertEquals(newOfferId, newOffer.getId());
assertEquals("BUY", newOffer.getDirection());
assertFalse(newOffer.getUseMarketBasedPrice());
assertEquals(100001234, newOffer.getPrice());
assertEquals(300001234, newOffer.getPrice());
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesDummyAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("USD", newOffer.getCounterCurrencyCode());
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
@ -129,16 +133,17 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {
@Test
@Order(3)
public void testCreateEURBTCSellOfferUsingFixedPrice95001234() {
PaymentAccount eurAccount = createDummyF2FAccount(alicedaemon, "FR");
var req = CreateOfferRequest.newBuilder()
.setPaymentAccountId(alicesDummyAcct.getId())
.setPaymentAccountId(eurAccount.getId())
.setDirection("sell")
.setCurrencyCode("eur")
.setAmount(10000000)
.setMinAmount(10000000)
.setUseMarketBasedPrice(false)
.setMarketPriceMargin(0.00)
.setPrice("9500.1234")
.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent())
.setPrice("29500.1234")
.setBuyerSecurityDeposit(getDefaultBuyerSecurityDepositAsPercent())
.setMakerFeeCurrencyCode(MAKER_FEE_CURRENCY_CODE)
.build();
var newOffer = aliceStubs.offersService.createOffer(req).getOffer();
@ -146,24 +151,24 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {
assertNotEquals("", newOfferId);
assertEquals("SELL", newOffer.getDirection());
assertFalse(newOffer.getUseMarketBasedPrice());
assertEquals(95001234, newOffer.getPrice());
assertEquals(295001234, newOffer.getPrice());
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesDummyAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(eurAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("EUR", newOffer.getCounterCurrencyCode());
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());
newOffer = getOffer(newOfferId);
newOffer = getMyOffer(newOfferId);
assertEquals(newOfferId, newOffer.getId());
assertEquals("SELL", newOffer.getDirection());
assertFalse(newOffer.getUseMarketBasedPrice());
assertEquals(95001234, newOffer.getPrice());
assertEquals(295001234, newOffer.getPrice());
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesDummyAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(eurAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("EUR", newOffer.getCounterCurrencyCode());
assertFalse(newOffer.getIsCurrencyForMakerFeeBtc());

View File

@ -17,7 +17,7 @@
package bisq.apitest.method.offer;
import bisq.core.btc.wallet.Restrictions;
import bisq.core.payment.PaymentAccount;
import bisq.proto.grpc.CreateOfferRequest;
import bisq.proto.grpc.OfferInfo;
@ -32,8 +32,10 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static bisq.common.util.MathUtils.scaleDownByPowerOf10;
import static bisq.common.util.MathUtils.scaleUpByPowerOf10;
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
import static java.lang.Math.abs;
import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -55,9 +57,10 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
@Test
@Order(1)
public void testCreateUSDBTCBuyOffer5PctPriceMargin() {
PaymentAccount usdAccount = createDummyF2FAccount(alicedaemon, "US");
double priceMarginPctInput = 5.00;
var req = CreateOfferRequest.newBuilder()
.setPaymentAccountId(alicesDummyAcct.getId())
.setPaymentAccountId(usdAccount.getId())
.setDirection("buy")
.setCurrencyCode("usd")
.setAmount(10000000)
@ -65,7 +68,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
.setUseMarketBasedPrice(true)
.setMarketPriceMargin(priceMarginPctInput)
.setPrice("0")
.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent())
.setBuyerSecurityDeposit(getDefaultBuyerSecurityDepositAsPercent())
.setMakerFeeCurrencyCode(MAKER_FEE_CURRENCY_CODE)
.build();
var newOffer = aliceStubs.offersService.createOffer(req).getOffer();
@ -76,19 +79,19 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesDummyAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("USD", newOffer.getCounterCurrencyCode());
assertTrue(newOffer.getIsCurrencyForMakerFeeBtc());
newOffer = getOffer(newOfferId);
newOffer = getMyOffer(newOfferId);
assertEquals(newOfferId, newOffer.getId());
assertEquals("BUY", newOffer.getDirection());
assertTrue(newOffer.getUseMarketBasedPrice());
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesDummyAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("USD", newOffer.getCounterCurrencyCode());
assertTrue(newOffer.getIsCurrencyForMakerFeeBtc());
@ -99,9 +102,10 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
@Test
@Order(2)
public void testCreateNZDBTCBuyOfferMinus2PctPriceMargin() {
PaymentAccount nzdAccount = createDummyF2FAccount(alicedaemon, "NZ");
double priceMarginPctInput = -2.00;
var req = CreateOfferRequest.newBuilder()
.setPaymentAccountId(alicesDummyAcct.getId())
.setPaymentAccountId(nzdAccount.getId())
.setDirection("buy")
.setCurrencyCode("nzd")
.setAmount(10000000)
@ -109,7 +113,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
.setUseMarketBasedPrice(true)
.setMarketPriceMargin(priceMarginPctInput)
.setPrice("0")
.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent())
.setBuyerSecurityDeposit(getDefaultBuyerSecurityDepositAsPercent())
.setMakerFeeCurrencyCode(MAKER_FEE_CURRENCY_CODE)
.build();
var newOffer = aliceStubs.offersService.createOffer(req).getOffer();
@ -120,19 +124,19 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesDummyAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(nzdAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("NZD", newOffer.getCounterCurrencyCode());
assertTrue(newOffer.getIsCurrencyForMakerFeeBtc());
newOffer = getOffer(newOfferId);
newOffer = getMyOffer(newOfferId);
assertEquals(newOfferId, newOffer.getId());
assertEquals("BUY", newOffer.getDirection());
assertTrue(newOffer.getUseMarketBasedPrice());
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesDummyAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(nzdAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("NZD", newOffer.getCounterCurrencyCode());
assertTrue(newOffer.getIsCurrencyForMakerFeeBtc());
@ -143,9 +147,10 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
@Test
@Order(3)
public void testCreateGBPBTCSellOfferMinus1Point5PctPriceMargin() {
PaymentAccount gbpAccount = createDummyF2FAccount(alicedaemon, "GB");
double priceMarginPctInput = -1.5;
var req = CreateOfferRequest.newBuilder()
.setPaymentAccountId(alicesDummyAcct.getId())
.setPaymentAccountId(gbpAccount.getId())
.setDirection("sell")
.setCurrencyCode("gbp")
.setAmount(10000000)
@ -153,7 +158,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
.setUseMarketBasedPrice(true)
.setMarketPriceMargin(priceMarginPctInput)
.setPrice("0")
.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent())
.setBuyerSecurityDeposit(getDefaultBuyerSecurityDepositAsPercent())
.setMakerFeeCurrencyCode(MAKER_FEE_CURRENCY_CODE)
.build();
var newOffer = aliceStubs.offersService.createOffer(req).getOffer();
@ -165,19 +170,19 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesDummyAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(gbpAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("GBP", newOffer.getCounterCurrencyCode());
assertTrue(newOffer.getIsCurrencyForMakerFeeBtc());
newOffer = getOffer(newOfferId);
newOffer = getMyOffer(newOfferId);
assertEquals(newOfferId, newOffer.getId());
assertEquals("SELL", newOffer.getDirection());
assertTrue(newOffer.getUseMarketBasedPrice());
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesDummyAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(gbpAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("GBP", newOffer.getCounterCurrencyCode());
assertTrue(newOffer.getIsCurrencyForMakerFeeBtc());
@ -188,9 +193,10 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
@Test
@Order(4)
public void testCreateBRLBTCSellOffer6Point55PctPriceMargin() {
PaymentAccount brlAccount = createDummyF2FAccount(alicedaemon, "BR");
double priceMarginPctInput = 6.55;
var req = CreateOfferRequest.newBuilder()
.setPaymentAccountId(alicesDummyAcct.getId())
.setPaymentAccountId(brlAccount.getId())
.setDirection("sell")
.setCurrencyCode("brl")
.setAmount(10000000)
@ -198,7 +204,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
.setUseMarketBasedPrice(true)
.setMarketPriceMargin(priceMarginPctInput)
.setPrice("0")
.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent())
.setBuyerSecurityDeposit(getDefaultBuyerSecurityDepositAsPercent())
.setMakerFeeCurrencyCode(MAKER_FEE_CURRENCY_CODE)
.build();
var newOffer = aliceStubs.offersService.createOffer(req).getOffer();
@ -210,19 +216,19 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesDummyAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(brlAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("BRL", newOffer.getCounterCurrencyCode());
assertTrue(newOffer.getIsCurrencyForMakerFeeBtc());
newOffer = getOffer(newOfferId);
newOffer = getMyOffer(newOfferId);
assertEquals(newOfferId, newOffer.getId());
assertEquals("SELL", newOffer.getDirection());
assertTrue(newOffer.getUseMarketBasedPrice());
assertEquals(10000000, newOffer.getAmount());
assertEquals(10000000, newOffer.getMinAmount());
assertEquals(1500000, newOffer.getBuyerSecurityDeposit());
assertEquals(alicesDummyAcct.getId(), newOffer.getPaymentAccountId());
assertEquals(brlAccount.getId(), newOffer.getPaymentAccountId());
assertEquals("BTC", newOffer.getBaseCurrencyCode());
assertEquals("BRL", newOffer.getCounterCurrencyCode());
assertTrue(newOffer.getIsCurrencyForMakerFeeBtc());

View File

@ -17,7 +17,7 @@
package bisq.apitest.method.offer;
import bisq.core.btc.wallet.Restrictions;
import bisq.core.payment.PaymentAccount;
import bisq.proto.grpc.CreateOfferRequest;
@ -31,6 +31,8 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@ -42,8 +44,9 @@ public class ValidateCreateOfferTest extends AbstractOfferTest {
@Test
@Order(1)
public void testAmtTooLargeShouldThrowException() {
PaymentAccount usdAccount = createDummyF2FAccount(alicedaemon, "US");
var req = CreateOfferRequest.newBuilder()
.setPaymentAccountId(alicesDummyAcct.getId())
.setPaymentAccountId(usdAccount.getId())
.setDirection("buy")
.setCurrencyCode("usd")
.setAmount(100000000000L)
@ -51,7 +54,7 @@ public class ValidateCreateOfferTest extends AbstractOfferTest {
.setUseMarketBasedPrice(false)
.setMarketPriceMargin(0.00)
.setPrice("10000.0000")
.setBuyerSecurityDeposit(Restrictions.getDefaultBuyerSecurityDepositAsPercent())
.setBuyerSecurityDeposit(getDefaultBuyerSecurityDepositAsPercent())
.setMakerFeeCurrencyCode("bsq")
.build();
@SuppressWarnings("ResultOfMethodCallIgnored")

View File

@ -33,7 +33,7 @@ import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static java.lang.String.format;
import static java.lang.System.getProperty;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
@ -176,7 +176,6 @@ public class AbstractPaymentAccountTest extends MethodTest {
tmpJsonForm = File.createTempFile("temp_acct_form_",
".json",
Paths.get(getProperty("java.io.tmpdir")).toFile());
tmpJsonForm.deleteOnExit();
JsonWriter writer = new JsonWriter(new OutputStreamWriter(new FileOutputStream(tmpJsonForm), UTF_8));
writer.beginObject();
@ -199,6 +198,7 @@ public class AbstractPaymentAccountTest extends MethodTest {
log.error("", ex);
fail(format("Could not write json file from form entries %s", COMPLETED_FORM_MAP));
}
tmpJsonForm.deleteOnExit();
return tmpJsonForm;
}
}

View File

@ -17,6 +17,8 @@
package bisq.apitest.method.trade;
import bisq.core.payment.PaymentAccount;
import bisq.proto.grpc.BtcBalanceInfo;
import io.grpc.StatusRuntimeException;
@ -59,7 +61,8 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
@Order(1)
public void testTakeAlicesBuyOffer(final TestInfo testInfo) {
try {
var alicesOffer = createAliceOffer(alicesDummyAcct,
PaymentAccount alicesUsdAccount = createDummyF2FAccount(alicedaemon, "US");
var alicesOffer = createAliceOffer(alicesUsdAccount,
"buy",
"usd",
12500000,
@ -70,17 +73,20 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
// Wait for Alice's AddToOfferBook task.
// Wait times vary; my logs show >= 2 second delay.
sleep(3000); // TODO loop instead of hard code wait time
assertEquals(1, getOpenOffersCount(aliceStubs, "buy", "usd"));
var alicesUsdOffers = getMyOffersSortedByDate(aliceStubs, "buy", "usd");
assertEquals(1, alicesUsdOffers.size());
var trade = takeAlicesOffer(offerId, bobsDummyAcct.getId(), TRADE_FEE_CURRENCY_CODE);
PaymentAccount bobsUsdAccount = createDummyF2FAccount(bobdaemon, "US");
var trade = takeAlicesOffer(offerId, bobsUsdAccount.getId(), TRADE_FEE_CURRENCY_CODE);
assertNotNull(trade);
assertEquals(offerId, trade.getTradeId());
assertFalse(trade.getIsCurrencyForTakerFeeBtc());
// Cache the trade id for the other tests.
tradeId = trade.getTradeId();
genBtcBlocksThenWait(1, 2250);
assertEquals(0, getOpenOffersCount(aliceStubs, "buy", "usd"));
genBtcBlocksThenWait(1, 1000);
alicesUsdOffers = getMyOffersSortedByDate(aliceStubs, "buy", "usd");
assertEquals(0, alicesUsdOffers.size());
trade = getTrade(bobdaemon, trade.getTradeId());
EXPECTED_PROTOCOL_STATUS.setState(SELLER_PUBLISHED_DEPOSIT_TX)
@ -89,7 +95,7 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
verifyExpectedProtocolStatus(trade);
logTrade(log, testInfo, "Bob's view after taking offer and sending deposit", trade);
genBtcBlocksThenWait(1, 2250);
genBtcBlocksThenWait(1, 1000);
trade = getTrade(bobdaemon, trade.getTradeId());
EXPECTED_PROTOCOL_STATUS.setState(DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN)
.setPhase(DEPOSIT_CONFIRMED)
@ -142,14 +148,14 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
@Test
@Order(4)
public void testAlicesKeepFunds(final TestInfo testInfo) {
genBtcBlocksThenWait(1, 2250);
genBtcBlocksThenWait(1, 1000);
var trade = getTrade(alicedaemon, tradeId);
logTrade(log, testInfo, "Alice's view before keeping funds", trade);
keepFunds(alicedaemon, tradeId);
genBtcBlocksThenWait(1, 2250);
genBtcBlocksThenWait(1, 1000);
trade = getTrade(alicedaemon, tradeId);
EXPECTED_PROTOCOL_STATUS.setState(BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG)
@ -157,7 +163,7 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
verifyExpectedProtocolStatus(trade);
logTrade(log, testInfo, "Alice's view after keeping funds", trade);
BtcBalanceInfo currentBalance = getBtcBalances(bobdaemon);
log.info("{} Alice's current available balance: {} BTC",
log.debug("{} Alice's current available balance: {} BTC",
testName(testInfo),
formatSatoshis(currentBalance.getAvailableBalance()));
}

View File

@ -17,6 +17,8 @@
package bisq.apitest.method.trade;
import bisq.core.payment.PaymentAccount;
import bisq.proto.grpc.BtcBalanceInfo;
import io.grpc.StatusRuntimeException;
@ -58,7 +60,8 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
@Order(1)
public void testTakeAlicesSellOffer(final TestInfo testInfo) {
try {
var alicesOffer = createAliceOffer(alicesDummyAcct,
PaymentAccount alicesUsdAccount = createDummyF2FAccount(alicedaemon, "US");
var alicesOffer = createAliceOffer(alicesUsdAccount,
"sell",
"usd",
12500000,
@ -70,9 +73,11 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
// Wait times vary; my logs show >= 2 second delay, but taking sell offers
// seems to require more time to prepare.
sleep(3000); // TODO loop instead of hard code wait time
assertEquals(1, getOpenOffersCount(bobStubs, "sell", "usd"));
var alicesUsdOffers = getMyOffersSortedByDate(aliceStubs, "sell", "usd");
assertEquals(1, alicesUsdOffers.size());
var trade = takeAlicesOffer(offerId, bobsDummyAcct.getId(), TRADE_FEE_CURRENCY_CODE);
PaymentAccount bobsUsdAccount = createDummyF2FAccount(bobdaemon, "US");
var trade = takeAlicesOffer(offerId, bobsUsdAccount.getId(), TRADE_FEE_CURRENCY_CODE);
assertNotNull(trade);
assertEquals(offerId, trade.getTradeId());
assertTrue(trade.getIsCurrencyForTakerFeeBtc());
@ -80,7 +85,8 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
tradeId = trade.getTradeId();
genBtcBlocksThenWait(1, 4000);
assertEquals(0, getOpenOffersCount(bobStubs, "sell", "usd"));
var takeableUsdOffers = getOffersSortedByDate(bobStubs, "sell", "usd");
assertEquals(0, takeableUsdOffers.size());
trade = getTrade(bobdaemon, trade.getTradeId());
EXPECTED_PROTOCOL_STATUS.setState(BUYER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG)
@ -90,7 +96,7 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
logTrade(log, testInfo, "Bob's view after taking offer and sending deposit", trade);
genBtcBlocksThenWait(1, 2250);
genBtcBlocksThenWait(1, 1000);
trade = getTrade(bobdaemon, trade.getTradeId());
EXPECTED_PROTOCOL_STATUS.setState(DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN)
.setPhase(DEPOSIT_CONFIRMED)
@ -143,7 +149,7 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
@Test
@Order(4)
public void testBobsBtcWithdrawalToExternalAddress(final TestInfo testInfo) {
genBtcBlocksThenWait(1, 2250);
genBtcBlocksThenWait(1, 1000);
var trade = getTrade(bobdaemon, tradeId);
logTrade(log, testInfo, "Bob's view before withdrawing funds to external wallet", trade);
@ -151,7 +157,7 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
String toAddress = bitcoinCli.getNewBtcAddress();
withdrawFunds(bobdaemon, tradeId, toAddress, WITHDRAWAL_TX_MEMO);
genBtcBlocksThenWait(1, 2250);
genBtcBlocksThenWait(1, 1000);
trade = getTrade(bobdaemon, tradeId);
EXPECTED_PROTOCOL_STATUS.setState(WITHDRAW_COMPLETED)

View File

@ -20,11 +20,14 @@ import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static bisq.apitest.config.BisqAppConfig.arbdaemon;
import static bisq.apitest.config.BisqAppConfig.bobdaemon;
import static bisq.apitest.config.BisqAppConfig.seednode;
import static bisq.apitest.method.wallet.WalletTestUtil.ALICES_INITIAL_BSQ_BALANCES;
import static bisq.apitest.method.wallet.WalletTestUtil.BOBS_INITIAL_BSQ_BALANCES;
import static bisq.apitest.method.wallet.WalletTestUtil.bsqBalanceModel;
import static bisq.apitest.method.wallet.WalletTestUtil.verifyBsqBalances;
import static bisq.cli.TableFormat.formatBsqBalanceInfoTbl;
import static org.bitcoinj.core.NetworkParameters.PAYMENT_PROTOCOL_ID_MAINNET;
import static org.bitcoinj.core.NetworkParameters.PAYMENT_PROTOCOL_ID_REGTEST;
import static org.bitcoinj.core.NetworkParameters.PAYMENT_PROTOCOL_ID_TESTNET;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -40,24 +43,6 @@ import bisq.apitest.method.MethodTest;
@TestMethodOrder(OrderAnnotation.class)
public class BsqWalletTest extends MethodTest {
// Alice's regtest BSQ wallet is initialized with 1,000,000 BSQ.
private static final bisq.core.api.model.BsqBalanceInfo ALICES_INITIAL_BSQ_BALANCES =
expectedBsqBalanceModel(100000000,
0,
0,
0,
0,
0);
// Bob's regtest BSQ wallet is initialized with 1,500,000 BSQ.
private static final bisq.core.api.model.BsqBalanceInfo BOBS_INITIAL_BSQ_BALANCES =
expectedBsqBalanceModel(150000000,
0,
0,
0,
0,
0);
private static final String SEND_BSQ_AMOUNT = "25000.50";
@BeforeAll
@ -112,7 +97,7 @@ public class BsqWalletTest extends MethodTest {
sleep(2000);
BsqBalanceInfo alicesBsqBalances = getBsqBalances(alicedaemon);
BsqBalanceInfo bobsBsqBalances = waitForNonZeroUnverifiedBalance(bobdaemon);
BsqBalanceInfo bobsBsqBalances = waitForNonZeroBsqUnverifiedBalance(bobdaemon);
log.debug("BSQ Balances Before BTC Block Gen...");
printBobAndAliceBsqBalances(testInfo,
@ -120,7 +105,7 @@ public class BsqWalletTest extends MethodTest {
alicesBsqBalances,
alicedaemon);
verifyBsqBalances(expectedBsqBalanceModel(150000000,
verifyBsqBalances(bsqBalanceModel(150000000,
2500050,
0,
0,
@ -128,7 +113,7 @@ public class BsqWalletTest extends MethodTest {
0),
bobsBsqBalances);
verifyBsqBalances(expectedBsqBalanceModel(97499950,
verifyBsqBalances(bsqBalanceModel(97499950,
97499950,
97499950,
0,
@ -145,7 +130,7 @@ public class BsqWalletTest extends MethodTest {
genBtcBlocksThenWait(1, 4000);
BsqBalanceInfo alicesBsqBalances = getBsqBalances(alicedaemon);
BsqBalanceInfo bobsBsqBalances = waitForNewAvailableConfirmedBalance(bobdaemon, 150000000);
BsqBalanceInfo bobsBsqBalances = waitForBsqNewAvailableConfirmedBalance(bobdaemon, 150000000);
log.debug("See Available Confirmed BSQ Balances...");
printBobAndAliceBsqBalances(testInfo,
@ -153,7 +138,7 @@ public class BsqWalletTest extends MethodTest {
alicesBsqBalances,
alicedaemon);
verifyBsqBalances(expectedBsqBalanceModel(152500050,
verifyBsqBalances(bsqBalanceModel(152500050,
0,
0,
0,
@ -161,7 +146,7 @@ public class BsqWalletTest extends MethodTest {
0),
bobsBsqBalances);
verifyBsqBalances(expectedBsqBalanceModel(97499950,
verifyBsqBalances(bsqBalanceModel(97499950,
0,
0,
0,
@ -175,17 +160,7 @@ public class BsqWalletTest extends MethodTest {
tearDownScaffold();
}
private void verifyBsqBalances(bisq.core.api.model.BsqBalanceInfo expected,
BsqBalanceInfo actual) {
assertEquals(expected.getAvailableConfirmedBalance(), actual.getAvailableConfirmedBalance());
assertEquals(expected.getUnverifiedBalance(), actual.getUnverifiedBalance());
assertEquals(expected.getUnconfirmedChangeBalance(), actual.getUnconfirmedChangeBalance());
assertEquals(expected.getLockedForVotingBalance(), actual.getLockedForVotingBalance());
assertEquals(expected.getLockupBondsBalance(), actual.getLockupBondsBalance());
assertEquals(expected.getUnlockingBondsBalance(), actual.getUnlockingBondsBalance());
}
private BsqBalanceInfo waitForNonZeroUnverifiedBalance(BisqAppConfig daemon) {
private BsqBalanceInfo waitForNonZeroBsqUnverifiedBalance(BisqAppConfig daemon) {
// A BSQ recipient needs to wait for her daemon to detect a new tx.
// Loop here until her unverifiedBalance != 0, or give up after 15 seconds.
// A slow test is preferred over a flaky test.
@ -197,8 +172,8 @@ public class BsqWalletTest extends MethodTest {
return bsqBalance;
}
private BsqBalanceInfo waitForNewAvailableConfirmedBalance(BisqAppConfig daemon,
long staleBalance) {
private BsqBalanceInfo waitForBsqNewAvailableConfirmedBalance(BisqAppConfig daemon,
long staleBalance) {
BsqBalanceInfo bsqBalance = getBsqBalances(daemon);
for (int numRequests = 1;
numRequests <= 15 && bsqBalance.getAvailableConfirmedBalance() == staleBalance;
@ -226,19 +201,4 @@ public class BsqWalletTest extends MethodTest {
SEND_BSQ_AMOUNT,
formatBsqBalanceInfoTbl(alicesBsqBalances));
}
@SuppressWarnings("SameParameterValue")
private static bisq.core.api.model.BsqBalanceInfo expectedBsqBalanceModel(long availableConfirmedBalance,
long unverifiedBalance,
long unconfirmedChangeBalance,
long lockedForVotingBalance,
long lockupBondsBalance,
long unlockingBondsBalance) {
return bisq.core.api.model.BsqBalanceInfo.valueOf(availableConfirmedBalance,
unverifiedBalance,
unconfirmedChangeBalance,
lockedForVotingBalance,
lockupBondsBalance,
unlockingBondsBalance);
}
}

View File

@ -17,6 +17,8 @@ import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind;
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static bisq.apitest.config.BisqAppConfig.bobdaemon;
import static bisq.apitest.config.BisqAppConfig.seednode;
import static bisq.apitest.method.wallet.WalletTestUtil.INITIAL_BTC_BALANCES;
import static bisq.apitest.method.wallet.WalletTestUtil.verifyBtcBalances;
import static bisq.cli.TableFormat.formatAddressBalanceTbl;
import static bisq.cli.TableFormat.formatBtcBalanceInfoTbl;
import static java.util.Collections.singletonList;
@ -36,14 +38,6 @@ public class BtcWalletTest extends MethodTest {
private static final String TX_MEMO = "tx memo";
// All api tests depend on the DAO / regtest environment, and Bob & Alice's wallets
// are initialized with 10 BTC during the scaffolding setup.
private static final bisq.core.api.model.BtcBalanceInfo INITIAL_BTC_BALANCES =
bisq.core.api.model.BtcBalanceInfo.valueOf(1000000000,
0,
1000000000,
0);
@BeforeAll
public static void setUp() {
startSupportingApps(false,
@ -60,10 +54,10 @@ public class BtcWalletTest extends MethodTest {
// Bob & Alice's regtest Bisq wallets were initialized with 10 BTC.
BtcBalanceInfo alicesBalances = getBtcBalances(alicedaemon);
log.info("{} Alice's BTC Balances:\n{}", testName(testInfo), formatBtcBalanceInfoTbl(alicesBalances));
log.debug("{} Alice's BTC Balances:\n{}", testName(testInfo), formatBtcBalanceInfoTbl(alicesBalances));
BtcBalanceInfo bobsBalances = getBtcBalances(bobdaemon);
log.info("{} Bob's BTC Balances:\n{}", testName(testInfo), formatBtcBalanceInfoTbl(bobsBalances));
log.debug("{} Bob's BTC Balances:\n{}", testName(testInfo), formatBtcBalanceInfoTbl(bobsBalances));
assertEquals(INITIAL_BTC_BALANCES.getAvailableBalance(), alicesBalances.getAvailableBalance());
assertEquals(INITIAL_BTC_BALANCES.getAvailableBalance(), bobsBalances.getAvailableBalance());
@ -74,13 +68,13 @@ public class BtcWalletTest extends MethodTest {
public void testFundAlicesBtcWallet(final TestInfo testInfo) {
String newAddress = getUnusedBtcAddress(alicedaemon);
bitcoinCli.sendToAddress(newAddress, "2.5");
genBtcBlocksThenWait(1, 1500);
genBtcBlocksThenWait(1, 1000);
BtcBalanceInfo btcBalanceInfo = getBtcBalances(alicedaemon);
// New balance is 12.5 BTC
assertEquals(1250000000, btcBalanceInfo.getAvailableBalance());
log.info("{} -> Alice's Funded Address Balance -> \n{}",
log.debug("{} -> Alice's Funded Address Balance -> \n{}",
testName(testInfo),
formatAddressBalanceTbl(singletonList(getAddressBalance(alicedaemon, newAddress))));
@ -92,7 +86,7 @@ public class BtcWalletTest extends MethodTest {
1250000000,
0);
verifyBtcBalances(alicesExpectedBalances, btcBalanceInfo);
log.info("{} -> Alice's BTC Balances After Sending 2.5 BTC -> \n{}",
log.debug("{} -> Alice's BTC Balances After Sending 2.5 BTC -> \n{}",
testName(testInfo),
formatBtcBalanceInfoTbl(btcBalanceInfo));
}
@ -112,7 +106,7 @@ public class BtcWalletTest extends MethodTest {
// Note that the memo is not set on the tx yet.
assertTrue(txInfo.getMemo().isEmpty());
genBtcBlocksThenWait(1, 3000);
genBtcBlocksThenWait(1, 1000);
// Fetch the tx and check for confirmation and memo.
txInfo = getTransaction(alicedaemon, txInfo.getTxId());
@ -134,9 +128,9 @@ public class BtcWalletTest extends MethodTest {
log.debug("{} Bob's BTC Balances:\n{}",
testName(testInfo),
formatBtcBalanceInfoTbl(bobsBalances));
// We cannot (?) predict the exact tx size and calculate how much in tx fees were
// deducted from the 5.5 BTC sent to Bob, but we do know Bob should have something
// between 15.49978000 and 15.49978100 BTC.
// The sendbtc tx weight and size randomly varies between two distinct values
// (876 wu, 219 bytes, OR 880 wu, 220 bytes) from test run to test run, hence
// the assertion of an available balance range [1549978000, 1549978100].
assertTrue(bobsBalances.getAvailableBalance() >= 1549978000);
assertTrue(bobsBalances.getAvailableBalance() <= 1549978100);
}
@ -145,12 +139,4 @@ public class BtcWalletTest extends MethodTest {
public static void tearDown() {
tearDownScaffold();
}
private void verifyBtcBalances(bisq.core.api.model.BtcBalanceInfo expected,
BtcBalanceInfo actual) {
assertEquals(expected.getAvailableBalance(), actual.getAvailableBalance());
assertEquals(expected.getReservedBalance(), actual.getReservedBalance());
assertEquals(expected.getTotalAvailableBalance(), actual.getTotalAvailableBalance());
assertEquals(expected.getLockedBalance(), actual.getLockedBalance());
}
}

View File

@ -0,0 +1,72 @@
package bisq.apitest.method.wallet;
import bisq.proto.grpc.BsqBalanceInfo;
import bisq.proto.grpc.BtcBalanceInfo;
import lombok.extern.slf4j.Slf4j;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Slf4j
public class WalletTestUtil {
// All api tests depend on the DAO / regtest environment, and Bob & Alice's wallets
// are initialized with 10 BTC during the scaffolding setup.
public static final bisq.core.api.model.BtcBalanceInfo INITIAL_BTC_BALANCES =
bisq.core.api.model.BtcBalanceInfo.valueOf(1000000000,
0,
1000000000,
0);
// Alice's regtest BSQ wallet is initialized with 1,000,000 BSQ.
public static final bisq.core.api.model.BsqBalanceInfo ALICES_INITIAL_BSQ_BALANCES =
bsqBalanceModel(100000000,
0,
0,
0,
0,
0);
// Bob's regtest BSQ wallet is initialized with 1,500,000 BSQ.
public static final bisq.core.api.model.BsqBalanceInfo BOBS_INITIAL_BSQ_BALANCES =
bsqBalanceModel(150000000,
0,
0,
0,
0,
0);
@SuppressWarnings("SameParameterValue")
public static bisq.core.api.model.BsqBalanceInfo bsqBalanceModel(long availableConfirmedBalance,
long unverifiedBalance,
long unconfirmedChangeBalance,
long lockedForVotingBalance,
long lockupBondsBalance,
long unlockingBondsBalance) {
return bisq.core.api.model.BsqBalanceInfo.valueOf(availableConfirmedBalance,
unverifiedBalance,
unconfirmedChangeBalance,
lockedForVotingBalance,
lockupBondsBalance,
unlockingBondsBalance);
}
public static void verifyBsqBalances(bisq.core.api.model.BsqBalanceInfo expected,
BsqBalanceInfo actual) {
assertEquals(expected.getAvailableConfirmedBalance(), actual.getAvailableConfirmedBalance());
assertEquals(expected.getUnverifiedBalance(), actual.getUnverifiedBalance());
assertEquals(expected.getUnconfirmedChangeBalance(), actual.getUnconfirmedChangeBalance());
assertEquals(expected.getLockedForVotingBalance(), actual.getLockedForVotingBalance());
assertEquals(expected.getLockupBondsBalance(), actual.getLockupBondsBalance());
assertEquals(expected.getUnlockingBondsBalance(), actual.getUnlockingBondsBalance());
}
public static void verifyBtcBalances(bisq.core.api.model.BtcBalanceInfo expected,
BtcBalanceInfo actual) {
assertEquals(expected.getAvailableBalance(), actual.getAvailableBalance());
assertEquals(expected.getReservedBalance(), actual.getReservedBalance());
assertEquals(expected.getTotalAvailableBalance(), actual.getTotalAvailableBalance());
assertEquals(expected.getLockedBalance(), actual.getLockedBalance());
}
}

View File

@ -28,7 +28,7 @@ configure(subprojects) {
ext { // in alphabetical order
bcVersion = '1.63'
bitcoinjVersion = 'dcf8af0'
bitcoinjVersion = '2a80db4'
btcdCli4jVersion = '27b94333'
codecVersion = '1.13'
easybindVersion = '1.0.3'
@ -386,7 +386,7 @@ configure(project(':desktop')) {
apply plugin: 'witness'
apply from: '../gradle/witness/gradle-witness.gradle'
version = '1.5.1-SNAPSHOT'
version = '1.5.4-SNAPSHOT'
mainClassName = 'bisq.desktop.app.BisqAppMain'

View File

@ -25,6 +25,8 @@ import bisq.proto.grpc.CreatePaymentAccountRequest;
import bisq.proto.grpc.GetAddressBalanceRequest;
import bisq.proto.grpc.GetBalancesRequest;
import bisq.proto.grpc.GetFundingAddressesRequest;
import bisq.proto.grpc.GetMyOfferRequest;
import bisq.proto.grpc.GetMyOffersRequest;
import bisq.proto.grpc.GetOfferRequest;
import bisq.proto.grpc.GetOffersRequest;
import bisq.proto.grpc.GetPaymentAccountFormRequest;
@ -96,7 +98,9 @@ public class CliMain {
createoffer,
canceloffer,
getoffer,
getmyoffer,
getoffers,
getmyoffers,
takeoffer,
gettrade,
confirmpaymentstarted,
@ -418,6 +422,19 @@ public class CliMain {
reply.getOffer().getCounterCurrencyCode()));
return;
}
case getmyoffer: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("incorrect parameter count, expecting offer id");
var offerId = nonOptionArgs.get(1);
var request = GetMyOfferRequest.newBuilder()
.setId(offerId)
.build();
var reply = offersService.getMyOffer(request);
out.println(formatOfferTable(singletonList(reply.getOffer()),
reply.getOffer().getCounterCurrencyCode()));
return;
}
case getoffers: {
if (nonOptionArgs.size() < 3)
throw new IllegalArgumentException("incorrect parameter count,"
@ -440,6 +457,28 @@ public class CliMain {
return;
}
case getmyoffers: {
if (nonOptionArgs.size() < 3)
throw new IllegalArgumentException("incorrect parameter count,"
+ " expecting direction (buy|sell), currency code");
var direction = nonOptionArgs.get(1);
var currencyCode = nonOptionArgs.get(2);
var request = GetMyOffersRequest.newBuilder()
.setDirection(direction)
.setCurrencyCode(currencyCode)
.build();
var reply = offersService.getMyOffers(request);
List<OfferInfo> offers = reply.getOffersList();
if (offers.isEmpty())
out.printf("no %s %s offers found%n", direction, currencyCode);
else
out.println(formatOfferTable(reply.getOffersList(), currencyCode));
return;
}
case takeoffer: {
if (nonOptionArgs.size() < 3)
throw new IllegalArgumentException("incorrect parameter count, " +
@ -727,11 +766,11 @@ public class CliMain {
stream.println();
parser.printHelpOn(stream);
stream.println();
String rowFormat = "%-22s%-50s%s%n";
String rowFormat = "%-24s%-52s%s%n";
stream.format(rowFormat, "Method", "Params", "Description");
stream.format(rowFormat, "------", "------", "------------");
stream.format(rowFormat, "getversion", "", "Get server version");
stream.format(rowFormat, "getbalance [,currency code = bsq|btc]", "", "Get server wallet balances");
stream.format(rowFormat, "getbalance", "[currency code = bsq|btc]", "Get server wallet balances");
stream.format(rowFormat, "getaddressbalance", "address", "Get server wallet address balance");
stream.format(rowFormat, "getfundingaddresses", "", "Get BTC funding addresses");
stream.format(rowFormat, "getunusedbsqaddress", "", "Get unused BSQ address");
@ -747,8 +786,10 @@ public class CliMain {
stream.format(rowFormat, "", "[,maker fee currency code = bsq|btc]", "");
stream.format(rowFormat, "canceloffer", "offer id", "Cancel offer with id");
stream.format(rowFormat, "getoffer", "offer id", "Get current offer with id");
stream.format(rowFormat, "getmyoffer", "offer id", "Get my current offer with id");
stream.format(rowFormat, "getoffers", "buy | sell, currency code", "Get current offers");
stream.format(rowFormat, "takeoffer", "offer id, [,taker fee currency code = bsq|btc]", "Take offer with id");
stream.format(rowFormat, "getmyoffers", "buy | sell, currency code", "Get my current offers");
stream.format(rowFormat, "takeoffer", "offer id [,taker fee currency code = bsq|btc]", "Take offer with id");
stream.format(rowFormat, "gettrade", "trade id [,showcontract = true|false]", "Get trade summary or full contract");
stream.format(rowFormat, "confirmpaymentstarted", "trade id", "Confirm payment started");
stream.format(rowFormat, "confirmpaymentreceived", "trade id", "Confirm payment received");

View File

@ -22,25 +22,26 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class AsciiLogo {
public static void showAsciiLogo() {
log.info("\n\n" +
" ........ ...... \n" +
" .............. ...... \n" +
" ................. ...... \n" +
" ...... .......... .. ...... \n" +
" ...... ...... ...... ............... ..... ......... .......... \n" +
" ....... ........ .................. ..... ............. ............... \n" +
" ...... ........ .......... ....... ..... ...... ... ........ ....... \n" +
" ...... ..... ....... ..... ..... ..... ..... ...... \n" +
" ...... ... ... ...... ...... ..... ........... ...... ...... \n" +
" ...... ..... .... ...... ...... ..... ............ ..... ...... \n" +
" ...... ..... ...... ..... ........ ...... ...... \n" +
" ...... .... ... ...... ...... ..... .. ...... ...... ........ \n" +
" ........ .. ....... ................. ..... .............. ................... \n" +
" .......... ......... ............. ..... ............ ................. \n" +
" ...................... ..... .... .... ...... \n" +
" ................ ...... \n" +
" .... ...... \n" +
" ...... \n" +
"\n\n");
String ls = System.lineSeparator();
log.info(ls + ls +
" ........ ...... " + ls +
" .............. ...... " + ls +
" ................. ...... " + ls +
" ...... .......... .. ...... " + ls +
" ...... ...... ...... ............... ..... ......... .......... " + ls +
" ....... ........ .................. ..... ............. ............... " + ls +
" ...... ........ .......... ....... ..... ...... ... ........ ....... " + ls +
" ...... ..... ....... ..... ..... ..... ..... ...... " + ls +
" ...... ... ... ...... ...... ..... ........... ...... ...... " + ls +
" ...... ..... .... ...... ...... ..... ............ ..... ...... " + ls +
" ...... ..... ...... ..... ........ ...... ...... " + ls +
" ...... .... ... ...... ...... ..... .. ...... ...... ........ " + ls +
" ........ .. ....... ................. ..... .............. ................... " + ls +
" .......... ......... ............. ..... ............ ................. " + ls +
" ...................... ..... .... .... ...... " + ls +
" ................ ...... " + ls +
" .... ...... " + ls +
" ...... " + ls +
ls + ls);
}
}

View File

@ -30,14 +30,14 @@ public class Version {
// VERSION = 0.5.0 introduces proto buffer for the P2P network and local DB and is a not backward compatible update
// Therefore all sub versions start again with 1
// We use semantic versioning with major, minor and patch
public static final String VERSION = "1.5.1";
public static final String VERSION = "1.5.4";
/**
* Holds a list of the tagged resource files for optimizing the getData requests.
* This must not contain each version but only those where we add new version-tagged resource files for
* historical data stores.
*/
public static final List<String> HISTORICAL_RESOURCE_FILE_VERSION_TAGS = Arrays.asList("1.4.0", "1.5.0");
public static final List<String> HISTORICAL_RESOURCE_FILE_VERSION_TAGS = Arrays.asList("1.4.0", "1.5.0", "1.5.2");
public static int getMajorVersion(String version) {
return getSubVersion(version, 0);

View File

@ -119,6 +119,7 @@ public class Config {
public static final String ALLOW_FAULTY_DELAYED_TXS = "allowFaultyDelayedTxs";
public static final String API_PASSWORD = "apiPassword";
public static final String API_PORT = "apiPort";
public static final String PREVENT_PERIODIC_SHUTDOWN_AT_SEED_NODE = "preventPeriodicShutdownAtSeedNode";
// Default values for certain options
public static final int UNSPECIFIED_PORT = -1;
@ -205,6 +206,7 @@ public class Config {
public final boolean allowFaultyDelayedTxs;
public final String apiPassword;
public final int apiPort;
public final boolean preventPeriodicShutdownAtSeedNode;
// Properties derived from options but not exposed as options themselves
public final File torDir;
@ -639,6 +641,13 @@ public class Config {
.ofType(Integer.class)
.defaultsTo(9998);
ArgumentAcceptingOptionSpec<Boolean> preventPeriodicShutdownAtSeedNodeOpt =
parser.accepts(PREVENT_PERIODIC_SHUTDOWN_AT_SEED_NODE,
"Prevents periodic shutdown at seed nodes")
.withRequiredArg()
.ofType(boolean.class)
.defaultsTo(false);
try {
CompositeOptionSet options = new CompositeOptionSet();
@ -754,6 +763,7 @@ public class Config {
this.allowFaultyDelayedTxs = options.valueOf(allowFaultyDelayedTxsOpt);
this.apiPassword = options.valueOf(apiPasswordOpt);
this.apiPort = options.valueOf(apiPortOpt);
this.preventPeriodicShutdownAtSeedNode = options.valueOf(preventPeriodicShutdownAtSeedNodeOpt);
} catch (OptionException ex) {
throw new ConfigException("problem parsing option '%s': %s",
ex.options().get(0),

View File

@ -28,6 +28,7 @@ import com.google.common.base.Enums;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@ -105,4 +106,7 @@ public class ProtoUtil {
return CollectionUtils.isEmpty(protocolStringList) ? new ArrayList<>() : new ArrayList<>(protocolStringList);
}
public static Set<String> protocolStringListToSet(ProtocolStringList protocolStringList) {
return CollectionUtils.isEmpty(protocolStringList) ? new HashSet<>() : new HashSet<>(protocolStringList);
}
}

View File

@ -73,6 +73,7 @@ public class SignedWitnessService {
private final KeyRing keyRing;
private final P2PService p2PService;
private final ArbitratorManager arbitratorManager;
private final SignedWitnessStorageService signedWitnessStorageService;
private final User user;
private final FilterManager filterManager;
@ -82,6 +83,17 @@ public class SignedWitnessService {
// This avoids iterations over the signedWitnessMap for getting the set of such SignedWitnesses.
private final Map<P2PDataStorage.ByteArray, Set<SignedWitness>> signedWitnessSetByAccountAgeWitnessHash = new HashMap<>();
// Iterating over all SignedWitnesses and do a byte array comparison is a bit expensive and
// it is called at filtering the offer book many times, so we use a lookup map for fast
// access to the set of SignedWitness which match the ownerPubKey.
private final Map<P2PDataStorage.ByteArray, Set<SignedWitness>> signedWitnessSetByOwnerPubKey = new HashMap<>();
// The signature verification calls are rather expensive and called at filtering the offer book many times,
// so we cache the results using the hash as key. The hash is created from the accountAgeWitnessHash and the
// signature.
private final Map<P2PDataStorage.ByteArray, Boolean> verifySignatureWithDSAKeyResultCache = new HashMap<>();
private final Map<P2PDataStorage.ByteArray, Boolean> verifySignatureWithECKeyResultCache = new HashMap<>();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -98,6 +110,7 @@ public class SignedWitnessService {
this.keyRing = keyRing;
this.p2PService = p2PService;
this.arbitratorManager = arbitratorManager;
this.signedWitnessStorageService = signedWitnessStorageService;
this.user = user;
this.filterManager = filterManager;
@ -117,7 +130,7 @@ public class SignedWitnessService {
});
// At startup the P2PDataStorage initializes earlier, otherwise we get the listener called.
p2PService.getP2PDataStorage().getAppendOnlyDataStoreMap().values().forEach(e -> {
signedWitnessStorageService.getMap().values().forEach(e -> {
if (e instanceof SignedWitness)
addToMap((SignedWitness) e);
});
@ -322,32 +335,45 @@ public class SignedWitnessService {
}
private boolean verifySignatureWithECKey(SignedWitness signedWitness) {
P2PDataStorage.ByteArray hash = new P2PDataStorage.ByteArray(signedWitness.getHash());
if (verifySignatureWithECKeyResultCache.containsKey(hash)) {
return verifySignatureWithECKeyResultCache.get(hash);
}
try {
String message = Utilities.encodeToHex(signedWitness.getAccountAgeWitnessHash());
String signatureBase64 = new String(signedWitness.getSignature(), Charsets.UTF_8);
ECKey key = ECKey.fromPublicOnly(signedWitness.getSignerPubKey());
if (arbitratorManager.isPublicKeyInList(Utilities.encodeToHex(key.getPubKey()))) {
key.verifyMessage(message, signatureBase64);
verifySignatureWithECKeyResultCache.put(hash, true);
return true;
} else {
log.warn("Provided EC key is not in list of valid arbitrators.");
verifySignatureWithECKeyResultCache.put(hash, false);
return false;
}
} catch (SignatureException e) {
log.warn("verifySignature signedWitness failed. signedWitness={}", signedWitness);
log.warn("Caused by ", e);
verifySignatureWithECKeyResultCache.put(hash, false);
return false;
}
}
private boolean verifySignatureWithDSAKey(SignedWitness signedWitness) {
P2PDataStorage.ByteArray hash = new P2PDataStorage.ByteArray(signedWitness.getHash());
if (verifySignatureWithDSAKeyResultCache.containsKey(hash)) {
return verifySignatureWithDSAKeyResultCache.get(hash);
}
try {
PublicKey signaturePubKey = Sig.getPublicKeyFromBytes(signedWitness.getSignerPubKey());
Sig.verify(signaturePubKey, signedWitness.getAccountAgeWitnessHash(), signedWitness.getSignature());
verifySignatureWithDSAKeyResultCache.put(hash, true);
return true;
} catch (CryptoException e) {
log.warn("verifySignature signedWitness failed. signedWitness={}", signedWitness);
log.warn("Caused by ", e);
verifySignatureWithDSAKeyResultCache.put(hash, false);
return false;
}
}
@ -393,10 +419,15 @@ public class SignedWitnessService {
// witnessOwnerPubKey
private Set<SignedWitness> getSignedWitnessSetByOwnerPubKey(byte[] ownerPubKey,
Stack<P2PDataStorage.ByteArray> excluded) {
return getSignedWitnessMapValues().stream()
.filter(e -> Arrays.equals(e.getWitnessOwnerPubKey(), ownerPubKey))
.filter(e -> !excluded.contains(new P2PDataStorage.ByteArray(e.getSignerPubKey())))
.collect(Collectors.toSet());
P2PDataStorage.ByteArray key = new P2PDataStorage.ByteArray(ownerPubKey);
if (signedWitnessSetByOwnerPubKey.containsKey(key)) {
return signedWitnessSetByOwnerPubKey.get(key).stream()
.filter(e -> !excluded.contains(new P2PDataStorage.ByteArray(e.getSignerPubKey())))
.collect(Collectors.toSet());
} else {
return new HashSet<>();
}
}
public boolean isSignedAccountAgeWitness(AccountAgeWitness accountAgeWitness) {
@ -498,6 +529,10 @@ public class SignedWitnessService {
P2PDataStorage.ByteArray accountAgeWitnessHash = new P2PDataStorage.ByteArray(signedWitness.getAccountAgeWitnessHash());
signedWitnessSetByAccountAgeWitnessHash.putIfAbsent(accountAgeWitnessHash, new HashSet<>());
signedWitnessSetByAccountAgeWitnessHash.get(accountAgeWitnessHash).add(signedWitness);
P2PDataStorage.ByteArray ownerPubKey = new P2PDataStorage.ByteArray(signedWitness.getWitnessOwnerPubKey());
signedWitnessSetByOwnerPubKey.putIfAbsent(ownerPubKey, new HashSet<>());
signedWitnessSetByOwnerPubKey.get(ownerPubKey).add(signedWitness);
}
private void publishSignedWitness(SignedWitness signedWitness) {
@ -526,6 +561,15 @@ public class SignedWitnessService {
signedWitnessSetByAccountAgeWitnessHash.remove(accountAgeWitnessHash);
}
}
P2PDataStorage.ByteArray ownerPubKey = new P2PDataStorage.ByteArray(signedWitness.getWitnessOwnerPubKey());
if (signedWitnessSetByOwnerPubKey.containsKey(ownerPubKey)) {
Set<SignedWitness> set = signedWitnessSetByOwnerPubKey.get(ownerPubKey);
set.remove(signedWitness);
if (set.isEmpty()) {
signedWitnessSetByOwnerPubKey.remove(ownerPubKey);
}
}
}
// Remove SignedWitnesses that are signed by TRADE that also have an ARBITRATOR signature

View File

@ -64,6 +64,8 @@ import com.google.common.annotations.VisibleForTesting;
import java.security.PublicKey;
import java.time.Clock;
import java.util.Arrays;
import java.util.Date;
import java.util.GregorianCalendar;
@ -136,6 +138,8 @@ public class AccountAgeWitnessService {
private final User user;
private final SignedWitnessService signedWitnessService;
private final ChargeBackRisk chargeBackRisk;
private final AccountAgeWitnessStorageService accountAgeWitnessStorageService;
private final Clock clock;
private final FilterManager filterManager;
@Getter
private final AccountAgeWitnessUtils accountAgeWitnessUtils;
@ -161,12 +165,15 @@ public class AccountAgeWitnessService {
ChargeBackRisk chargeBackRisk,
AccountAgeWitnessStorageService accountAgeWitnessStorageService,
AppendOnlyDataStoreService appendOnlyDataStoreService,
Clock clock,
FilterManager filterManager) {
this.keyRing = keyRing;
this.p2PService = p2PService;
this.user = user;
this.signedWitnessService = signedWitnessService;
this.chargeBackRisk = chargeBackRisk;
this.accountAgeWitnessStorageService = accountAgeWitnessStorageService;
this.clock = clock;
this.filterManager = filterManager;
accountAgeWitnessUtils = new AccountAgeWitnessUtils(
@ -190,10 +197,10 @@ public class AccountAgeWitnessService {
});
// At startup the P2PDataStorage initializes earlier, otherwise we get the listener called.
p2PService.getP2PDataStorage().getAppendOnlyDataStoreMap().values().forEach(e -> {
if (e instanceof AccountAgeWitness)
addToMap((AccountAgeWitness) e);
});
accountAgeWitnessStorageService.getMapOfAllData().values().stream()
.filter(e -> e instanceof AccountAgeWitness)
.map(e -> (AccountAgeWitness) e)
.forEach(this::addToMap);
if (p2PService.isBootstrapped()) {
onBootStrapped();
@ -217,13 +224,18 @@ public class AccountAgeWitnessService {
private void republishAllFiatAccounts() {
if (user.getPaymentAccounts() != null)
user.getPaymentAccounts().stream()
.filter(e -> !(e instanceof AssetAccount))
.forEach(e -> {
// We delay with a random interval of 20-60 sec to ensure to be better connected and don't
// stress the P2P network with publishing all at once at startup time.
final int delayInSec = 20 + new Random().nextInt(40);
UserThread.runAfter(() -> p2PService.addPersistableNetworkPayload(getMyWitness(
e.getPaymentAccountPayload()), true), delayInSec);
.filter(account -> !(account instanceof AssetAccount))
.forEach(account -> {
AccountAgeWitness myWitness = getMyWitness(account.getPaymentAccountPayload());
// We only publish if the date of our witness is inside the date tolerance.
// It would be rejected otherwise from the peers.
if (myWitness.isDateInTolerance(clock)) {
// We delay with a random interval of 20-60 sec to ensure to be better connected and don't
// stress the P2P network with publishing all at once at startup time.
int delayInSec = 20 + new Random().nextInt(40);
UserThread.runAfter(() ->
p2PService.addPersistableNetworkPayload(myWitness, true), delayInSec);
}
});
}

View File

@ -108,10 +108,18 @@ public class CoreApi {
return coreOffersService.getOffer(id);
}
public Offer getMyOffer(String id) {
return coreOffersService.getMyOffer(id);
}
public List<Offer> getOffers(String direction, String currencyCode) {
return coreOffersService.getOffers(direction, currencyCode);
}
public List<Offer> getMyOffers(String direction, String currencyCode) {
return coreOffersService.getMyOffers(direction, currencyCode);
}
public void createAnPlaceOffer(String currencyCode,
String directionAsString,
String priceAsString,

View File

@ -27,6 +27,8 @@ import bisq.core.offer.OpenOfferManager;
import bisq.core.payment.PaymentAccount;
import bisq.core.user.User;
import bisq.common.crypto.KeyRing;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.utils.Fiat;
@ -38,6 +40,7 @@ import java.math.BigDecimal;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
@ -49,10 +52,15 @@ import static bisq.core.locale.CurrencyUtil.isCryptoCurrency;
import static bisq.core.offer.OfferPayload.Direction;
import static bisq.core.offer.OfferPayload.Direction.BUY;
import static java.lang.String.format;
import static java.util.Comparator.comparing;
@Slf4j
class CoreOffersService {
private final Supplier<Comparator<Offer>> priceComparator = () -> comparing(Offer::getPrice);
private final Supplier<Comparator<Offer>> reversePriceComparator = () -> comparing(Offer::getPrice).reversed();
private final KeyRing keyRing;
private final CreateOfferService createOfferService;
private final OfferBookService offerBookService;
private final OpenOfferManager openOfferManager;
@ -60,11 +68,13 @@ class CoreOffersService {
private final User user;
@Inject
public CoreOffersService(CreateOfferService createOfferService,
public CoreOffersService(KeyRing keyRing,
CreateOfferService createOfferService,
OfferBookService offerBookService,
OpenOfferManager openOfferManager,
OfferUtil offerUtil,
User user) {
this.keyRing = keyRing;
this.createOfferService = createOfferService;
this.offerBookService = offerBookService;
this.openOfferManager = openOfferManager;
@ -79,24 +89,27 @@ class CoreOffersService {
new IllegalStateException(format("offer with id '%s' not found", id)));
}
Offer getMyOffer(String id) {
return offerBookService.getOffers().stream()
.filter(o -> o.getId().equals(id))
.filter(o -> o.isMyOffer(keyRing))
.findAny().orElseThrow(() ->
new IllegalStateException(format("offer with id '%s' not found", id)));
}
List<Offer> getOffers(String direction, String currencyCode) {
List<Offer> offers = offerBookService.getOffers().stream()
.filter(o -> {
var offerOfWantedDirection = o.getDirection().name().equalsIgnoreCase(direction);
var offerInWantedCurrency = o.getOfferPayload().getCounterCurrencyCode()
.equalsIgnoreCase(currencyCode);
return offerOfWantedDirection && offerInWantedCurrency;
})
return offerBookService.getOffers().stream()
.filter(o -> offerMatchesDirectionAndCurrency(o, direction, currencyCode))
.sorted(priceComparator(direction))
.collect(Collectors.toList());
}
// A buyer probably wants to see sell orders in price ascending order.
// A seller probably wants to see buy orders in price descending order.
if (direction.equalsIgnoreCase(BUY.name()))
offers.sort(Comparator.comparing(Offer::getPrice).reversed());
else
offers.sort(Comparator.comparing(Offer::getPrice));
return offers;
List<Offer> getMyOffers(String direction, String currencyCode) {
return offerBookService.getOffers().stream()
.filter(o -> o.isMyOffer(keyRing))
.filter(o -> offerMatchesDirectionAndCurrency(o, direction, currencyCode))
.sorted(priceComparator(direction))
.collect(Collectors.toList());
}
// Create and place new offer.
@ -182,9 +195,11 @@ class CoreOffersService {
double buyerSecurityDeposit,
boolean useSavingsWallet,
Consumer<Transaction> resultHandler) {
// TODO add support for triggerPrice parameter. If value is 0 it is interpreted as not used. Its an optional value
openOfferManager.placeOffer(offer,
buyerSecurityDeposit,
useSavingsWallet,
0,
resultHandler::accept,
log::error);
@ -192,6 +207,23 @@ class CoreOffersService {
throw new IllegalStateException(offer.getErrorMessage());
}
private boolean offerMatchesDirectionAndCurrency(Offer offer,
String direction,
String currencyCode) {
var offerOfWantedDirection = offer.getDirection().name().equalsIgnoreCase(direction);
var offerInWantedCurrency = offer.getOfferPayload().getCounterCurrencyCode()
.equalsIgnoreCase(currencyCode);
return offerOfWantedDirection && offerInWantedCurrency;
}
private Comparator<Offer> priceComparator(String direction) {
// A buyer probably wants to see sell orders in price ascending order.
// A seller probably wants to see buy orders in price descending order.
return direction.equalsIgnoreCase(BUY.name())
? reversePriceComparator.get()
: priceComparator.get();
}
private long priceStringToLong(String priceAsString, String currencyCode) {
int precision = isCryptoCurrency(currencyCode) ? Altcoin.SMALLEST_UNIT_EXPONENT : Fiat.SMALLEST_UNIT_EXPONENT;
double priceAsDouble = new BigDecimal(priceAsString).doubleValue();

View File

@ -73,6 +73,7 @@ class CorePaymentAccountsService {
String getPaymentAccountFormAsString(String paymentMethodId) {
File jsonForm = getPaymentAccountForm(paymentMethodId);
jsonForm.deleteOnExit(); // If just asking for a string, delete the form file.
return paymentAccountForm.toJsonString(jsonForm);
}

View File

@ -108,6 +108,7 @@ class CoreTradesService {
offer,
paymentAccountId,
useSavingsWallet,
true,
resultHandler::accept,
errorMessage -> {
log.error(errorMessage);

View File

@ -18,6 +18,7 @@
package bisq.core.app;
import bisq.core.account.sign.SignedWitness;
import bisq.core.account.sign.SignedWitnessStorageService;
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.alert.Alert;
import bisq.core.alert.AlertManager;
@ -51,6 +52,7 @@ import bisq.common.UserThread;
import bisq.common.app.DevEnv;
import bisq.common.app.Log;
import bisq.common.app.Version;
import bisq.common.config.BaseCurrencyNetwork;
import bisq.common.config.Config;
import bisq.common.util.InvalidVersionException;
import bisq.common.util.Utilities;
@ -123,6 +125,7 @@ public class BisqSetup {
private final WalletsSetup walletsSetup;
private final BtcWalletService btcWalletService;
private final P2PService p2PService;
private final SignedWitnessStorageService signedWitnessStorageService;
private final TradeManager tradeManager;
private final OpenOfferManager openOfferManager;
private final Preferences preferences;
@ -204,6 +207,7 @@ public class BisqSetup {
WalletsSetup walletsSetup,
BtcWalletService btcWalletService,
P2PService p2PService,
SignedWitnessStorageService signedWitnessStorageService,
TradeManager tradeManager,
OpenOfferManager openOfferManager,
Preferences preferences,
@ -224,6 +228,7 @@ public class BisqSetup {
this.walletsSetup = walletsSetup;
this.btcWalletService = btcWalletService;
this.p2PService = p2PService;
this.signedWitnessStorageService = signedWitnessStorageService;
this.tradeManager = tradeManager;
this.openOfferManager = openOfferManager;
this.preferences = preferences;
@ -273,7 +278,8 @@ public class BisqSetup {
public void start() {
// If user tried to downgrade we require a shutdown
if (hasDowngraded(downGradePreventionHandler)) {
if (Config.baseCurrencyNetwork() == BaseCurrencyNetwork.BTC_MAINNET &&
hasDowngraded(downGradePreventionHandler)) {
return;
}
@ -650,7 +656,7 @@ public class BisqSetup {
private void checkSigningState(AccountAgeWitnessService.SignState state,
String key, Consumer<String> displayHandler) {
boolean signingStateFound = p2PService.getP2PDataStorage().getAppendOnlyDataStoreMap().values().stream()
boolean signingStateFound = signedWitnessStorageService.getMap().values().stream()
.anyMatch(payload -> isSignedWitnessOfMineWithState(payload, state));
maybeTriggerDisplayHandler(key, displayHandler, signingStateFound);

View File

@ -21,6 +21,7 @@ import bisq.core.alert.AlertModule;
import bisq.core.btc.BitcoinModule;
import bisq.core.dao.DaoModule;
import bisq.core.filter.FilterModule;
import bisq.core.network.CoreNetworkFilter;
import bisq.core.network.p2p.seed.DefaultSeedNodeRepository;
import bisq.core.offer.OfferModule;
import bisq.core.presentation.CorePresentationModule;
@ -35,6 +36,7 @@ import bisq.core.util.coin.ImmutableCoinFormatter;
import bisq.network.crypto.EncryptionServiceModule;
import bisq.network.p2p.P2PModule;
import bisq.network.p2p.network.BridgeAddressProvider;
import bisq.network.p2p.network.NetworkFilter;
import bisq.network.p2p.seed.SeedNodeRepository;
import bisq.common.app.AppModule;
@ -44,6 +46,8 @@ import bisq.common.crypto.PubKeyRingProvider;
import bisq.common.proto.network.NetworkProtoResolver;
import bisq.common.proto.persistable.PersistenceProtoResolver;
import com.google.inject.Singleton;
import java.io.File;
import static bisq.common.config.Config.*;
@ -62,6 +66,7 @@ public class CoreModule extends AppModule {
bind(BridgeAddressProvider.class).to(Preferences.class);
bind(SeedNodeRepository.class).to(DefaultSeedNodeRepository.class);
bind(NetworkFilter.class).to(CoreNetworkFilter.class).in(Singleton.class);
bind(File.class).annotatedWith(named(STORAGE_DIR)).toInstance(config.storageDir);

View File

@ -34,6 +34,7 @@ import bisq.core.notifications.alerts.TradeEvents;
import bisq.core.notifications.alerts.market.MarketAlerts;
import bisq.core.notifications.alerts.price.PriceAlert;
import bisq.core.offer.OpenOfferManager;
import bisq.core.offer.TriggerPriceService;
import bisq.core.payment.RevolutAccount;
import bisq.core.payment.TradeLimits;
import bisq.core.provider.fee.FeeService;
@ -106,6 +107,7 @@ public class DomainInitialisation {
private final MarketAlerts marketAlerts;
private final User user;
private final DaoStateSnapshotService daoStateSnapshotService;
private final TriggerPriceService triggerPriceService;
@Inject
public DomainInitialisation(ClockWatcher clockWatcher,
@ -141,7 +143,8 @@ public class DomainInitialisation {
PriceAlert priceAlert,
MarketAlerts marketAlerts,
User user,
DaoStateSnapshotService daoStateSnapshotService) {
DaoStateSnapshotService daoStateSnapshotService,
TriggerPriceService triggerPriceService) {
this.clockWatcher = clockWatcher;
this.tradeLimits = tradeLimits;
this.arbitrationManager = arbitrationManager;
@ -176,6 +179,7 @@ public class DomainInitialisation {
this.marketAlerts = marketAlerts;
this.user = user;
this.daoStateSnapshotService = daoStateSnapshotService;
this.triggerPriceService = triggerPriceService;
}
public void initDomainServices(Consumer<String> rejectedTxErrorMessageHandler,
@ -254,6 +258,7 @@ public class DomainInitialisation {
disputeMsgEvents.onAllServicesInitialized();
priceAlert.onAllServicesInitialized();
marketAlerts.onAllServicesInitialized();
triggerPriceService.onAllServicesInitialized();
if (revolutAccountsUpdateHandler != null) {
revolutAccountsUpdateHandler.accept(user.getPaymentAccountsAsObservable().stream()

View File

@ -107,7 +107,7 @@ public class WalletAppSetup {
Runnable downloadCompleteHandler,
Runnable walletInitializedHandler) {
log.info("Initialize WalletAppSetup with BitcoinJ version {} and hash of BitcoinJ commit {}",
VersionMessage.BITCOINJ_VERSION, "dcf8af0");
VersionMessage.BITCOINJ_VERSION, "2a80db4");
ObjectProperty<Throwable> walletServiceException = new SimpleObjectProperty<>();
btcInfoBinding = EasyBind.combine(walletsSetup.downloadPercentageProperty(),

View File

@ -22,6 +22,7 @@ import bisq.core.app.TorSetup;
import bisq.core.btc.BitcoinModule;
import bisq.core.dao.DaoModule;
import bisq.core.filter.FilterModule;
import bisq.core.network.CoreNetworkFilter;
import bisq.core.network.p2p.seed.DefaultSeedNodeRepository;
import bisq.core.offer.OfferModule;
import bisq.core.proto.network.CoreNetworkProtoResolver;
@ -33,6 +34,7 @@ import bisq.core.user.User;
import bisq.network.crypto.EncryptionServiceModule;
import bisq.network.p2p.P2PModule;
import bisq.network.p2p.network.BridgeAddressProvider;
import bisq.network.p2p.network.NetworkFilter;
import bisq.network.p2p.seed.SeedNodeRepository;
import bisq.common.ClockWatcher;
@ -73,6 +75,7 @@ public class ModuleForAppWithP2p extends AppModule {
bind(TorSetup.class).in(Singleton.class);
bind(SeedNodeRepository.class).to(DefaultSeedNodeRepository.class).in(Singleton.class);
bind(NetworkFilter.class).to(CoreNetworkFilter.class).in(Singleton.class);
bind(File.class).annotatedWith(named(STORAGE_DIR)).toInstance(config.storageDir);
bind(File.class).annotatedWith(named(KEY_STORAGE_DIR)).toInstance(config.keyStorageDir);
@ -81,6 +84,7 @@ public class ModuleForAppWithP2p extends AppModule {
bindConstant().annotatedWith(named(USE_DEV_MODE)).to(config.useDevMode);
bindConstant().annotatedWith(named(USE_DEV_MODE_HEADER)).to(config.useDevModeHeader);
bindConstant().annotatedWith(named(REFERRAL_ID)).to(config.referralId);
bindConstant().annotatedWith(named(PREVENT_PERIODIC_SHUTDOWN_AT_SEED_NODE)).to(config.preventPeriodicShutdownAtSeedNode);
// ordering is used for shut down sequence
install(new TradeModule(config));

View File

@ -30,11 +30,16 @@ import javax.annotation.concurrent.Immutable;
@EqualsAndHashCode
@Immutable
public final class RawTransactionInput implements NetworkPayload, PersistablePayload {
// Payload
public final long index;
public final byte[] parentTransaction;
public final long index; // Index of spending txo
public final byte[] parentTransaction; // Spending tx (fromTx)
public final long value;
/**
* Holds the relevant data for the connected output for a tx input.
* @param index the index of the parentTransaction
* @param parentTransaction the spending output tx, not the parent tx of the input
* @param value the number of satoshis being spent
*/
public RawTransactionInput(long index, byte[] parentTransaction, long value) {
this.index = index;
this.parentTransaction = parentTransaction;

View File

@ -1111,15 +1111,7 @@ public class TradeWalletService {
byte[] buyerPubKey = ECKey.fromPublicOnly(Utils.HEX.decode(buyerPubKeyAsHex)).getPubKey();
byte[] sellerPubKey = ECKey.fromPublicOnly(Utils.HEX.decode(sellerPubKeyAsHex)).getPubKey();
Script redeemScript = get2of2MultiSigRedeemScript(buyerPubKey, sellerPubKey);
Script hashedMultiSigOutputScript = get2of2MultiSigOutputScript(buyerPubKey, sellerPubKey,
hashedMultiSigOutputIsLegacy);
Coin msOutputValue = buyerPayoutAmount.add(sellerPayoutAmount).add(txFee);
TransactionOutput hashedMultiSigOutput = new TransactionOutput(params, null, msOutputValue, hashedMultiSigOutputScript.getProgram());
Transaction depositTx = new Transaction(params);
depositTx.addOutput(hashedMultiSigOutput);
Transaction payoutTx = new Transaction(params);
Sha256Hash spendTxHash = Sha256Hash.wrap(depositTxHex);
payoutTx.addInput(new TransactionInput(params, payoutTx, new byte[]{}, new TransactionOutPoint(params, 0, spendTxHash), msOutputValue));

View File

@ -631,7 +631,7 @@ public class DaoFacade implements DaoSetupService {
}
public int getNumIssuanceTransactions(IssuanceType issuanceType) {
return daoStateService.getIssuanceSet(issuanceType).size();
return daoStateService.getIssuanceSetForType(issuanceType).size();
}
public Set<Tx> getBurntFeeTxs() {

View File

@ -286,7 +286,7 @@ public class MyBlindVoteListService implements PersistedDataHost, DaoStateListen
.filter(txId -> periodService.isTxInPastCycle(txId, periodService.getChainHeight()))
.collect(Collectors.toSet());
return new MeritList(daoStateService.getIssuanceSet(IssuanceType.COMPENSATION).stream()
return new MeritList(daoStateService.getIssuanceSetForType(IssuanceType.COMPENSATION).stream()
.map(issuance -> {
checkArgument(issuance.getIssuanceType() == IssuanceType.COMPENSATION,
"IssuanceType must be COMPENSATION for MeritList");

View File

@ -102,7 +102,7 @@ public final class RepublishGovernanceDataHandler {
connectToNextNode();
} else {
log.warn("We have stopped already. We ignore that timeoutTimer.run call. " +
"Might be caused by an previous networkNode.sendMessage.onFailure.");
"Might be caused by a previous networkNode.sendMessage.onFailure.");
}
},
TIMEOUT);
@ -118,7 +118,7 @@ public final class RepublishGovernanceDataHandler {
stop();
} else {
log.trace("We have stopped already. We ignore that networkNode.sendMessage.onSuccess call." +
"Might be caused by an previous timeout.");
"Might be caused by a previous timeout.");
}
}
@ -133,7 +133,7 @@ public final class RepublishGovernanceDataHandler {
connectToNextNode();
} else {
log.trace("We have stopped already. We ignore that networkNode.sendMessage.onFailure call. " +
"Might be caused by an previous timeout.");
"Might be caused by a previous timeout.");
}
}
}, MoreExecutors.directExecutor());

View File

@ -69,6 +69,7 @@ public class ProposalService implements HashMapChangedListener, AppendOnlyDataSt
DaoStateListener, DaoSetupService {
private final P2PService p2PService;
private final PeriodService periodService;
private final ProposalStorageService proposalStorageService;
private final DaoStateService daoStateService;
private final ProposalValidatorProvider validatorProvider;
@ -100,6 +101,7 @@ public class ProposalService implements HashMapChangedListener, AppendOnlyDataSt
@Named(Config.DAO_ACTIVATED) boolean daoActivated) {
this.p2PService = p2PService;
this.periodService = periodService;
this.proposalStorageService = proposalStorageService;
this.daoStateService = daoStateService;
this.validatorProvider = validatorProvider;
@ -217,7 +219,7 @@ public class ProposalService implements HashMapChangedListener, AppendOnlyDataSt
}
private void fillListFromAppendOnlyDataStore() {
p2PService.getP2PDataStorage().getAppendOnlyDataStoreMap().values().forEach(e -> onAppendOnlyDataAdded(e, false));
proposalStorageService.getMap().values().forEach(e -> onAppendOnlyDataAdded(e, false));
}
private void maybePublishToAppendOnlyDataStore() {

View File

@ -371,7 +371,7 @@ public class DaoStateMonitoringService implements DaoSetupService, DaoStateListe
if (this.isInConflictWithSeedNode)
log.warn("Conflict with seed nodes: {}", conflictMsg);
else if (this.isInConflictWithNonSeedNode)
log.info("Conflict with non-seed nodes: {}", conflictMsg);
log.debug("Conflict with non-seed nodes: {}", conflictMsg);
}

View File

@ -116,7 +116,7 @@ abstract class RequestStateHashesHandler<Req extends GetStateHashesRequest, Res
handleFault(errorMessage, nodeAddress, CloseConnectionReason.SEND_MSG_TIMEOUT);
} else {
log.trace("We have stopped already. We ignore that timeoutTimer.run call. " +
"Might be caused by an previous networkNode.sendMessage.onFailure.");
"Might be caused by a previous networkNode.sendMessage.onFailure.");
}
},
TIMEOUT);
@ -134,7 +134,7 @@ abstract class RequestStateHashesHandler<Req extends GetStateHashesRequest, Res
nodeAddress.getFullAddress());
} else {
log.trace("We have stopped already. We ignore that networkNode.sendMessage.onSuccess call." +
"Might be caused by an previous timeout.");
"Might be caused by a previous timeout.");
}
}
@ -149,7 +149,7 @@ abstract class RequestStateHashesHandler<Req extends GetStateHashesRequest, Res
handleFault(errorMessage, nodeAddress, CloseConnectionReason.SEND_MSG_FAILURE);
} else {
log.trace("We have stopped already. We ignore that networkNode.sendMessage.onFailure call. " +
"Might be caused by an previous timeout.");
"Might be caused by a previous timeout.");
}
}
}, MoreExecutors.directExecutor());

View File

@ -189,7 +189,7 @@ public abstract class BsqNode implements DaoSetupService {
if (chainHeight > genesisBlockHeight)
startBlockHeight = chainHeight + 1;
log.info("Start parse blocks:\n" +
log.info("getStartBlockHeight:\n" +
" Start block height={}\n" +
" Genesis txId={}\n" +
" Genesis block height={}\n" +
@ -223,15 +223,14 @@ public abstract class BsqNode implements DaoSetupService {
// height we have no block but chainHeight is initially set to genesis height (bad design ;-( but a bit tricky
// to change now as it used in many areas.)
if (daoStateService.getBlockAtHeight(rawBlock.getHeight()).isPresent()) {
log.debug("We have already a block with the height of the new block. Height of new block={}", rawBlock.getHeight());
log.info("We have already a block with the height of the new block. Height of new block={}", rawBlock.getHeight());
return Optional.empty();
}
try {
Block block = blockParser.parseBlock(rawBlock);
if (pendingBlocks.contains(rawBlock))
pendingBlocks.remove(rawBlock);
pendingBlocks.remove(rawBlock);
// After parsing we check if we have pending blocks we might have received earlier but which have been
// not connecting from the latest height we had. The list is sorted by height

View File

@ -43,7 +43,7 @@ enum JsonTxOutputType {
INVALID_OUTPUT("Invalid");
@Getter
private String displayString;
private final String displayString;
JsonTxOutputType(String displayString) {
this.displayString = displayString;

View File

@ -40,7 +40,7 @@ enum JsonTxType {
IRREGULAR("Irregular");
@Getter
private String displayString;
private final String displayString;
JsonTxType(String displayString) {
this.displayString = displayString;

View File

@ -136,51 +136,62 @@ public class FullNodeNetworkService implements MessageListener, PeerManager.List
@Override
public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) {
if (networkEnvelope instanceof GetBlocksRequest) {
// We received a GetBlocksRequest from a liteNode
if (!stopped) {
final String uid = connection.getUid();
if (!getBlocksRequestHandlers.containsKey(uid)) {
GetBlocksRequestHandler requestHandler = new GetBlocksRequestHandler(networkNode,
daoStateService,
new GetBlocksRequestHandler.Listener() {
@Override
public void onComplete() {
getBlocksRequestHandlers.remove(uid);
}
@Override
public void onFault(String errorMessage, @Nullable Connection connection) {
getBlocksRequestHandlers.remove(uid);
if (!stopped) {
log.trace("GetDataRequestHandler failed.\n\tConnection={}\n\t" +
"ErrorMessage={}", connection, errorMessage);
peerManager.handleConnectionFault(connection);
} else {
log.warn("We have stopped already. We ignore that getDataRequestHandler.handle.onFault call.");
}
}
});
getBlocksRequestHandlers.put(uid, requestHandler);
requestHandler.onGetBlocksRequest((GetBlocksRequest) networkEnvelope, connection);
} else {
log.warn("We have already a GetDataRequestHandler for that connection started. " +
"We start a cleanup timer if the handler has not closed by itself in between 2 minutes.");
UserThread.runAfter(() -> {
if (getBlocksRequestHandlers.containsKey(uid)) {
GetBlocksRequestHandler handler = getBlocksRequestHandlers.get(uid);
handler.stop();
getBlocksRequestHandlers.remove(uid);
}
}, CLEANUP_TIMER);
}
} else {
log.warn("We have stopped already. We ignore that onMessage call.");
}
handleGetBlocksRequest((GetBlocksRequest) networkEnvelope, connection);
} else if (networkEnvelope instanceof RepublishGovernanceDataRequest) {
log.warn("We received a RepublishGovernanceDataRequest and re-published all proposalPayloads and " +
"blindVotePayloads to the P2P network.");
missingDataRequestService.reRepublishAllGovernanceData();
handleRepublishGovernanceDataRequest();
}
}
private void handleGetBlocksRequest(GetBlocksRequest getBlocksRequest, Connection connection) {
if (stopped) {
log.warn("We have stopped already. We ignore that onMessage call.");
return;
}
String uid = connection.getUid();
if (getBlocksRequestHandlers.containsKey(uid)) {
log.warn("We have already a GetDataRequestHandler for that connection started. " +
"We start a cleanup timer if the handler has not closed by itself in between 2 minutes.");
UserThread.runAfter(() -> {
if (getBlocksRequestHandlers.containsKey(uid)) {
GetBlocksRequestHandler handler = getBlocksRequestHandlers.get(uid);
handler.stop();
getBlocksRequestHandlers.remove(uid);
}
}, CLEANUP_TIMER);
return;
}
GetBlocksRequestHandler requestHandler = new GetBlocksRequestHandler(networkNode,
daoStateService,
new GetBlocksRequestHandler.Listener() {
@Override
public void onComplete() {
getBlocksRequestHandlers.remove(uid);
}
@Override
public void onFault(String errorMessage, @Nullable Connection connection) {
getBlocksRequestHandlers.remove(uid);
if (!stopped) {
log.trace("GetDataRequestHandler failed.\n\tConnection={}\n\t" +
"ErrorMessage={}", connection, errorMessage);
if (connection != null) {
peerManager.handleConnectionFault(connection);
}
} else {
log.warn("We have stopped already. We ignore that getDataRequestHandler.handle.onFault call.");
}
}
});
getBlocksRequestHandlers.put(uid, requestHandler);
requestHandler.onGetBlocksRequest(getBlocksRequest, connection);
}
private void handleRepublishGovernanceDataRequest() {
log.warn("We received a RepublishGovernanceDataRequest and re-published all proposalPayloads and " +
"blindVotePayloads to the P2P network.");
missingDataRequestService.reRepublishAllGovernanceData();
}
}

View File

@ -49,7 +49,7 @@ import org.jetbrains.annotations.NotNull;
*/
@Slf4j
class GetBlocksRequestHandler {
private static final long TIMEOUT = 120;
private static final long TIMEOUT_MIN = 3;
///////////////////////////////////////////////////////////////////////////////////////////
@ -89,22 +89,28 @@ class GetBlocksRequestHandler {
// API
///////////////////////////////////////////////////////////////////////////////////////////
public void onGetBlocksRequest(GetBlocksRequest getBlocksRequest, final Connection connection) {
public void onGetBlocksRequest(GetBlocksRequest getBlocksRequest, Connection connection) {
long ts = System.currentTimeMillis();
// We limit number of blocks to 6000 which is about 1.5 month.
List<Block> blocks = new LinkedList<>(daoStateService.getBlocksFromBlockHeight(getBlocksRequest.getFromBlockHeight(), 6000));
List<RawBlock> rawBlocks = blocks.stream().map(RawBlock::fromBlock).collect(Collectors.toList());
GetBlocksResponse getBlocksResponse = new GetBlocksResponse(rawBlocks, getBlocksRequest.getNonce());
log.info("Received GetBlocksRequest from {} for blocks from height {}",
connection.getPeersNodeAddressOptional(), getBlocksRequest.getFromBlockHeight());
if (timeoutTimer == null) {
timeoutTimer = UserThread.runAfter(() -> { // setup before sending to avoid race conditions
String errorMessage = "A timeout occurred for getBlocksResponse.requestNonce:" +
getBlocksResponse.getRequestNonce() +
" on connection:" + connection;
handleFault(errorMessage, CloseConnectionReason.SEND_MSG_TIMEOUT, connection);
},
TIMEOUT, TimeUnit.SECONDS);
log.info("Received GetBlocksRequest from {} for blocks from height {}. " +
"Building GetBlocksResponse with {} blocks took {} ms.",
connection.getPeersNodeAddressOptional(), getBlocksRequest.getFromBlockHeight(),
rawBlocks.size(), System.currentTimeMillis() - ts);
if (timeoutTimer != null) {
timeoutTimer.stop();
log.warn("Timeout was already running. We stopped it.");
}
timeoutTimer = UserThread.runAfter(() -> { // setup before sending to avoid race conditions
String errorMessage = "A timeout occurred for getBlocksResponse.requestNonce:" +
getBlocksResponse.getRequestNonce() +
" on connection: " + connection;
handleFault(errorMessage, CloseConnectionReason.SEND_MSG_TIMEOUT, connection);
},
TIMEOUT_MIN, TimeUnit.MINUTES);
SettableFuture<Connection> future = networkNode.sendMessage(connection, getBlocksResponse);
Futures.addCallback(future, new FutureCallback<>() {
@ -145,7 +151,7 @@ class GetBlocksRequestHandler {
private void handleFault(String errorMessage, CloseConnectionReason closeConnectionReason, Connection connection) {
if (!stopped) {
log.debug(errorMessage + "\n\tcloseConnectionReason=" + closeConnectionReason);
log.warn("{}, closeConnectionReason={}", errorMessage, closeConnectionReason);
cleanup();
listener.onFault(errorMessage, connection);
} else {

View File

@ -17,6 +17,7 @@
package bisq.core.dao.node.lite;
import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.dao.node.BsqNode;
import bisq.core.dao.node.explorer.ExportJsonFilesService;
@ -37,6 +38,8 @@ import bisq.common.UserThread;
import com.google.inject.Inject;
import javafx.beans.value.ChangeListener;
import java.util.ArrayList;
import java.util.List;
@ -54,7 +57,9 @@ public class LiteNode extends BsqNode {
private final LiteNodeNetworkService liteNodeNetworkService;
private final BsqWalletService bsqWalletService;
private final WalletsSetup walletsSetup;
private Timer checkForBlockReceivedTimer;
private final ChangeListener<Number> blockDownloadListener;
///////////////////////////////////////////////////////////////////////////////////////////
@ -69,11 +74,19 @@ public class LiteNode extends BsqNode {
P2PService p2PService,
LiteNodeNetworkService liteNodeNetworkService,
BsqWalletService bsqWalletService,
WalletsSetup walletsSetup,
ExportJsonFilesService exportJsonFilesService) {
super(blockParser, daoStateService, daoStateSnapshotService, p2PService, exportJsonFilesService);
this.liteNodeNetworkService = liteNodeNetworkService;
this.bsqWalletService = bsqWalletService;
this.walletsSetup = walletsSetup;
blockDownloadListener = (observable, oldValue, newValue) -> {
if ((double) newValue == 1) {
setupWalletBestBlockListener();
}
};
}
@ -87,7 +100,18 @@ public class LiteNode extends BsqNode {
liteNodeNetworkService.start();
bsqWalletService.addNewBestBlockListener(block -> {
// We wait until the wallet is synced before using it for triggering requests
if (walletsSetup.isDownloadComplete()) {
setupWalletBestBlockListener();
} else {
walletsSetup.downloadPercentageProperty().addListener(blockDownloadListener);
}
}
private void setupWalletBestBlockListener() {
walletsSetup.downloadPercentageProperty().removeListener(blockDownloadListener);
bsqWalletService.addNewBestBlockListener(blockFromWallet -> {
// Check if we are done with parsing
if (!daoStateService.isParseBlockChainComplete())
return;
@ -97,18 +121,18 @@ public class LiteNode extends BsqNode {
checkForBlockReceivedTimer.stop();
}
int height = block.getHeight();
log.info("New block at height {} from bsqWalletService", height);
int walletBlockHeight = blockFromWallet.getHeight();
log.info("New block at height {} from bsqWalletService", walletBlockHeight);
// We expect to receive the new BSQ block from the network shortly after BitcoinJ has been aware of it.
// If we don't receive it we request it manually from seed nodes
checkForBlockReceivedTimer = UserThread.runAfter(() -> {
int chainHeight = daoStateService.getChainHeight();
if (chainHeight < height) {
log.warn("We did not receive a block from the network {} seconds after we saw the new block in BicoinJ. " +
int daoChainHeight = daoStateService.getChainHeight();
if (daoChainHeight < walletBlockHeight) {
log.warn("We did not receive a block from the network {} seconds after we saw the new block in BitcoinJ. " +
"We request from our seed nodes missing blocks from block height {}.",
CHECK_FOR_BLOCK_RECEIVED_DELAY_SEC, chainHeight + 1);
liteNodeNetworkService.requestBlocks(chainHeight + 1);
CHECK_FOR_BLOCK_RECEIVED_DELAY_SEC, daoChainHeight + 1);
liteNodeNetworkService.requestBlocks(daoChainHeight + 1);
}
}, CHECK_FOR_BLOCK_RECEIVED_DELAY_SEC);
});
@ -157,7 +181,6 @@ public class LiteNode extends BsqNode {
// First we request the blocks from a full node
@Override
protected void startParseBlocks() {
log.info("startParseBlocks");
liteNodeNetworkService.requestBlocks(getStartBlockHeight());
}
@ -199,8 +222,12 @@ public class LiteNode extends BsqNode {
runDelayedBatchProcessing(new ArrayList<>(blockList),
() -> {
log.debug("Parsing {} blocks took {} seconds.", blockList.size(), (System.currentTimeMillis() - ts) / 1000d);
if (daoStateService.getChainHeight() < bsqWalletService.getBestChainHeight()) {
log.info("runDelayedBatchProcessing Parsing {} blocks took {} seconds.", blockList.size(),
(System.currentTimeMillis() - ts) / 1000d);
// We only request again if wallet is synced, otherwise we would get repeated calls we want to avoid.
// We deal with that case at the setupWalletBestBlockListener method above.
if (walletsSetup.isDownloadComplete() &&
daoStateService.getChainHeight() < bsqWalletService.getBestChainHeight()) {
liteNodeNetworkService.requestBlocks(getStartBlockHeight());
} else {
onParsingComplete.run();
@ -229,11 +256,13 @@ public class LiteNode extends BsqNode {
// We received a new block
private void onNewBlockReceived(RawBlock block) {
int blockHeight = block.getHeight();
log.debug("onNewBlockReceived: block at height {}, hash={}", blockHeight, block.getHash());
log.info("onNewBlockReceived: block at height {}, hash={}. Our DAO chainHeight={}",
blockHeight, block.getHash(), chainTipHeight);
// We only update chainTipHeight if we get a newer block
if (blockHeight > chainTipHeight)
if (blockHeight > chainTipHeight) {
chainTipHeight = blockHeight;
}
try {
doParseBlock(block);

View File

@ -99,7 +99,7 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen
private final Map<Tuple2<NodeAddress, Integer>, RequestBlocksHandler> requestBlocksHandlerMap = new HashMap<>();
private Timer retryTimer;
private boolean stopped;
private Set<String> receivedBlocks = new HashSet<>();
private final Set<String> receivedBlocks = new HashSet<>();
///////////////////////////////////////////////////////////////////////////////////////////
@ -129,7 +129,6 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen
peerManager.addListener(this);
}
@SuppressWarnings("Duplicates")
public void shutDown() {
stopped = true;
stopRetryTimer();
@ -152,19 +151,21 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen
Optional<Connection> connectionToSeedNodeOptional = networkNode.getConfirmedConnections().stream()
.filter(peerManager::isSeedNode)
.findAny();
if (connectionToSeedNodeOptional.isPresent() &&
connectionToSeedNodeOptional.get().getPeersNodeAddressOptional().isPresent()) {
requestBlocks(connectionToSeedNodeOptional.get().getPeersNodeAddressOptional().get(), startBlockHeight);
} else {
tryWithNewSeedNode(startBlockHeight);
}
connectionToSeedNodeOptional.flatMap(Connection::getPeersNodeAddressOptional)
.ifPresentOrElse(candidate -> {
seedNodeAddresses.remove(candidate);
requestBlocks(candidate, startBlockHeight);
}, () -> {
tryWithNewSeedNode(startBlockHeight);
});
}
public void reset() {
lastRequestedBlockHeight = 0;
lastReceivedBlockHeight = 0;
retryCounter = 0;
requestBlocksHandlerMap.values().forEach(RequestBlocksHandler::cancel);
requestBlocksHandlerMap.values().forEach(RequestBlocksHandler::terminate);
}
@ -202,7 +203,6 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen
closeAllHandlers();
stopRetryTimer();
stopped = true;
tryWithNewSeedNode(lastRequestedBlockHeight);
}
@ -218,8 +218,7 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen
log.info("onAwakeFromStandby");
closeAllHandlers();
stopped = false;
if (!networkNode.getAllConnections().isEmpty())
tryWithNewSeedNode(lastRequestedBlockHeight);
tryWithNewSeedNode(lastRequestedBlockHeight);
}
@ -232,17 +231,20 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen
if (networkEnvelope instanceof NewBlockBroadcastMessage) {
NewBlockBroadcastMessage newBlockBroadcastMessage = (NewBlockBroadcastMessage) networkEnvelope;
// We combine blockHash and txId list in case we receive blocks with different transactions.
List<String> txIds = newBlockBroadcastMessage.getBlock().getRawTxs().stream().map(BaseTx::getId).collect(Collectors.toList());
String extBlockId = newBlockBroadcastMessage.getBlock().getHash() + ":" + txIds;
if (!receivedBlocks.contains(extBlockId)) {
log.debug("We received a new message from peer {} and broadcast it to our peers. extBlockId={}",
connection.getPeersNodeAddressOptional().orElse(null), extBlockId);
receivedBlocks.add(extBlockId);
broadcaster.broadcast(newBlockBroadcastMessage, connection.getPeersNodeAddressOptional().orElse(null));
listeners.forEach(listener -> listener.onNewBlockReceived(newBlockBroadcastMessage));
} else {
log.debug("We had that message already and do not further broadcast it. extBlockId={}", extBlockId);
List<String> txIds = newBlockBroadcastMessage.getBlock().getRawTxs().stream()
.map(BaseTx::getId)
.collect(Collectors.toList());
String blockUid = newBlockBroadcastMessage.getBlock().getHash() + ":" + txIds;
if (receivedBlocks.contains(blockUid)) {
log.debug("We had that message already and do not further broadcast it. blockUid={}", blockUid);
return;
}
log.info("We received a NewBlockBroadcastMessage from peer {} and broadcast it to our peers. blockUid={}",
connection.getPeersNodeAddressOptional().orElse(null), blockUid);
receivedBlocks.add(blockUid);
broadcaster.broadcast(newBlockBroadcastMessage, connection.getPeersNodeAddressOptional().orElse(null));
listeners.forEach(listener -> listener.onNewBlockReceived(newBlockBroadcastMessage));
}
}
@ -252,78 +254,85 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen
///////////////////////////////////////////////////////////////////////////////////////////
private void requestBlocks(NodeAddress peersNodeAddress, int startBlockHeight) {
if (!stopped) {
final Tuple2<NodeAddress, Integer> key = new Tuple2<>(peersNodeAddress, startBlockHeight);
if (!requestBlocksHandlerMap.containsKey(key)) {
if (startBlockHeight >= lastReceivedBlockHeight) {
RequestBlocksHandler requestBlocksHandler = new RequestBlocksHandler(networkNode,
peerManager,
peersNodeAddress,
startBlockHeight,
new RequestBlocksHandler.Listener() {
@Override
public void onComplete(GetBlocksResponse getBlocksResponse) {
log.debug("requestBlocksHandler of outbound connection complete. nodeAddress={}",
peersNodeAddress);
stopRetryTimer();
// need to remove before listeners are notified as they cause the update call
requestBlocksHandlerMap.remove(key);
// we only notify if our request was latest
if (startBlockHeight >= lastReceivedBlockHeight) {
lastReceivedBlockHeight = startBlockHeight;
listeners.forEach(listener -> listener.onRequestedBlocksReceived(getBlocksResponse,
() -> {
// After we received the blocks we allow to disconnect seed nodes.
// We delay 20 seconds to allow multiple requests to finish.
UserThread.runAfter(() -> peerManager.setAllowDisconnectSeedNodes(true), 20);
}));
} else {
log.warn("We got a response which is already obsolete because we receive a " +
"response from a request with a higher block height. " +
"This could theoretically happen, but is very unlikely.");
}
}
@Override
public void onFault(String errorMessage, @Nullable Connection connection) {
log.warn("requestBlocksHandler with outbound connection failed.\n\tnodeAddress={}\n\t" +
"ErrorMessage={}", peersNodeAddress, errorMessage);
peerManager.handleConnectionFault(peersNodeAddress);
requestBlocksHandlerMap.remove(key);
listeners.forEach(listener -> listener.onFault(errorMessage, connection));
tryWithNewSeedNode(startBlockHeight);
}
});
requestBlocksHandlerMap.put(key, requestBlocksHandler);
log.info("requestBlocks with startBlockHeight={} from peer {}", startBlockHeight, peersNodeAddress);
requestBlocksHandler.requestBlocks();
} else {
log.warn("startBlockHeight must not be smaller than lastReceivedBlockHeight. That should never happen." +
"startBlockHeight={},lastReceivedBlockHeight={}", startBlockHeight, lastReceivedBlockHeight);
DevEnv.logErrorAndThrowIfDevMode("startBlockHeight must be larger than lastReceivedBlockHeight. startBlockHeight=" +
startBlockHeight + " / lastReceivedBlockHeight=" + lastReceivedBlockHeight);
}
} else {
log.warn("We have started already a requestDataHandshake for startBlockHeight {} to peer. nodeAddress={}\n" +
"We start a cleanup timer if the handler has not closed by itself in between 2 minutes.",
peersNodeAddress, startBlockHeight);
UserThread.runAfter(() -> {
if (requestBlocksHandlerMap.containsKey(key)) {
RequestBlocksHandler handler = requestBlocksHandlerMap.get(key);
handler.stop();
requestBlocksHandlerMap.remove(key);
}
}, CLEANUP_TIMER);
}
} else {
if (stopped) {
log.warn("We have stopped already. We ignore that requestData call.");
return;
}
Tuple2<NodeAddress, Integer> key = new Tuple2<>(peersNodeAddress, startBlockHeight);
if (requestBlocksHandlerMap.containsKey(key)) {
log.warn("We have started already a requestDataHandshake for startBlockHeight {} to peer. nodeAddress={}\n" +
"We start a cleanup timer if the handler has not closed by itself in between 2 minutes.",
peersNodeAddress, startBlockHeight);
UserThread.runAfter(() -> {
if (requestBlocksHandlerMap.containsKey(key)) {
RequestBlocksHandler handler = requestBlocksHandlerMap.get(key);
handler.terminate();
requestBlocksHandlerMap.remove(key);
}
}, CLEANUP_TIMER);
return;
}
if (startBlockHeight < lastReceivedBlockHeight) {
log.warn("startBlockHeight must not be smaller than lastReceivedBlockHeight. That should never happen." +
"startBlockHeight={},lastReceivedBlockHeight={}", startBlockHeight, lastReceivedBlockHeight);
DevEnv.logErrorAndThrowIfDevMode("startBlockHeight must be larger than lastReceivedBlockHeight. startBlockHeight=" +
startBlockHeight + " / lastReceivedBlockHeight=" + lastReceivedBlockHeight);
return;
}
// In case we would have had an earlier request and had set allowDisconnectSeedNodes to true we un-do that
// if we get a repeated request.
peerManager.setAllowDisconnectSeedNodes(false);
RequestBlocksHandler requestBlocksHandler = new RequestBlocksHandler(networkNode,
peerManager,
peersNodeAddress,
startBlockHeight,
new RequestBlocksHandler.Listener() {
@Override
public void onComplete(GetBlocksResponse getBlocksResponse) {
log.info("requestBlocksHandler to {} completed", peersNodeAddress);
stopRetryTimer();
// need to remove before listeners are notified as they cause the update call
requestBlocksHandlerMap.remove(key);
// we only notify if our request was latest
if (startBlockHeight >= lastReceivedBlockHeight) {
lastReceivedBlockHeight = startBlockHeight;
listeners.forEach(listener -> listener.onRequestedBlocksReceived(getBlocksResponse,
() -> {
// After we received the blocks we allow to disconnect seed nodes.
// We delay 20 seconds to allow multiple requests to finish.
UserThread.runAfter(() -> peerManager.setAllowDisconnectSeedNodes(true), 20);
}));
} else {
log.warn("We got a response which is already obsolete because we received a " +
"response from a request with a higher block height. " +
"This could theoretically happen, but is very unlikely.");
}
}
@Override
public void onFault(String errorMessage, @Nullable Connection connection) {
log.warn("requestBlocksHandler with outbound connection failed.\n\tnodeAddress={}\n\t" +
"ErrorMessage={}", peersNodeAddress, errorMessage);
peerManager.handleConnectionFault(peersNodeAddress);
requestBlocksHandlerMap.remove(key);
listeners.forEach(listener -> listener.onFault(errorMessage, connection));
// We allow now to disconnect from that seed.
peerManager.setAllowDisconnectSeedNodes(true);
tryWithNewSeedNode(startBlockHeight);
}
});
requestBlocksHandlerMap.put(key, requestBlocksHandler);
requestBlocksHandler.requestBlocks();
}
@ -332,37 +341,52 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen
///////////////////////////////////////////////////////////////////////////////////////////
private void tryWithNewSeedNode(int startBlockHeight) {
if (retryTimer == null) {
retryCounter++;
if (retryCounter <= MAX_RETRY) {
retryTimer = UserThread.runAfter(() -> {
stopped = false;
stopRetryTimer();
List<NodeAddress> 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 {
log.warn("No more seed nodes available we could try.");
listeners.forEach(Listener::onNoSeedNodeAvailable);
}
},
RETRY_DELAY_SEC);
} else {
log.warn("We tried {} times but could not connect to a seed node.", retryCounter);
listeners.forEach(Listener::onNoSeedNodeAvailable);
}
} else {
log.warn("We have a retry timer already running.");
if (networkNode.getAllConnections().isEmpty()) {
return;
}
if (lastRequestedBlockHeight == 0) {
return;
}
if (stopped) {
return;
}
if (retryTimer != null) {
log.warn("We have a retry timer already running.");
return;
}
retryCounter++;
if (retryCounter > MAX_RETRY) {
log.warn("We tried {} times but could not connect to a seed node.", retryCounter);
listeners.forEach(Listener::onNoSeedNodeAvailable);
return;
}
retryTimer = UserThread.runAfter(() -> {
stopped = false;
stopRetryTimer();
List<NodeAddress> 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 from {} with startBlockHeight={}", nextCandidate, startBlockHeight);
requestBlocks(nextCandidate, startBlockHeight);
} else {
log.warn("No more seed nodes available we could try.");
listeners.forEach(Listener::onNoSeedNodeAvailable);
}
},
RETRY_DELAY_SEC);
}
private void stopRetryTimer() {
@ -386,17 +410,14 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen
requestBlocksHandlerMap.entrySet().stream()
.filter(e -> e.getKey().first.equals(nodeAddress))
.findAny()
.map(Map.Entry::getValue)
.ifPresent(handler -> {
final Tuple2<NodeAddress, Integer> key = new Tuple2<>(handler.getNodeAddress(), handler.getStartBlockHeight());
requestBlocksHandlerMap.get(key).cancel();
requestBlocksHandlerMap.remove(key);
.ifPresent(e -> {
e.getValue().terminate();
requestBlocksHandlerMap.remove(e.getKey());
});
}
private void closeAllHandlers() {
requestBlocksHandlerMap.values().forEach(RequestBlocksHandler::cancel);
requestBlocksHandlerMap.values().forEach(RequestBlocksHandler::terminate);
requestBlocksHandlerMap.clear();
}
}

View File

@ -36,7 +36,9 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@ -44,14 +46,12 @@ import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Sends a GetBlocksRequest to a full node and listens on corresponding GetBlocksResponse from the full node.
*/
@Slf4j
public class RequestBlocksHandler implements MessageListener {
private static final long TIMEOUT = 120;
private static final long TIMEOUT_MIN = 3;
///////////////////////////////////////////////////////////////////////////////////////////
@ -98,66 +98,61 @@ public class RequestBlocksHandler implements MessageListener {
this.listener = listener;
}
public void cancel() {
cleanup();
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
public void requestBlocks() {
if (!stopped) {
GetBlocksRequest getBlocksRequest = new GetBlocksRequest(startBlockHeight, nonce, networkNode.getNodeAddress());
log.debug("getBlocksRequest " + getBlocksRequest);
if (timeoutTimer == null) {
timeoutTimer = UserThread.runAfter(() -> { // setup before sending to avoid race conditions
if (!stopped) {
String errorMessage = "A timeout occurred when sending getBlocksRequest:" + getBlocksRequest +
" on peersNodeAddress:" + nodeAddress;
log.debug(errorMessage + " / RequestDataHandler=" + RequestBlocksHandler.this);
handleFault(errorMessage, nodeAddress, CloseConnectionReason.SEND_MSG_TIMEOUT);
} else {
log.trace("We have stopped already. We ignore that timeoutTimer.run call. " +
"Might be caused by an previous networkNode.sendMessage.onFailure.");
}
},
TIMEOUT);
if (stopped) {
log.warn("We have stopped already. We ignore that requestData call.");
return;
}
GetBlocksRequest getBlocksRequest = new GetBlocksRequest(startBlockHeight, nonce, networkNode.getNodeAddress());
if (timeoutTimer != null) {
log.warn("We had a timer already running and stop it.");
timeoutTimer.stop();
}
timeoutTimer = UserThread.runAfter(() -> { // setup before sending to avoid race conditions
if (!stopped) {
String errorMessage = "A timeout occurred when sending getBlocksRequest:" + getBlocksRequest +
" on peersNodeAddress:" + nodeAddress;
log.debug("{} / RequestDataHandler={}", errorMessage, RequestBlocksHandler.this);
handleFault(errorMessage, nodeAddress, CloseConnectionReason.SEND_MSG_TIMEOUT);
} else {
log.warn("We have stopped already. We ignore that timeoutTimer.run call. " +
"Might be caused by a previous networkNode.sendMessage.onFailure.");
}
},
TIMEOUT_MIN, TimeUnit.MINUTES);
log.info("We request blocks from peer {} from block height {}.", nodeAddress, getBlocksRequest.getFromBlockHeight());
networkNode.addMessageListener(this);
SettableFuture<Connection> future = networkNode.sendMessage(nodeAddress, getBlocksRequest);
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(Connection connection) {
log.info("Sending of GetBlocksRequest message to peer {} succeeded.", nodeAddress.getFullAddress());
}
log.info("We request blocks from peer {} from block height {}.", nodeAddress, getBlocksRequest.getFromBlockHeight());
networkNode.addMessageListener(this);
SettableFuture<Connection> future = networkNode.sendMessage(nodeAddress, getBlocksRequest);
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(Connection connection) {
if (!stopped) {
log.info("Sending of GetBlocksRequest message to peer {} succeeded.", nodeAddress.getFullAddress());
} else {
log.trace("We have stopped already. We ignore that networkNode.sendMessage.onSuccess call." +
"Might be caused by a previous timeout.");
}
@Override
public void onFailure(@NotNull Throwable throwable) {
if (!stopped) {
String errorMessage = "Sending getBlocksRequest to " + nodeAddress +
" failed. That is expected if the peer is offline.\n\t" +
"getBlocksRequest=" + getBlocksRequest + "." +
"\n\tException=" + throwable.getMessage();
log.error(errorMessage);
handleFault(errorMessage, nodeAddress, CloseConnectionReason.SEND_MSG_FAILURE);
} else {
log.warn("We have stopped already. We ignore that networkNode.sendMessage.onFailure call.");
}
@Override
public void onFailure(@NotNull Throwable throwable) {
if (!stopped) {
String errorMessage = "Sending getBlocksRequest to " + nodeAddress +
" failed. That is expected if the peer is offline.\n\t" +
"getBlocksRequest=" + getBlocksRequest + "." +
"\n\tException=" + throwable.getMessage();
log.error(errorMessage);
handleFault(errorMessage, nodeAddress, CloseConnectionReason.SEND_MSG_FAILURE);
} else {
log.trace("We have stopped already. We ignore that networkNode.sendMessage.onFailure call. " +
"Might be caused by a previous timeout.");
}
}
}, MoreExecutors.directExecutor());
} else {
log.warn("We have stopped already. We ignore that requestData call.");
}
}
}, MoreExecutors.directExecutor());
}
@ -168,56 +163,60 @@ public class RequestBlocksHandler implements MessageListener {
@Override
public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) {
if (networkEnvelope instanceof GetBlocksResponse) {
if (connection.getPeersNodeAddressOptional().isPresent() && connection.getPeersNodeAddressOptional().get().equals(nodeAddress)) {
if (!stopped) {
GetBlocksResponse getBlocksResponse = (GetBlocksResponse) networkEnvelope;
if (getBlocksResponse.getRequestNonce() == nonce) {
stopTimeoutTimer();
checkArgument(connection.getPeersNodeAddressOptional().isPresent(),
"RequestDataHandler.onMessage: connection.getPeersNodeAddressOptional() must be present " +
"at that moment");
cleanup();
log.info("We received from peer {} a BlocksResponse with {} blocks",
nodeAddress.getFullAddress(), getBlocksResponse.getBlocks().size());
listener.onComplete(getBlocksResponse);
} else {
log.warn("Nonce not matching. That can happen rarely if we get a response after a canceled " +
"handshake (timeout causes connection close but peer might have sent a msg before " +
"connection was closed).\n\t" +
"We drop that message. nonce={} / requestNonce={}",
nonce, getBlocksResponse.getRequestNonce());
}
} else {
log.warn("We have stopped already. We ignore that onDataRequest call.");
}
} else {
log.warn("We got a message from ourselves. That should never happen.");
if (stopped) {
log.warn("We have stopped already. We ignore that onDataRequest call.");
return;
}
Optional<NodeAddress> optionalNodeAddress = connection.getPeersNodeAddressOptional();
if (!optionalNodeAddress.isPresent()) {
log.warn("Peers node address is not present, that is not expected.");
// We do not return here as in case the connection has been created from the peers side we might not
// have the address set. As we check the nonce later we do not care that much for the check if the
// connection address is the same as the one we used.
} else if (!optionalNodeAddress.get().equals(nodeAddress)) {
log.warn("Peers node address is not the same we used for the request. This is not expected. We ignore that message.");
return;
}
GetBlocksResponse getBlocksResponse = (GetBlocksResponse) networkEnvelope;
if (getBlocksResponse.getRequestNonce() != nonce) {
log.warn("Nonce not matching. That can happen rarely if we get a response after a canceled " +
"handshake (timeout causes connection close but peer might have sent a msg before " +
"connection was closed).\n\t" +
"We drop that message. nonce={} / requestNonce={}",
nonce, getBlocksResponse.getRequestNonce());
return;
}
terminate();
log.info("We received from peer {} a BlocksResponse with {} blocks",
nodeAddress.getFullAddress(), getBlocksResponse.getBlocks().size());
listener.onComplete(getBlocksResponse);
}
}
public void stop() {
cleanup();
public void terminate() {
stopped = true;
networkNode.removeMessageListener(this);
stopTimeoutTimer();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("UnusedParameters")
private void handleFault(String errorMessage, NodeAddress nodeAddress, CloseConnectionReason closeConnectionReason) {
cleanup();
private void handleFault(String errorMessage,
NodeAddress nodeAddress,
CloseConnectionReason closeConnectionReason) {
terminate();
peerManager.handleConnectionFault(nodeAddress);
listener.onFault(errorMessage, null);
}
private void cleanup() {
stopped = true;
networkNode.removeMessageListener(this);
stopTimeoutTimer();
}
private void stopTimeoutTimer() {
if (timeoutTimer != null) {
timeoutTimer.stop();

View File

@ -27,7 +27,8 @@ import com.google.common.collect.ImmutableList;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import javax.annotation.Nullable;
@ -36,7 +37,8 @@ import javax.annotation.Nullable;
* After parsing it will get cloned to the immutable Tx.
* We don't need to implement the ProtoBuffer methods as it is not persisted or sent over the wire.
*/
@Data
@Getter
@Setter
public class TempTx extends BaseTx {
static TempTx fromRawTx(RawTx rawTx) {
return new TempTx(rawTx.getTxVersion(),

View File

@ -24,7 +24,8 @@ import bisq.core.dao.state.model.blockchain.TxOutputType;
import java.util.Objects;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import javax.annotation.Nullable;
@ -32,7 +33,8 @@ import javax.annotation.Nullable;
* Contains mutable BSQ specific data (TxOutputType) and used only during tx parsing.
* Will get converted to immutable TxOutput after tx parsing is completed.
*/
@Data
@Getter
@Setter
public class TempTxOutput extends BaseTxOutput {
static TempTxOutput fromRawTxOutput(RawTxOutput txOutput) {
return new TempTxOutput(txOutput.getIndex(),
@ -78,6 +80,10 @@ public class TempTxOutput extends BaseTxOutput {
this.unlockBlockHeight = unlockBlockHeight;
}
public boolean isOpReturnOutput() {
// We do not check for pubKeyScript.scriptType.NULL_DATA because that is only set if dumpBlockchainData is true
return getOpReturnData() != null;
}
@Override
public String toString() {
@ -88,12 +94,6 @@ public class TempTxOutput extends BaseTxOutput {
"\n} " + super.toString();
}
public boolean isOpReturnOutput() {
// We do not check for pubKeyScript.scriptType.NULL_DATA because that is only set if dumpBlockchainData is true
return getOpReturnData() != null;
}
// Enums must not be used directly for hashCode or equals as it delivers the Object.hashCode (internal address)!
// The equals and hashCode methods cannot be overwritten in Enums.
@Override

View File

@ -87,9 +87,9 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
@Slf4j
class TxOutputParser {
private static int ACTIVATE_HARD_FORK_1_HEIGHT_MAINNET = 605000;
private static int ACTIVATE_HARD_FORK_1_HEIGHT_TESTNET = 1583054;
private static int ACTIVATE_HARD_FORK_1_HEIGHT_REGTEST = 1;
private static final int ACTIVATE_HARD_FORK_1_HEIGHT_MAINNET = 605000;
private static final int ACTIVATE_HARD_FORK_1_HEIGHT_TESTNET = 1583054;
private static final int ACTIVATE_HARD_FORK_1_HEIGHT_REGTEST = 1;
private final DaoStateService daoStateService;
// Setters

View File

@ -24,7 +24,7 @@ import lombok.Getter;
@Getter
public class BlockHashNotConnectingException extends Exception {
private RawBlock rawBlock;
private final RawBlock rawBlock;
public BlockHashNotConnectingException(RawBlock rawBlock) {
this.rawBlock = rawBlock;

View File

@ -24,7 +24,7 @@ import lombok.Getter;
@Getter
public class BlockHeightNotConnectingException extends Exception {
private RawBlock rawBlock;
private final RawBlock rawBlock;
public BlockHeightNotConnectingException(RawBlock rawBlock) {
this.rawBlock = rawBlock;

View File

@ -24,7 +24,7 @@ import lombok.Getter;
@Getter
public class RequiredReorgFromSnapshotException extends Exception {
private RawBlock rawBlock;
private final RawBlock rawBlock;
public RequiredReorgFromSnapshotException(RawBlock rawBlock) {
this.rawBlock = rawBlock;

View File

@ -597,23 +597,18 @@ public class DaoStateService implements DaoSetupService {
daoState.getIssuanceMap().put(issuance.getTxId(), issuance);
}
public Set<Issuance> getIssuanceSet(IssuanceType issuanceType) {
public Set<Issuance> getIssuanceSetForType(IssuanceType issuanceType) {
return daoState.getIssuanceMap().values().stream()
.filter(issuance -> issuance.getIssuanceType() == issuanceType)
.collect(Collectors.toSet());
}
public Optional<Issuance> getIssuance(String txId, IssuanceType issuanceType) {
return daoState.getIssuanceMap().values().stream()
.filter(issuance -> issuance.getTxId().equals(txId))
.filter(issuance -> issuance.getIssuanceType() == issuanceType)
.findAny();
return getIssuance(txId).filter(issuance -> issuance.getIssuanceType() == issuanceType);
}
public Optional<Issuance> getIssuance(String txId) {
return daoState.getIssuanceMap().values().stream()
.filter(issuance -> issuance.getTxId().equals(txId))
.findAny();
return Optional.ofNullable(daoState.getIssuanceMap().get(txId));
}
public boolean isIssuanceTx(String txId) {

View File

@ -81,6 +81,8 @@ public class DaoState implements PersistablePayload {
private final LinkedList<Cycle> cycles;
// These maps represent mutual data which can get changed at parsing a transaction
// We use TreeMaps instead of HashMaps because we need deterministic sorting of the maps for the hashChains
// used for the DAO monitor.
@Getter
private final TreeMap<TxOutputKey, TxOutput> unspentTxOutputMap;
@Getter

View File

@ -29,7 +29,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.Value;
import lombok.Getter;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@ -39,7 +39,7 @@ import javax.annotation.concurrent.Immutable;
* Gets persisted.
*/
@Immutable
@Value
@Getter
public final class Tx extends BaseTx implements PersistablePayload, ImmutableDaoStateModel {
// Created after parsing of a tx is completed. We store only the immutable tx in the block.
public static Tx fromTempTx(TempTx tempTx) {

View File

@ -24,7 +24,7 @@ import bisq.common.proto.persistable.PersistablePayload;
import java.util.Objects;
import lombok.Data;
import lombok.Getter;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@ -36,7 +36,7 @@ import javax.annotation.concurrent.Immutable;
* Gets persisted.
*/
@Immutable
@Data
@Getter
public class TxOutput extends BaseTxOutput implements PersistablePayload, ImmutableDaoStateModel {
public static TxOutput fromTempOutput(TempTxOutput tempTxOutput) {
return new TxOutput(tempTxOutput.getIndex(),

View File

@ -29,14 +29,14 @@ import java.util.Date;
import java.util.Map;
import java.util.Objects;
import lombok.Value;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.concurrent.Immutable;
@Immutable
@Slf4j
@Value
@Getter
public final class ChangeParamProposal extends Proposal implements ImmutableDaoStateModel {
private final Param param;
private final String paramValue;

View File

@ -35,6 +35,7 @@ import java.security.PublicKey;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@ -47,7 +48,7 @@ import javax.annotation.Nullable;
@Value
public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
private final List<String> bannedOfferIds;
private final List<String> bannedNodeAddress;
private final List<String> nodeAddressesBannedFromTrading;
private final List<String> bannedAutoConfExplorers;
private final List<PaymentAccountFilter> bannedPaymentAccounts;
private final List<String> bannedCurrencies;
@ -91,10 +92,14 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
// added at v1.3.8
private final boolean disableAutoConf;
// added at v1.5.5
private final Set<String> nodeAddressesBannedFromNetwork;
private final boolean disableApi;
// After we have created the signature from the filter data we clone it and apply the signature
static Filter cloneWithSig(Filter filter, String signatureAsBase64) {
return new Filter(filter.getBannedOfferIds(),
filter.getBannedNodeAddress(),
filter.getNodeAddressesBannedFromTrading(),
filter.getBannedPaymentAccounts(),
filter.getBannedCurrencies(),
filter.getBannedPaymentMethods(),
@ -117,13 +122,15 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
filter.getSignerPubKeyAsHex(),
filter.getBannedPrivilegedDevPubKeys(),
filter.isDisableAutoConf(),
filter.getBannedAutoConfExplorers());
filter.getBannedAutoConfExplorers(),
filter.getNodeAddressesBannedFromNetwork(),
filter.isDisableApi());
}
// Used for signature verification as we created the sig without the signatureAsBase64 field we set it to null again
static Filter cloneWithoutSig(Filter filter) {
return new Filter(filter.getBannedOfferIds(),
filter.getBannedNodeAddress(),
filter.getNodeAddressesBannedFromTrading(),
filter.getBannedPaymentAccounts(),
filter.getBannedCurrencies(),
filter.getBannedPaymentMethods(),
@ -146,11 +153,13 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
filter.getSignerPubKeyAsHex(),
filter.getBannedPrivilegedDevPubKeys(),
filter.isDisableAutoConf(),
filter.getBannedAutoConfExplorers());
filter.getBannedAutoConfExplorers(),
filter.getNodeAddressesBannedFromNetwork(),
filter.isDisableApi());
}
public Filter(List<String> bannedOfferIds,
List<String> bannedNodeAddress,
List<String> nodeAddressesBannedFromTrading,
List<PaymentAccountFilter> bannedPaymentAccounts,
List<String> bannedCurrencies,
List<String> bannedPaymentMethods,
@ -170,9 +179,11 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
String signerPubKeyAsHex,
List<String> bannedPrivilegedDevPubKeys,
boolean disableAutoConf,
List<String> bannedAutoConfExplorers) {
List<String> bannedAutoConfExplorers,
Set<String> nodeAddressesBannedFromNetwork,
boolean disableApi) {
this(bannedOfferIds,
bannedNodeAddress,
nodeAddressesBannedFromTrading,
bannedPaymentAccounts,
bannedCurrencies,
bannedPaymentMethods,
@ -195,7 +206,9 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
signerPubKeyAsHex,
bannedPrivilegedDevPubKeys,
disableAutoConf,
bannedAutoConfExplorers);
bannedAutoConfExplorers,
nodeAddressesBannedFromNetwork,
disableApi);
}
@ -205,7 +218,7 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
@VisibleForTesting
public Filter(List<String> bannedOfferIds,
List<String> bannedNodeAddress,
List<String> nodeAddressesBannedFromTrading,
List<PaymentAccountFilter> bannedPaymentAccounts,
List<String> bannedCurrencies,
List<String> bannedPaymentMethods,
@ -228,9 +241,11 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
String signerPubKeyAsHex,
List<String> bannedPrivilegedDevPubKeys,
boolean disableAutoConf,
List<String> bannedAutoConfExplorers) {
List<String> bannedAutoConfExplorers,
Set<String> nodeAddressesBannedFromNetwork,
boolean disableApi) {
this.bannedOfferIds = bannedOfferIds;
this.bannedNodeAddress = bannedNodeAddress;
this.nodeAddressesBannedFromTrading = nodeAddressesBannedFromTrading;
this.bannedPaymentAccounts = bannedPaymentAccounts;
this.bannedCurrencies = bannedCurrencies;
this.bannedPaymentMethods = bannedPaymentMethods;
@ -254,6 +269,8 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
this.bannedPrivilegedDevPubKeys = bannedPrivilegedDevPubKeys;
this.disableAutoConf = disableAutoConf;
this.bannedAutoConfExplorers = bannedAutoConfExplorers;
this.nodeAddressesBannedFromNetwork = nodeAddressesBannedFromNetwork;
this.disableApi = disableApi;
// ownerPubKeyBytes can be null when called from tests
if (ownerPubKeyBytes != null) {
@ -270,7 +287,7 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
.collect(Collectors.toList());
protobuf.Filter.Builder builder = protobuf.Filter.newBuilder().addAllBannedOfferIds(bannedOfferIds)
.addAllBannedNodeAddress(bannedNodeAddress)
.addAllNodeAddressesBannedFromTrading(nodeAddressesBannedFromTrading)
.addAllBannedPaymentAccounts(paymentAccountFilterList)
.addAllBannedCurrencies(bannedCurrencies)
.addAllBannedPaymentMethods(bannedPaymentMethods)
@ -291,7 +308,9 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
.setCreationDate(creationDate)
.addAllBannedPrivilegedDevPubKeys(bannedPrivilegedDevPubKeys)
.setDisableAutoConf(disableAutoConf)
.addAllBannedAutoConfExplorers(bannedAutoConfExplorers);
.addAllBannedAutoConfExplorers(bannedAutoConfExplorers)
.addAllNodeAddressesBannedFromNetwork(nodeAddressesBannedFromNetwork)
.setDisableApi(disableApi);
Optional.ofNullable(signatureAsBase64).ifPresent(builder::setSignatureAsBase64);
Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData);
@ -306,7 +325,7 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
return new Filter(ProtoUtil.protocolStringListToList(proto.getBannedOfferIdsList()),
ProtoUtil.protocolStringListToList(proto.getBannedNodeAddressList()),
ProtoUtil.protocolStringListToList(proto.getNodeAddressesBannedFromTradingList()),
bannedPaymentAccountsList,
ProtoUtil.protocolStringListToList(proto.getBannedCurrenciesList()),
ProtoUtil.protocolStringListToList(proto.getBannedPaymentMethodsList()),
@ -329,7 +348,9 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
proto.getSignerPubKeyAsHex(),
ProtoUtil.protocolStringListToList(proto.getBannedPrivilegedDevPubKeysList()),
proto.getDisableAutoConf(),
ProtoUtil.protocolStringListToList(proto.getBannedAutoConfExplorersList())
ProtoUtil.protocolStringListToList(proto.getBannedAutoConfExplorersList()),
ProtoUtil.protocolStringListToSet(proto.getNodeAddressesBannedFromNetworkList()),
proto.getDisableApi()
);
}
@ -347,7 +368,7 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
public String toString() {
return "Filter{" +
"\n bannedOfferIds=" + bannedOfferIds +
",\n bannedNodeAddress=" + bannedNodeAddress +
",\n nodeAddressesBannedFromTrading=" + nodeAddressesBannedFromTrading +
",\n bannedAutoConfExplorers=" + bannedAutoConfExplorers +
",\n bannedPaymentAccounts=" + bannedPaymentAccounts +
",\n bannedCurrencies=" + bannedCurrencies +
@ -372,6 +393,8 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
",\n extraDataMap=" + extraDataMap +
",\n ownerPubKey=" + ownerPubKey +
",\n disableAutoConf=" + disableAutoConf +
",\n nodeAddressesBannedFromNetwork=" + nodeAddressesBannedFromNetwork +
",\n disableApi=" + disableApi +
"\n}";
}
}

View File

@ -28,6 +28,7 @@ import bisq.core.user.User;
import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.P2PService;
import bisq.network.p2p.P2PServiceListener;
import bisq.network.p2p.network.NetworkFilter;
import bisq.network.p2p.storage.HashMapChangedListener;
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
@ -115,6 +116,7 @@ public class FilterManager {
Preferences preferences,
Config config,
ProvidersRepository providersRepository,
NetworkFilter networkFilter,
@Named(Config.IGNORE_DEV_MSG) boolean ignoreDevMsg,
@Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys) {
this.p2PService = p2PService;
@ -131,6 +133,7 @@ public class FilterManager {
"029340c3e7d4bb0f9e651b5f590b434fecb6175aeaa57145c7804ff05d210e534f",
"034dc7530bf66ffd9580aa98031ea9a18ac2d269f7c56c0e71eca06105b9ed69f9");
networkFilter.setBannedNodeFunction(this::isNodeAddressBannedFromNetwork);
}
@ -394,7 +397,13 @@ public class FilterManager {
public boolean isNodeAddressBanned(NodeAddress nodeAddress) {
return getFilter() != null &&
getFilter().getBannedNodeAddress().stream()
getFilter().getNodeAddressesBannedFromTrading().stream()
.anyMatch(e -> e.equals(nodeAddress.getFullAddress()));
}
public boolean isNodeAddressBannedFromNetwork(NodeAddress nodeAddress) {
return getFilter() != null &&
getFilter().getNodeAddressesBannedFromNetwork().stream()
.anyMatch(e -> e.equals(nodeAddress.getFullAddress()));
}
@ -466,7 +475,11 @@ public class FilterManager {
Filter currentFilter = getFilter();
if (!isFilterPublicKeyInList(newFilter)) {
log.warn("isFilterPublicKeyInList failed. Filter.getSignerPubKeyAsHex={}", newFilter.getSignerPubKeyAsHex());
if (newFilter.getSignerPubKeyAsHex() != null && !newFilter.getSignerPubKeyAsHex().isEmpty()) {
log.warn("isFilterPublicKeyInList failed. Filter.getSignerPubKeyAsHex={}", newFilter.getSignerPubKeyAsHex());
} else {
log.info("isFilterPublicKeyInList failed. Filter.getSignerPubKeyAsHex not set (expected case for pre v1.3.9 filter)");
}
return;
}
if (!isSignatureValid(newFilter)) {
@ -593,7 +606,7 @@ public class FilterManager {
private boolean isFilterPublicKeyInList(Filter filter) {
String signerPubKeyAsHex = filter.getSignerPubKeyAsHex();
if (!isPublicKeyInList(signerPubKeyAsHex)) {
log.warn("Invalid filter (expected case for pre v1.3.9 filter as we still keep that in the network " +
log.info("Invalid filter (expected case for pre v1.3.9 filter as we still keep that in the network " +
"but the new version does not recognize it as valid filter): " +
"signerPubKeyAsHex from filter is not part of our pub key list. " +
"signerPubKeyAsHex={}, publicKeys={}, filterCreationDate={}",
@ -606,7 +619,7 @@ public class FilterManager {
private boolean isPublicKeyInList(String pubKeyAsHex) {
boolean isPublicKeyInList = publicKeys.contains(pubKeyAsHex);
if (!isPublicKeyInList) {
log.warn("pubKeyAsHex is not part of our pub key list. pubKeyAsHex={}, publicKeys={}", pubKeyAsHex, publicKeys);
log.info("pubKeyAsHex is not part of our pub key list (expected case for pre v1.3.9 filter). pubKeyAsHex={}, publicKeys={}", pubKeyAsHex, publicKeys);
}
return isPublicKeyInList;
}

View File

@ -301,7 +301,6 @@ public class CurrencyUtil {
new FiatCurrency("MAD"),
new FiatCurrency("NPR"),
new FiatCurrency("NZD"),
new FiatCurrency("NGN"),
new FiatCurrency("NOK"),
new FiatCurrency("PKR"),
new FiatCurrency("PEN"),

View File

@ -0,0 +1,58 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.network;
import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.network.NetworkFilter;
import bisq.common.config.Config;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class CoreNetworkFilter implements NetworkFilter {
private final Set<NodeAddress> bannedPeersFromOptions = new HashSet<>();
private Function<NodeAddress, Boolean> bannedNodeFunction;
/**
* @param banList List of banned peers from program argument
*/
@Inject
public CoreNetworkFilter(@Named(Config.BAN_LIST) List<String> banList) {
banList.stream().map(NodeAddress::new).forEach(bannedPeersFromOptions::add);
}
@Override
public void setBannedNodeFunction(Function<NodeAddress, Boolean> bannedNodeFunction) {
this.bannedNodeFunction = bannedNodeFunction;
}
@Override
public boolean isPeerBanned(NodeAddress nodeAddress) {
return bannedPeersFromOptions.contains(nodeAddress) ||
bannedNodeFunction != null && bannedNodeFunction.apply(nodeAddress);
}
}

View File

@ -21,9 +21,13 @@ package bisq.core.network.p2p.inventory.messages;
import bisq.common.app.Version;
import bisq.common.proto.network.NetworkEnvelope;
import lombok.Value;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Value
@Getter
@EqualsAndHashCode(callSuper = false)
@ToString
public class GetInventoryRequest extends NetworkEnvelope {
private final String version;

View File

@ -28,9 +28,13 @@ import com.google.common.base.Optional;
import java.util.HashMap;
import java.util.Map;
import lombok.Value;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@Value
@Getter
@EqualsAndHashCode(callSuper = false)
@ToString
public class GetInventoryResponse extends NetworkEnvelope {
private final Map<InventoryItem, String> inventory;

View File

@ -32,7 +32,7 @@ public enum InventoryItem {
// Percentage deviation
OfferPayload("OfferPayload",
true,
new DeviationByPercentage(0.8, 1.2, 0.9, 1.1), 5),
new DeviationByPercentage(0.5, 1.5, 0.75, 1.25), 10),
MailboxStoragePayload("MailboxStoragePayload",
true,
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05), 2),

View File

@ -29,7 +29,7 @@ import org.jetbrains.annotations.Nullable;
@Getter
public class RequestInfo {
// Carries latest commit hash of feature changes (not latest commit as that is then the commit for editing that field)
public static final String COMMIT_HASH = "7f83d1b3";
public static final String COMMIT_HASH = "1c50cb6c";
private final long requestStartTime;
@Setter

View File

@ -55,8 +55,9 @@ public class MyOfferTakenEvents {
}
private void onOpenOfferRemoved(OpenOffer openOffer) {
log.info("We got a offer removed. id={}, state={}", openOffer.getId(), openOffer.getState());
if (openOffer.getState() == OpenOffer.State.RESERVED) {
OpenOffer.State state = openOffer.getState();
if (state == OpenOffer.State.RESERVED) {
log.info("We got a offer removed. id={}, state={}", openOffer.getId(), state);
String shortId = openOffer.getShortId();
MobileMessage message = new MobileMessage(Res.get("account.notifications.offer.message.title"),
Res.get("account.notifications.offer.message.msg", shortId),

View File

@ -27,5 +27,7 @@ public enum AvailabilityResult {
NO_MEDIATORS,
USER_IGNORED,
MISSING_MANDATORY_CAPABILITY,
NO_REFUND_AGENTS
NO_REFUND_AGENTS,
UNCONF_TX_LIMIT_HIT,
MAKER_DENIED_API_USER
}

View File

@ -110,7 +110,7 @@ public class CreateOfferService {
double buyerSecurityDepositAsDouble,
PaymentAccount paymentAccount) {
log.info("offerId={}, \n" +
log.info("create and get offer with offerId={}, \n" +
"currencyCode={}, \n" +
"direction={}, \n" +
"price={}, \n" +
@ -118,14 +118,21 @@ public class CreateOfferService {
"marketPriceMargin={}, \n" +
"amount={}, \n" +
"minAmount={}, \n" +
"buyerSecurityDeposit={}, \n" +
offerId, currencyCode, direction, price.getValue(), useMarketBasedPrice, marketPriceMargin,
amount.value, minAmount.value, buyerSecurityDepositAsDouble);
"buyerSecurityDeposit={}",
offerId,
currencyCode,
direction,
price.getValue(),
useMarketBasedPrice,
marketPriceMargin,
amount.value,
minAmount.value,
buyerSecurityDepositAsDouble);
// prints our param list for dev testing api
log.info("{} " +
"{} " +
"{} " +
// Log an approximate api CLI 'createoffer' dev/test param list.
log.info("cli's createoffer positional option names: paymentAccountId direction currencyCode amount minAmount"
+ " useMarketBasedPrice fixedPrice|marketPriceMargin buyerSecurityDeposit");
log.info("cli's createoffer positional option values: {} " +
"{} " +
"{} " +
"{} " +
@ -133,8 +140,14 @@ public class CreateOfferService {
"{} " +
"{} " +
"{}",
offerId, currencyCode, direction.name(), price.getValue(), useMarketBasedPrice, marketPriceMargin,
amount.value, minAmount.value, buyerSecurityDepositAsDouble, paymentAccount.getId());
paymentAccount.getId(),
direction.name(),
currencyCode,
amount.value,
minAmount.value,
useMarketBasedPrice,
(useMarketBasedPrice ? marketPriceMargin : price.getValue()),
buyerSecurityDepositAsDouble);
long creationTime = new Date().getTime();
NodeAddress makerAddress = p2PService.getAddress();

View File

@ -0,0 +1,209 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.offer;
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.filter.FilterManager;
import bisq.core.locale.CurrencyUtil;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.PaymentAccountUtil;
import bisq.core.user.Preferences;
import bisq.core.user.User;
import bisq.common.app.Version;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import javax.inject.Singleton;
import javafx.collections.SetChangeListener;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Singleton
public class OfferFilter {
private final User user;
private final Preferences preferences;
private final FilterManager filterManager;
private final AccountAgeWitnessService accountAgeWitnessService;
private final Map<String, Boolean> insufficientCounterpartyTradeLimitCache = new HashMap<>();
private final Map<String, Boolean> myInsufficientTradeLimitCache = new HashMap<>();
@Inject
public OfferFilter(User user,
Preferences preferences,
FilterManager filterManager,
AccountAgeWitnessService accountAgeWitnessService) {
this.user = user;
this.preferences = preferences;
this.filterManager = filterManager;
this.accountAgeWitnessService = accountAgeWitnessService;
if (user != null) {
// If our accounts have changed we reset our myInsufficientTradeLimitCache as it depends on account data
user.getPaymentAccountsAsObservable().addListener((SetChangeListener<PaymentAccount>) c ->
myInsufficientTradeLimitCache.clear());
}
}
public enum Result {
VALID(true),
API_DISABLED,
HAS_NO_PAYMENT_ACCOUNT_VALID_FOR_OFFER,
HAS_NOT_SAME_PROTOCOL_VERSION,
IS_IGNORED,
IS_OFFER_BANNED,
IS_CURRENCY_BANNED,
IS_PAYMENT_METHOD_BANNED,
IS_NODE_ADDRESS_BANNED,
REQUIRE_UPDATE_TO_NEW_VERSION,
IS_INSUFFICIENT_COUNTERPARTY_TRADE_LIMIT,
IS_MY_INSUFFICIENT_TRADE_LIMIT;
@Getter
private final boolean isValid;
Result(boolean isValid) {
this.isValid = isValid;
}
Result() {
this(false);
}
}
public Result canTakeOffer(Offer offer, boolean isTakerApiUser) {
if (isTakerApiUser && filterManager.getFilter() != null && filterManager.getFilter().isDisableApi()) {
return Result.API_DISABLED;
}
if (!isAnyPaymentAccountValidForOffer(offer)) {
return Result.HAS_NO_PAYMENT_ACCOUNT_VALID_FOR_OFFER;
}
if (!hasSameProtocolVersion(offer)) {
return Result.HAS_NOT_SAME_PROTOCOL_VERSION;
}
if (isIgnored(offer)) {
return Result.IS_IGNORED;
}
if (isOfferBanned(offer)) {
return Result.IS_OFFER_BANNED;
}
if (isCurrencyBanned(offer)) {
return Result.IS_CURRENCY_BANNED;
}
if (isPaymentMethodBanned(offer)) {
return Result.IS_PAYMENT_METHOD_BANNED;
}
if (isNodeAddressBanned(offer)) {
return Result.IS_NODE_ADDRESS_BANNED;
}
if (requireUpdateToNewVersion()) {
return Result.REQUIRE_UPDATE_TO_NEW_VERSION;
}
if (isInsufficientCounterpartyTradeLimit(offer)) {
return Result.IS_INSUFFICIENT_COUNTERPARTY_TRADE_LIMIT;
}
if (isMyInsufficientTradeLimit(offer)) {
return Result.IS_MY_INSUFFICIENT_TRADE_LIMIT;
}
return Result.VALID;
}
public boolean isAnyPaymentAccountValidForOffer(Offer offer) {
return user.getPaymentAccounts() != null &&
PaymentAccountUtil.isAnyTakerPaymentAccountValidForOffer(offer, user.getPaymentAccounts());
}
public boolean hasSameProtocolVersion(Offer offer) {
return offer.getProtocolVersion() == Version.TRADE_PROTOCOL_VERSION;
}
public boolean isIgnored(Offer offer) {
return preferences.getIgnoreTradersList().stream()
.anyMatch(i -> i.equals(offer.getMakerNodeAddress().getFullAddress()));
}
public boolean isOfferBanned(Offer offer) {
return filterManager.isOfferIdBanned(offer.getId());
}
public boolean isCurrencyBanned(Offer offer) {
return filterManager.isCurrencyBanned(offer.getCurrencyCode());
}
public boolean isPaymentMethodBanned(Offer offer) {
return filterManager.isPaymentMethodBanned(offer.getPaymentMethod());
}
public boolean isNodeAddressBanned(Offer offer) {
return filterManager.isNodeAddressBanned(offer.getMakerNodeAddress());
}
public boolean requireUpdateToNewVersion() {
return filterManager.requireUpdateToNewVersionForTrading();
}
// This call is a bit expensive so we cache results
public boolean isInsufficientCounterpartyTradeLimit(Offer offer) {
String offerId = offer.getId();
if (insufficientCounterpartyTradeLimitCache.containsKey(offerId)) {
return insufficientCounterpartyTradeLimitCache.get(offerId);
}
boolean result = CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) &&
!accountAgeWitnessService.verifyPeersTradeAmount(offer, offer.getAmount(),
errorMessage -> {
});
insufficientCounterpartyTradeLimitCache.put(offerId, result);
return result;
}
// This call is a bit expensive so we cache results
public boolean isMyInsufficientTradeLimit(Offer offer) {
String offerId = offer.getId();
if (myInsufficientTradeLimitCache.containsKey(offerId)) {
return myInsufficientTradeLimitCache.get(offerId);
}
Optional<PaymentAccount> accountOptional = PaymentAccountUtil.getMostMaturePaymentAccountForOffer(offer,
user.getPaymentAccounts(),
accountAgeWitnessService);
long myTradeLimit = accountOptional
.map(paymentAccount -> accountAgeWitnessService.getMyTradeLimit(paymentAccount,
offer.getCurrencyCode(), offer.getMirroredDirection()))
.orElse(0L);
long offerMinAmount = offer.getMinAmount().value;
log.debug("isInsufficientTradeLimit accountOptional={}, myTradeLimit={}, offerMinAmount={}, ",
accountOptional.isPresent() ? accountOptional.get().getAccountName() : "null",
Coin.valueOf(myTradeLimit).toFriendlyString(),
Coin.valueOf(offerMinAmount).toFriendlyString());
boolean result = CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) &&
accountOptional.isPresent() &&
myTradeLimit < offerMinAmount;
myInsufficientTradeLimitCache.put(offerId, result);
return result;
}
}

View File

@ -69,9 +69,18 @@ public final class OpenOffer implements Tradable {
@Nullable
private NodeAddress refundAgentNodeAddress;
// Added in v1.5.3.
// If market price reaches that trigger price the offer gets deactivated
@Getter
private final long triggerPrice;
public OpenOffer(Offer offer) {
this(offer, 0);
}
public OpenOffer(Offer offer, long triggerPrice) {
this.offer = offer;
this.triggerPrice = triggerPrice;
state = State.AVAILABLE;
}
@ -83,12 +92,14 @@ public final class OpenOffer implements Tradable {
State state,
@Nullable NodeAddress arbitratorNodeAddress,
@Nullable NodeAddress mediatorNodeAddress,
@Nullable NodeAddress refundAgentNodeAddress) {
@Nullable NodeAddress refundAgentNodeAddress,
long triggerPrice) {
this.offer = offer;
this.state = state;
this.arbitratorNodeAddress = arbitratorNodeAddress;
this.mediatorNodeAddress = mediatorNodeAddress;
this.refundAgentNodeAddress = refundAgentNodeAddress;
this.triggerPrice = triggerPrice;
if (this.state == State.RESERVED)
setState(State.AVAILABLE);
@ -98,6 +109,7 @@ public final class OpenOffer implements Tradable {
public protobuf.Tradable toProtoMessage() {
protobuf.OpenOffer.Builder builder = protobuf.OpenOffer.newBuilder()
.setOffer(offer.toProtoMessage())
.setTriggerPrice(triggerPrice)
.setState(protobuf.OpenOffer.State.valueOf(state.name()));
Optional.ofNullable(arbitratorNodeAddress).ifPresent(nodeAddress -> builder.setArbitratorNodeAddress(nodeAddress.toProtoMessage()));
@ -112,7 +124,8 @@ public final class OpenOffer implements Tradable {
ProtoUtil.enumFromProto(OpenOffer.State.class, proto.getState().name()),
proto.hasArbitratorNodeAddress() ? NodeAddress.fromProto(proto.getArbitratorNodeAddress()) : null,
proto.hasMediatorNodeAddress() ? NodeAddress.fromProto(proto.getMediatorNodeAddress()) : null,
proto.hasRefundAgentNodeAddress() ? NodeAddress.fromProto(proto.getRefundAgentNodeAddress()) : null);
proto.hasRefundAgentNodeAddress() ? NodeAddress.fromProto(proto.getRefundAgentNodeAddress()) : null,
proto.getTriggerPrice());
}
@ -178,6 +191,7 @@ public final class OpenOffer implements Tradable {
",\n arbitratorNodeAddress=" + arbitratorNodeAddress +
",\n mediatorNodeAddress=" + mediatorNodeAddress +
",\n refundAgentNodeAddress=" + refundAgentNodeAddress +
",\n triggerPrice=" + triggerPrice +
"\n}";
}
}

View File

@ -49,6 +49,7 @@ import bisq.network.p2p.DecryptedMessageWithPubKey;
import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.P2PService;
import bisq.network.p2p.SendDirectMessageListener;
import bisq.network.p2p.peers.Broadcaster;
import bisq.network.p2p.peers.PeerManager;
import bisq.common.Timer;
@ -117,6 +118,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
private final RefundAgentManager refundAgentManager;
private final DaoFacade daoFacade;
private final FilterManager filterManager;
private final Broadcaster broadcaster;
private final PersistenceManager<TradableList<OpenOffer>> persistenceManager;
private final Map<String, OpenOffer> offersToBeEdited = new HashMap<>();
private final TradableList<OpenOffer> openOffers = new TradableList<>();
@ -148,6 +150,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
RefundAgentManager refundAgentManager,
DaoFacade daoFacade,
FilterManager filterManager,
Broadcaster broadcaster,
PersistenceManager<TradableList<OpenOffer>> persistenceManager) {
this.createOfferService = createOfferService;
this.keyRing = keyRing;
@ -166,6 +169,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
this.refundAgentManager = refundAgentManager;
this.daoFacade = daoFacade;
this.filterManager = filterManager;
this.broadcaster = broadcaster;
this.persistenceManager = persistenceManager;
this.persistenceManager.initialize(openOffers, "OpenOffers", PersistenceManager.Source.PRIVATE);
@ -214,10 +218,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
});
}
private void shutDown() {
shutDown(null);
}
public void shutDown(@Nullable Runnable completeHandler) {
stopped = true;
p2PService.getPeerManager().removeListener(this);
@ -235,6 +235,11 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
UserThread.execute(() -> openOffers.forEach(
openOffer -> offerBookService.removeOfferAtShutDown(openOffer.getOffer().getOfferPayload())
));
// Force broadcaster to send out immediately, otherwise we could have a 2 sec delay until the
// bundled messages sent out.
broadcaster.flush();
if (completeHandler != null) {
// For typical number of offers we are tolerant with delay to give enough time to broadcast.
// If number of offers is very high we limit to 3 sec. to not delay other shutdown routines.
@ -358,6 +363,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
public void placeOffer(Offer offer,
double buyerSecurityDeposit,
boolean useSavingsWallet,
long triggerPrice,
TransactionResultHandler resultHandler,
ErrorMessageHandler errorMessageHandler) {
checkNotNull(offer.getMakerFee(), "makerFee must not be null");
@ -382,7 +388,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
PlaceOfferProtocol placeOfferProtocol = new PlaceOfferProtocol(
model,
transaction -> {
OpenOffer openOffer = new OpenOffer(offer);
OpenOffer openOffer = new OpenOffer(offer, triggerPrice);
openOffers.add(openOffer);
requestPersistence();
resultHandler.handleResult(transaction);
@ -486,6 +492,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
}
public void editOpenOfferPublish(Offer editedOffer,
long triggerPrice,
OpenOffer.State originalState,
ResultHandler resultHandler,
ErrorMessageHandler errorMessageHandler) {
@ -498,7 +505,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
openOffer.setState(OpenOffer.State.CANCELED);
openOffers.remove(openOffer);
OpenOffer editedOpenOffer = new OpenOffer(editedOffer);
OpenOffer editedOpenOffer = new OpenOffer(editedOffer, triggerPrice);
editedOpenOffer.setState(originalState);
openOffers.add(editedOpenOffer);
@ -627,47 +634,50 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
NodeAddress refundAgentNodeAddress = null;
if (openOfferOptional.isPresent()) {
OpenOffer openOffer = openOfferOptional.get();
if (openOffer.getState() == OpenOffer.State.AVAILABLE) {
Offer offer = openOffer.getOffer();
if (preferences.getIgnoreTradersList().stream().noneMatch(fullAddress -> fullAddress.equals(peer.getFullAddress()))) {
availabilityResult = AvailabilityResult.AVAILABLE;
if (!apiUserDeniedByOffer(request)) {
if (openOffer.getState() == OpenOffer.State.AVAILABLE) {
Offer offer = openOffer.getOffer();
if (preferences.getIgnoreTradersList().stream().noneMatch(fullAddress -> fullAddress.equals(peer.getFullAddress()))) {
mediatorNodeAddress = DisputeAgentSelection.getLeastUsedMediator(tradeStatisticsManager, mediatorManager).getNodeAddress();
openOffer.setMediatorNodeAddress(mediatorNodeAddress);
mediatorNodeAddress = DisputeAgentSelection.getLeastUsedMediator(tradeStatisticsManager, mediatorManager).getNodeAddress();
openOffer.setMediatorNodeAddress(mediatorNodeAddress);
refundAgentNodeAddress = DisputeAgentSelection.getLeastUsedRefundAgent(tradeStatisticsManager, refundAgentManager).getNodeAddress();
openOffer.setRefundAgentNodeAddress(refundAgentNodeAddress);
refundAgentNodeAddress = DisputeAgentSelection.getLeastUsedRefundAgent(tradeStatisticsManager, refundAgentManager).getNodeAddress();
openOffer.setRefundAgentNodeAddress(refundAgentNodeAddress);
try {
// Check also tradePrice to avoid failures after taker fee is paid caused by a too big difference
// in trade price between the peers. Also here poor connectivity might cause market price API connection
// losses and therefore an outdated market price.
offer.checkTradePriceTolerance(request.getTakersTradePrice());
} catch (TradePriceOutOfToleranceException e) {
log.warn("Trade price check failed because takers price is outside out tolerance.");
availabilityResult = AvailabilityResult.PRICE_OUT_OF_TOLERANCE;
} catch (MarketPriceNotAvailableException e) {
log.warn(e.getMessage());
availabilityResult = AvailabilityResult.MARKET_PRICE_NOT_AVAILABLE;
} catch (Throwable e) {
log.warn("Trade price check failed. " + e.getMessage());
availabilityResult = AvailabilityResult.UNKNOWN_FAILURE;
try {
// Check also tradePrice to avoid failures after taker fee is paid caused by a too big difference
// in trade price between the peers. Also here poor connectivity might cause market price API connection
// losses and therefore an outdated market price.
offer.checkTradePriceTolerance(request.getTakersTradePrice());
availabilityResult = AvailabilityResult.AVAILABLE;
} catch (TradePriceOutOfToleranceException e) {
log.warn("Trade price check failed because takers price is outside out tolerance.");
availabilityResult = AvailabilityResult.PRICE_OUT_OF_TOLERANCE;
} catch (MarketPriceNotAvailableException e) {
log.warn(e.getMessage());
availabilityResult = AvailabilityResult.MARKET_PRICE_NOT_AVAILABLE;
} catch (Throwable e) {
log.warn("Trade price check failed. " + e.getMessage());
availabilityResult = AvailabilityResult.UNKNOWN_FAILURE;
}
} else {
availabilityResult = AvailabilityResult.USER_IGNORED;
}
} else {
availabilityResult = AvailabilityResult.USER_IGNORED;
availabilityResult = AvailabilityResult.OFFER_TAKEN;
}
} else {
availabilityResult = AvailabilityResult.OFFER_TAKEN;
availabilityResult = AvailabilityResult.MAKER_DENIED_API_USER;
}
} else {
log.warn("handleOfferAvailabilityRequest: openOffer not found. That should never happen.");
log.warn("handleOfferAvailabilityRequest: openOffer not found.");
availabilityResult = AvailabilityResult.OFFER_TAKEN;
}
if (btcWalletService.isUnconfirmedTransactionsLimitHit() || bsqWalletService.isUnconfirmedTransactionsLimitHit()) {
errorMessage = Res.get("shared.unconfirmedTransactionsLimitReached");
log.warn(errorMessage);
availabilityResult = AvailabilityResult.UNKNOWN_FAILURE;
availabilityResult = AvailabilityResult.UNCONF_TX_LIMIT_HIT;
}
OfferAvailabilityResponse offerAvailabilityResponse = new OfferAvailabilityResponse(request.offerId,
@ -709,6 +719,10 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
}
}
private boolean apiUserDeniedByOffer(OfferAvailabilityRequest request) {
return preferences.isDenyApiTaker() && request.isTakerApiUser();
}
private void sendAckMessage(OfferAvailabilityRequest message,
NodeAddress sender,
boolean result,
@ -855,7 +869,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
updatedOffer.setPriceFeedService(priceFeedService);
updatedOffer.setState(originalOfferState);
OpenOffer updatedOpenOffer = new OpenOffer(updatedOffer);
OpenOffer updatedOpenOffer = new OpenOffer(updatedOffer, originalOpenOffer.getTriggerPrice());
updatedOpenOffer.setState(originalOpenOfferState);
openOffers.add(updatedOpenOffer);
requestPersistence();
@ -871,41 +885,53 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
///////////////////////////////////////////////////////////////////////////////////////////
private void republishOffers() {
int size = openOffers.size();
final ArrayList<OpenOffer> openOffersList = new ArrayList<>(openOffers.getList());
if (!stopped) {
stopPeriodicRefreshOffersTimer();
for (int i = 0; i < size; i++) {
// we delay to avoid reaching throttle limits
if (stopped) {
return;
}
long delay = 700;
final long minDelay = (i + 1) * delay;
final long maxDelay = (i + 2) * delay;
final OpenOffer openOffer = openOffersList.get(i);
UserThread.runAfterRandomDelay(() -> {
if (openOffers.contains(openOffer)) {
String id = openOffer.getId();
if (id != null && !openOffer.isDeactivated())
republishOffer(openOffer);
}
stopPeriodicRefreshOffersTimer();
}, minDelay, maxDelay, TimeUnit.MILLISECONDS);
}
List<OpenOffer> openOffersList = new ArrayList<>(openOffers.getList());
processListForRepublishOffers(openOffersList);
}
private void processListForRepublishOffers(List<OpenOffer> list) {
if (list.isEmpty()) {
return;
}
OpenOffer openOffer = list.remove(0);
if (openOffers.contains(openOffer) && !openOffer.isDeactivated()) {
// TODO It is not clear yet if it is better for the node and the network to send out all add offer
// messages in one go or to spread it over a delay. With power users who have 100-200 offers that can have
// some significant impact to user experience and the network
republishOffer(openOffer, () -> processListForRepublishOffers(list));
/* republishOffer(openOffer,
() -> UserThread.runAfter(() -> processListForRepublishOffers(list),
30, TimeUnit.MILLISECONDS));*/
} else {
log.debug("We have stopped already. We ignore that republishOffers call.");
// If the offer was removed in the meantime or if its deactivated we skip and call
// processListForRepublishOffers again with the list where we removed the offer already.
processListForRepublishOffers(list);
}
}
private void republishOffer(OpenOffer openOffer) {
republishOffer(openOffer, null);
}
private void republishOffer(OpenOffer openOffer, @Nullable Runnable completeHandler) {
offerBookService.addOffer(openOffer.getOffer(),
() -> {
if (!stopped) {
log.debug("Successfully added offer to P2P network.");
// Refresh means we send only the data needed to refresh the TTL (hash, signature and sequence no.)
if (periodicRefreshOffersTimer == null)
if (periodicRefreshOffersTimer == null) {
startPeriodicRefreshOffersTimer();
} else {
log.debug("We have stopped already. We ignore that offerBookService.republishOffers.onSuccess call.");
}
if (completeHandler != null) {
completeHandler.run();
}
}
},
errorMessage -> {
@ -914,26 +940,25 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
stopRetryRepublishOffersTimer();
retryRepublishOffersTimer = UserThread.runAfter(OpenOfferManager.this::republishOffers,
RETRY_REPUBLISH_DELAY_SEC);
} else {
log.debug("We have stopped already. We ignore that offerBookService.republishOffers.onFault call.");
if (completeHandler != null) {
completeHandler.run();
}
}
});
}
private void startPeriodicRepublishOffersTimer() {
stopped = false;
if (periodicRepublishOffersTimer == null)
if (periodicRepublishOffersTimer == null) {
periodicRepublishOffersTimer = UserThread.runPeriodically(() -> {
if (!stopped) {
republishOffers();
} else {
log.debug("We have stopped already. We ignore that periodicRepublishOffersTimer.run call.");
}
},
REPUBLISH_INTERVAL_MS,
TimeUnit.MILLISECONDS);
else
log.trace("periodicRepublishOffersTimer already stated");
}
}
private void startPeriodicRefreshOffersTimer() {

View File

@ -0,0 +1,163 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.offer;
import bisq.core.locale.CurrencyUtil;
import bisq.core.monetary.Altcoin;
import bisq.core.monetary.Price;
import bisq.core.provider.price.MarketPrice;
import bisq.core.provider.price.PriceFeedService;
import bisq.common.util.MathUtils;
import org.bitcoinj.utils.Fiat;
import javax.inject.Inject;
import javax.inject.Singleton;
import javafx.collections.ListChangeListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import static bisq.common.util.MathUtils.roundDoubleToLong;
import static bisq.common.util.MathUtils.scaleUpByPowerOf10;
@Slf4j
@Singleton
public class TriggerPriceService {
private final OpenOfferManager openOfferManager;
private final PriceFeedService priceFeedService;
private final Map<String, Set<OpenOffer>> openOffersByCurrency = new HashMap<>();
@Inject
public TriggerPriceService(OpenOfferManager openOfferManager, PriceFeedService priceFeedService) {
this.openOfferManager = openOfferManager;
this.priceFeedService = priceFeedService;
}
public void onAllServicesInitialized() {
openOfferManager.getObservableList().addListener((ListChangeListener<OpenOffer>) c -> {
c.next();
if (c.wasAdded()) {
onAddedOpenOffers(c.getAddedSubList());
}
if (c.wasRemoved()) {
onRemovedOpenOffers(c.getRemoved());
}
});
onAddedOpenOffers(openOfferManager.getObservableList());
priceFeedService.updateCounterProperty().addListener((observable, oldValue, newValue) -> onPriceFeedChanged());
onPriceFeedChanged();
}
private void onPriceFeedChanged() {
openOffersByCurrency.keySet().stream()
.map(priceFeedService::getMarketPrice)
.filter(Objects::nonNull)
.filter(marketPrice -> openOffersByCurrency.containsKey(marketPrice.getCurrencyCode()))
.forEach(marketPrice -> {
openOffersByCurrency.get(marketPrice.getCurrencyCode()).stream()
.filter(openOffer -> !openOffer.isDeactivated())
.forEach(openOffer -> checkPriceThreshold(marketPrice, openOffer));
});
}
public static boolean wasTriggered(MarketPrice marketPrice, OpenOffer openOffer) {
Price price = openOffer.getOffer().getPrice();
if (price == null) {
return false;
}
String currencyCode = openOffer.getOffer().getCurrencyCode();
boolean cryptoCurrency = CurrencyUtil.isCryptoCurrency(currencyCode);
int smallestUnitExponent = cryptoCurrency ?
Altcoin.SMALLEST_UNIT_EXPONENT :
Fiat.SMALLEST_UNIT_EXPONENT;
long marketPriceAsLong = roundDoubleToLong(
scaleUpByPowerOf10(marketPrice.getPrice(), smallestUnitExponent));
long triggerPrice = openOffer.getTriggerPrice();
if (triggerPrice <= 0) {
return false;
}
OfferPayload.Direction direction = openOffer.getOffer().getDirection();
boolean isSellOffer = direction == OfferPayload.Direction.SELL;
boolean condition = isSellOffer && !cryptoCurrency || !isSellOffer && cryptoCurrency;
return condition ?
marketPriceAsLong < triggerPrice :
marketPriceAsLong > triggerPrice;
}
private void checkPriceThreshold(MarketPrice marketPrice, OpenOffer openOffer) {
if (wasTriggered(marketPrice, openOffer)) {
String currencyCode = openOffer.getOffer().getCurrencyCode();
int smallestUnitExponent = CurrencyUtil.isCryptoCurrency(currencyCode) ?
Altcoin.SMALLEST_UNIT_EXPONENT :
Fiat.SMALLEST_UNIT_EXPONENT;
long triggerPrice = openOffer.getTriggerPrice();
log.info("Market price exceeded the trigger price of the open offer.\n" +
"We deactivate the open offer with ID {}.\nCurrency: {};\nOffer direction: {};\n" +
"Market price: {};\nTrigger price: {}",
openOffer.getOffer().getShortId(),
currencyCode,
openOffer.getOffer().getDirection(),
marketPrice.getPrice(),
MathUtils.scaleDownByPowerOf10(triggerPrice, smallestUnitExponent)
);
openOfferManager.deactivateOpenOffer(openOffer, () -> {
}, errorMessage -> {
});
}
}
private void onAddedOpenOffers(List<? extends OpenOffer> openOffers) {
openOffers.forEach(openOffer -> {
String currencyCode = openOffer.getOffer().getCurrencyCode();
openOffersByCurrency.putIfAbsent(currencyCode, new HashSet<>());
openOffersByCurrency.get(currencyCode).add(openOffer);
MarketPrice marketPrice = priceFeedService.getMarketPrice(openOffer.getOffer().getCurrencyCode());
if (marketPrice != null) {
checkPriceThreshold(marketPrice, openOffer);
}
});
}
private void onRemovedOpenOffers(List<? extends OpenOffer> openOffers) {
openOffers.forEach(openOffer -> {
String currencyCode = openOffer.getOffer().getCurrencyCode();
if (openOffersByCurrency.containsKey(currencyCode)) {
Set<OpenOffer> set = openOffersByCurrency.get(currencyCode);
set.remove(openOffer);
if (set.isEmpty()) {
openOffersByCurrency.remove(currencyCode);
}
}
});
}
}

View File

@ -66,19 +66,24 @@ public class OfferAvailabilityModel implements Model {
@Getter
private NodeAddress selectedRefundAgent;
// Added in v1.5.5
@Getter
private final boolean isTakerApiUser;
public OfferAvailabilityModel(Offer offer,
PubKeyRing pubKeyRing,
P2PService p2PService,
User user,
MediatorManager mediatorManager,
TradeStatisticsManager tradeStatisticsManager) {
TradeStatisticsManager tradeStatisticsManager,
boolean isTakerApiUser) {
this.offer = offer;
this.pubKeyRing = pubKeyRing;
this.p2PService = p2PService;
this.user = user;
this.mediatorManager = mediatorManager;
this.tradeStatisticsManager = tradeStatisticsManager;
this.isTakerApiUser = isTakerApiUser;
}
public NodeAddress getPeerNodeAddress() {

View File

@ -39,7 +39,8 @@ public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
try {
runInterceptHook();
OfferAvailabilityRequest message = new OfferAvailabilityRequest(model.getOffer().getId(), model.getPubKeyRing(), model.getTakersTradePrice());
OfferAvailabilityRequest message = new OfferAvailabilityRequest(model.getOffer().getId(),
model.getPubKeyRing(), model.getTakersTradePrice(), model.isTakerApiUser());
log.info("Send {} with offerId {} and uid {} to peer {}",
message.getClass().getSimpleName(), message.getOfferId(),
message.getUid(), model.getPeerNodeAddress());

View File

@ -42,13 +42,16 @@ public final class OfferAvailabilityRequest extends OfferMessage implements Supp
private final long takersTradePrice;
@Nullable
private final Capabilities supportedCapabilities;
private final boolean isTakerApiUser;
public OfferAvailabilityRequest(String offerId,
PubKeyRing pubKeyRing,
long takersTradePrice) {
long takersTradePrice,
boolean isTakerApiUser) {
this(offerId,
pubKeyRing,
takersTradePrice,
isTakerApiUser,
Capabilities.app,
Version.getP2PMessageVersion(),
UUID.randomUUID().toString());
@ -62,12 +65,14 @@ public final class OfferAvailabilityRequest extends OfferMessage implements Supp
private OfferAvailabilityRequest(String offerId,
PubKeyRing pubKeyRing,
long takersTradePrice,
boolean isTakerApiUser,
@Nullable Capabilities supportedCapabilities,
int messageVersion,
@Nullable String uid) {
super(messageVersion, offerId, uid);
this.pubKeyRing = pubKeyRing;
this.takersTradePrice = takersTradePrice;
this.isTakerApiUser = isTakerApiUser;
this.supportedCapabilities = supportedCapabilities;
}
@ -76,7 +81,8 @@ public final class OfferAvailabilityRequest extends OfferMessage implements Supp
final protobuf.OfferAvailabilityRequest.Builder builder = protobuf.OfferAvailabilityRequest.newBuilder()
.setOfferId(offerId)
.setPubKeyRing(pubKeyRing.toProtoMessage())
.setTakersTradePrice(takersTradePrice);
.setTakersTradePrice(takersTradePrice)
.setIsTakerApiUser(isTakerApiUser);
Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities)));
Optional.ofNullable(uid).ifPresent(e -> builder.setUid(uid));
@ -90,6 +96,7 @@ public final class OfferAvailabilityRequest extends OfferMessage implements Supp
return new OfferAvailabilityRequest(proto.getOfferId(),
PubKeyRing.fromProto(proto.getPubKeyRing()),
proto.getTakersTradePrice(),
proto.getIsTakerApiUser(),
Capabilities.fromIntList(proto.getSupportedCapabilitiesList()),
messageVersion,
proto.getUid().isEmpty() ? null : proto.getUid());

View File

@ -97,12 +97,25 @@ public abstract class PaymentAccount implements PersistablePayload {
}
public static PaymentAccount fromProto(protobuf.PaymentAccount proto, CoreProtoResolver coreProtoResolver) {
PaymentAccount account = PaymentAccountFactory.getPaymentAccount(PaymentMethod.getPaymentMethodById(proto.getPaymentMethod().getId()));
String paymentMethodId = proto.getPaymentMethod().getId();
List<TradeCurrency> tradeCurrencies = proto.getTradeCurrenciesList().stream()
.map(TradeCurrency::fromProto)
.collect(Collectors.toList());
// We need to remove NGN for Transferwise
Optional<TradeCurrency> ngnTwOptional = tradeCurrencies.stream()
.filter(e -> paymentMethodId.equals(PaymentMethod.TRANSFERWISE_ID))
.filter(e -> e.getCode().equals("NGN"))
.findAny();
// We cannot remove it in the stream as it would cause a concurrentModificationException
ngnTwOptional.ifPresent(tradeCurrencies::remove);
PaymentAccount account = PaymentAccountFactory.getPaymentAccount(PaymentMethod.getPaymentMethodById(paymentMethodId));
account.getTradeCurrencies().clear();
account.setId(proto.getId());
account.setCreationDate(proto.getCreationDate());
account.setAccountName(proto.getAccountName());
account.getTradeCurrencies().addAll(proto.getTradeCurrenciesList().stream().map(TradeCurrency::fromProto).collect(Collectors.toList()));
account.getTradeCurrencies().addAll(tradeCurrencies);
account.setPaymentAccountPayload(coreProtoResolver.fromProto(proto.getPaymentAccountPayload()));
if (proto.hasSelectedTradeCurrency())

View File

@ -185,7 +185,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable<Payme
REVOLUT = new PaymentMethod(REVOLUT_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK),
PERFECT_MONEY = new PaymentMethod(PERFECT_MONEY_ID, DAY, DEFAULT_TRADE_LIMIT_LOW_RISK),
ADVANCED_CASH = new PaymentMethod(ADVANCED_CASH_ID, DAY, DEFAULT_TRADE_LIMIT_VERY_LOW_RISK),
TRANSFERWISE = new PaymentMethod(TRANSFERWISE_ID, DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK),
TRANSFERWISE = new PaymentMethod(TRANSFERWISE_ID, 4 * DAY, DEFAULT_TRADE_LIMIT_HIGH_RISK),
// Japan
JAPAN_BANK = new PaymentMethod(JAPAN_BANK_ID, DAY, DEFAULT_TRADE_LIMIT_LOW_RISK),

View File

@ -79,10 +79,8 @@ public class ProvidersRepository {
fillProviderList();
selectNextProviderBaseUrl();
if (bannedNodes == null) {
log.info("Selected provider baseUrl={}, providerList={}", baseUrl, providerList);
} else if (!bannedNodes.isEmpty()) {
log.warn("We have banned provider nodes: bannedNodes={}, selected provider baseUrl={}, providerList={}",
if (bannedNodes != null && !bannedNodes.isEmpty()) {
log.info("Excluded provider nodes from filter: nodes={}, selected provider baseUrl={}, providerList={}",
bannedNodes, baseUrl, providerList);
}
}

View File

@ -47,6 +47,8 @@ public class CoreNetworkCapabilities {
if (config.daoActivated) {
maybeApplyDaoFullMode(config);
}
log.info(Capabilities.app.prettyPrint());
}
public static void maybeApplyDaoFullMode(Config config) {
@ -54,12 +56,10 @@ public class CoreNetworkCapabilities {
// bit later than we call that method so we have to add DAO_FULL_NODE Capability at preferences as well to
// be sure it is set in both cases.
if (config.fullDaoNode) {
log.info("Set Capability.DAO_FULL_NODE");
Capabilities.app.addAll(Capability.DAO_FULL_NODE);
} else {
// A lite node has the capability to receive bsq blocks. We do not want to send BSQ blocks to full nodes
// as they ignore them anyway.
log.info("Set Capability.RECEIVE_BSQ_BLOCK");
Capabilities.app.addAll(Capability.RECEIVE_BSQ_BLOCK);
}
}

View File

@ -0,0 +1,48 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.support.dispute.agent;
import bisq.core.locale.Res;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
@Slf4j
public class DisputeAgentLookupMap {
// See also: https://bisq.wiki/Finding_your_mediator
@Nullable
public static String getKeyBaseUserName(String fullAddress) {
switch (fullAddress) {
case "sjlho4zwp3gecspf.onion:9999":
return "leo816";
case "wizbisqzd7ku25di7p2ztsajioabihlnyp5lq5av66tmu7do2dke2tid.onion:9999":
return "wiz";
case "apbp7ubuyezav4hy.onion:9999":
return "bisq_knight";
case "a56olqlmmpxrn5q34itq5g5tb5d3fg7vxekpbceq7xqvfl3cieocgsyd.onion:9999":
return "leo816";
case "3z5jnirlccgxzoxc6zwkcgwj66bugvqplzf6z2iyd5oxifiaorhnanqd.onion:9999":
return "refundagent2";
default:
log.warn("No user name for dispute agent with address {} found.", fullAddress);
return Res.get("shared.na");
}
}
}

View File

@ -373,6 +373,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
///////////////////////////////////////////////////////////////////////////////////////////
public void checkOfferAvailability(Offer offer,
boolean isTakerApiUser,
ResultHandler resultHandler,
ErrorMessageHandler errorMessageHandler) {
if (btcWalletService.isUnconfirmedTransactionsLimitHit() ||
@ -383,7 +384,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
return;
}
offer.checkOfferAvailability(getOfferAvailabilityModel(offer), resultHandler, errorMessageHandler);
offer.checkOfferAvailability(getOfferAvailabilityModel(offer, isTakerApiUser), resultHandler, errorMessageHandler);
}
// First we check if offer is still available then we create the trade with the protocol
@ -396,12 +397,13 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
Offer offer,
String paymentAccountId,
boolean useSavingsWallet,
boolean isTakerApiUser,
TradeResultHandler tradeResultHandler,
ErrorMessageHandler errorMessageHandler) {
checkArgument(!wasOfferAlreadyUsedInTrade(offer.getId()));
OfferAvailabilityModel model = getOfferAvailabilityModel(offer);
OfferAvailabilityModel model = getOfferAvailabilityModel(offer, isTakerApiUser);
offer.checkOfferAvailability(model,
() -> {
if (offer.getState() == Offer.State.AVAILABLE) {
@ -464,14 +466,15 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
processModelServiceProvider.getKeyRing().getPubKeyRing());
}
private OfferAvailabilityModel getOfferAvailabilityModel(Offer offer) {
private OfferAvailabilityModel getOfferAvailabilityModel(Offer offer, boolean isTakerApiUser) {
return new OfferAvailabilityModel(
offer,
keyRing.getPubKeyRing(),
p2PService,
user,
mediatorManager,
tradeStatisticsManager);
tradeStatisticsManager,
isTakerApiUser);
}

View File

@ -286,8 +286,10 @@ public class FluentProtocol {
log.info(info);
return Result.VALID.info(info);
} else {
String info = MessageFormat.format("We received a {0} but we are are not in the expected phase. " +
"Expected phases={1}, Trade phase={2}, Trade state= {3}, tradeId={4}",
String info = MessageFormat.format("We received a {0} but we are are not in the expected phase.\n" +
"This can be an expected case if we get a repeated CounterCurrencyTransferStartedMessage " +
"after we have already received one as the peer re-sends that message at each startup.\n" +
"Expected phases={1},\nTrade phase={2},\nTrade state= {3},\ntradeId={4}",
trigger,
expectedPhases,
trade.getPhase(),

View File

@ -196,7 +196,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
.condition(condition)
.resultHandler(result -> {
if (!result.isValid()) {
log.error(result.getInfo());
log.warn(result.getInfo());
handleTaskRunnerFault(null,
result.name(),
result.getInfo());

View File

@ -132,11 +132,8 @@ public class XmrTxProofService implements AssetTxProofService {
onP2pNetworkAndWalletReady();
} else {
p2pNetworkAndWalletReady = EasyBind.combine(isP2pBootstrapped, hasSufficientBtcPeers, isBtcBlockDownloadComplete,
(bootstrapped, sufficientPeers, downloadComplete) -> {
log.info("isP2pBootstrapped={}, hasSufficientBtcPeers={} isBtcBlockDownloadComplete={}",
bootstrapped, sufficientPeers, downloadComplete);
return bootstrapped && sufficientPeers && downloadComplete;
});
(bootstrapped, sufficientPeers, downloadComplete) ->
bootstrapped && sufficientPeers && downloadComplete);
p2pNetworkAndWalletReadyListener = (observable, oldValue, newValue) -> {
if (newValue) {

View File

@ -0,0 +1,63 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.user;
import bisq.common.proto.ProtoUtil;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Nullable;
/**
* Serves as flexible container for persisting UI states, layout,...
* Should not be over-used for domain specific data where type safety and data integrity is important.
*/
public class Cookie extends HashMap<CookieKey, String> {
public void putAsDouble(CookieKey key, double value) {
put(key, String.valueOf(value));
}
public Optional<Double> getAsOptionalDouble(CookieKey key) {
try {
return containsKey(key) ?
Optional.of(Double.parseDouble(get(key))) :
Optional.empty();
} catch (Throwable t) {
return Optional.empty();
}
}
public Map<String, String> toProtoMessage() {
Map<String, String> protoMap = new HashMap<>();
this.forEach((key, value) -> protoMap.put(key.name(), value));
return protoMap;
}
public static Cookie fromProto(@Nullable Map<String, String> protoMap) {
Cookie cookie = new Cookie();
if (protoMap != null) {
protoMap.forEach((key, value) -> cookie.put(ProtoUtil.enumFromProto(CookieKey.class, key), value));
}
return cookie;
}
}

View File

@ -0,0 +1,26 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.user;
// Used for persistence of Cookie. Entries must not be changes or removed. Only adding entries is permitted.
public enum CookieKey {
STAGE_X,
STAGE_Y,
STAGE_W,
STAGE_H
}

View File

@ -487,6 +487,11 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
});
}
public void setHideNonAccountPaymentMethods(boolean hideNonAccountPaymentMethods) {
prefPayload.setHideNonAccountPaymentMethods(hideNonAccountPaymentMethods);
requestPersistence();
}
private void requestPersistence() {
if (initialReadDone)
persistenceManager.requestPersistence();
@ -767,6 +772,16 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
requestPersistence();
}
public void setShowOffersMatchingMyAccounts(boolean value) {
prefPayload.setShowOffersMatchingMyAccounts(value);
requestPersistence();
}
public void setDenyApiTaker(boolean value) {
prefPayload.setDenyApiTaker(value);
requestPersistence();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getter
@ -1074,5 +1089,11 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
void setBsqAverageTrimThreshold(double bsqAverageTrimThreshold);
void setAutoConfirmSettings(AutoConfirmSettings autoConfirmSettings);
void setHideNonAccountPaymentMethods(boolean hideNonAccountPaymentMethods);
void setShowOffersMatchingMyAccounts(boolean value);
void setDenyApiTaker(boolean value);
}
}

View File

@ -129,6 +129,10 @@ public final class PreferencesPayload implements PersistableEnvelope {
// Added at 1.3.8
private List<AutoConfirmSettings> autoConfirmSettingsList = new ArrayList<>();
// Added in 1.5.5
private boolean hideNonAccountPaymentMethods;
private boolean showOffersMatchingMyAccounts;
private boolean denyApiTaker;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -192,7 +196,10 @@ public final class PreferencesPayload implements PersistableEnvelope {
.setBsqAverageTrimThreshold(bsqAverageTrimThreshold)
.addAllAutoConfirmSettings(autoConfirmSettingsList.stream()
.map(autoConfirmSettings -> ((protobuf.AutoConfirmSettings) autoConfirmSettings.toProtoMessage()))
.collect(Collectors.toList()));
.collect(Collectors.toList()))
.setHideNonAccountPaymentMethods(hideNonAccountPaymentMethods)
.setShowOffersMatchingMyAccounts(showOffersMatchingMyAccounts)
.setDenyApiTaker(denyApiTaker);
Optional.ofNullable(backupDirectory).ifPresent(builder::setBackupDirectory);
Optional.ofNullable(preferredTradeCurrency).ifPresent(e -> builder.setPreferredTradeCurrency((protobuf.TradeCurrency) e.toProtoMessage()));
@ -286,7 +293,10 @@ public final class PreferencesPayload implements PersistableEnvelope {
proto.getAutoConfirmSettingsList().isEmpty() ? new ArrayList<>() :
new ArrayList<>(proto.getAutoConfirmSettingsList().stream()
.map(AutoConfirmSettings::fromProto)
.collect(Collectors.toList()))
.collect(Collectors.toList())),
proto.getHideNonAccountPaymentMethods(),
proto.getShowOffersMatchingMyAccounts(),
proto.getDenyApiTaker()
);
}
}

View File

@ -514,4 +514,8 @@ public class User implements PersistedDataHost {
private boolean paymentAccountExists(PaymentAccount paymentAccount) {
return getPaymentAccountsAsObservable().stream().anyMatch(e -> e.equals(paymentAccount));
}
public Cookie getCookie() {
return userPayload.getCookie();
}
}

View File

@ -80,6 +80,11 @@ public class UserPayload implements PersistableEnvelope {
@Nullable
private List<RefundAgent> acceptedRefundAgents = new ArrayList<>();
// Added at 1.5.3
// Generic map for persisting various UI states. We keep values un-typed as string to
// provide sufficient flexibility.
private Cookie cookie = new Cookie();
public UserPayload() {
}
@ -118,6 +123,7 @@ public class UserPayload implements PersistableEnvelope {
Optional.ofNullable(acceptedRefundAgents)
.ifPresent(e -> builder.addAllAcceptedRefundAgents(ProtoUtil.collectionToProto(acceptedRefundAgents,
message -> ((protobuf.StoragePayload) message).getRefundAgent())));
Optional.ofNullable(cookie).ifPresent(e -> builder.putAllCookie(cookie.toProtoMessage()));
return protobuf.PersistableEnvelope.newBuilder().setUserPayload(builder).build();
}
@ -147,7 +153,8 @@ public class UserPayload implements PersistableEnvelope {
proto.hasRegisteredRefundAgent() ? RefundAgent.fromProto(proto.getRegisteredRefundAgent()) : null,
proto.getAcceptedRefundAgentsList().isEmpty() ? new ArrayList<>() : new ArrayList<>(proto.getAcceptedRefundAgentsList().stream()
.map(RefundAgent::fromProto)
.collect(Collectors.toList()))
.collect(Collectors.toList())),
Cookie.fromProto(proto.getCookieMap())
);
}
}

View File

@ -171,7 +171,7 @@ public class FormattingUtils {
return formatMarketPrice(price, 8);
}
private static String formatMarketPrice(double price, int precision) {
public static String formatMarketPrice(double price, int precision) {
return formatRoundedDoubleWithPrecision(price, precision);
}

View File

@ -21,6 +21,7 @@ import bisq.core.locale.Res;
import java.math.BigInteger;
import java.util.Objects;
import java.util.function.Function;
public class InputValidator {
@ -65,6 +66,12 @@ public class InputValidator {
'}';
}
public boolean errorMessageEquals(ValidationResult other) {
if (this == other) return true;
if (other == null) return false;
return Objects.equals(errorMessage, other.errorMessage);
}
public interface Validator extends Function<String, ValidationResult> {
}

View File

@ -105,7 +105,6 @@ shared.selectTradingAccount=Select trading account
shared.fundFromSavingsWalletButton=Transfer funds from Bisq wallet
shared.fundFromExternalWalletButton=Open your external wallet for funding
shared.openDefaultWalletFailed=Failed to open a Bitcoin wallet application. Are you sure you have one installed?
shared.distanceInPercent=Distance in % from market price
shared.belowInPercent=Below % from market price
shared.aboveInPercent=Above % from market price
shared.enterPercentageValue=Enter % value
@ -293,7 +292,8 @@ mainView.version.update=(Update available)
####################################################################
market.tabs.offerBook=Offer book
market.tabs.spread=Details
market.tabs.spreadCurrency=Offers by Currency
market.tabs.spreadPayment=Offers by Payment Method
market.tabs.trades=Trades
# OfferBookChartView
@ -312,6 +312,7 @@ market.spread.numberOfBuyOffersColumn=Buy BTC ({0})
market.spread.numberOfSellOffersColumn=Sell BTC ({0})
market.spread.totalAmountColumn=Total BTC ({0})
market.spread.spreadColumn=Spread
market.spread.expanded=Expanded view
# TradesChartsView
market.trades.nrOfTrades=Trades: {0}
@ -341,6 +342,7 @@ offerbook.offerersAcceptedBankSeats=Accepted seat of bank countries (taker):\n {
offerbook.availableOffers=Available offers
offerbook.filterByCurrency=Filter by currency
offerbook.filterByPaymentMethod=Filter by payment method
offerbook.matchingOffers=Offers matching my accounts
offerbook.timeSinceSigning=Account info
offerbook.timeSinceSigning.info=This account was verified and {0}
offerbook.timeSinceSigning.info.arbitrator=signed by an arbitrator and can sign peer accounts
@ -455,6 +457,13 @@ createOffer.warning.buyAboveMarketPrice=You will always pay {0}% more than the c
createOffer.tradeFee.descriptionBTCOnly=Trade fee
createOffer.tradeFee.descriptionBSQEnabled=Select trade fee currency
createOffer.triggerPrice.prompt=Set optional trigger price
createOffer.triggerPrice.label=Deactivate offer if market price is {0}
createOffer.triggerPrice.tooltip=As protecting against drastic price movements you can set a trigger price which \
deactivates the offer if the market price reaches that value.
createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0}
createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries
createOffer.placeOfferButton=Review: Place offer to {0} bitcoin
createOffer.createOfferFundWalletInfo.headline=Fund your offer
@ -551,6 +560,11 @@ takeOffer.tac=With taking this offer I agree to the trade conditions as defined
# Offerbook / Edit offer
####################################################################
openOffer.header.triggerPrice=Trigger price
openOffer.triggerPrice=Trigger price {0}
openOffer.triggered=The offer has been deactivated because the market price reached your trigger price.\n\
Please edit the offer to define a new trigger price
editOffer.setPrice=Set price
editOffer.confirmEdit=Confirm: Edit offer
editOffer.publishOffer=Publishing your offer.
@ -1114,6 +1128,7 @@ support.error=Receiver could not process message. Error: {0}
support.buyerAddress=BTC buyer address
support.sellerAddress=BTC seller address
support.role=Role
support.agent=Support agent
support.state=State
support.closed=Closed
support.open=Open
@ -1207,6 +1222,8 @@ setting.preferences.showOwnOffers=Show my own offers in offer book
setting.preferences.useAnimations=Use animations
setting.preferences.useDarkMode=Use dark mode
setting.preferences.sortWithNumOffers=Sort market lists with no. of offers/trades
setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods
setting.preferences.denyApiTaker=Deny takers using the API
setting.preferences.resetAllFlags=Reset all \"Don't show again\" flags
settings.preferences.languageChange=To apply the language change to all screens requires a restart.
settings.preferences.supportLanguageWarning=In case of a dispute, please note that mediation is handled in {0} and arbitration in {1}.
@ -2582,7 +2599,8 @@ enterPrivKeyWindow.headline=Enter private key for registration
filterWindow.headline=Edit filter list
filterWindow.offers=Filtered offers (comma sep.)
filterWindow.onions=Filtered onion addresses (comma sep.)
filterWindow.onions=Banned from trading addresses (comma sep.)
filterWindow.bannedFromNetwork=Banned from network 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.)
@ -2603,6 +2621,7 @@ filterWindow.disableTradeBelowVersion=Min. version required for trading
filterWindow.add=Add filter
filterWindow.remove=Remove filter
filterWindow.btcFeeReceiverAddresses=BTC fee receiver addresses
filterWindow.disableApi=Disable API
offerDetailsWindow.minBtcAmount=Min. BTC amount
offerDetailsWindow.min=(min. {0})
@ -3321,11 +3340,15 @@ payment.payid=PayID linked to financial institution. Like email address or mobil
payment.payid.info=A PayID like a phone number, email address or an Australian Business Number (ABN), that you can securely link to your \
bank, credit union or building society account. You need to have already created a PayID with your Australian financial institution. \
Both sending and receiving financial institutions must support PayID. For more information please check [HYPERLINK:https://payid.com.au/faqs/]
payment.amazonGiftCard.info=To pay with Amazon eGift Card you need to purchase an Amazon eGift Card at your Amazon account and \
use the BTC seller''s email or mobile nr. as receiver. Amazon sends then an email or text message to the receiver. \
Use the trade ID for the message field.\n\n\
Amazon eGift Cards can only be redeemed by Amazon accounts with the same currency.\n\n\
For more information visit the Amazon eGift Card webpage. [HYPERLINK:https://www.amazon.com/Amazon-1_US_Email-eGift-Card/dp/B004LLIKVU]
payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the BTC seller via your Amazon account. \n\n\
Bisq will show the BTC seller''s email address or phone number where the gift card should be sent, and you must include the trade ID in the gift \
card''s message field. Please see the wiki [HYPERLINK:https://bisq.wiki/Amazon_eGift_card] for further details and best practices. \n\n\
Three important notes:\n\
- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n\
- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat \
to tell your trading peer the reference text you picked so they can verify your payment)\n\
- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it)
# We use constants from the code so we do not use our normal naming convention
# dynamic values are not recognized by IntelliJ

File diff suppressed because it is too large Load Diff

View File

@ -71,6 +71,7 @@ shared.amountWithCur=Betrag in {0}
shared.volumeWithCur=Volumen in {0}
shared.currency=Währung
shared.market=Markt
shared.deviation=Abweichung
shared.paymentMethod=Zahlungsmethode
shared.tradeCurrency=Handelswährung
shared.offerType=Angebotstyp
@ -104,7 +105,6 @@ shared.selectTradingAccount=Handelskonto auswählen
shared.fundFromSavingsWalletButton=Gelder aus Bisq-Wallet überweisen
shared.fundFromExternalWalletButton=Ihre externe Wallet zum Finanzieren öffnen
shared.openDefaultWalletFailed=Öffnen einer Bitcoin Wallet Applikation fehlgeschlagen. Bist du sicher, dass du eine installiert hast?
shared.distanceInPercent=Abstand vom Marktpreis in %
shared.belowInPercent=% unter dem Marktpreis
shared.aboveInPercent=% über dem Marktpreis
shared.enterPercentageValue=%-Wert eingeben
@ -191,7 +191,7 @@ shared.tradeWalletBalance=Guthaben der Handels-Wallet
shared.makerTxFee=Ersteller: {0}
shared.takerTxFee=Abnehmer: {0}
shared.iConfirm=Ich bestätige
shared.tradingFeeInBsqInfo=gleichwertig mit {0} als Trading-Gebühr
shared.tradingFeeInBsqInfo=≈ {0}
shared.openURL=Öffne {0}
shared.fiat=Fiat
shared.crypto=Crypto
@ -218,6 +218,9 @@ shared.refundAgentForSupportStaff=Rückerstattungsbeauftragten
shared.delayedPayoutTxId=Verzögerte Auszahlungs-ID der Transaktion
shared.delayedPayoutTxReceiverAddress=Verzögerte Auszahlungs-Transaktion gesendet zu
shared.unconfirmedTransactionsLimitReached=Sie haben im Moment zu viele unbestätigte Transaktionen. Bitte versuchen Sie es später noch einmal.
shared.numItemsLabel=Number of entries: {0}
shared.filter=Filter
shared.enabled=Aktiviert
####################################################################
@ -248,14 +251,14 @@ mainView.balance.locked=In Trades gesperrt
mainView.balance.reserved.short=Reserviert
mainView.balance.locked.short=Gesperrt
mainView.footer.usingTor=(nutzt Tor)
mainView.footer.usingTor=(über Tor)
mainView.footer.localhostBitcoinNode=(localhost)
mainView.footer.btcInfo={0} {1} {2}
mainView.footer.btcInfo={0} {1}
mainView.footer.btcFeeRate=/ Aktuelle Gebühr: {0} sat/vB
mainView.footer.btcInfo.initializing=Verbindung mit Bitcoin-Netzwerk wird hergestellt
mainView.footer.bsqInfo.synchronizing=/ Synchronisiere DAO
mainView.footer.btcInfo.synchronizingWith=Synchronisiere mit
mainView.footer.btcInfo.synchronizedWith=Synchronisiert mit
mainView.footer.btcInfo.synchronizingWith=Synchronisierung mit {0} bei Block: {1} / {2}
mainView.footer.btcInfo.synchronizedWith=Synchronisierung mit {0} bei Block {1}
mainView.footer.btcInfo.connectingTo=Verbinde mit
mainView.footer.btcInfo.connectionFailed=Verbindung fehlgeschlagen zu
mainView.footer.p2pInfo=Bitcoin Netzwerk Peers: {0} / Bisq Netzwerk Peers: {1}
@ -336,10 +339,10 @@ offerbook.offerersAcceptedBankSeats=Als Banksitz akzeptierte Länder (Abnehmer):
offerbook.availableOffers=Verfügbare Angebote
offerbook.filterByCurrency=Nach Währung filtern
offerbook.filterByPaymentMethod=Nach Zahlungsmethode filtern
offerbook.timeSinceSigning=Unterzeichnet seit
offerbook.timeSinceSigning=Konto Informationen
offerbook.timeSinceSigning.info=Dieses Konto wurde verifiziert und {0}
offerbook.timeSinceSigning.info.arbitrator=von einem Vermittler unterzeichnet und kann Partner-Konten unterzeichnen
offerbook.timeSinceSigning.info.peer=von einem Partner unterzeichnet, der darauf wartet, dass die Limits aufgehoben werden
offerbook.timeSinceSigning.info.peer=von einem Peer unterzeichnet, der %d Tage wartet bis die Limits aufgehoben werden
offerbook.timeSinceSigning.info.peerLimitLifted=von einem Partner unterzeichnet und Limits wurden aufgehoben
offerbook.timeSinceSigning.info.signer=vom Partner unterzeichnet und kann Partner-Konten unterzeichnen (Limits aufgehoben)
offerbook.timeSinceSigning.info.banned=Konto wurde geblockt
@ -349,9 +352,12 @@ offerbook.xmrAutoConf=Automatische Bestätigung aktiviert
offerbook.timeSinceSigning.help=Wenn Sie einen Trade mit einem Partner erfolgreich abschließen, der ein unterzeichnetes Zahlungskonto hat, wird Ihr Zahlungskonto unterzeichnet.\n{0} Tage später wird das anfängliche Limit von {1} aufgehoben und Ihr Konto kann die Zahlungskonten anderer Partner unterzeichnen.
offerbook.timeSinceSigning.notSigned=Noch nicht unterzeichnet
offerbook.timeSinceSigning.notSigned.ageDays={0} Tage
offerbook.timeSinceSigning.notSigned.noNeed=N/A
shared.notSigned=DIeses Konto wurde noch nicht unterzeichnet
shared.notSigned.noNeed=Dieser Kontotyp verwendet keine Unterzeichnung
shared.notSigned=Dieses Konto wurde noch nicht unterzeichnet und wurde {0} Tage zuvor erstellt
shared.notSigned.noNeed=Dieser Kontotyp benötigt keine Unterzeichnung
shared.notSigned.noNeedDays=Dieser Kontotyp benötigt keine Unterzeichnung und wurde {0} zuvor unterzeichnet
shared.notSigned.noNeedAlts=Altcoin Konten benötigen weisen keine Unterzeichnung und kein Konto-Alter auf.
offerbook.nrOffers=Anzahl der Angebote: {0}
offerbook.volume={0} (min - max)
@ -435,11 +441,15 @@ createOffer.warning.sellBelowMarketPrice=Sie erhalten immer {0}% weniger als der
createOffer.warning.buyAboveMarketPrice=Sie zahlen immer {0}% mehr als der aktuelle Marktpreis, da ihr Angebot ständig aktualisiert wird.
createOffer.tradeFee.descriptionBTCOnly=Handelsgebühr
createOffer.tradeFee.descriptionBSQEnabled=Gebührenwährung festlegen
createOffer.tradeFee.fiatAndPercent=≈ {0} / {1} vom Handelsbetrag
createOffer.triggerPrice.prompt=Set optional trigger price
createOffer.triggerPrice.label=Deactivate offer if market price is {0}
createOffer.triggerPrice.tooltip=As protecting against drastic price movements you can set a trigger price which deactivates the offer if the market price reaches that value.
createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0}
createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries
createOffer.placeOfferButton=Überprüfung: Anbieten Bitcoins zu {0}
createOffer.alreadyFunded=Sie hatten das Angebot bereits finanziert.\nIhre Gelder wurden in Ihre lokale Bisq-Wallet verschoben und können unter \"Gelder/Gelder senden\" abgehoben werden.
createOffer.createOfferFundWalletInfo.headline=Ihr Angebot finanzieren
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Handelsbetrag: {0} \n
@ -496,7 +506,6 @@ takeOffer.error.message=Bei der Angebotsannahme trat ein Fehler auf.\n\n{0}
# new entries
takeOffer.takeOfferButton=Überprüfung: Angebot annehmen Bitcoins zu {0}
takeOffer.noPriceFeedAvailable=Sie können dieses Angebot nicht annehmen, da es auf einem Prozentsatz vom Marktpreis basiert, jedoch keiner verfügbar ist.
takeOffer.alreadyFunded.movedFunds=Sie haben das Angebot bereits finanziert.\nIhre Gelder wurden in Ihre lokale Bisq-Wallet verschoben und können unter \"Gelder/Gelder senden\" abgehoben werden.
takeOffer.takeOfferFundWalletInfo.headline=Ihren Handel finanzieren
# suppress inspection "TrailingSpacesInProperty"
takeOffer.takeOfferFundWalletInfo.tradeAmount=- Handelsbetrag: {0}\n
@ -524,6 +533,10 @@ takeOffer.tac=Mit der Annahme dieses Angebots stimme ich den oben festgelegten H
# Offerbook / Edit offer
####################################################################
openOffer.header.triggerPrice=Triggerpreis
openOffer.triggerPrice=Trigger price {0}
openOffer.triggered=The offer has been deactivated because the market price reached your trigger price.\nPlease edit the offer to define a new trigger price
editOffer.setPrice=Preis festlegen
editOffer.confirmEdit=Bestätigen: Angebot bearbeiten
editOffer.publishOffer=Ihr Angebot wird veröffentlicht.
@ -541,6 +554,8 @@ portfolio.tab.history=Verlauf
portfolio.tab.failed=Fehlgeschlagen
portfolio.tab.editOpenOffer=Angebot bearbeiten
portfolio.closedTrades.deviation.help=Prozentuale Preisabweichung vom Markt
portfolio.pending.invalidDelayedPayoutTx=Es gibt ein Problem mit einer fehlenden oder ungültigen Transaktion.\n\nBitte senden Sie KEINE Fiat oder Altcoin Zahlung. Kontaktieren Sie Bisq Entwickler auf Keybase [HYPERLINK:https://keybase.io/team/bisq] oder im Forum [HYPERLINK:https://bisq.community] for further assistance.\n\nFehlermeldung: {0}
portfolio.pending.step1.waitForConf=Auf Blockchain-Bestätigung warten
@ -886,13 +901,12 @@ funds.tx.noTxAvailable=Keine Transaktionen verfügbar
funds.tx.revert=Umkehren
funds.tx.txSent=Transaktion erfolgreich zu einer neuen Adresse in der lokalen Bisq-Wallet gesendet.
funds.tx.direction.self=An Sie selbst senden
funds.tx.daoTxFee=Mining-Gebühr für DAO Tx
funds.tx.daoTxFee=Mining-Gebühr für BSQ-Tx
funds.tx.reimbursementRequestTxFee=Rückerstattungsantrag
funds.tx.compensationRequestTxFee=Entlohnungsanfrage
funds.tx.dustAttackTx=Staub erhalten
funds.tx.dustAttackTx.popup=Diese Transaktion sendet einen sehr kleinen BTC Betrag an Ihre Wallet und kann von Chainanalyse Unternehmen genutzt werden um ihre Wallet zu spionieren.\n\nWenn Sie den Transaktionsausgabe in einer Ausgabe nutzen, wird es lernen, dass Sie wahrscheinlich auch Besitzer der anderen Adressen sind (coin merge),\n\nUm Ihre Privatsphäre zu schützen, wir die Bisqwallet Staubausgaben für Ausgaben und bei der Anzeige der Guthabens ignorieren. Sie können den Grenzwert, ab wann ein Wert als Staub angesehen wird in den Einstellungen ändern.
####################################################################
# Support
####################################################################
@ -943,6 +957,7 @@ support.error=Empfänger konnte die Nachricht nicht verarbeiten. Fehler: {0}
support.buyerAddress=BTC-Adresse des Käufers
support.sellerAddress=BTC-Adresse des Verkäufers
support.role=Rolle
support.agent=Support agent
support.state=Status
support.closed=Geschlossen
support.open=Offen
@ -1052,6 +1067,7 @@ settings.net.creationDateColumn=Eingerichtet
settings.net.connectionTypeColumn=Ein/Aus
settings.net.sentDataLabel=Daten-Statistiken senden
settings.net.receivedDataLabel=Daten-Statistiken empfangen
settings.net.chainHeightLabel=Letzte BTC Blockzeit
settings.net.roundTripTimeColumn=Umlaufzeit
settings.net.sentBytesColumn=Gesendet
settings.net.receivedBytesColumn=Erhalten
@ -1066,6 +1082,7 @@ settings.net.needRestart=Sie müssen die Anwendung neustarten, um die Änderunge
settings.net.notKnownYet=Noch nicht bekannt...
settings.net.sentData=Gesendete Daten: {0}, {1} Nachrichten, {2} Nachrichten/Sekunde
settings.net.receivedData=Empfangene Daten: {0}, {1} Nachrichten, {2} Nachrichten/Sekunde
settings.net.chainHeight=Bisq: {0} | Peers: {1}
settings.net.ips=[IP Adresse:Port | Hostname:Port | Onion-Adresse:Port] (Komma getrennt). Port kann weggelassen werden, wenn Standard genutzt wird (8333).
settings.net.seedNode=Seed-Knoten
settings.net.directPeer=Peer (direkt)
@ -1074,7 +1091,7 @@ settings.net.inbound=eingehend
settings.net.outbound=ausgehend
settings.net.reSyncSPVChainLabel=SPV-Kette neu synchronisieren
settings.net.reSyncSPVChainButton=SPV-Datei löschen und neu synchronisieren
settings.net.reSyncSPVSuccess=Die SPV-Chain-Datei wird beim nächsten Start gelöscht. Sie müssen Ihre Anwendung jetzt neu starten.\n\nNach dem Neustart kann es eine Weile dauern, bis die Neusynchronisierung mit dem Netzwerk abgeschlossen ist, und Sie sehen erst nach Abschluss der Neusynchronisierung alle Transaktionen.\n\nAbhängig von der Anzahl der Transaktionen und dem Alter Ihrer Wallet kann die Resynchronisation bis zu einigen Stunden dauern und verbraucht 100% der CPU. Unterbrechen Sie den Prozess nicht, da Sie ihn sonst wiederholen müssen.
settings.net.reSyncSPVSuccess=Sind Sie sicher, dass Sie den SPV Resync starten möchten? Wenn Sie fortfahren, wird die SPV chain beim nächsten Start gelöscht.\n\nNach dem Restart kann der Resync des Netzwerks etwas Zeit in Anspruch nehmen und Sie werden die Transaktionen erst sehen wenn der Resync vollständig durchgeführt wurde.\n\nAbhängig von der Anzahl an Transaktionen und dem Alter Ihrer Wallet kann der Resync mehrere Stunden dauern und 100% der CPU Power beanspruchen. Unterbrechen Sie den Resync nicht, ansonsten müssen Sie ihn wiederholen.
settings.net.reSyncSPVAfterRestart=Die SPV-Kettendatei wurde gelöscht. Haben Sie bitte Geduld, es kann eine Weile dauern mit dem Netzwerk neu zu synchronisieren.
settings.net.reSyncSPVAfterRestartCompleted=Die erneute Synchronisation ist jetzt abgeschlossen. Bitte starten Sie die Anwendung neu.
settings.net.reSyncSPVFailed=Konnte SPV-Kettendatei nicht löschen.\nFehler: {0}
@ -1161,9 +1178,19 @@ account.menu.paymentAccount=Nationale Währungskonten
account.menu.altCoinsAccountView=Altcoin-Konten
account.menu.password=Wallet-Passwort
account.menu.seedWords=Wallet-Seed
account.menu.walletInfo=Wallet info
account.menu.backup=Backup
account.menu.notifications=Benachrichtigungen
account.menu.walletInfo.balance.headLine=Wallet balances
account.menu.walletInfo.balance.info=This shows the internal wallet balance including unconfirmed transactions.\nFor BTC, the internal wallet balance shown below should match the sum of the 'Available' and 'Reserved' balances shown in the top right of this window.
account.menu.walletInfo.xpub.headLine=Watch keys (xpub keys)
account.menu.walletInfo.walletSelector={0} {1} wallet
account.menu.walletInfo.path.headLine=HD keychain paths
account.menu.walletInfo.path.info=If you import seed words into another wallet (like Electrum), you'll need to define the path. This should only be done in emergency cases when you lose access to the Bisq wallet and data directory.\nKeep in mind that spending funds from a non-Bisq wallet can bungle the internal Bisq data structures associated with the wallet data, which can lead to failed trades.\n\nNEVER send BSQ from a non-Bisq wallet, as it will probably lead to an invalid BSQ transaction and losing your BSQ.
account.menu.walletInfo.openDetails=Show raw wallet details and private keys
## TODO should we rename the following to a gereric name?
account.arbitratorRegistration.pubKey=Öffentlicher Schlüssel
@ -1796,7 +1823,7 @@ dao.wallet.send.setDestinationAddress=Tragen Sie Ihre Zieladresse ein
dao.wallet.send.send=BSQ-Gelder senden
dao.wallet.send.sendBtc=BTC-Gelder senden
dao.wallet.send.sendFunds.headline=Abhebeanfrage bestätigen
dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequired transaction fee is: {2} ({3} satoshis/vbyte)\nTransaction vsize: {4} vKb\n\nThe recipient will receive: {5}\n\nAre you sure you want to withdraw that amount?
dao.wallet.send.sendFunds.details=Senden: {0}\nEmpfangsadresse: {1}.\nBenötigte Mining-Gebühr beträgt: {2} ({3} satoshis/vbyte)\nTransaktion vsize: {4} vKb\n\nDer empfänger wird erhalten: {5}\n\nSind Sie sich sicher die Menge abzuheben?
dao.wallet.chainHeightSynced=Synchronisiert bis Block: {0}
dao.wallet.chainHeightSyncing=Erwarte Blöcke... {0} von {1} Blöcken verifiziert
dao.wallet.tx.type=Typ
@ -1854,9 +1881,9 @@ dao.proposal.create.missingMinerFeeFunds=Du hast nicht ausreichend BTC, um die V
dao.proposal.create.missingIssuanceFunds=Sie haben nicht ausreichend BTC, um die Vorschlags-Transaktion zu erstellen. Jede BSQ-Transaktion benötigt eine Mining-Gebühr in BTC, Ausgabetransaktionen brauchen auch BTC für den angefragten BSQ Betrag ({0} Satoshis/BSQ).\nEs fehlen: {1}
dao.feeTx.confirm=Bestätige {0} Transaktion
dao.feeTx.confirm.details={0} fee: {1}\nMining fee: {2} ({3} Satoshis/vbyte)\nTransaction vsize: {4} vKb\n\nAre you sure you want to publish the {5} transaction?
dao.feeTx.confirm.details={0} Gebühr: {1} \nMining-Gebühr: {2} ({3} Satoshis/vByte)\nTransaktionsgröße: {4} Kb\n\nSind Sie sicher, dass Sie die {5} Transaktion senden wollen?
dao.feeTx.issuanceProposal.confirm.details={0} fee: {1}\nBTC needed for BSQ issuance: {2} ({3} Satoshis/BSQ)\nMining fee: {4} ({5} Satoshis/vbyte)\nTransaction vsize: {6} vKb\n\nIf your request is approved, you will receive the amount you requested net of the 2 BSQ proposal fee.\n\nAre you sure you want to publish the {7} transaction?
dao.feeTx.issuanceProposal.confirm.details={0} Gebühr: {1}\nBenötigte BTC für die BSQ Ausgabe: {2} ({3} Satoshis/BSQ)\nMining-Gebühr: {4} ({5} Satoshis/Byte)\nTransaktionsgröße: {6} Kb\n\nFalls Ihre Anfrage angenommen wird, erhalten Sie den angefragten Betrag minus die 2 BSQ Antragsgebühr.\n\nSind Sie sicher, dass Sie die {7} Transaktion veröffentlichen wollen?
dao.news.bisqDAO.title=DER BISQ DAO
dao.news.bisqDAO.description=Genauso wie der Bisq Handelsplatz dezentral und resistent gegen Zensur ist, ist auch die Führung der DAO - die Bisq DAO und der BSQ Token machen es möglich.
@ -2000,7 +2027,7 @@ disputeSummaryWindow.openDate=Erstellungsdatum des Tickets
disputeSummaryWindow.role=Rolle des Händlers
disputeSummaryWindow.payout=Auszahlung des Handelsbetrags
disputeSummaryWindow.payout.getsTradeAmount=Der BTC-{0} erhält die Auszahlung des Handelsbetrags
disputeSummaryWindow.payout.getsAll=Der BTC-{0} erhält alles
disputeSummaryWindow.payout.getsAll=Menge in BTC zu {0}
disputeSummaryWindow.payout.custom=Spezifische Auszahlung
disputeSummaryWindow.payoutAmount.buyer=Auszahlungsbetrag des Käufers
disputeSummaryWindow.payoutAmount.seller=Auszahlungsbetrag des Verkäufers
@ -2052,7 +2079,7 @@ disputeSummaryWindow.close.txDetails.headline=Rückerstattungstransaktion veröf
disputeSummaryWindow.close.txDetails.buyer=Käufer erhält {0} an Adresse: {1}\n
# suppress inspection "TrailingSpacesInProperty"
disputeSummaryWindow.close.txDetails.seller=Verkäufer erhält {0} an Adresse: {1}\n
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3} ({4} satoshis/vbyte)\nTransaction vsize: {5} vKb\n\nAre you sure you want to publish this transaction?
disputeSummaryWindow.close.txDetails=Ausgaben: {0}\n{1}{2}Transaktionsgebühr: {3} ({4} Satoshis/Byte)\nTransaktionsgröße: {5} Kb\n\nSind Sie sicher, dass Sie diese Transaktion veröffentlichen möchten?
disputeSummaryWindow.close.noPayout.headline=Ohne Auszahlung schließen
disputeSummaryWindow.close.noPayout.text=Wollen Sie schließen ohne eine Auszahlung zu tätigen?
@ -2087,7 +2114,7 @@ filterWindow.btcNode=Gefilterte Bitcoinknoten (Komma getr. Adresse + Port)
filterWindow.preventPublicBtcNetwork=Nutzung des öffentlichen Bitcoin-Netzwerks verhindern
filterWindow.disableDao=DAO deaktivieren
filterWindow.disableAutoConf=Automatische Bestätigung deaktivieren
filterWindow.autoConfExplorers=Filtered auto-confirm explorers (comma sep. addresses)
filterWindow.autoConfExplorers=Gefilterter Explorer mit Auto-Bestätigung (Adressen mit Komma separiert)
filterWindow.disableDaoBelowVersion=Min. für DAO erforderliche Version
filterWindow.disableTradeBelowVersion=Min. zum Handeln erforderliche Version
filterWindow.add=Filter hinzufügen
@ -2154,6 +2181,7 @@ tradeDetailsWindow.tradingPeersOnion=Onion-Adresse des Handelspartners
tradeDetailsWindow.tradingPeersPubKeyHash=Trading Peers Pubkey Hash
tradeDetailsWindow.tradeState=Handelsstatus
tradeDetailsWindow.agentAddresses=Vermittler/Mediator
tradeDetailsWindow.detailData=Detaillierte Daten
walletPasswordWindow.headline=Passwort zum Entsperren eingeben
@ -2183,6 +2211,8 @@ feeOptionWindow.info=Sie können wählen, die Gebühr in BSQ oder BTC zu zahlen.
feeOptionWindow.optionsLabel=Währung für Handelsgebührzahlung auswählen
feeOptionWindow.useBTC=BTC nutzen
feeOptionWindow.fee={0} (≈ {1})
feeOptionWindow.btcFeeWithFiatAndPercentage={0} (≈ {1} / {2})
feeOptionWindow.btcFeeWithPercentage={0} ({1})
####################################################################
@ -2226,6 +2256,7 @@ popup.warning.noMediatorsAvailable=Es sind keine Mediatoren verfügbar.
popup.warning.notFullyConnected=Sie müssen warten, bis Sie vollständig mit dem Netzwerk verbunden sind.\nDas kann bis ungefähr 2 Minuten nach dem Start dauern.
popup.warning.notSufficientConnectionsToBtcNetwork=Sie müssen warten, bis Sie wenigstens {0} Verbindungen zum Bitcoinnetzwerk haben.
popup.warning.downloadNotComplete=Sie müssen warten bis der Download der fehlenden Bitcoinblöcke abgeschlossen ist.
popup.warning.chainNotSynced=Die Blockchain Größe der Bisq Wallet ist nicht korrekt synchronisiert. Wenn Sie kürzlich die Applikation geöffnet haben, warten Sie bitte bis ein Bitcoin Block veröffentlicht wurde.\n\nSie können die Blockchain Größe unter Einstellungen/Netzwerkinformationen finden. Wenn mehr als ein Block veröffentlicht wird und das Problem weiterhin bestehen sollte, wird es eventuell abgewürgt werden. Dann sollten Sie einen SPV Resync durchführen. [HYPERLINK:https://bisq.wiki/Resyncing_SPV_file]
popup.warning.removeOffer=Sind Sie sicher, dass Sie das Angebot entfernen wollen?\nDie Erstellergebühr von {0} geht verloren, wenn Sie des Angebot entfernen.
popup.warning.tooLargePercentageValue=Es kann kein Prozentsatz von 100% oder mehr verwendet werden.
popup.warning.examplePercentageValue=Bitte geben sei einen Prozentsatz wie folgt ein \"5.4\" für 5.4%
@ -2254,7 +2285,7 @@ popup.warning.openOffer.makerFeeTxRejected=Die Verkäufergebühren-Transaktion f
popup.warning.trade.txRejected.tradeFee=Trade-Gebühr
popup.warning.trade.txRejected.deposit=Kaution
popup.warning.trade.txRejected=Die {0} Transaktion für den Trade mit der ID {1} wurde vom Bitcoin-Netzwerk abgelehnt.\nTransaktions-ID={2}}\nDer Trade wurde in gescheiterte Trades verschoben.\nBitte gehen Sie zu \"Einstellungen/Netzwerkinformationen\" und führen Sie eine SPV-Resynchronisierung durch.\nFür weitere Hilfe wenden Sie sich bitte an den Bisq-Support-Kanal des Bisq Keybase Teams.
popup.warning.trade.txRejected=Die {0} Transaktion für den Trade mit der ID {1} wurde vom Bitcoin-Netzwerk abgelehnt.\nTransaktions-ID={2}}\nDer Trade wurde in gescheiterte Trades verschoben.\nBitte gehen Sie zu \"Einstellungen/Netzwerkinformationen\" und führen Sie einen SPV Resync durch.\nFür weitere Hilfe wenden Sie sich bitte an den Bisq-Support-Kanal des Bisq Keybase Teams.
popup.warning.openOfferWithInvalidMakerFeeTx=Die Verkäufergebühren-Transaktion für das Angebot mit der ID {0} ist ungültig.\nTransaktions-ID={1}.\nBitte gehen Sie zu \"Einstellungen/Netzwerkinformationen\" und führen Sie eine SPV-Resynchronisierung durch.\nFür weitere Hilfe wenden Sie sich bitte an den Bisq-Support-Kanal des Bisq Keybase Teams.
@ -2264,14 +2295,15 @@ popup.info.cashDepositInfo=Stellen Sie sicher, dass eine Bank-Filiale in Ihrer N
popup.info.cashDepositInfo.confirm=Ich bestätige, dass ich die Kaution zahlen kann
popup.info.shutDownWithOpenOffers=Bisq wird heruntergefahren, aber Sie haben offene Angebote verfügbar.\n\nDiese Angebote sind nach dem Herunterfahren nicht mehr verfügbar und werden erneut im P2P-Netzwerk veröffentlicht wenn Sie das nächste Mal Bisq starten.\n\nLassen Sie Bisq weiter laufen und stellen Sie sicher, dass Ihr Computer online bleibt, um Ihre Angebote verfügbar zu halten (z.B.: verhindern Sie den Standby-Modus... der Standby-Modus des Monitors stellt kein Problem dar).
popup.info.qubesOSSetupInfo=Es scheint so als ob Sie Bisq auf Qubes OS laufen haben.\n\nBitte stellen Sie sicher, dass Bisq qube nach unserem Setup Guide eingerichtet wurde: [HYPERLINK:https://bisq.wiki/Running_Bisq_on_Qubes].
popup.warn.downGradePrevention=Downgrade von Version {0} auf Version {1} wird nicht unterstützt. Bitte nutzen Sie die aktuelle Bisq Version.
popup.warn.daoRequiresRestart=There was a problem with synchronizing the DAO state. You have to restart the application to fix the issue.
popup.privateNotification.headline=Wichtige private Benachrichtigung!
popup.securityRecommendation.headline=Wichtige Sicherheitsempfehlung
popup.securityRecommendation.msg=Wir würden Sie gerne daran erinnern, sich zu überlegen, den Passwortschutz Ihrer Wallet zu verwenden, falls Sie diesen noch nicht aktiviert haben.\n\nEs wird außerdem dringend empfohlen, dass Sie die Wallet-Seed-Wörter aufschreiben. Diese Seed-Wörter sind wie ein Master-Passwort zum Wiederherstellen ihrer Bitcoin-Wallet.\nIm \"Wallet-Seed\"-Abschnitt finden Sie weitere Informationen.\n\nZusätzlich sollten Sie ein Backup des ganzen Anwendungsdatenordners im \"Backup\"-Abschnitt erstellen.
popup.bitcoinLocalhostNode.msg=Bisq hat einen lokal laufenden Bitcoin-Core-Node entdeckt (bei localhost).\nStellen Sie bitte sicher, dass dieser Node vollständig synchronisiert ist, bevor Sie Bisq starten und dass er nicht im Pruned-Modus läuft.
popup.bitcoinLocalhostNode.additionalRequirements=\n\nFür einen gut konfigurierten Node sind die Anforderungen, dass das Pruning deaktiviert und die Bloom-Filter aktiviert sind.
popup.bitcoinLocalhostNode.msg=Bisq detected a Bitcoin Core node running on this machine (at localhost).\n\nPlease ensure:\n- the node is fully synced before starting Bisq\n- pruning is disabled ('prune=0' in bitcoin.conf)\n- bloom filters are enabled ('peerbloomfilters=1' in bitcoin.conf)
popup.shutDownInProgress.headline=Anwendung wird heruntergefahren
popup.shutDownInProgress.msg=Das Herunterfahren der Anwendung kann einige Sekunden dauern.\nBitte unterbrechen Sie diesen Vorgang nicht.
@ -2303,15 +2335,12 @@ popup.accountSigning.signedByPeer=Eines Ihrer Zahlungskonten wurde von einem Tra
popup.accountSigning.peerLimitLifted=Das anfängliche Limit für eines Ihrer Konten wurde aufgehoben.\n\n{0}
popup.accountSigning.peerSigner=Eines Ihrer Konten ist reif genug, um andere Zahlungskonten zu unterzeichnen, und das anfängliche Limit für eines Ihrer Konten wurde aufgehoben.\n\n{0}
popup.accountSigning.singleAccountSelect.headline=Zeuge für Konto-Alter auswählen
popup.accountSigning.singleAccountSelect.description=Nach Zeuge für Konto-Alter suchen.
popup.accountSigning.singleAccountSelect.datePicker=Zeitpunkt für Unterzeichnung auswählen
popup.accountSigning.singleAccountSelect.headline=Import unsigned account age witness
popup.accountSigning.confirmSingleAccount.headline=Ausgewählten Zeugen für Konto-Alter bestätigen
popup.accountSigning.confirmSingleAccount.selectedHash=Ausgewählter Zeugen-Hash
popup.accountSigning.confirmSingleAccount.button=Zeuge für Konto-Alter unterzeichnen
popup.accountSigning.successSingleAccount.description=Zeuge {0} wurde unterzeichnet
popup.accountSigning.successSingleAccount.success.headline=Erfolg
popup.accountSigning.successSingleAccount.signError=Zeuge nicht unterzeichnet (fehlgeschlagen), {0}
popup.accountSigning.unsignedPubKeys.headline=Nicht unterzeichnete Pubkeys
popup.accountSigning.unsignedPubKeys.sign=Pubkeys unterzeichnen
@ -2447,8 +2476,8 @@ navigation.dao.wallet.receive=\"DAO/BSQ-Wallet/Erhalten\"
formatter.formatVolumeLabel={0} Betrag{1}
formatter.makerTaker=Ersteller als {0} {1} / Abnehmer als {2} {3}
formatter.youAreAsMaker=Sie {0} {1} als Ersteller / Abnehmer wird {3} {2}
formatter.youAreAsTaker=Sie {0} {1} als Abnehmer / Ersteller wird {3} {2}
formatter.youAreAsMaker=You are: {1} {0} (maker) / Taker is: {3} {2}
formatter.youAreAsTaker=You are: {1} {0} (taker) / Maker is: {3} {2}
formatter.youAre=Sie {0} {1} ({2} {3})
formatter.youAreCreatingAnOffer.fiat=Sie erstellen ein Angebot um {1} zu {0}
formatter.youAreCreatingAnOffer.altcoin=Sie erstellen ein Angebot {1} zu {0} ({3} zu {2})
@ -2498,7 +2527,7 @@ password.deriveKey=Schlüssel aus Passwort ableiten
password.walletDecrypted=Die Wallet wurde erfolgreich entschlüsselt und der Passwortschutz entfernt.
password.wrongPw=Sie haben das falsche Passwort eingegeben.\n\nVersuchen Sie bitte Ihr Passwort erneut einzugeben, wobei Sie dies vorsichtig auf Tipp- und Rechtschreibfehler überprüfen sollten.
password.walletEncrypted=Die Wallet wurde erfolgreich verschlüsselt und der Passwortschutz aktiviert.
password.walletEncryptionFailed=Wallet password could not be set. You may have imported seed words which do not match the wallet database. Please contact the developers on Keybase ([HYPERLINK:https://keybase.io/team/bisq]).
password.walletEncryptionFailed=Wallet Passwort konnte nicht eingerichtet werden. Sie haben vielleicht Seed-Wörter importiert, die nicht mit der Wallet-Datenbank übereinstimmen. Bitte kontaktieren Sie die Entwickler auf Keybase ([HYPERLINK:https://keybase.io/team/bisq]).
password.passwordsDoNotMatch=Die 2 eingegebenen Passwörter stimmen nicht überein.
password.forgotPassword=Passwort vergessen?
password.backupReminder=Beachten Sie, dass wenn Sie ein Passwort setzen, alle automatisch erstellten Backups der unverschlüsselten Wallet gelöscht werden.\n\nEs wird dringend empfohlen ein Backup des Anwendungsverzeichnisses zu erstellen und die Seed-Wörter aufzuschreiben, bevor Sie ein Passwort erstellen!
@ -2561,6 +2590,7 @@ payment.venmo.venmoUserName=Venmo Nutzername
payment.popmoney.accountId=E-Mail oder Telefonnummer
payment.promptPay.promptPayId=Personalausweis/Steuernummer oder Telefonnr.
payment.supportedCurrencies=Unterstützte Währungen
payment.supportedCurrenciesForReceiver=Currencies for receiving funds
payment.limitations=Einschränkungen
payment.salt=Salt für Überprüfung des Kontoalters
payment.error.noHexSalt=Der Salt muss im HEX-Format sein.\nEs wird empfohlen das Salt-Feld zu bearbeiten, wenn Sie den Salt von einem alten Konto übertragen, um das Alter Ihres Kontos zu erhalten. Das Alter des Kontos wird durch den Konto-Salt und die Kontodaten (z.B. IBAN) verifiziert.
@ -2599,18 +2629,18 @@ payment.savings=Ersparnisse
payment.personalId=Personalausweis
payment.clearXchange.info=Zelle ist ein Geldtransferdienst, der am besten *durch* eine andere Bank funktioniert.\n\n1. Sehen Sie auf dieser Seite nach, ob (und wie) Ihre Bank mit Zelle zusammenarbeitet:\nhttps://www.zellepay.com/get-started\n\n2. Achten Sie besonders auf Ihre Überweisungslimits - die Sendelimits variieren je nach Bank, und die Banken geben oft separate Tages-, Wochen- und Monatslimits an.\n\n3. Wenn Ihre Bank nicht mit Zelle zusammenarbeitet, können Sie die Zahlungsmethode trotzdem über die Zelle Mobile App benutzen, aber Ihre Überweisungslimits werden viel niedriger sein.\n\n4. Der auf Ihrem Bisq-Konto angegebene Name MUSS mit dem Namen auf Ihrem Zelle/Bankkonto übereinstimmen. \n\nWenn Sie eine Zelle Transaktion nicht wie in Ihrem Handelsvertrag angegeben durchführen können, verlieren Sie möglicherweise einen Teil (oder die gesamte) Sicherheitskaution.\n\nWegen des etwas höheren Chargeback-Risikos von Zelle wird Verkäufern empfohlen, nicht unterzeichnete Käufer per E-Mail oder SMS zu kontaktieren, um zu überprüfen, ob der Käufer wirklich das in Bisq angegebene Zelle-Konto besitzt.
payment.fasterPayments.newRequirements.info=Einige Banken haben damit begonnen, den vollständigen Namen des Empfängers für Faster Payments Überweisungen zu überprüfen. Ihr aktuelles Faster Payments-Konto gibt keinen vollständigen Namen an.\n\nBitte erwägen Sie, Ihr Faster Payments-Konto in Bisq neu einzurichten, um zukünftigen {0} Käufern einen vollständigen Namen zu geben.\n\nWenn Sie das Konto neu erstellen, stellen Sie sicher, dass Sie die genaue Bankleitzahl, Kontonummer und die "Salt"-Werte für die Altersverifikation von Ihrem alten Konto auf Ihr neues Konto kopieren. Dadurch wird sichergestellt, dass das Alter und der Unterschriftsstatus Ihres bestehenden Kontos erhalten bleiben.
payment.moneyGram.info=When using MoneyGram the BTC buyer has to send the Authorisation number and a photo of the receipt by email to the BTC seller. The receipt must clearly show the seller's full name, country, state and the amount. The seller's email will be displayed to the buyer during the trade process.
payment.westernUnion.info=When using Western Union the BTC buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the BTC seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process.
payment.moneyGram.info=Bei der Nutzung von MoneyGram, muss der BTC Käufer die MoneyGram Zulassungsnummer und ein Foto der Quittung per E-Mail an den BTC-Verkäufer senden. Die Quittung muss den vollständigen Namen, das Land, das Bundesland des Verkäufers und den Betrag deutlich zeigen. Der Käufer bekommt die E-Mail-Adresse des Verkäufers im Handelsprozess angezeigt.
payment.westernUnion.info=Bei der Nutzung von Western Union, muss der BTC Käufer die MTCN (Tracking-Nummer) Foto der Quittung per E-Mail an den BTC-Verkäufer senden. Die Quittung muss den vollständigen Namen, das Land, die Stadt des Verkäufers und den Betrag deutlich zeigen. Der Käufer bekommt die E-Mail-Adresse des Verkäufers im Handelsprozess angezeigt.
payment.halCash.info=Bei Verwendung von HalCash muss der BTC-Käufer dem BTC-Verkäufer den HalCash-Code per SMS vom Mobiltelefon senden.\n\nBitte achten Sie darauf, dass Sie den maximalen Betrag, den Sie bei Ihrer Bank mit HalCash versenden dürfen, nicht überschreiten. Der Mindestbetrag pro Auszahlung beträgt 10 EUR und der Höchstbetrag 600 EUR. Bei wiederholten Abhebungen sind es 3000 EUR pro Empfänger pro Tag und 6000 EUR pro Empfänger pro Monat. Bitte überprüfen Sie diese Limits bei Ihrer Bank, um sicherzustellen, dass sie die gleichen Limits wie hier angegeben verwenden.\n\nDer Auszahlungsbetrag muss ein Vielfaches von 10 EUR betragen, da Sie keine anderen Beträge an einem Geldautomaten abheben können. Die Benutzeroberfläche beim Erstellen und Annehmen eines Angebots passt den BTC-Betrag so an, dass der EUR-Betrag korrekt ist. Sie können keinen marktbasierten Preis verwenden, da sich der EUR-Betrag bei sich ändernden Preisen ändern würde.\n\nIm Streitfall muss der BTC-Käufer den Nachweis erbringen, dass er die EUR geschickt hat.
# suppress inspection "UnusedMessageFormatParameter"
payment.limits.info=Please be aware that all bank transfers carry a certain amount of chargeback risk. To mitigate this risk, Bisq sets per-trade limits based on the estimated level of chargeback risk for the payment method used.\n\nFor this payment method, your per-trade limit for buying and selling is {2}.\n\nThis limit only applies to the size of a single trade—you can place as many trades as you like.\n\nSee more details on the wiki [HYPERLINK:https://bisq.wiki/Account_limits].
payment.limits.info=Bitte beachten Sie, dass alle Banküberweisungen mit einem gewissen Rückbuchungsrisiko verbunden sind. Um dieses Risiko zu mindern, setzt Bisq Limits pro Trade fest, je nachdem wie hoch das Rückbuchungsrisiko der Zahlungsmethode ist. \n\nFür diese Zahlungsmethode beträgt Ihr Pro-Trade-Limit zum Kaufen oder Verkaufen {2}.\nDieses Limit gilt nur für die Größe eines einzelnen Trades - Sie können soviele Trades platzieren wie Sie möchten.\n\nFinden Sie mehr Informationen im Wiki [HYPERLINK:https://bisq.wiki/Account_limits].
# suppress inspection "UnusedProperty"
payment.limits.info.withSigning=To limit chargeback risk, Bisq sets per-trade limits for this payment account type based on the following 2 factors:\n\n1. General chargeback risk for the payment method\n2. Account signing status\n\nThis payment account is not yet signed, so it is limited to buying {0} per trade. After signing, buy limits will increase as follows:\n\n● Before signing, and for 30 days after signing, your per-trade buy limit will be {0}\n● 30 days after signing, your per-trade buy limit will be {1}\n● 60 days after signing, your per-trade buy limit will be {2}\n\nSell limits are not affected by account signing. You can sell {2} in a single trade immediately.\n\nThese limits only apply to the size of a single trade—you can place as many trades as you like. \n\nSee more details on the wiki [HYPERLINK:https://bisq.wiki/Account_limits].
payment.limits.info.withSigning=Um das Risiko einer Rückbuchung zu minimieren, setzt Bisq für diese Zahlungsmethode Limits pro Trade auf der Grundlage der folgenden 2 Faktoren fest:\n\n1. Allgemeines Rückbuchungsrisiko für die Zahlungsmethode\n2. Status der Kontounterzeichnung\n\nDieses Zahlungskonto ist noch nicht unterzeichnet. Es ist daher auf den Kauf von {0} pro Trade beschränkt ist. Nach der Unterzeichnung werden die Kauflimits wie folgt erhöht:\n\n● Vor der Unterzeichnung und für 30 Tage nach der Unterzeichnung beträgt Ihr Kauflimit pro Trade {0}\n● 30 Tage nach der Unterzeichnung beträgt Ihr Kauflimit pro Trade {1}\n● 60 Tage nach der Unterzeichnung beträgt Ihr Kauflimit pro Trade {2}\n\nVerkaufslimits sind von der Kontounterzeichnung nicht betroffen. Sie können {2} in einem einzigen Trade sofort verkaufen.\n\nDieses Limit gilt nur für die Größe eines einzelnen Trades - Sie können soviele Trades platzieren wie sie möchten.\n\nWeitere Informationen gibt es im Wiki [HYPERLINK:https://bisq.wiki/Account_limits].
payment.cashDeposit.info=Bitte bestätigen Sie, dass Ihre Bank Bareinzahlungen in Konten von anderen Personen erlaubt. Zum Beispiel werden diese Einzahlungen bei der Bank of America und Wells Fargo nicht mehr erlaubt.
payment.revolut.info=Revolut benötigt den "Benutzernamen" als Account ID und nicht die Telefonnummer oder E-Mail, wie es in der Vergangenheit war.
payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a ''User name''.\nPlease enter your Revolut ''User name'' to update your account data.\nThis will not affect your account age signing status.
payment.account.revolut.addUserNameInfo={0}\nDein existierendes Revolut Konto ({1}) hat keinen "Benutzernamen".\nBitte geben Sie Ihren Revolut "Benutzernamen" ein um Ihre Kontodaten zu aktualisieren.\nDas wird Ihr Kontoalter und die Verifizierung nicht beeinflussen.
payment.revolut.addUserNameInfo.headLine=Revolut Account updaten
payment.usPostalMoneyOrder.info=Der Handel auf Bisq unter Verwendung eines US Postal Money Orders (USPMO) setzt voraus, dass Sie Folgendes verstehen:\n\n- Der BTC-Käufer muss den Namen des BTC-Verkäufers sowohl in das Feld des Zahlers als auch in das Feld des Zahlungsempfängers eintragen und vor dem Versand ein hochauflösendes Foto des USPMO und des Umschlags mit dem Tracking-Nachweis machen.\n- Der BTC-Käufer muss den USPMO zusammen mit der Lieferbestätigung an den BTC-Verkäufer schicken.\n\nFür den Fall, dass eine Mediation erforderlich ist oder es zu einem Handelskonflikt kommt, müssen Sie die Fotos zusammen mit der USPMO-Seriennummer, der Nummer des Postamtes und dem Dollarbetrag an den Bisq-Mediator oder Erstattungsagenten schicken, damit dieser die Angaben auf der Website des US-Postamtes überprüfen kann.\n\nWenn Sie dem Mediator oder Vermittler die erforderlichen Informationen nicht zur Verfügung stellen, führt dies dazu, dass der Konflikt zu Ihrem Nachteil entschieden wird.\n\nIn allen Konfliktfällen trägt der USPMO-Absender 100 % der Verantwortung für die Bereitstellung von Beweisen/Nachweisen für den Mediator oder Vermittler.\n\nWenn Sie diese Anforderungen nicht verstehen, handeln Sie bitte nicht auf Bisq unter Verwendung eines USPMO.
@ -2623,7 +2653,7 @@ payment.f2f.optionalExtra=Freiwillige zusätzliche Informationen
payment.f2f.extra=Zusätzliche Informationen
payment.f2f.extra.prompt=Der Ersteller kann "Geschäftsbedingungen" festlegen oder öffentliche Kontaktinformationen hinterlegen. Diese werden mit dem Angebot angezeigt.
payment.f2f.info='Face to Face' trades have different rules and come with different risks than online transactions.\n\nThe main differences are:\n● The trading peers need to exchange information about the meeting location and time by using their provided contact details.\n● The trading peers need to bring their laptops and do the confirmation of 'payment sent' and 'payment received' at the meeting place.\n● If a maker has special 'terms and conditions' they must state those in the 'Additional information' text field in the account.\n● By taking an offer the taker agrees to the maker's stated 'terms and conditions'.\n● In case of a dispute the mediator or arbitrator cannot be of much assistance as it is usually difficult to get tamper-proof evidence of what happened at the meeting. In such cases the BTC funds might get locked indefinitely or until the trading peers come to an agreement.\n\nTo be sure you fully understand the differences with 'Face to Face' trades please read the instructions and recommendations at: [HYPERLINK:https://docs.bisq.network/trading-rules.html#f2f-trading]
payment.f2f.info=Persönliche 'Face to Face' Trades haben unterschiedliche Regeln und sind mit anderen Risiken verbunden als gewöhnliche Online-Trades.\n\nDie Hauptunterschiede sind:\n● Die Trading Partner müssen die Kontaktdaten und Informationen über den Ort und die Uhrzeit des Treffens austauschen.\n● Die Trading Partner müssen ihre Laptops mitbringen und die Bestätigung der "gesendeten Zahlung" und der "erhaltenen Zahlung" am Treffpunkt vornehmen.\n● Wenn ein Ersteller eines Angebots spezielle "Allgemeine Geschäftsbedingungen" hat, muss er diese im Textfeld "Zusatzinformationen" des Kontos angeben.\n● Mit der Annahme eines Angebots erklärt sich der Käufer mit den vom Anbieter angegebenen "Allgemeinen Geschäftsbedingungen" einverstanden.\n● Im Konfliktfall kann der Mediator oder Arbitrator nicht viel tun, da es in der Regel schwierig ist zu bestimmen, was beim Treffen passiert ist. In solchen Fällen können die Bitcoin auf unbestimmte Zeit oder bis zu einer Einigung der Trading Peers gesperrt werden.\n\nUm sicherzustellen, dass Sie die Besonderheiten der persönlichen 'Face to Face' Trades vollständig verstehen, lesen Sie bitte die Anweisungen und Empfehlungen unter: [HYPERLINK:https://docs.bisq.network/trading-rules.html#f2f-trading]
payment.f2f.info.openURL=Webseite öffnen
payment.f2f.offerbook.tooltip.countryAndCity=Land und Stadt: {0} / {1}
payment.f2f.offerbook.tooltip.extra=Zusätzliche Informationen: {0}
@ -2634,8 +2664,9 @@ payment.japan.account=Konto
payment.japan.recipient=Name
payment.australia.payid=PayID
payment.payid=PayIDs wie E-Mail Adressen oder Telefonnummern die mit Finanzinstitutionen verbunden sind.
payment.payid.info=A PayID like a phone number, email address or an Australian Business Number (ABN), that you can securely link to your bank, credit union or building society account. You need to have already created a PayID with your Australian financial institution. Both sending and receiving financial institutions must support PayID. For more information please check [HYPERLINK:https://payid.com.au/faqs/]
payment.amazonGiftCard.info=To pay with Amazon eGift Card you need to purchase an Amazon eGift Card at your Amazon account and use the BTC seller''s email or mobile nr. as receiver. Amazon sends then an email or text message to the receiver. Use the trade ID for the message field.\n\nAmazon eGift Cards can only be redeemed by Amazon accounts with the same currency.\n\nFor more information visit the Amazon eGift Card webpage. [HYPERLINK:https://www.amazon.com/Amazon-1_US_Email-eGift-Card/dp/B004LLIKVU]
payment.payid.info=Eine PayID wie eine Telefonnummer, E-Mail Adresse oder Australische Business Number (ABN) mit der Sie sicher Ihre Bank, Kreditgenossenschaft oder Bausparkassenkonto verlinken können. Sie müssen bereits eine PayID mit Ihrer Australischen Finanzinstitution erstellt haben. Beide Institutionen, die die sendet und die die empfängt, müssen PayID unterstützen. Weitere informationen finden Sie unter [HYPERLINK:https://payid.com.au/faqs/]
payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the BTC seller via your Amazon account. \n\nBisq will show the BTC seller''s email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card''s message field. Please see the wiki [HYPERLINK:https://bisq.wiki/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it)
# We use constants from the code so we do not use our normal naming convention
# dynamic values are not recognized by IntelliJ
@ -2852,7 +2883,7 @@ validation.numberFormatException=Zahlenformat Ausnahme {0}
validation.mustNotBeNegative=Eingabe darf nicht negativ sein
validation.phone.missingCountryCode=Es wird eine zweistellige Ländervorwahl benötigt, um die Telefonnummer zu bestätigen
validation.phone.invalidCharacters=Telefonnummer {0} enthält ungültige Zeichen
validation.phone.insufficientDigits=Das ist keine gültige Telefonnummer. Sie habe nicht genügend Stellen angegeben.
validation.phone.insufficientDigits=Das ist keine gültige Telefonnummer. Sie habe nicht genügend Stellen angegeben.
validation.phone.tooManyDigits=Es sind zu viele Ziffern in {0} um eine gültige Telefonnummer zu sein.
validation.phone.invalidDialingCode=Country dialing code for number {0} is invalid for country {1}. The correct dialing code is {2}.
validation.phone.invalidDialingCode=Die Ländervorwahl in der Nummer {0} ist für das Land {1} ungültig. Die richtige Vorwahl ist {2}.
validation.invalidAddressList=Muss eine kommagetrennte Liste der gültigen Adressen sein

View File

@ -71,6 +71,7 @@ shared.amountWithCur=Cantidad en {0}
shared.volumeWithCur=Volumen en {0}
shared.currency=Moneda
shared.market=Mercado
shared.deviation=Desviación
shared.paymentMethod=Método de pago
shared.tradeCurrency=Moneda de intercambio
shared.offerType=Tipo de oferta
@ -104,7 +105,6 @@ shared.selectTradingAccount=Selecionar cuenta de intercambio
shared.fundFromSavingsWalletButton=Transferir fondos desde la cartera Bisq
shared.fundFromExternalWalletButton=Abrir su monedero externo para agregar fondos
shared.openDefaultWalletFailed=Fallo al abrir la aplicación de cartera predeterminada. ¿Tal vez no tenga una instalada?
shared.distanceInPercent=Distancia en % del precio de mercado
shared.belowInPercent=% por debajo del precio de mercado
shared.aboveInPercent=% por encima del precio de mercado
shared.enterPercentageValue=Introduzca valor %
@ -123,7 +123,7 @@ shared.noDateAvailable=Sin fecha disponible
shared.noDetailsAvailable=Sin detalles disponibles
shared.notUsedYet=Sin usar aún
shared.date=Fecha
shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired mining fee is: {3} ({4} satoshis/vbyte)\nTransaction vsize: {5} vKb\n\nThe recipient will receive: {6}\n\nAre you sure you want to withdraw this amount?
shared.sendFundsDetailsWithFee=Enviando: {0}\nDesde la dirección: {1}\nA la dirección receptora: {2}.\nLa comisión requerida de transacción es: {3} ({4} Satoshis/vbyte)\nTamaño de la transacción: {5} vKb\n\nEl receptor recibirá: {6}\n\nSeguro que quiere retirar esta cantidad?
# suppress inspection "TrailingSpacesInProperty"
shared.sendFundsDetailsDust=Bisq detectó que esta transacción crearía una salida que está por debajo del umbral mínimo considerada polvo (y no está permitida por las reglas de consenso en Bitcoin). En cambio, esta transacción polvo ({0} satoshi {1}) se agregará a la tarifa de minería.\n\n\n
shared.copyToClipboard=Copiar al portapapeles
@ -191,7 +191,7 @@ shared.tradeWalletBalance=Saldo de la cartera de intercambio
shared.makerTxFee=Creador: {0}
shared.takerTxFee=Tomador: {0}
shared.iConfirm=Confirmo
shared.tradingFeeInBsqInfo=equivalente a {0} usado como tasa de intercambio
shared.tradingFeeInBsqInfo=≈ {0}
shared.openURL=Abrir {0}
shared.fiat=Fiat
shared.crypto=Cripto
@ -218,6 +218,9 @@ shared.refundAgentForSupportStaff=Agente de devolución de fondos
shared.delayedPayoutTxId=ID de transacción del pago demorado
shared.delayedPayoutTxReceiverAddress=Transacción de pago demorado enviada a
shared.unconfirmedTransactionsLimitReached=Tiene demasiadas transacciones no confirmadas en este momento. Por favor, inténtelo de nuevo más tarde.
shared.numItemsLabel=Número de entradas: {0}
shared.filter=Filtro
shared.enabled=Habilitado
####################################################################
@ -248,14 +251,14 @@ mainView.balance.locked=Bloqueado en intercambios
mainView.balance.reserved.short=Reservado
mainView.balance.locked.short=Bloqueado
mainView.footer.usingTor=(usando Tor)
mainView.footer.usingTor=(via Tor)
mainView.footer.localhostBitcoinNode=(localhost)
mainView.footer.btcInfo={0} {1} {2}
mainView.footer.btcFeeRate=/ Tasas actuales: {0} sat/vB
mainView.footer.btcInfo={0} {1}
mainView.footer.btcFeeRate=/Tasas actuales: {0} sat/vB
mainView.footer.btcInfo.initializing=Conectando a la red Bitcoin
mainView.footer.bsqInfo.synchronizing=/ Sincronizando DAO
mainView.footer.btcInfo.synchronizingWith=Sincronizando con
mainView.footer.btcInfo.synchronizedWith=Sincronizado con
mainView.footer.btcInfo.synchronizingWith=Sincronizando con {0} en el bloque: {1} / {2}
mainView.footer.btcInfo.synchronizedWith=Sincronizado con {0} en el bloque {1}
mainView.footer.btcInfo.connectingTo=Conectando a
mainView.footer.btcInfo.connectionFailed=Conexión fallida a
mainView.footer.p2pInfo=Pares de Bitcoin: {0} / Pares de la red de Bisq: {1}
@ -336,10 +339,10 @@ offerbook.offerersAcceptedBankSeats=Países de sede de banco aceptados (tomador)
offerbook.availableOffers=Ofertas disponibles
offerbook.filterByCurrency=Filtrar por moneda
offerbook.filterByPaymentMethod=Filtrar por método de pago
offerbook.timeSinceSigning=Firmado desde
offerbook.timeSinceSigning=Información de la cuenta
offerbook.timeSinceSigning.info=Esta cuenta fue verificada y {0}
offerbook.timeSinceSigning.info.arbitrator=firmada por un árbitro y puede firmar cuentas de pares
offerbook.timeSinceSigning.info.peer=firmado por un par, esperando a que se eleven los límites
offerbook.timeSinceSigning.info.peer=firmado por un par, esperando %d días para aumentar límites
offerbook.timeSinceSigning.info.peerLimitLifted=firmador por un par y los límites se elevaron
offerbook.timeSinceSigning.info.signer=firmado por un par y puede firmar cuentas de pares (límites elevados)
offerbook.timeSinceSigning.info.banned=La cuenta fue bloqueada
@ -349,9 +352,12 @@ offerbook.xmrAutoConf=¿Está habilitada la confirmación automática?
offerbook.timeSinceSigning.help=Cuando complete con éxito un intercambio con un par que tenga una cuenta de pago firmada, su cuenta de pago es firmada.\n{0} días después, el límite inicial de {1} se eleva y su cuenta puede firmar tras cuentas de pago.
offerbook.timeSinceSigning.notSigned=No firmada aún
offerbook.timeSinceSigning.notSigned.ageDays={0} días
offerbook.timeSinceSigning.notSigned.noNeed=No disponible
shared.notSigned=Esta cuenta aún no se ha firmado
shared.notSigned.noNeed=Este tipo de cuenta no utiliza firmado
shared.notSigned=Esta cuenta no ha sido firmada aún y fue creada hace {0} días
shared.notSigned.noNeed=Este tipo de cuenta no necesita firmado
shared.notSigned.noNeedDays=Este tipo de cuenta no necesita firmado y fue creada hace {0} días
shared.notSigned.noNeedAlts=Las cuentas de altcoin no necesitan firmado o edad
offerbook.nrOffers=Número de ofertas: {0}
offerbook.volume={0} (min - max)
@ -435,11 +441,15 @@ createOffer.warning.sellBelowMarketPrice=Siempre tendrá {0}% menos que el preci
createOffer.warning.buyAboveMarketPrice=Siempre pagará {0}% más que el precio de mercado ya que el precio de su oferta será actualizado continuamente.
createOffer.tradeFee.descriptionBTCOnly=Comisión de transacción
createOffer.tradeFee.descriptionBSQEnabled=Seleccionar moneda de comisión de intercambio
createOffer.tradeFee.fiatAndPercent=≈ {0} / {1} de cantidad de intercambio
createOffer.triggerPrice.prompt=Set optional trigger price
createOffer.triggerPrice.label=Deactivate offer if market price is {0}
createOffer.triggerPrice.tooltip=As protecting against drastic price movements you can set a trigger price which deactivates the offer if the market price reaches that value.
createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0}
createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries
createOffer.placeOfferButton=Revisar: Poner oferta para {0} bitcoin
createOffer.alreadyFunded=Ya había destinado fondos para esa oferta.\nSus fondos han sido movidos a la cartera Bisq local y están disponibles para retirar en la pantalla \"Fondos/Enviar fondos\".
createOffer.createOfferFundWalletInfo.headline=Dote de fondos su trato.
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Cantidad a intercambiar: {0}\n
@ -496,7 +506,6 @@ takeOffer.error.message=Un error ocurrió al tomar la oferta.\n\n{0}
# new entries
takeOffer.takeOfferButton=Revisión: Tomar oferta a {0} bitcoin
takeOffer.noPriceFeedAvailable=No puede tomar esta oferta porque utiliza un precio porcentual basado en el precio de mercado y no hay fuentes de precio disponibles.
takeOffer.alreadyFunded.movedFunds=Ya había destinado fondos para esta oferta.\nSus fondos han sido movidos a la cartera Bisq local y están disponibles para retirar en la pantalla \"Fondos/Enviar fondos\".
takeOffer.takeOfferFundWalletInfo.headline=Dotar de fondos su intercambio
# suppress inspection "TrailingSpacesInProperty"
takeOffer.takeOfferFundWalletInfo.tradeAmount=- Cantidad a intercambiar: {0}\n
@ -524,6 +533,10 @@ takeOffer.tac=Al tomar esta oferta, afirmo estar de acuerdo con las condiciones
# Offerbook / Edit offer
####################################################################
openOffer.header.triggerPrice=Precio de activación
openOffer.triggerPrice=Trigger price {0}
openOffer.triggered=The offer has been deactivated because the market price reached your trigger price.\nPlease edit the offer to define a new trigger price
editOffer.setPrice=Establecer precio
editOffer.confirmEdit=Confirmar: Editar oferta
editOffer.publishOffer=Publicando su oferta.
@ -541,6 +554,8 @@ portfolio.tab.history=Historial
portfolio.tab.failed=Fallidas
portfolio.tab.editOpenOffer=Editar oferta
portfolio.closedTrades.deviation.help=Desviación porcentual de precio de mercado
portfolio.pending.invalidDelayedPayoutTx=Hay un problema con una transacción no válida o faltante.\n\nNO envíe el pago fiat o altcoin. Póngase en contacto con los desarrolladores de Bisq en Keybase [HYPERLINK:https://keybase.io/team/bisq] o en el foro [HYPERLINK:https://bisq.community] para obtener más ayuda.\n\nMensaje de error: {0}
portfolio.pending.step1.waitForConf=Esperar a la confirmación en la cadena de bloques
@ -607,7 +622,7 @@ portfolio.pending.step2_buyer.moneyGram.extra=REQUERIMIENTO IMPORTANTE:\nDespué
portfolio.pending.step2_buyer.westernUnion=Por favor pague {0} al vendedor de BTC usando Western Union.\n\n
portfolio.pending.step2_buyer.westernUnion.extra=REQUERIMIENTO IMPORTANTE:\nDespués de haber realizado el pago envíe el MTCN (número de seguimiento) y una foto de el recibo por email a el vendedor de BTC.\nEl recibo debe mostrar claramente el nombre completo del emisor, la ciudad, país y la cantidad. El email del vendedor es: {0}.
# suppress inspection "TrailingSpacesInProperty"
portfolio.pending.step2_buyer.amazonGiftCard=Please purchase an Amazon eGift Card for {0} at your Amazon account and use the BTC seller''s email or mobile number as receiver. In case the trade amount exceeds the permitted amount send multiple cards.\n\n
portfolio.pending.step2_buyer.amazonGiftCard=Por favor compre una Tarjeta Amazon eGift por {0} en su cuenta Amazon y use el email del vendedor BTC o el número de móvil como receptor. En caso de que la cantidad de intercambio exceda la cantidad permitida, envíe múltiples tarjetas.
# suppress inspection "TrailingSpacesInProperty"
portfolio.pending.step2_buyer.postal=Por favor envíe {0} mediante \"US Postal Money Order\" a el vendedor de BTC.\n\n
@ -680,7 +695,7 @@ portfolio.pending.step3_seller.cash=Debido a que el pago se hecho vía depósito
portfolio.pending.step3_seller.moneyGram=El comprador tiene que enviarle el número de autorización y una foto del recibo por correo electrónico.\n\nEl recibo debe mostrar claramente el monto, asi como su nombre completo, país y demarcación (departamento,estado, etc.). Por favor revise su correo electrónico si recibió el número de autorización.\n\nDespués de cerrar esa ventana emergente (popup), verá el nombre y la dirección del comprador de BTC para retirar el dinero de MoneyGram.\n\n¡Solo confirme el recibo de transacción después de haber obtenido el dinero con éxito!
portfolio.pending.step3_seller.westernUnion=El comprador tiene que enviarle el MTCN (número de seguimiento) y una foto de el recibo por email.\nEl recibo debe mostrar claramente su nombre completo, ciudad, país y la cantidad. Por favor compruebe su email si ha recibido el MTCN.\n\nDespués de cerrar ese popup verá el nombre del comprador de BTC y la dirección para recoger el dinero de Western Union.\n\nSolo confirme el recibo después de haber recogido satisfactoriamente el dinero!
portfolio.pending.step3_seller.halCash=El comprador tiene que enviarle el código HalCash como un mensaje de texto. Junto a esto recibirá un mensaje desde HalCash con la información requerida para retirar los EUR de un cajero que soporte HalCash.\n\nDespués de retirar el dinero del cajero confirme aquí la recepción del pago!
portfolio.pending.step3_seller.amazonGiftCard=The buyer has sent you an Amazon eGift Card by email or by text message to your mobile phone. Please redeem now the Amazon eGift Card at your Amazon account and once accepted confirm the payment receipt.
portfolio.pending.step3_seller.amazonGiftCard=El comprador le ha enviado una Tarjeta Amazon eGift por email o mensaje de texto al teléfono móvil. Por favor canjee ahora la Tarjeta Amazon eGift en su cuenta Amazon y una vez aceptado confirme el recibo del pago.
portfolio.pending.step3_seller.bankCheck=\n\nPor favor verifique también que el nombre y el emisor especificado en el contrato de intercambio se corresponde con el nombre que aparece en su declaración bancaria:\nNombre del emisor, para el contrato de intercambio: {0}\n\nSi los nombres no son exactamente los mismos, {1}
# suppress inspection "TrailingSpacesInProperty"
@ -886,13 +901,12 @@ funds.tx.noTxAvailable=Sin transacciones disponibles
funds.tx.revert=Revertir
funds.tx.txSent=Transacción enviada exitosamente a una nueva dirección en la billetera Bisq local.
funds.tx.direction.self=Enviado a usted mismo
funds.tx.daoTxFee=Comisión de minería para la tx DAO
funds.tx.daoTxFee=Comisión de minería para la tx BSQ
funds.tx.reimbursementRequestTxFee=Solicitud de reembolso
funds.tx.compensationRequestTxFee=Solicitud de compensación
funds.tx.dustAttackTx=Dust recibido
funds.tx.dustAttackTx.popup=Esta transacción está enviando una cantidad de BTC muy pequeña a su monedero y puede ser un intento de compañías de análisis de cadenas para espiar su monedero.\n\nSi usa este output para gastar en una transacción, conocerán que probablemente usted sea el propietario de sus otras direcciones (fusión de monedas).\n\nPara proteger su privacidad el monedero Bisq ignora estos outputs para propósitos de gasto y en el balance mostrado. Puede establecer el umbral en el que un output es considerado dust en ajustes.
####################################################################
# Support
####################################################################
@ -943,6 +957,7 @@ support.error=El receptor no pudo procesar el mensaje. Error: {0}
support.buyerAddress=Dirección del comprador de BTC
support.sellerAddress=Dirección del vendedor de BTC
support.role=Rol
support.agent=Support agent
support.state=Estado
support.closed=Cerrado
support.open=Abierto
@ -986,10 +1001,10 @@ setting.preferences.autoConfirmRequiredConfirmations=Confirmaciones requeridas
setting.preferences.autoConfirmMaxTradeSize=Cantidad máxima de intecambio (BTC)
setting.preferences.autoConfirmServiceAddresses=Explorador de URLs Monero (usa Tor, excepto para localhost, direcciones LAN IP, y hostnames *.local)
setting.preferences.deviationToLarge=No se permiten valores superiores a {0}%
setting.preferences.txFee=Withdrawal transaction fee (satoshis/vbyte)
setting.preferences.txFee=Tasa de transacción de retiro (satoshis/vbyte)
setting.preferences.useCustomValue=Usar valor personalizado
setting.preferences.txFeeMin=Transaction fee must be at least {0} satoshis/vbyte
setting.preferences.txFeeTooLarge=Your input is above any reasonable value (>5000 satoshis/vbyte). Transaction fee is usually in the range of 50-400 satoshis/vbyte.
setting.preferences.txFeeMin=La tasa de transacción debe ser al menos de {0} sat/vbyte
setting.preferences.txFeeTooLarge=El valor introducido está muy por encima de lo razonable (>5000 satoshis/vbyte). La tasa de transacción normalmente está en el rango de 50-400 satoshis/vbyte.
setting.preferences.ignorePeers=Pares ignorados [dirección onion:puerto]
setting.preferences.ignoreDustThreshold=Valor mínimo de output que no sea dust
setting.preferences.currenciesInList=Monedas en lista para precio de mercado
@ -1052,6 +1067,7 @@ settings.net.creationDateColumn=Establecido
settings.net.connectionTypeColumn=Dentro/Fuera
settings.net.sentDataLabel=Estadísticas de datos enviados
settings.net.receivedDataLabel=Estadísticas de datos recibidos
settings.net.chainHeightLabel=Altura del último bloque BTC
settings.net.roundTripTimeColumn=Tiempo de ida y vuelta
settings.net.sentBytesColumn=Enviado
settings.net.receivedBytesColumn=Recibido
@ -1066,6 +1082,7 @@ settings.net.needRestart=Necesita reiniciar la aplicación para aplicar ese camb
settings.net.notKnownYet=Aún no conocido...
settings.net.sentData=Datos enviados: {0}, mensajes {1}, mensajes {2} mensajes por segundo
settings.net.receivedData=Datos recibidos: {0}, mensajes {1}, mensajes por segundo {2}
settings.net.chainHeight=Bisq: {0} | Pares: {1}
settings.net.ips=[Dirección IP:puerto | host:puerto | dirección onion:puerto] (separado por coma). El puerto puede ser omitido si se utiliza el predeterminado (8333).
settings.net.seedNode=Nodo semilla
settings.net.directPeer=Par (directo)
@ -1074,7 +1091,7 @@ settings.net.inbound=entrante
settings.net.outbound=saliente
settings.net.reSyncSPVChainLabel=Resincronizar cadena SPV
settings.net.reSyncSPVChainButton=Borrar archivo SPV y resincronizar
settings.net.reSyncSPVSuccess=El archivo de cadena SPV se borrará en el siguiente inicio. Necesita reiniciar la aplicación ahora.\n\nDespués del reinicio puede llevar un rato resincronizar con la red y verá todas las transacciones una vez completada la resincronización.\n\nDependiendo de el número de transacciones y la edad de su monedero la resincronización puede llevar unas horas y consume el 100% de la CPU. No interrumpa el proceso o tendrá que repetirlo.
settings.net.reSyncSPVSuccess=Está seguro de quere hacer una resincronización SPV? Si procede, la cadena SPV se borrará al siguiente inicio.\n\nDespués de reiniciar puede llevarle un rato resincronizar con la red y solo verá las transacciones una vez se haya completado la resincronización.\n\nDependiendo del número de transacciones y la edad de su monedero, la resincronización puede llevarle hasta algunas horas y consumir el 100% de su CPU. No interrumpa el proceso o tendrá que repetirlo.
settings.net.reSyncSPVAfterRestart=La cadena SPV ha sido borrada. Por favor, sea paciente. Puede llevar un tiempo resincronizar con la red.
settings.net.reSyncSPVAfterRestartCompleted=La resincronización se ha completado. Por favor, reinicie la aplicación.
settings.net.reSyncSPVFailed=No se pudo borrar el archivo de cadena SPV\nError: {0}
@ -1161,9 +1178,19 @@ account.menu.paymentAccount=Cuentas de moneda nacional
account.menu.altCoinsAccountView=Cuentas de altcoin
account.menu.password=Contraseña de monedero
account.menu.seedWords=Semilla del monedero
account.menu.walletInfo=Información de monedero
account.menu.backup=Copia de seguridad
account.menu.notifications=Notificaciones
account.menu.walletInfo.balance.headLine=Balances de monedero
account.menu.walletInfo.balance.info=Esto muestrta el balance interno del monedero, incluyendo transacciones no confirmadas.\nPara BTC, el balance interno de monedero mostrado abajo debe cuadrar con la suma de balances 'Disponible' y 'Reservado' mostrado arriba a la derecha de esta ventana.
account.menu.walletInfo.xpub.headLine=Claves centinela (xpub keys)
account.menu.walletInfo.walletSelector={0} {1} monedero
account.menu.walletInfo.path.headLine=ruta HD keychain
account.menu.walletInfo.path.info=Si importa las palabras semilla en otro monedero (como Electru), tendrá que definir la ruta. Esto debería hacerse solo en casos de emergencia, cuando pierda acceso a el monedero Bisq y el directorio de datos.\nTenga en cuenta que gastar fondos desde un monedero no-Bisq puede estropear la estructura de datos interna de Bisq asociado a los datos de monedero, lo que puede llevar a intercambios fallidos.\n\nNUNCA envíe BSQ desde un monedero no-Bisq, ya que probablemente llevará a una transacción inválida de BSQ y le hará perder sus BSQ.
account.menu.walletInfo.openDetails=Mostrar detalles en bruto y claves privadas
## TODO should we rename the following to a gereric name?
account.arbitratorRegistration.pubKey=Llave pública
@ -1498,9 +1525,9 @@ dao.bond.reputation.salt=Salt
dao.bond.reputation.hash=Hash
dao.bond.reputation.lockupButton=Bloquear
dao.bond.reputation.lockup.headline=Confirmar transacción de bloqueo
dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/vbyte)\nTransaction vsize: {5} Kb\n\nAre you sure you want to proceed?
dao.bond.reputation.lockup.details=Cantidad bloqueada: {0}\nTiempo de desbloqueo: {1} bloque(s) (≈{2})\n\nComisión de minado: {3} ({4} Satoshis/vbyte)\nTamaño de la transacción: {5} Kb\n\n¿Seguro que quiere proceder?
dao.bond.reputation.unlock.headline=Confirmar desbloqueo de transacción
dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nMining fee: {3} ({4} Satoshis/vbyte)\nTransaction vsize: {5} Kb\n\nAre you sure you want to proceed?
dao.bond.reputation.unlock.details=Cantidad de desbloqueo: {0}\nTiempo de desbloqueo: {1} bloque(s) (≈{2})\n\nComisión de minado: {3} ({4} Satoshis/vbyte)\nTamaño de transacción: {5} Kb\n\n¿Seguro que quiere proceder?
dao.bond.allBonds.header=Todas las garantías
@ -1796,7 +1823,7 @@ dao.wallet.send.setDestinationAddress=Introduzca su dirección de destino
dao.wallet.send.send=Enviar fondos BSQ
dao.wallet.send.sendBtc=Enviar fondos BTC
dao.wallet.send.sendFunds.headline=Confirme la petición de retiro
dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequired transaction fee is: {2} ({3} satoshis/vbyte)\nTransaction vsize: {4} vKb\n\nThe recipient will receive: {5}\n\nAre you sure you want to withdraw that amount?
dao.wallet.send.sendFunds.details=Enviando: {0}\nA la dirección receptora: {1}.\nLa tasa de minado requerida es: {2} ({3} satoshis/vbyte)\nTamaño de la transacción: {4} Kb\n\nEl receptor recibirá: {5}\n\nEstá seguro de que quiere retirar esa cantidad?
dao.wallet.chainHeightSynced=Último bloque verificado: {0}
dao.wallet.chainHeightSyncing=Esperando bloques... {0} bloques verificados de {1}
dao.wallet.tx.type=Tipo
@ -1854,9 +1881,9 @@ dao.proposal.create.missingMinerFeeFunds=No tiene suficientes fondos BTC para cr
dao.proposal.create.missingIssuanceFunds=No tiene suficientes fondos BTC para crear la transacción de propuesta. Todas las transacciones BSQ requieren una comisión de minado en BTC, y la emisión de transacciones también requieren BTC para la cantidad de BSQ solicitada ({0} Satoshis/BSQ).\nNecesarios: {1}
dao.feeTx.confirm=Confirmar transacción {0}
dao.feeTx.confirm.details={0} fee: {1}\nMining fee: {2} ({3} Satoshis/vbyte)\nTransaction vsize: {4} vKb\n\nAre you sure you want to publish the {5} transaction?
dao.feeTx.confirm.details={0} comisión: {1}\nComisión de minado: {2} ({3} Satoshis/vbyte)\nTamaño de la transacción: {4} Kb\n\n¿Está seguro de que quiere publicar la transacción {5}?
dao.feeTx.issuanceProposal.confirm.details={0} fee: {1}\nBTC needed for BSQ issuance: {2} ({3} Satoshis/BSQ)\nMining fee: {4} ({5} Satoshis/vbyte)\nTransaction vsize: {6} vKb\n\nIf your request is approved, you will receive the amount you requested net of the 2 BSQ proposal fee.\n\nAre you sure you want to publish the {7} transaction?
dao.feeTx.issuanceProposal.confirm.details={0} comisión: {1}\nBTC necesarios para emisión BSQ: {2} ({3} Satoshis/BSQ)\nTasa de minado: {4} ({5} Satoshis/vbyte)\nTamaño de transacción: {6} Kb\n\nSi la solicitud se aprueba, recibirá la cantidad neta que ha solicitado de las 2 BSQ de comisión de propuesta.\n¿Está seguro de que quiere publicar la transacción de {7}?
dao.news.bisqDAO.title=LA DAO BISQ
dao.news.bisqDAO.description=Tal como el exchange Bisq es descentralizado y resistente a la censura, lo es su modelo de governanza - y la DAO BISQ y el token BSQ son herramientas que lo hacen posible.
@ -1928,9 +1955,9 @@ dao.factsAndFigures.menuItem.transactions=Transacciones BSQ
dao.factsAndFigures.dashboard.avgPrice90=Medía de 90 días del precio de intercambio BSQ/BTC
dao.factsAndFigures.dashboard.avgPrice30=Medía de 30 días del precio de intercambio BSQ/BTC
dao.factsAndFigures.dashboard.avgUSDPrice90=Media ponderada de 90 días del precio de intercambio USD/BSQ
dao.factsAndFigures.dashboard.avgUSDPrice30=Media ponderada de 30 días del precio de intercambio USD / BSQ
dao.factsAndFigures.dashboard.marketCap=Capitalización de mercado (basado en el precio de intercambio)
dao.factsAndFigures.dashboard.avgUSDPrice90=Media ponderada por volumen de 90 días del precio de USD/BSQ
dao.factsAndFigures.dashboard.avgUSDPrice30=Media ponderada por volumen de 30 días del precio de USD/BSQ
dao.factsAndFigures.dashboard.marketCap=Capitalización de mercado (basado en la media de 30 días del precio USD/BSQ)
dao.factsAndFigures.dashboard.availableAmount=BSQ totales disponibles
dao.factsAndFigures.supply.issuedVsBurnt=BSQ emitidos v. BSQ quemados
@ -2000,7 +2027,7 @@ disputeSummaryWindow.openDate=Fecha de apertura de ticket
disputeSummaryWindow.role=Rol del trader
disputeSummaryWindow.payout=Pago de la cantidad de intercambio
disputeSummaryWindow.payout.getsTradeAmount=BTC {0} obtiene la cantidad de pago de intercambio
disputeSummaryWindow.payout.getsAll=El {0} BTC obtiene todo
disputeSummaryWindow.payout.getsAll=Cantidad máxima de pago BTC {0}
disputeSummaryWindow.payout.custom=Pago personalizado
disputeSummaryWindow.payoutAmount.buyer=Cantidad de pago del comprador
disputeSummaryWindow.payoutAmount.seller=Cantidad de pago del vendedor
@ -2052,7 +2079,7 @@ disputeSummaryWindow.close.txDetails.headline=Publicar transacción de devoluci
disputeSummaryWindow.close.txDetails.buyer=El comprador recibe {0} en la dirección: {1}\n
# suppress inspection "TrailingSpacesInProperty"
disputeSummaryWindow.close.txDetails.seller=El vendedor recibe {0} en la dirección: {1}\n
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3} ({4} satoshis/vbyte)\nTransaction vsize: {5} vKb\n\nAre you sure you want to publish this transaction?
disputeSummaryWindow.close.txDetails=Gastando: {0}\n{1}{2}Tasa de transacción: {3} ({4} satoshis/vbyte)\nTamaño virtual de transacción: {5} vKb\n\n¿Está seguro de que quiere publicar esta transacción?\n
disputeSummaryWindow.close.noPayout.headline=Cerrar sin realizar algún pago
disputeSummaryWindow.close.noPayout.text=¿Quiere cerrar sin realizar algún pago?
@ -2154,6 +2181,7 @@ tradeDetailsWindow.tradingPeersOnion=Dirección onion de par de intercambio
tradeDetailsWindow.tradingPeersPubKeyHash=Hash de las llaves públicas de pares de intercambio
tradeDetailsWindow.tradeState=Estado del intercambio
tradeDetailsWindow.agentAddresses=Árbitro/Mediador
tradeDetailsWindow.detailData=Detallar datos
walletPasswordWindow.headline=Introducir contraseña para desbloquear
@ -2183,6 +2211,8 @@ feeOptionWindow.info=Puede elegir pagar la tasa de intercambio en BSQ o BTC. Si
feeOptionWindow.optionsLabel=Elija moneda para el pago de comisiones de intercambio
feeOptionWindow.useBTC=Usar BTC
feeOptionWindow.fee={0} (≈ {1})
feeOptionWindow.btcFeeWithFiatAndPercentage={0} (≈ {1} / {2})
feeOptionWindow.btcFeeWithPercentage={0} ({1})
####################################################################
@ -2192,7 +2222,7 @@ feeOptionWindow.fee={0} (≈ {1})
popup.headline.notification=Notificación
popup.headline.instruction=Por favor, tenga en cuenta:
popup.headline.attention=Atención
popup.headline.backgroundInfo=Información de fondo
popup.headline.backgroundInfo=Información general
popup.headline.feedback=Completado
popup.headline.confirmation=Confirmación
popup.headline.information=Información
@ -2226,6 +2256,7 @@ popup.warning.noMediatorsAvailable=No hay mediadores disponibles.
popup.warning.notFullyConnected=Necesita esperar hasta que esté completamente conectado a la red.\nPuede llevar hasta 2 minutos al inicio.
popup.warning.notSufficientConnectionsToBtcNetwork=Necesita esperar hasta que tenga al menos {0} conexiones a la red Bitcoin.
popup.warning.downloadNotComplete=Tiene que esperar hasta que finalice la descarga de los bloques Bitcoin que faltan.
popup.warning.chainNotSynced=La cadena de bloques del monedero Bisq no está sincronizada correctamente. Si ha iniciado la aplicación recientemente, espere a que se haya publicado al menos un bloque Bitcoin.\n\nPuede comprobar la altura de la cadena de bloques en Configuración/Información de red. Si se encuentra más de un bloque y el problema persiste podría estar estancado, en cuyo caso deberá hacer una resincronización SPV.\n[HYPERLINK:https://bisq.wiki/Resyncing_SPV_file]
popup.warning.removeOffer=¿Está seguro que quiere eliminar la oferta?\nLa comisión de creador de {0} se perderá si elimina la oferta.
popup.warning.tooLargePercentageValue=No puede establecer un porcentaje del 100% o superior.
popup.warning.examplePercentageValue=Por favor, introduzca un número de porcentaje como \"5.4\" para 5.4%
@ -2264,14 +2295,15 @@ popup.info.cashDepositInfo=Por favor asegúrese de que tiene una oficina bancari
popup.info.cashDepositInfo.confirm=Confirmo que puedo hacer el depósito
popup.info.shutDownWithOpenOffers=Bisq se está cerrando, pero hay ofertas abiertas.\n\nEstas ofertas no estarán disponibles en la red P2P mientras Bisq esté cerrado, pero serán re-publicadas a la red P2P la próxima vez que inicie Bisq.\n\nPara mantener sus ofertas en línea, mantenga Bisq ejecutándose y asegúrese de que la computadora permanece en línea también (Ej. asegúrese de que no se pone en modo standby... el monitor en espera no es un problema).
popup.info.qubesOSSetupInfo=Parece que está ejecutando Bisq en Qubes OS\n\nAsegúrese de que su Bisq qube esté configurado de acuerdo con nuestra Guía de configuración en [HYPERLINK:https://bisq.wiki/Running_Bisq_on_Qubes]
popup.warn.downGradePrevention=Degradar desde la versión {0} a la versión {1} no está soportado. Por favor use la última versión de Bisq.
popup.warn.daoRequiresRestart=Hubo un problema sincronizando el estado de la DAO. Tiene que reiniciar la aplicación para solucionar el problema.
popup.privateNotification.headline=Notificación privada importante!
popup.securityRecommendation.headline=Recomendación de seguridad importante
popup.securityRecommendation.msg=Nos gustaría recordarle que considere usar protección por contraseña para su cartera, si no la ha activado ya.\n\nTambién es muy recomendable que escriba en un papel las palabras semilla del monedero. Esas palabras semilla son como una contraseña maestra para recuperar su cartera Bitcoin.\nEn la sección \"Semilla de cartera\" encontrará más información.\n\nAdicionalmente, debería hacer una copia de seguridad completa del directorio de aplicación en la sección \"Copia de seguridad\"
popup.bitcoinLocalhostNode.msg=Bisq detectó un nodo Bitcoin Core operando localmente (en localhost).\nPor favor asegúrese de que este nodo esté completamente sincronizado antes de iniciar Bisq y que no esté operando en modo poda (pruned mode).
popup.bitcoinLocalhostNode.additionalRequirements=\n\nPara un nodo bien configurado, los requisitos son que el nodo tenga la poda desactivada y los filtros bloom habilitados.
popup.bitcoinLocalhostNode.msg=Bisq ha detectado un nodo de Bitcoin Core ejecutándose en esta máquina (en local).\n\nPor favor, asegúrese de:\n- que el nodo está completamente sincronizado al iniciar Bisq\n- que el podado está desabilitado ('prune=0' en bitcoin.conf)\n- que los filtros bloom están deshabilitados ('peerbloomfilters=1' in bitcoin.conf)
popup.shutDownInProgress.headline=Cerrando aplicación...
popup.shutDownInProgress.msg=Cerrar la aplicación puede llevar unos segundos.\nPor favor no interrumpa el proceso.
@ -2303,15 +2335,12 @@ popup.accountSigning.signedByPeer=Una de sus cuentas de pago ha sido verificada
popup.accountSigning.peerLimitLifted=El límite inicial para una de sus cuentas se ha elevado.\n\n{0}
popup.accountSigning.peerSigner=Una de sus cuentas es suficiente antigua para firmar otras cuentas de pago y el límite inicial para una de sus cuentas se ha elevado.\n\n{0}
popup.accountSigning.singleAccountSelect.headline=Seleccionar el testigo de edad de cuenta
popup.accountSigning.singleAccountSelect.description=Buscar un testigo de edad de cuenta
popup.accountSigning.singleAccountSelect.datePicker=Seleccionar un punto en el tiempo para el firmadoº
popup.accountSigning.singleAccountSelect.headline=Importar edad de cuenta de testigos no firmados
popup.accountSigning.confirmSingleAccount.headline=Confirmar el testigo de cuenta seleccionado
popup.accountSigning.confirmSingleAccount.selectedHash=Hash del testigo seleccionado
popup.accountSigning.confirmSingleAccount.button=Firmar testigo de edad de cuenta
popup.accountSigning.successSingleAccount.description=Se seleccionón el testigo {0}
popup.accountSigning.successSingleAccount.success.headline=Éxito
popup.accountSigning.successSingleAccount.signError=Error al firmar el testigo, {0}
popup.accountSigning.unsignedPubKeys.headline=Claves públicas no firmadas
popup.accountSigning.unsignedPubKeys.sign=Firmar claves públicas
@ -2354,7 +2383,7 @@ systemTray.tooltip=Bisq: Una red de intercambio de bitcoin descentralizada
# GUI Util
####################################################################
guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is at least {0} satoshis/vbyte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute.
guiUtil.miningFeeInfo=Por favor asegúrese de que la comisión de minado usada en su monedero externo es de al menos {0} sat/vbyte. De lo contrario, las transacciones de intercambio podrían no confirmarse y el intercambio acabaría en disputa.
guiUtil.accountExport.savedToPath=Las cuentas de intercambio se han guardado en el directorio:\n{0}
guiUtil.accountExport.noAccountSetup=No tiene cuentas de intercambio configuradas para exportar.
@ -2447,8 +2476,8 @@ navigation.dao.wallet.receive=\"DAO/Monedero BSQ/Recibir\"
formatter.formatVolumeLabel={0} cantidad{1}
formatter.makerTaker=Creador como {0} {1} / Tomador como {2} {3}
formatter.youAreAsMaker=Usted está {0} {1} como creador / Tomador está {2} {3}
formatter.youAreAsTaker=Usted está {0} {1} como tomador / Creador está {2} {3}
formatter.youAreAsMaker=Usted es: {1} {0} (creador) / El tomador es: {3} {2}
formatter.youAreAsTaker=Usted es: {1} {0} (tomador) / Creador es: {3} {2}
formatter.youAre=Usted es {0} {1} ({2} {3})
formatter.youAreCreatingAnOffer.fiat=Está creando una oferta a {0} {1}
formatter.youAreCreatingAnOffer.altcoin=Está creando una oferta a {0} {1} ({2} {3})
@ -2561,6 +2590,7 @@ payment.venmo.venmoUserName=Nombre de usuario Venmo
payment.popmoney.accountId=Correo electrónico o núm. de telefóno
payment.promptPay.promptPayId=Citizen ID/Tax ID o número de teléfono
payment.supportedCurrencies=Monedas soportadas
payment.supportedCurrenciesForReceiver=Monedas para recibir fondos
payment.limitations=Límitaciones:
payment.salt="Salt" de la verificación de edad de la cuenta.
payment.error.noHexSalt=El "salt" necesitar estar en formato HEX.\nSolo se recomienda editar el "salt" si quiere transferir el "salt" desde una cuenta antigua para mantener su edad de cuenta. La edad de cuenta se verifica usando el "salt" de la cuenta y datos de identificación de cuenta (Ej. IBAN).
@ -2634,8 +2664,9 @@ payment.japan.account=Cuenta
payment.japan.recipient=Nombre
payment.australia.payid=PayID
payment.payid=PayID conectado a una institución financiera. Como la dirección email o el número de móvil.
payment.payid.info=A PayID like a phone number, email address or an Australian Business Number (ABN), that you can securely link to your bank, credit union or building society account. You need to have already created a PayID with your Australian financial institution. Both sending and receiving financial institutions must support PayID. For more information please check [HYPERLINK:https://payid.com.au/faqs/]
payment.amazonGiftCard.info=To pay with Amazon eGift Card you need to purchase an Amazon eGift Card at your Amazon account and use the BTC seller''s email or mobile nr. as receiver. Amazon sends then an email or text message to the receiver. Use the trade ID for the message field.\n\nAmazon eGift Cards can only be redeemed by Amazon accounts with the same currency.\n\nFor more information visit the Amazon eGift Card webpage. [HYPERLINK:https://www.amazon.com/Amazon-1_US_Email-eGift-Card/dp/B004LLIKVU]
payment.payid.info=Un PayID como un número de teléfono, dirección email o Australian Business Number (ABN), que puede conectar con seguridad a su banco, unión de crédito o cuenta de construcción de sociedad. Necesita haber creado una PayID con su institución financiera australiana. Tanto para enviar y recibir las instituciones financieras deben soportar PayID. Para más información por favor compruebe [HYPERLINK:https://payid.com.au/faqs/]
payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the BTC seller via your Amazon account. \n\nBisq will show the BTC seller''s email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card''s message field. Please see the wiki [HYPERLINK:https://bisq.wiki/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it)
# We use constants from the code so we do not use our normal naming convention
# dynamic values are not recognized by IntelliJ
@ -2713,7 +2744,7 @@ ADVANCED_CASH=Advanced Cash
# suppress inspection "UnusedProperty"
TRANSFERWISE=TransferWise
# suppress inspection "UnusedProperty"
AMAZON_GIFT_CARD=Amazon eGift Card
AMAZON_GIFT_CARD=Tarjeta Amazon eGift
# suppress inspection "UnusedProperty"
BLOCK_CHAINS_INSTANT=Altcoins instant
@ -2765,7 +2796,7 @@ ADVANCED_CASH_SHORT=Advanced Cash
# suppress inspection "UnusedProperty"
TRANSFERWISE_SHORT=TransferWise
# suppress inspection "UnusedProperty"
AMAZON_GIFT_CARD_SHORT=Amazon eGift Card
AMAZON_GIFT_CARD_SHORT=Tarjeta Amazon eGift
# suppress inspection "UnusedProperty"
BLOCK_CHAINS_INSTANT_SHORT=Altcoins instant

View File

@ -71,6 +71,7 @@ shared.amountWithCur=مقدار در {0}
shared.volumeWithCur=حجم در {0}
shared.currency=ارز
shared.market=بازار
shared.deviation=Deviation
shared.paymentMethod=نحوه پرداخت
shared.tradeCurrency=ارز معامله
shared.offerType=نوع پیشنهاد
@ -104,7 +105,6 @@ shared.selectTradingAccount=حساب معاملات را انتخاب کنید
shared.fundFromSavingsWalletButton=انتقال وجه از کیف Bisq
shared.fundFromExternalWalletButton=برای تهیه پول، کیف پول بیرونی خود را باز کنید
shared.openDefaultWalletFailed=Failed to open a Bitcoin wallet application. Are you sure you have one installed?
shared.distanceInPercent=فاصله در ٪ از قیمت بازار
shared.belowInPercent= ٪ زیر قیمت بازار
shared.aboveInPercent= ٪ بالای قیمت بازار
shared.enterPercentageValue=ارزش ٪ را وارد کنید
@ -191,7 +191,7 @@ shared.tradeWalletBalance=موجودی کیف‌پول معاملات
shared.makerTxFee=سفارش گذار: {0}
shared.takerTxFee=پذیرنده سفارش: {0}
shared.iConfirm=تایید می‌کنم
shared.tradingFeeInBsqInfo=equivalent to {0} used as trading fee
shared.tradingFeeInBsqInfo=≈ {0}
shared.openURL=باز {0}
shared.fiat=فیات
shared.crypto=کریپتو
@ -218,6 +218,9 @@ shared.refundAgentForSupportStaff=Refund agent
shared.delayedPayoutTxId=Delayed payout transaction ID
shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to
shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later.
shared.numItemsLabel=Number of entries: {0}
shared.filter=Filter
shared.enabled=Enabled
####################################################################
@ -248,14 +251,14 @@ mainView.balance.locked=قفل شده در معاملات
mainView.balance.reserved.short=اندوخته
mainView.balance.locked.short=قفل شده
mainView.footer.usingTor=(استفاده از Tor)
mainView.footer.usingTor=(via Tor)
mainView.footer.localhostBitcoinNode=(لوکال هاست)
mainView.footer.btcInfo={0} {1} {2}
mainView.footer.btcFeeRate=/ Current fee rate: {0} sat/vB
mainView.footer.btcInfo={0} {1}
mainView.footer.btcFeeRate=/ Fee rate: {0} sat/vB
mainView.footer.btcInfo.initializing=در حال ارتباط با شبکه بیت‌کوین
mainView.footer.bsqInfo.synchronizing=/ همگام‌سازی DAO
mainView.footer.btcInfo.synchronizingWith=در حال همگام شدن با
mainView.footer.btcInfo.synchronizedWith=Synced with
mainView.footer.btcInfo.synchronizingWith=Synchronizing with {0} at block: {1} / {2}
mainView.footer.btcInfo.synchronizedWith=Synced with {0} at block {1}
mainView.footer.btcInfo.connectingTo=در حال ایجاد ارتباط با
mainView.footer.btcInfo.connectionFailed=Connection failed to
mainView.footer.p2pInfo=Bitcoin network peers: {0} / Bisq network peers: {1}
@ -336,10 +339,10 @@ offerbook.offerersAcceptedBankSeats=بانک‌های کشورهای پذیرف
offerbook.availableOffers=پیشنهادهای موجود
offerbook.filterByCurrency=فیلتر بر اساس ارز
offerbook.filterByPaymentMethod=فیلتر بر اساس روش پرداخت
offerbook.timeSinceSigning=Signed since
offerbook.timeSinceSigning=Account info
offerbook.timeSinceSigning.info=This account was verified and {0}
offerbook.timeSinceSigning.info.arbitrator=signed by an arbitrator and can sign peer accounts
offerbook.timeSinceSigning.info.peer=signed by a peer, waiting for limits to be lifted
offerbook.timeSinceSigning.info.peer=signed by a peer, waiting %d days for limits to be lifted
offerbook.timeSinceSigning.info.peerLimitLifted=signed by a peer and limits were lifted
offerbook.timeSinceSigning.info.signer=signed by peer and can sign peer accounts (limits lifted)
offerbook.timeSinceSigning.info.banned=account was banned
@ -349,9 +352,12 @@ offerbook.xmrAutoConf=Is auto-confirm enabled
offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts.
offerbook.timeSinceSigning.notSigned=Not signed yet
offerbook.timeSinceSigning.notSigned.ageDays={0} روز
offerbook.timeSinceSigning.notSigned.noNeed=بدون پاسخ
shared.notSigned=This account hasn't been signed yet
shared.notSigned.noNeed=This account type doesn't use signing
shared.notSigned=This account has not been signed yet and was created {0} days ago
shared.notSigned.noNeed=This account type does not require signing
shared.notSigned.noNeedDays=This account type does not require signing and was created {0} days ago
shared.notSigned.noNeedAlts=Altcoin accounts do not feature signing or aging
offerbook.nrOffers=تعداد پیشنهادها: {0}
offerbook.volume={0} (حداقل - حداکثر)
@ -435,11 +441,15 @@ createOffer.warning.sellBelowMarketPrice=شما همیشه {0}% کمتر از ن
createOffer.warning.buyAboveMarketPrice=شما همیشه {0}% کمتر از نرخ روز فعلی بازار پرداخت خواهید کرد، زیرا قیمت پیشنهادتان به طور مداوم به روز رسانی خواهد شد.
createOffer.tradeFee.descriptionBTCOnly=کارمزد معامله
createOffer.tradeFee.descriptionBSQEnabled=انتخاب ارز برای کارمزد معامله
createOffer.tradeFee.fiatAndPercent=≈ {1} / {0} از مبلغ معامله
createOffer.triggerPrice.prompt=Set optional trigger price
createOffer.triggerPrice.label=Deactivate offer if market price is {0}
createOffer.triggerPrice.tooltip=As protecting against drastic price movements you can set a trigger price which deactivates the offer if the market price reaches that value.
createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0}
createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries
createOffer.placeOfferButton=بررسی: پیشنهاد را برای {0} بیتکوین بگذارید
createOffer.alreadyFunded=در حال حاضر آن پیشنهاد را تامین وجه کرده‌اید.\nوجوه شما به کیف پول محلی Bisq منتقل شده و برای برداشت در صفحه \"وجوه/ارسال وجوه\" در دسترس است.
createOffer.createOfferFundWalletInfo.headline=پیشنهاد خود را تامین وجه نمایید
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=مقدار معامله:{0}\n
@ -496,7 +506,6 @@ takeOffer.error.message=هنگام قبول کردن پیشنهاد، اتفاق
# new entries
takeOffer.takeOfferButton=بررسی: برای {0} بیتکوین پیشنهاد بگذارید.
takeOffer.noPriceFeedAvailable=امکان پذیرفتن پیشنهاد وجود ندارد. پیشنهاد از قیمت درصدی مبتنی بر قیمت روز بازار استفاده می‌کند و قیمت‌های بازار هم‌اکنون در دسترس نیست.
takeOffer.alreadyFunded.movedFunds=شما در حال حاضر آن پیشنهاد را تامین وجه کرده‌اید.\nوجوه شما به کیف پول محلی Bisq منتقل شده و برای برداشت در صفحه \"وجوه/ارسال وجوه\" در دسترس است.
takeOffer.takeOfferFundWalletInfo.headline=معامله خود را تأمین وجه نمایید
# suppress inspection "TrailingSpacesInProperty"
takeOffer.takeOfferFundWalletInfo.tradeAmount=مقدار معامله: {0}\n
@ -524,6 +533,10 @@ takeOffer.tac=با پذیرفتن این پیشنهاد، من قبول می‌
# Offerbook / Edit offer
####################################################################
openOffer.header.triggerPrice=قیمت نشان‌شده
openOffer.triggerPrice=Trigger price {0}
openOffer.triggered=The offer has been deactivated because the market price reached your trigger price.\nPlease edit the offer to define a new trigger price
editOffer.setPrice=تنظیم قیمت
editOffer.confirmEdit=تأیید: ویرایش پیشنهاد
editOffer.publishOffer=انتشار پیشنهاد شما.
@ -541,6 +554,8 @@ portfolio.tab.history=تاریخچه
portfolio.tab.failed=ناموفق
portfolio.tab.editOpenOffer=ویرایش پیشنهاد
portfolio.closedTrades.deviation.help=Percentage price deviation from market
portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the fiat or altcoin payment. Contact Bisq developers on Keybase [HYPERLINK:https://keybase.io/team/bisq] or on the forum [HYPERLINK:https://bisq.community] for further assistance.\n\nError message: {0}
portfolio.pending.step1.waitForConf=برای تأییدیه بلاک چین منتظر باشید
@ -886,13 +901,12 @@ funds.tx.noTxAvailable=هیچ تراکنشی موجود نیست
funds.tx.revert=عودت
funds.tx.txSent=تراکنش به طور موفقیت آمیز به یک آدرس جدید در کیف پول محلی Bisq ارسال شد.
funds.tx.direction.self=ارسال شده به خودتان
funds.tx.daoTxFee=کارمزد اسخراج برای تراکنش DAO
funds.tx.daoTxFee=کارمزد استخراج برای تراکنش BSQ
funds.tx.reimbursementRequestTxFee=درخواست بازپرداخت
funds.tx.compensationRequestTxFee=درخواست خسارت
funds.tx.dustAttackTx=Received dust
funds.tx.dustAttackTx.popup=This transaction is sending a very small BTC amount to your wallet and might be an attempt from chain analysis companies to spy on your wallet.\n\nIf you use that transaction output in a spending transaction they will learn that you are likely the owner of the other address as well (coin merge).\n\nTo protect your privacy the Bisq wallet ignores such dust outputs for spending purposes and in the balance display. You can set the threshold amount when an output is considered dust in the settings.
####################################################################
# Support
####################################################################
@ -943,6 +957,7 @@ support.error=گیرنده نتوانست پیام را پردازش کند. خ
support.buyerAddress=آدرس خریدار بیتکوین
support.sellerAddress=آدرس فروشنده بیتکوین
support.role=نقش
support.agent=Support agent
support.state=حالت
support.closed=بسته
support.open=باز
@ -1052,6 +1067,7 @@ settings.net.creationDateColumn=تثبیت شده
settings.net.connectionTypeColumn=درون/بیرون
settings.net.sentDataLabel=Sent data statistics
settings.net.receivedDataLabel=Received data statistics
settings.net.chainHeightLabel=Latest BTC block height
settings.net.roundTripTimeColumn=تاخیر چرخشی
settings.net.sentBytesColumn=ارسال شده
settings.net.receivedBytesColumn=دریافت شده
@ -1066,6 +1082,7 @@ settings.net.needRestart=به منظور اعمال آن تغییر باید ب
settings.net.notKnownYet=هنوز شناخته شده نیست ...
settings.net.sentData=Sent data: {0}, {1} messages, {2} messages/sec
settings.net.receivedData=Received data: {0}, {1} messages, {2} messages/sec
settings.net.chainHeight=Bisq: {0} | Peers: {1}
settings.net.ips=[آدرس آی پی: پورت | نام میزبان: پورت | آدرس Onion : پورت] (جدا شده با ویرگول). اگر از پیش فرض (8333) استفاده می شود، پورت می تواند حذف شود.
settings.net.seedNode=گره ی اصلی
settings.net.directPeer=همتا (مستقیم)
@ -1074,7 +1091,7 @@ settings.net.inbound=وارد شونده
settings.net.outbound=خارج شونده
settings.net.reSyncSPVChainLabel=همگام سازی مجدد زنجیره SPV 
settings.net.reSyncSPVChainButton=حذف فایل SPV  و همگام سازی مجدد
settings.net.reSyncSPVSuccess=The SPV chain file will be deleted on the next startup. You need to restart your application now.\n\nAfter the restart it can take a while to resync with the network and you will only see all transactions once the resync is completed.\n\nDepending on the number of transactions and the age of your wallet the resync can take up to a few hours and consumes 100% of CPU. Do not interrupt the process otherwise you have to repeat it.
settings.net.reSyncSPVSuccess=Are you sure you want to do an SPV resync? If you proceed, the SPV chain file will be deleted on the next startup.\n\nAfter the restart it can take a while to resync with the network and you will only see all transactions once the resync is completed.\n\nDepending on the number of transactions and the age of your wallet the resync can take up to a few hours and consumes 100% of CPU. Do not interrupt the process otherwise you have to repeat it.
settings.net.reSyncSPVAfterRestart=فایل زنجیره SPV حذف شده است. لطفاً صبور باشید، همگام سازی مجدد با شبکه کمی طول خواهد کشید.
settings.net.reSyncSPVAfterRestartCompleted=همگام سازی مجدد هم اکنون تکمیل شده است. لطفاً برنامه را مجدداً راه اندازی نمایید.
settings.net.reSyncSPVFailed=حذف فایل زنجیره SPV امکان پذیر نیست. \nخطا: {0}
@ -1161,9 +1178,19 @@ account.menu.paymentAccount=حساب های ارز ملی
account.menu.altCoinsAccountView=حساب های آلت کوین
account.menu.password=رمز کیف پول
account.menu.seedWords=رمز پشتیبان کیف پول
account.menu.walletInfo=Wallet info
account.menu.backup=پشتیبان
account.menu.notifications=اعلان‌ها
account.menu.walletInfo.balance.headLine=Wallet balances
account.menu.walletInfo.balance.info=This shows the internal wallet balance including unconfirmed transactions.\nFor BTC, the internal wallet balance shown below should match the sum of the 'Available' and 'Reserved' balances shown in the top right of this window.
account.menu.walletInfo.xpub.headLine=Watch keys (xpub keys)
account.menu.walletInfo.walletSelector={0} {1} wallet
account.menu.walletInfo.path.headLine=HD keychain paths
account.menu.walletInfo.path.info=If you import seed words into another wallet (like Electrum), you'll need to define the path. This should only be done in emergency cases when you lose access to the Bisq wallet and data directory.\nKeep in mind that spending funds from a non-Bisq wallet can bungle the internal Bisq data structures associated with the wallet data, which can lead to failed trades.\n\nNEVER send BSQ from a non-Bisq wallet, as it will probably lead to an invalid BSQ transaction and losing your BSQ.
account.menu.walletInfo.openDetails=Show raw wallet details and private keys
## TODO should we rename the following to a gereric name?
account.arbitratorRegistration.pubKey=کلید عمومی
@ -1796,7 +1823,7 @@ dao.wallet.send.setDestinationAddress=آدرس مقصد خود را پر کنی
dao.wallet.send.send=ارسال وجوه BSQ 
dao.wallet.send.sendBtc=ارسال وجوه BTC
dao.wallet.send.sendFunds.headline=تأیید درخواست برداشت
dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequired transaction fee is: {2} ({3} satoshis/vbyte)\nTransaction vsize: {4} vKb\n\nThe recipient will receive: {5}\n\nAre you sure you want to withdraw that amount?
dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequired mining fee is: {2} ({3} satoshis/vbyte)\nTransaction vsize: {4} vKb\n\nThe recipient will receive: {5}\n\nAre you sure you want to withdraw that amount?
dao.wallet.chainHeightSynced=آخرین بلاک تایید شده: {0}
dao.wallet.chainHeightSyncing=منتظر بلاک‌ها... {0} تا از {1} بلاک تایید شده است
dao.wallet.tx.type=نوع
@ -1928,9 +1955,9 @@ dao.factsAndFigures.menuItem.transactions=تراکنش‌های BSQ
dao.factsAndFigures.dashboard.avgPrice90=90 days average BSQ/BTC trade price
dao.factsAndFigures.dashboard.avgPrice30=30 days average BSQ/BTC trade price
dao.factsAndFigures.dashboard.avgUSDPrice90=90 days volume weighted average USD/BSQ trade price
dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average USD/BSQ trade price
dao.factsAndFigures.dashboard.marketCap=ارزش بازار (بر مبنای قیمت معاملاتی)
dao.factsAndFigures.dashboard.avgUSDPrice90=90 days volume weighted average USD/BSQ price
dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average USD/BSQ price
dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on 30 days average USD/BSQ price)
dao.factsAndFigures.dashboard.availableAmount=مجموع BSQ در دسترس
dao.factsAndFigures.supply.issuedVsBurnt=BSQ issued v. BSQ burnt
@ -2000,7 +2027,7 @@ disputeSummaryWindow.openDate=تاریخ ایجاد تیکت
disputeSummaryWindow.role=نقش معامله گر
disputeSummaryWindow.payout=پرداختی مقدار معامله
disputeSummaryWindow.payout.getsTradeAmount=BTC {0} پرداختی مبلغ معامله را دریافت می کند
disputeSummaryWindow.payout.getsAll=BTC {0} همه را دریافت می کند
disputeSummaryWindow.payout.getsAll=Max. payout to BTC {0}
disputeSummaryWindow.payout.custom=پرداخت سفارشی
disputeSummaryWindow.payoutAmount.buyer=مقدار پرداختی خریدار
disputeSummaryWindow.payoutAmount.seller=مقدار پرداختی فروشنده
@ -2154,6 +2181,7 @@ tradeDetailsWindow.tradingPeersOnion=آدرس Onion همتایان معامله:
tradeDetailsWindow.tradingPeersPubKeyHash=Trading peers pubkey hash
tradeDetailsWindow.tradeState=وضعیت معامله
tradeDetailsWindow.agentAddresses=Arbitrator/Mediator
tradeDetailsWindow.detailData=Detail data
walletPasswordWindow.headline=وارد کردن رمز عبور به منظور باز کردن
@ -2183,6 +2211,8 @@ feeOptionWindow.info=شما می توانید انتخاب کنید که هزی
feeOptionWindow.optionsLabel=انتخاب ارز برای پرداخت کارمزد معامله
feeOptionWindow.useBTC=استفاده از BTC
feeOptionWindow.fee={0} (≈ {1})
feeOptionWindow.btcFeeWithFiatAndPercentage={0} (≈ {1} / {2})
feeOptionWindow.btcFeeWithPercentage={0} ({1})
####################################################################
@ -2226,6 +2256,7 @@ popup.warning.noMediatorsAvailable=There are no mediators available.
popup.warning.notFullyConnected=شما باید منتظر بمانید تا به طور کامل به شبکه متصل شوید. \nاین ممکن است در هنگام راه اندازی حدود 2 دقیقه طول بکشد.
popup.warning.notSufficientConnectionsToBtcNetwork=شما باید منتظر بمانید تا حداقل {0} اتصال به شبکه بیتکوین داشته باشید.
popup.warning.downloadNotComplete=شما باید منتظر بمانید تا بارگیری بلاک های بیتکوین باقیمانده کامل شود.
popup.warning.chainNotSynced=The Bisq wallet blockchain height is not synced correctly. If you recently started the application, please wait until one Bitcoin block has been published.\n\nYou can check the blockchain height in Settings/Network Info. If more than one block passes and this problem persists it may be stalled, in which case you should do an SPV resync. [HYPERLINK:https://bisq.wiki/Resyncing_SPV_file]
popup.warning.removeOffer=آیا شما مطمئن هستید که می خواهید این پیشنهاد را حذف کنید؟\nاگر آن پیشنهاد را حذف کنید، هزینه سفارش گذار {0} از دست خواهد رفت .
popup.warning.tooLargePercentageValue=شما نمیتوانید درصد 100٪ یا بیشتر را تنظیم کنید.
popup.warning.examplePercentageValue=لطفا یک عدد درصد مانند \"5.4\" برای 5.4% وارد کنید
@ -2254,7 +2285,7 @@ popup.warning.openOffer.makerFeeTxRejected=The maker fee transaction for offer w
popup.warning.trade.txRejected.tradeFee=trade fee
popup.warning.trade.txRejected.deposit=deposit
popup.warning.trade.txRejected=The {0} transaction for trade with ID {1} was rejected by the Bitcoin network.\nTransaction ID={2}}\nThe trade has been moved to failed trades.\nPlease go to \"Settings/Network info\" and do a SPV resync.\nFor further help please contact the Bisq support channel at the Bisq Keybase team.
popup.warning.trade.txRejected=The {0} transaction for trade with ID {1} was rejected by the Bitcoin network.\nTransaction ID={2}\nThe trade has been moved to failed trades.\nPlease go to \"Settings/Network info\" and do a SPV resync.\nFor further help please contact the Bisq support channel at the Bisq Keybase team.
popup.warning.openOfferWithInvalidMakerFeeTx=The maker fee transaction for offer with ID {0} is invalid.\nTransaction ID={1}.\nPlease go to \"Settings/Network info\" and do a SPV resync.\nFor further help please contact the Bisq support channel at the Bisq Keybase team.
@ -2264,14 +2295,15 @@ popup.info.cashDepositInfo=لطفا مطمئن شوید که شما یک شعب
popup.info.cashDepositInfo.confirm=تأیید می کنم که می توانم سپرده را ایجاد کنم
popup.info.shutDownWithOpenOffers=Bisq در حال خاموش شدن است ولی پیشنهاداتی وجود دارند که باز هستند.\n\nزمانی که Bisq بسته باشد این پیشنهادات در شبکه P2P در دسترس نخواهند بود، ولی هر وقت دوباره Bisq را باز کنید این پیشنهادات دوباره در شبکه P2P منتشر خواهند شد.\n\n برای اینکه پیشنهادات شما برخط بمانند، بگذارید Bisq در حال اجرابماند و همچنین مطمئن شوید که این کامپیوتر به اینترنت متصل است. (به عنوان مثال مطمئن شوید که به حالت آماده باش نمی‌رود.. البته حالت آماده باش برای نمایشگر ایرادی ندارد).
popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\nPlease make sure your Bisq qube is setup according to our Setup Guide at [HYPERLINK:https://bisq.wiki/Running_Bisq_on_Qubes].
popup.warn.downGradePrevention=Downgrade from version {0} to version {1} is not supported. Please use the latest Bisq version.
popup.warn.daoRequiresRestart=There was a problem with synchronizing the DAO state. You have to restart the application to fix the issue.
popup.privateNotification.headline=اعلان خصوصی مهم!
popup.securityRecommendation.headline=توصیه امنیتی مهم
popup.securityRecommendation.msg=ما می خواهیم به شما یادآوری کنیم که استفاده از رمز محافظت برای کیف پول خود را در نظر بگیرید اگر از قبل آن را فعال نکرده اید.\n\nهمچنین شدیداً توصیه می شود که کلمات رمز خصوصی کیف پول را بنویسید. این کلمات رمز خصوصی مانند یک رمزعبور اصلی برای بازیابی کیف پول بیتکوین شما هستند. \nدر قسمت \"کلمات رمز خصوصی کیف پول\" اطلاعات بیشتری کسب می کنید.\n\n علاوه بر این شما باید از پوشه داده های کامل نرم افزار در بخش \"پشتیبان گیری\" پشتیبان تهیه کنید.
popup.bitcoinLocalhostNode.msg=Bisq یک گره بیتکوین هسته محلی در حال اجرا را (در لوکا هاست) شناسایی کرد.\n لطفا اطمینان حاصل کنید که این گره قبل از شروع Bisq به طور کامل همگام سازی شده و در حالت هرس شده اجرا نمی شود.
popup.bitcoinLocalhostNode.additionalRequirements=\n\nFor a well configured node, the requirements are for the node to have pruning disabled and bloom filters enabled.
popup.bitcoinLocalhostNode.msg=Bisq detected a Bitcoin Core node running on this machine (at localhost).\n\nPlease ensure:\n- the node is fully synced before starting Bisq\n- pruning is disabled ('prune=0' in bitcoin.conf)\n- bloom filters are enabled ('peerbloomfilters=1' in bitcoin.conf)
popup.shutDownInProgress.headline=خاموش شدن در حال انجام است
popup.shutDownInProgress.msg=خاتمه دادن به برنامه می تواند چند ثانیه طول بکشد.\n لطفا این روند را قطع نکنید.
@ -2303,15 +2335,12 @@ popup.accountSigning.signedByPeer=One of your payment accounts has been verified
popup.accountSigning.peerLimitLifted=The initial limit for one of your accounts has been lifted.\n\n{0}
popup.accountSigning.peerSigner=One of your accounts is mature enough to sign other payment accounts and the initial limit for one of your accounts has been lifted.\n\n{0}
popup.accountSigning.singleAccountSelect.headline=Select account age witness
popup.accountSigning.singleAccountSelect.description=Search for account age witness.
popup.accountSigning.singleAccountSelect.datePicker=Select point of time for signing
popup.accountSigning.singleAccountSelect.headline=Import unsigned account age witness
popup.accountSigning.confirmSingleAccount.headline=Confirm selected account age witness
popup.accountSigning.confirmSingleAccount.selectedHash=Selected witness hash
popup.accountSigning.confirmSingleAccount.button=Sign account age witness
popup.accountSigning.successSingleAccount.description=Witness {0} was signed
popup.accountSigning.successSingleAccount.success.headline=Success
popup.accountSigning.successSingleAccount.signError=Failed to sign witness, {0}
popup.accountSigning.unsignedPubKeys.headline=Unsigned Pubkeys
popup.accountSigning.unsignedPubKeys.sign=Sign Pubkeys
@ -2447,8 +2476,8 @@ navigation.dao.wallet.receive=\"DAO/کیف پول BSQ/دریافت\"
formatter.formatVolumeLabel={0} مبلغ {1}
formatter.makerTaker=سفارش گذار به عنوان {0} {1} / پذیرنده به عنوان {2} {3}
formatter.youAreAsMaker=شما {0} {1} به عنوان سفارش گذار هستید/ پذیرنده {2} {3} هست
formatter.youAreAsTaker=شما {0} {1} به عنوان پذیرنده هستید/ سفارش گذار {2} {3} هست
formatter.youAreAsMaker=You are: {1} {0} (maker) / Taker is: {3} {2}
formatter.youAreAsTaker=You are: {1} {0} (taker) / Maker is: {3} {2}
formatter.youAre=شما {0} {1} ({2} {3}) هستید
formatter.youAreCreatingAnOffer.fiat=شما در حال ایجاد یک پیشنهاد به {0} {1} هستید
formatter.youAreCreatingAnOffer.altcoin=شما در حال ایجاد یک پیشنهاد به {0} {1} ({2} {3}) هستید
@ -2561,6 +2590,7 @@ payment.venmo.venmoUserName=نام کاربری Venmo
payment.popmoney.accountId=ایمیل یا شماره تلفن
payment.promptPay.promptPayId=شناسه شهروندی/شناسه مالیاتی یا شماره تلفن
payment.supportedCurrencies=ارزهای مورد حمایت
payment.supportedCurrenciesForReceiver=Currencies for receiving funds
payment.limitations=محدودیت‌ها
payment.salt=داده‌های تصافی برای اعتبارسنجی سن حساب
payment.error.noHexSalt=The salt needs to be in HEX format.\nIt is only recommended to edit the salt field if you want to transfer the salt from an old account to keep your account age. The account age is verified by using the account salt and the identifying account data (e.g. IBAN).
@ -2635,7 +2665,8 @@ payment.japan.recipient=نام
payment.australia.payid=PayID
payment.payid=PayID linked to financial institution. Like email address or mobile phone.
payment.payid.info=A PayID like a phone number, email address or an Australian Business Number (ABN), that you can securely link to your bank, credit union or building society account. You need to have already created a PayID with your Australian financial institution. Both sending and receiving financial institutions must support PayID. For more information please check [HYPERLINK:https://payid.com.au/faqs/]
payment.amazonGiftCard.info=To pay with Amazon eGift Card you need to purchase an Amazon eGift Card at your Amazon account and use the BTC seller''s email or mobile nr. as receiver. Amazon sends then an email or text message to the receiver. Use the trade ID for the message field.\n\nAmazon eGift Cards can only be redeemed by Amazon accounts with the same currency.\n\nFor more information visit the Amazon eGift Card webpage. [HYPERLINK:https://www.amazon.com/Amazon-1_US_Email-eGift-Card/dp/B004LLIKVU]
payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the BTC seller via your Amazon account. \n\nBisq will show the BTC seller''s email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card''s message field. Please see the wiki [HYPERLINK:https://bisq.wiki/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it)
# We use constants from the code so we do not use our normal naming convention
# dynamic values are not recognized by IntelliJ

View File

@ -35,7 +35,7 @@ shared.no=Non
shared.iUnderstand=Je comprends
shared.na=N/A
shared.shutDown=Éteindre
shared.reportBug=Report bug on GitHub
shared.reportBug=Signaler des bugs sur Github
shared.buyBitcoin=Achat Bitcoin
shared.sellBitcoin=Vendre des Bitcoins
shared.buyCurrency=Achat {0}
@ -71,6 +71,7 @@ shared.amountWithCur=Montant en {0}
shared.volumeWithCur=Volume en {0}
shared.currency=Devise
shared.market=Marché
shared.deviation=Deviation
shared.paymentMethod=Mode de paiement
shared.tradeCurrency=Devise d'échange
shared.offerType=Type d'ordre
@ -95,21 +96,20 @@ shared.BTCMinMax=BTC (min - max)
shared.removeOffer=Retirer l'ordre
shared.dontRemoveOffer=Ne pas retirer l'ordre
shared.editOffer=Éditer l'ordre
shared.openLargeQRWindow=Open large QR code window
shared.openLargeQRWindow=Ouvrez et agrandissez la fenêtre du code QR
shared.tradingAccount=Compte de trading
shared.faq=Visit FAQ page
shared.faq=Visitez la page FAQ
shared.yesCancel=Oui, annuler
shared.nextStep=Étape suivante
shared.selectTradingAccount=Sélectionner le compte de trading
shared.fundFromSavingsWalletButton=Transférer des fonds depuis le portefeuille Bisq
shared.fundFromExternalWalletButton=Ouvrez votre portefeuille externe pour provisionner
shared.openDefaultWalletFailed=Failed to open a Bitcoin wallet application. Are you sure you have one installed?
shared.distanceInPercent=Écart en % par rapport au du prix du marché
shared.openDefaultWalletFailed=L'ouverture de l'application de portefeuille Bitcoin par défaut a échoué. Êtes-vous sûr de l'avoir installée?
shared.belowInPercent=% sous le prix du marché
shared.aboveInPercent=% au-dessus du prix du marché
shared.enterPercentageValue=Entrez la valeur en %
shared.OR=OU
shared.notEnoughFunds=You don''t have enough funds in your Bisq wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Bisq wallet at Funds > Receive Funds.
shared.notEnoughFunds=Il n'y a pas suffisamment de fonds dans votre portefeuille Bisq pour payer cette transaction. La transaction a besoin de {0} Votre solde disponible est de {1}. \n\nVeuillez injecter des fonds à partir d'un portefeuille Bitcoin externe ou recharger votre portefeuille Bisq dans «Fonds / Dépôts».
shared.waitingForFunds=En attente des fonds...
shared.depositTransactionId=ID de la transaction de dépôt
shared.TheBTCBuyer=L'acheteur de BTC
@ -125,15 +125,15 @@ shared.notUsedYet=Pas encore utilisé
shared.date=Date
shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired mining fee is: {3} ({4} satoshis/vbyte)\nTransaction vsize: {5} vKb\n\nThe recipient will receive: {6}\n\nAre you sure you want to withdraw this amount?
# suppress inspection "TrailingSpacesInProperty"
shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and therefore not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n
shared.sendFundsDetailsDust=Bisq détecte que la transaction produira une sortie inférieure au seuil de fraction minimum (non autorisé par les règles de consensus Bitcoin). Au lieu de cela, ces fractions ({0} satoshi {1}) seront ajoutées aux frais de traitement minier.
shared.copyToClipboard=Copier dans le presse-papiers
shared.language=Langue
shared.country=Pays
shared.applyAndShutDown=Appliquer et éteindre
shared.selectPaymentMethod=Sélectionner un mode de paiement
shared.accountNameAlreadyUsed=That account name is already used for another saved account.\nPlease choose another name.
shared.accountNameAlreadyUsed=Ce nom de compte a été utilisé par un compte enregistré. Veuillez utiliser un autre nom.
shared.askConfirmDeleteAccount=Voulez-vous vraiment supprimer le compte sélectionné?
shared.cannotDeleteAccount=You cannot delete that account because it is being used in an open offer (or in an open trade).
shared.cannotDeleteAccount=Vous ne pouvez pas supprimer ce compte car il est utilisé dans des devis ou des transactions.
shared.noAccountsSetupYet=Il n'y a pas encore de comptes établis.
shared.manageAccounts=Gérer les comptes
shared.addNewAccount=Ajouter un nouveau compte
@ -191,7 +191,7 @@ shared.tradeWalletBalance=Solde du portefeuille de trading
shared.makerTxFee=Maker: {0}
shared.takerTxFee=Taker: {0}
shared.iConfirm=Je confirme
shared.tradingFeeInBsqInfo=Équivalent à {0} utilisé en frais de transaction
shared.tradingFeeInBsqInfo=≈ {0}
shared.openURL=Ouvert {0}
shared.fiat=Fiat
shared.crypto=Crypto
@ -218,6 +218,9 @@ shared.refundAgentForSupportStaff=Agent de remboursement
shared.delayedPayoutTxId=Delayed payout transaction ID
shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to
shared.unconfirmedTransactionsLimitReached=Vous avez trop de transactions non confirmées pour le moment. Veuillez réessayer plus tard.
shared.numItemsLabel=Number of entries: {0}
shared.filter=Filter
shared.enabled=Enabled
####################################################################
@ -248,14 +251,14 @@ mainView.balance.locked=Bloqué en transactions
mainView.balance.reserved.short=Réservé
mainView.balance.locked.short=Vérouillé
mainView.footer.usingTor=(utilisant Tor)
mainView.footer.usingTor=(via Tor)
mainView.footer.localhostBitcoinNode=(localhost)
mainView.footer.btcInfo={0} {1} {2}
mainView.footer.btcFeeRate=/ Current fee rate: {0} sat/vB
mainView.footer.btcInfo={0} {1}
mainView.footer.btcFeeRate=/ Fee rate: {0} sat/vB
mainView.footer.btcInfo.initializing=Connexion au réseau Bitcoin en cours
mainView.footer.bsqInfo.synchronizing=/ Synchronisation DAO en cours
mainView.footer.btcInfo.synchronizingWith=Synchronisation avec
mainView.footer.btcInfo.synchronizedWith=Synced with
mainView.footer.btcInfo.synchronizingWith=Synchronizing with {0} at block: {1} / {2}
mainView.footer.btcInfo.synchronizedWith=Synced with {0} at block {1}
mainView.footer.btcInfo.connectingTo=Se connecte à
mainView.footer.btcInfo.connectionFailed=Connection failed to
mainView.footer.p2pInfo=Bitcoin network peers: {0} / Bisq network peers: {1}
@ -336,10 +339,10 @@ offerbook.offerersAcceptedBankSeats=Pays acceptés où se situe le siège de la
offerbook.availableOffers=Ordres disponibles
offerbook.filterByCurrency=Filtrer par devise
offerbook.filterByPaymentMethod=Filtrer par mode de paiement
offerbook.timeSinceSigning=Signed since
offerbook.timeSinceSigning=Account info
offerbook.timeSinceSigning.info=Ce compte a été vérifié et {0}
offerbook.timeSinceSigning.info.arbitrator=signé par un arbitre et pouvant signer des comptes pairs
offerbook.timeSinceSigning.info.peer=signé par un pair, en attente de la levée des limites
offerbook.timeSinceSigning.info.peer=signed by a peer, waiting %d days for limits to be lifted
offerbook.timeSinceSigning.info.peerLimitLifted=signé par un pair et les limites ont été levées
offerbook.timeSinceSigning.info.signer=signé par un pair et pouvant signer des comptes de pairs (limites levées)
offerbook.timeSinceSigning.info.banned=Ce compte a été banni
@ -349,14 +352,17 @@ offerbook.xmrAutoConf=Is auto-confirm enabled
offerbook.timeSinceSigning.help=Lorsque vous effectuez avec succès une transaction avec un pair disposant d''un compte de paiement signé, votre compte de paiement est signé.\n{0} Jours plus tard, la limite initiale de {1} est levée et votre compte peut signer les comptes de paiement d''un autre pair.
offerbook.timeSinceSigning.notSigned=Pas encore signé
offerbook.timeSinceSigning.notSigned.ageDays={0} jours
offerbook.timeSinceSigning.notSigned.noNeed=N/A
shared.notSigned=Ce compte n'a pas encore été signé
shared.notSigned.noNeed=Ce type de compte n'utilise pas de signature
shared.notSigned=This account has not been signed yet and was created {0} days ago
shared.notSigned.noNeed=This account type does not require signing
shared.notSigned.noNeedDays=This account type does not require signing and was created {0} days ago
shared.notSigned.noNeedAlts=Altcoin accounts do not feature signing or aging
offerbook.nrOffers=Nombre d''ordres: {0}
offerbook.volume={0} (min - max)
offerbook.deposit=Deposit BTC (%)
offerbook.deposit.help=Deposit paid by each trader to guarantee the trade. Will be returned when the trade is completed.
offerbook.deposit.help=Les deux parties à la transaction ont payé un dépôt pour assurer que la transaction se déroule normalement. Ce montant sera remboursé une fois la transaction terminée.
offerbook.createOfferToBuy=Créer un nouvel ordre d''achat pour {0}
offerbook.createOfferToSell=Créer un nouvel ordre de vente pour {0}
@ -377,11 +383,11 @@ offerbook.withdrawFundsHint=Vous pouvez retirer les fonds investis depuis l''éc
offerbook.warning.noTradingAccountForCurrency.headline=No payment account for selected currency
offerbook.warning.noTradingAccountForCurrency.msg=You don't have a payment account set up for the selected currency.\n\nWould you like to create an offer for another currency instead?
offerbook.warning.noMatchingAccount.headline=No matching payment account.
offerbook.warning.noMatchingAccount.msg=This offer uses a payment method you haven't set up yet. \n\nWould you like to set up a new payment account now?
offerbook.warning.noMatchingAccount.msg=Cette offre utilise un mode de paiement que vous n'avez pas créé. \n\nVoulez-vous créer un nouveau compte de paiement maintenant?
offerbook.warning.counterpartyTradeRestrictions=Cette offre ne peut être acceptée en raison de restrictions d'échange imposées par les contreparties
offerbook.warning.newVersionAnnouncement=With this version of the software, trading peers can verify and sign each others' payment accounts to create a network of trusted payment accounts.\n\nAfter successfully trading with a peer with a verified payment account, your payment account will be signed and trading limits will be lifted after a certain time interval (length of this interval is based on the verification method).\n\nFor more information on account signing, please see the documentation at [HYPERLINK:https://docs.bisq.network/payment-methods#account-signing].
offerbook.warning.newVersionAnnouncement=Grâce à cette version du logiciel, les partenaires commerciaux peuvent confirmer et vérifier les comptes de paiement de chacun pour créer un réseau de comptes de paiement de confiance.\n\nUne fois la transaction réussie, votre compte de paiement sera vérifié et les restrictions de transaction seront levées après une certaine période de temps (cette durée est basée sur la méthode de vérification).\n\nPour plus d'informations sur la vérification de votre compte, veuillez consulter le document sur https://docs.bisq.network/payment-methods#account-signing
popup.warning.tradeLimitDueAccountAgeRestriction.seller=Le montant de transaction autorisé est limité à {0} en raison des restrictions de sécurité basées sur les critères suivants:\n- Le compte de l''acheteur n''a pas été signé par un arbitre ou par un pair\n- Le délai depuis la signature du compte de l''acheteur est inférieur à 30 jours\n- Le mode de paiement pour cette offre est considéré comme présentant un risque de rétrofacturation bancaire\n\n{1}
popup.warning.tradeLimitDueAccountAgeRestriction.buyer=Le montant de transaction autorisé est limité à {0} en raison des restrictions de sécurité basées sur les critères suivants:\n- Votre compte n''a pas été signé par un arbitre ou par un pair\n- Le délai depuis la signature de votre compte est inférieur à 30 jours\n- Le mode de paiement pour cette offre est considéré comme présentant un risque de rétrofacturation bancaire\n\n{1}
@ -392,8 +398,8 @@ offerbook.warning.offerBlocked=L'ordre a été bloqué par des développeurs de
offerbook.warning.currencyBanned=La devise utilisée pour cet ordre a été bloquée par les développeurs de Bisq.\nVeuillez visiter le Forum Bisq pour obtenir plus d'informations.
offerbook.warning.paymentMethodBanned=Le mode de paiement utilisé pour cet ordre a été bloqué par les développeurs de Bisq.\nVeuillez visiter le Forum Bisq pour obtenir plus d'informations.
offerbook.warning.nodeBlocked=L'adresse onion de ce trader a été bloquée par les développeurs de Bisq.\nIl s'agit peut être d'un bug qui cause des problèmes lors de l'acceptation de cet ordre.
offerbook.warning.requireUpdateToNewVersion=Your version of Bisq is not compatible for trading anymore.\nPlease update to the latest Bisq version at [HYPERLINK:https://bisq.network/downloads].
offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. It could be that your previous take-offer attempt resulted in a failed trade.
offerbook.warning.requireUpdateToNewVersion=Votre version Bisq n'est plus compatible avec les transactions. Veuillez mettre à jour la dernière version de Bisq via https://bisq.network/downloads
offerbook.warning.offerWasAlreadyUsedInTrade=Vous ne pouvez pas prendre la commande car vous avez déjà terminé l'opération. Il se peut que votre précédente tentative de prise de commandes ait entraîné l'échec de la transaction.
offerbook.info.sellAtMarketPrice=Vous vendrez au prix du marché (mis à jour chaque minute).
offerbook.info.buyAtMarketPrice=Vous achèterez au prix du marché (mis à jour chaque minute).
@ -435,11 +441,15 @@ createOffer.warning.sellBelowMarketPrice=Vous obtiendrez toujours {0}% de moins
createOffer.warning.buyAboveMarketPrice=Vous paierez toujours {0}% de plus que le prix actuel du marché car le prix de votre ordre sera continuellement mis à jour.
createOffer.tradeFee.descriptionBTCOnly=Frais de transaction
createOffer.tradeFee.descriptionBSQEnabled=Choisir la devise des frais de transaction
createOffer.tradeFee.fiatAndPercent=≈ {0} / {1} du montant de la transaction
createOffer.triggerPrice.prompt=Set optional trigger price
createOffer.triggerPrice.label=Deactivate offer if market price is {0}
createOffer.triggerPrice.tooltip=As protecting against drastic price movements you can set a trigger price which deactivates the offer if the market price reaches that value.
createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0}
createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries
createOffer.placeOfferButton=Review: Placer un ordre de {0} Bitcoin
createOffer.alreadyFunded=Vous aviez déjà financé cet ordre.\nVos fonds ont été transférés dans votre portefeuille Bisq local et peuvent être retirés dans l'onglet \"Fonds/Envoyer des fonds\"
createOffer.createOfferFundWalletInfo.headline=Financer votre ordre
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=Montant du trade: {0}\n\n
@ -496,7 +506,6 @@ takeOffer.error.message=Une erreur s''est produite pendant l'acceptation de l
# new entries
takeOffer.takeOfferButton=Vérifier: Accepter l''ordre de {0} Bitcoin
takeOffer.noPriceFeedAvailable=Vous ne pouvez pas accepter cet ordre, car celui-ci utilise un prix en pourcentage basé sur le prix du marché, mais il n'y a pas de prix de référence de disponible.
takeOffer.alreadyFunded.movedFunds=Vous aviez déjà provisionner cette ordre.\nVos fonds ont été transféré dans votre portefeuille bisq local et sont disponible dans l'onglet \"Fonds/Envoyer des fonds\"
takeOffer.takeOfferFundWalletInfo.headline=Provisionner votre trade
# suppress inspection "TrailingSpacesInProperty"
takeOffer.takeOfferFundWalletInfo.tradeAmount=- Montant du trade: {0}\n
@ -524,6 +533,10 @@ takeOffer.tac=En acceptant cet ordre vous acceptez les conditions de transaction
# Offerbook / Edit offer
####################################################################
openOffer.header.triggerPrice=Prix de déclenchement
openOffer.triggerPrice=Trigger price {0}
openOffer.triggered=The offer has been deactivated because the market price reached your trigger price.\nPlease edit the offer to define a new trigger price
editOffer.setPrice=Définir le prix
editOffer.confirmEdit=Confirmation: Modification de l'ordre
editOffer.publishOffer=Publication de votre ordre.
@ -541,7 +554,9 @@ portfolio.tab.history=Historique
portfolio.tab.failed=Échec
portfolio.tab.editOpenOffer=Éditer l'ordre
portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the fiat or altcoin payment. Contact Bisq developers on Keybase [HYPERLINK:https://keybase.io/team/bisq] or on the forum [HYPERLINK:https://bisq.community] for further assistance.\n\nError message: {0}
portfolio.closedTrades.deviation.help=Percentage price deviation from market
portfolio.pending.invalidDelayedPayoutTx=Il y a un problème causé par des transactions manquantes ou indisponibles. \n\nVeuillez ne pas envoyer de monnaie fiduciaire ou de monnaie numérique. Contactez les développeurs Bisq sur Keybase à https://keybase.io/team/bisq ou sur le forum [HYPERLINK:https://bisq.community] pour plus d'aide. \n\nMessage d'erreur: {0}
portfolio.pending.step1.waitForConf=Attendre la confirmation de la blockchain
portfolio.pending.step2_buyer.startPayment=Initier le paiement
@ -636,7 +651,7 @@ portfolio.pending.step2_buyer.confirmStart.headline=Confirmez que vous avez init
portfolio.pending.step2_buyer.confirmStart.msg=Avez-vous initié le {0} paiement auprès de votre partenaire de trading?
portfolio.pending.step2_buyer.confirmStart.yes=Oui, j'ai initié le paiement
portfolio.pending.step2_buyer.confirmStart.proof.warningTitle=You have not provided proof of payment
portfolio.pending.step2_buyer.confirmStart.proof.noneProvided=You have not entered the transaction ID and the transaction key.\n\nBy not providing this data the peer cannot use the auto-confirm feature to release the BTC as soon the XMR has been received.\nBeside that, Bisq requires that the sender of the XMR transaction is able to provide this information to the mediator or arbitrator in case of a dispute.\nSee more details on the Bisq wiki [HYPERLINK:https://bisq.wiki/Trading_Monero#Auto-confirming_trades].
portfolio.pending.step2_buyer.confirmStart.proof.noneProvided=Lorsque vous terminez une transaction BTC / XMR, vous pouvez utiliser la fonction de confirmation automatique pour vérifier si le montant correct de XMR a été envoyé à votre portefeuille, afin que Bisq puisse automatiquement marquer la transaction comme terminée et pour que tout le monde puisse aller plus vite. \n\nConfirmez automatiquement que les transactions XMR sont vérifiées sur au moins 2 nœuds d'explorateur de blocs XMR à l'aide de la clé de transaction fournie par l'expéditeur XMR. Par défaut, Bisq utilise un nœud d'explorateur de blocs exécuté par des contributeurs Bisq, mais nous vous recommandons d'exécuter votre propre nœud d'explorateur de blocs XMR pour maximiser la confidentialité et la sécurité. \n\nVous pouvez également définir le nombre maximum de BTC par transaction dans «Paramètres» pour confirmer automatiquement et le nombre de confirmations requises. \n\nPlus de détails sur Bisq Wiki (y compris comment configurer votre propre nœud d'explorateur de blocs): [HYPERLINK:https://bisq.wiki/Trading_Monero#Auto-confirming_trades]
portfolio.pending.step2_buyer.confirmStart.proof.invalidInput=Input is not a 32 byte hexadecimal value
portfolio.pending.step2_buyer.confirmStart.warningButton=Ignore and continue anyway
portfolio.pending.step2_seller.waitPayment.headline=En attende du paiement
@ -781,21 +796,21 @@ portfolio.pending.mediationResult.info.peerAccepted=Votre pair de trading a acce
portfolio.pending.mediationResult.button=Voir la résolution proposée
portfolio.pending.mediationResult.popup.headline=Résultat de la médiation pour la transaction avec l''ID: {0}
portfolio.pending.mediationResult.popup.headline.peerAccepted=Votre pair de trading a accepté la suggestion du médiateur pour la transaction {0}
portfolio.pending.mediationResult.popup.info=The mediator has suggested the following payout:\nYou receive: {0}\nYour trading peer receives: {1}\n\nYou can accept or reject this suggested payout.\n\nBy accepting, you sign the proposed payout transaction. If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\nIf one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nThe arbitrator may charge a small fee (fee maximum: the trader''s security deposit) as compensation for their work. Both traders agreeing to the mediator''s suggestion is the happy path—requesting arbitration is meant for exceptional circumstances, such as if a trader is sure the mediator did not make a fair payout suggestion (or if the other peer is unresponsive).\n\nMore details about the new arbitration model: [HYPERLINK:https://docs.bisq.network/trading-rules.html#arbitration]
portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout but it seems that your trading peer has not accepted it.\n\nOnce the lock time is over on {0} (block {1}), you can open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\nYou can find more details about the arbitration model at:[HYPERLINK:https://docs.bisq.network/trading-rules.html#arbitration]
portfolio.pending.mediationResult.popup.info=Les frais recommandés par le médiateur sont les suivants: \nVous paierez: {0} \nVotre partenaire commercial paiera: {1} \n\nVous pouvez accepter ou refuser ces frais de médiation. \n\nEn acceptant, vous avez vérifié l'opération de paiement du contrat. Si votre partenaire commercial accepte et vérifie également, le paiement sera effectué et la transaction sera clôturée. \n\nSi l'un de vous ou les deux refusent la proposition, vous devrez attendre le {2} (bloc {3}) pour commencer le deuxième tour de discussion sur le différend avec l'arbitre, et ce dernier étudiera à nouveau le cas. Le paiement sera fait en fonction de ses résultats. \n\nL'arbitre peut facturer une somme modique (la limite supérieure des honoraires: la marge de la transaction) en compensation de son travail. Les deux commerçants conviennent que la suggestion du médiateur est une voie agréable. La demande d'arbitrage concerne des circonstances particulières, par exemple si un professionnel est convaincu que le médiateur n'a pas fait une recommandation de d'indemnisation équitable (ou si l'autre partenaire n'a pas répondu). \n\nPlus de détails sur le nouveau modèle d'arbitrage: [HYPERLINK:https://docs.bisq.network/trading-rules.html#arbitration]
portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=Vous avez accepté la proposition de paiement du médiateur, mais il semble que votre contrepartie ne l'ait pas acceptée. \n\nUne fois que le temps de verrouillage atteint {0} (bloc {1}), vous pouvez ouvrir le second tour de litige pour que l'arbitre réétudie le cas et prend une nouvelle décision de dépenses. \n\nVous pouvez trouver plus d'informations sur le modèle d'arbitrage sur:[HYPERLINK:https://docs.bisq.network/trading-rules.html#arbitration]
portfolio.pending.mediationResult.popup.openArbitration=Refuser et demander un arbitrage
portfolio.pending.mediationResult.popup.alreadyAccepted=Vous avez déjà accepté
portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked and no trade fee has been paid. You can move this trade to failed trades.
portfolio.pending.failedTrade.maker.missingTakerFeeTx=The peer's taker fee transaction is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked. Your offer is still available to other traders, so you have not lost the maker fee. You can move this trade to failed trades.
portfolio.pending.failedTrade.missingDepositTx=The deposit transaction (the 2-of-2 multisig transaction) is missing.\n\nWithout this tx, the trade cannot be completed. No funds have been locked but your trade fee has been paid. You can make a request to be reimbursed the trade fee here: [HYPERLINK:https://github.com/bisq-network/support/issues]\n\nFeel free to move this trade to failed trades.
portfolio.pending.failedTrade.missingDepositTx=Cette transaction de marge (transaction multi-signature de 2 à 2) est manquante.\n\nSans ce tx, la transaction ne peut pas être complétée. Aucun fonds n'est bloqué, mais vos frais de transaction sont toujours payés. Vous pouvez lancer une demande de compensation des frais de transaction ici: [HYPERLINK:https://github.com/bisq-network/support/issues] \nN'hésitez pas à déplacer la transaction vers la transaction échouée.
portfolio.pending.failedTrade.buyer.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing, but funds have been locked in the deposit transaction.\n\nPlease do NOT send the fiat or altcoin payment to the BTC seller, because without the delayed payout tx, arbitration cannot be opened. Instead, open a mediation ticket with Cmd/Ctrl+o. The mediator should suggest that both peers each get back the the full amount of their security deposits (with seller receiving full trade amount back as well). This way, there is no security risk, and only trade fees are lost. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/bisq-network/support/issues]
portfolio.pending.failedTrade.seller.existingDepositTxButMissingDelayedPayoutTx=The delayed payout transaction is missing but funds have been locked in the deposit transaction.\n\nIf the buyer is also missing the delayed payout transaction, they will be instructed to NOT send the payment and open a mediation ticket instead. You should also open a mediation ticket with Cmd/Ctrl+o. \n\nIf the buyer has not sent payment yet, the mediator should suggest that both peers each get back the full amount of their security deposits (with seller receiving full trade amount back as well). Otherwise the trade amount should go to the buyer. \n\nYou can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/bisq-network/support/issues]
portfolio.pending.failedTrade.errorMsgSet=There was an error during trade protocol execution.\n\nError: {0}\n\nIt might be that this error is not critical, and the trade can be completed normally. If you are unsure, open a mediation ticket to get advice from Bisq mediators. \n\nIf the error was critical and the trade cannot be completed, you might have lost your trade fee. Request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/bisq-network/support/issues]
portfolio.pending.failedTrade.missingContract=The trade contract is not set.\n\nThe trade cannot be completed and you might have lost your trade fee. If so, you can request a reimbursement for lost trade fees here: [HYPERLINK:https://github.com/bisq-network/support/issues]
portfolio.pending.failedTrade.info.popup=The trade protocol encountered some problems.\n\n{0}
portfolio.pending.failedTrade.txChainInvalid.moveToFailed=The trade protocol encountered a serious problem.\n\n{0}\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time.
portfolio.pending.failedTrade.txChainValid.moveToFailed=The trade protocol encountered some problems.\n\n{0}\n\nThe trade transactions have been published and funds are locked. Only move the trade to failed trades if you are really sure. It might prevent options to resolve the problem.\n\nDo you want to move the trade to failed trades?\n\nYou cannot open mediation or arbitration from the failed trades view, but you can move a failed trade back to the open trades screen any time.
portfolio.pending.failedTrade.txChainValid.moveToFailed=Il y a des problèmes avec cet accord de transaction. \n\n{0}\n\nLa transaction de devis a été validée et les fonds ont été bloqués. Déplacer la transaction vers une transaction échouée uniquement si elle est certaine. Cela peut empêcher les options disponibles pour résoudre le problème. \n\nÊtes-vous sûr de vouloir déplacer cette transaction vers la transaction échouée? \n\nVous ne pouvez pas ouvrir une médiation ou un arbitrage dans une transaction échouée, mais vous pouvez déplacer une transaction échouée vers la transaction incomplète à tout moment.
portfolio.pending.failedTrade.moveTradeToFailedIcon.tooltip=Move trade to failed trades
portfolio.pending.failedTrade.warningIcon.tooltip=Click to open details about the issues of this trade
portfolio.failed.revertToPending.popup=Do you want to move this trade to open trades?
@ -886,13 +901,12 @@ funds.tx.noTxAvailable=Pas de transactions disponibles
funds.tx.revert=Revertir
funds.tx.txSent=Transaction envoyée avec succès vers une nouvelle adresse dans le portefeuille local bisq.
funds.tx.direction.self=Envoyé à vous même
funds.tx.daoTxFee=Frais de minage du tx de la DAO
funds.tx.daoTxFee=Frais de minage du tx BSQ
funds.tx.reimbursementRequestTxFee=Demande de remboursement
funds.tx.compensationRequestTxFee=Requête de compensation
funds.tx.dustAttackTx=dust reçues
funds.tx.dustAttackTx.popup=Cette transaction va envoyer un faible montant en BTC sur votre portefeuille ce qui pourrait constituer une tentative d'espionnage de la part de sociétés qui analyse la chaine.\n\nSi vous utilisez cette transaction de sortie des données dans le cadre d'une transaction représentant une dépense il sera alors possible de comprendre que vous êtes probablement aussi le propriétaire de l'autre adresse (coin merge).\n\nAfin de protéger votre vie privée, le portefeuille Bisq ne tient pas compte de ces "dust outputs" dans le cadre des transactions de vente et dans l'affichage de la balance. Vous pouvez définir une quantité seuil lorsqu'une "output" est considérée comme poussière dans les réglages.
####################################################################
# Support
####################################################################
@ -943,6 +957,7 @@ support.error=Le destinataire n''a pas pu traiter le message. Erreur : {0}
support.buyerAddress=Adresse de l'acheteur BTC
support.sellerAddress=Adresse du vendeur BTC
support.role=Rôle
support.agent=Support agent
support.state=État
support.closed=Fermé
support.open=Ouvert
@ -962,7 +977,7 @@ support.peerOpenedDispute=Votre pair de trading a fait une demande de litige.\n\
support.peerOpenedDisputeForMediation=Votre pair de trading a demandé une médiation.\n\n{0}\n\nVersion de Bisq: {1}
support.mediatorsDisputeSummary=System message: Mediator''s dispute summary:\n{0}
support.mediatorsAddress=Adresse du nœud du médiateur: {0}
support.warning.disputesWithInvalidDonationAddress=The delayed payout transaction has used an invalid receiver address. It does not match any of the DAO parameter values for the valid donation addresses.\n\nThis might be a scam attempt. Please inform the developers about that incident and do not close that case before the situation is resolved!\n\nAddress used in the dispute: {0}\n\nAll DAO param donation addresses: {1}\n\nTrade ID: {2}{3}
support.warning.disputesWithInvalidDonationAddress=La transaction de paiement différé a été utilisée pour une adresse de destinataire indisponible. Il ne correspond aux paramètres dans aucun DAO de l'adresse de donation valide. \n\nCela peut être une escroquerie. Veuillez informer le développeur et ne fermez pas le dossier jusqu'à ce que le problème est résolu! \n\nAdresse pour les litiges: {0} \n\nAdresse de donation dans tous les paramètres DAO: {1} \n\nTransaction: {2} {3}
support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute?
support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout.
@ -1013,7 +1028,7 @@ setting.preferences.daoOptions=Options DAO
setting.preferences.dao.resyncFromGenesis.label=Reconstituer l'état de la DAO à partir du tx genesis
setting.preferences.dao.resyncFromResources.label=Rebuild DAO state from resources
setting.preferences.dao.resyncFromResources.popup=After an application restart the Bisq network governance data will be reloaded from the seed nodes and the BSQ consensus state will be rebuilt from the latest resource files.
setting.preferences.dao.resyncFromGenesis.popup=A resync from genesis transaction can take considerable time and CPU resources. Are you sure you want to do that? Mostly a resync from latest resource files is sufficient and much faster.\n\nIf you proceed, after an application restart the Bisq network governance data will be reloaded from the seed nodes and the BSQ consensus state will be rebuilt from the genesis transaction.
setting.preferences.dao.resyncFromGenesis.popup=La synchronisation à partir de la transaction d'origine consomme beaucoup de temps et de ressources CPU. Êtes-vous sûr de vouloir resynchroniser ? En général, la resynchronisation à partir du dernier fichier de ressources est suffisante et plus rapide. \n\nAprès le redémarrage de l'application, les données de gestion du réseau Bisq seront rechargées à partir du nœud d'amorçage et l'état de synchronisation BSQ sera reconstruit à partir de la transaction initiale.
setting.preferences.dao.resyncFromGenesis.resync=Resync from genesis and shutdown
setting.preferences.dao.isDaoFullNode=Exécuter la DAO de Bisq en tant que full node
setting.preferences.dao.rpcUser=Nom d'utilisateur RPC
@ -1040,7 +1055,7 @@ settings.net.bitcoinNodesLabel=Nœuds Bitcoin Core pour se connecter à
settings.net.useProvidedNodesRadio=Utiliser les nœuds Bitcoin Core fournis
settings.net.usePublicNodesRadio=Utiliser le réseau Bitcoin public
settings.net.useCustomNodesRadio=Utiliser des nœuds Bitcoin Core personnalisés
settings.net.warn.usePublicNodes=If you use the public Bitcoin network you are exposed to a severe privacy problem caused by the broken bloom filter design and implementation which is used for SPV wallets like BitcoinJ (used in Bisq). Any full node you are connected to could find out that all your wallet addresses belong to one entity.\n\nPlease read more about the details at [HYPERLINK:https://bisq.network/blog/privacy-in-bitsquare].\n\nAre you sure you want to use the public nodes?
settings.net.warn.usePublicNodes=Si vous utilisez le réseau public Bitcoin, vous serez confronté à de sérieux problèmes de confidentialité. Ceci est dû à la conception et à la mise en œuvre du bloom filter cassé. Il convient aux portefeuilles SPV comme BitcoinJ (utilisé dans Bisq). Tout nœud complet que vous connectez peut découvrir que toutes les adresses de votre portefeuille appartiennent à une seule entité. \n\nPour plus d'informations, veuillez visiter: [HYPERLINK:https://bisq.network/blog/privacy-in-bitsquare] \n\nÊtes-vous sûr de vouloir utiliser un nœud public?
settings.net.warn.usePublicNodes.useProvided=Non, utiliser les nœuds fournis.
settings.net.warn.usePublicNodes.usePublic=Oui, utiliser un réseau public
settings.net.warn.useCustomNodes.B2XWarning=Veuillez vous assurer que votre nœud Bitcoin est un nœud Bitcoin Core de confiance !\n\nLa connexion à des nœuds qui ne respectent pas les règles du consensus de Bitcoin Core peut corrompre votre portefeuille et causer des problèmes dans le processus de trading.\n\nLes utilisateurs qui se connectent à des nœuds qui ne respectent pas les règles du consensus sont responsables des dommages qui en résultent. Tout litige qui en résulte sera tranché en faveur de l'autre pair. Aucune assistance technique ne sera apportée aux utilisateurs qui ignorent ces mécanismes d'alertes et de protections !
@ -1052,6 +1067,7 @@ settings.net.creationDateColumn=Établi
settings.net.connectionTypeColumn=In/Out
settings.net.sentDataLabel=Sent data statistics
settings.net.receivedDataLabel=Received data statistics
settings.net.chainHeightLabel=Latest BTC block height
settings.net.roundTripTimeColumn=Roundtrip
settings.net.sentBytesColumn=Envoyé
settings.net.receivedBytesColumn=Reçu
@ -1066,6 +1082,7 @@ settings.net.needRestart=Vous devez redémarrer l'application pour appliquer cet
settings.net.notKnownYet=Pas encore connu...
settings.net.sentData=Sent data: {0}, {1} messages, {2} messages/sec
settings.net.receivedData=Received data: {0}, {1} messages, {2} messages/sec
settings.net.chainHeight=Bisq: {0} | Peers: {1}
settings.net.ips=[IP address:port | host name:port | onion address:port] (séparés par des virgules). Le port peut être ignoré si utilisé par défaut (8333).
settings.net.seedNode=Seed node
settings.net.directPeer=Pair (direct)
@ -1074,7 +1091,7 @@ settings.net.inbound=inbound
settings.net.outbound=outbound
settings.net.reSyncSPVChainLabel=Resynchronisation de la chaîne SPV
settings.net.reSyncSPVChainButton=Supprimer le fichier SPV et resynchroniser
settings.net.reSyncSPVSuccess=Le fichier de la chaîne SPV sera supprimé au prochain démarrage. Vous devez redémarrer votre application maintenant.\n\nAprès le redémarrage, la resynchronisation avec le réseau peut prendre un certain temps, vous serez en mesure de voir toutes les transactions seulement une fois que la resynchronisation sera terminée.\n\nSelon le nombre de transactions et l"ancienneté de votre portefeuille, la resynchronisation peut prendre jusqu"à quelques heures et consomme 100% du CPU. N'interrompez pas le processus, sinon vous devez le recommencer.
settings.net.reSyncSPVSuccess=Are you sure you want to do an SPV resync? If you proceed, the SPV chain file will be deleted on the next startup.\n\nAfter the restart it can take a while to resync with the network and you will only see all transactions once the resync is completed.\n\nDepending on the number of transactions and the age of your wallet the resync can take up to a few hours and consumes 100% of CPU. Do not interrupt the process otherwise you have to repeat it.
settings.net.reSyncSPVAfterRestart=Le fichier de la chaîne SPV a été supprimé. Veuillez s'il vous plaît patienter. La resynchronisation avec le réseau peut nécessiter un certain temps.
settings.net.reSyncSPVAfterRestartCompleted=La resynchronisation est maintenant terminée. Veuillez redémarrer l'application.
settings.net.reSyncSPVFailed=Impossible de supprimer le fichier de la chaîne SPV.\nErreur: {0}
@ -1146,7 +1163,7 @@ setting.about.shortcuts.sendPrivateNotification=Envoyer une notification privée
setting.about.shortcuts.sendPrivateNotification.value=Open peer info at avatar and press: {0}
setting.info.headline=New XMR auto-confirm Feature
setting.info.msg=When selling BTC for XMR you can use the auto-confirm feature to verify that the correct amount of XMR was sent to your wallet so that Bisq can automatically mark the trade as complete, making trades quicker for everyone.\n\nAuto-confirm checks the XMR transaction on at least 2 XMR explorer nodes using the private transaction key provided by the XMR sender. By default, Bisq uses explorer nodes run by Bisq contributors, but we recommend running your own XMR explorer node for maximum privacy and security.\n\nYou can also set the maximum amount of BTC per trade to auto-confirm as well as the number of required confirmations here in Settings.\n\nSee more details (including how to set up your own explorer node) on the Bisq wiki [HYPERLINK:https://bisq.wiki/Trading_Monero#Auto-confirming_trades]
setting.info.msg=Vous n'avez pas saisi l'ID et la clé de transaction. \n\nSi vous ne fournissez pas ces données, votre partenaire commercial ne peut pas utiliser la fonction de confirmation automatique pour libérer rapidement le BTC après avoir reçu le XMR.\nEn outre, Bisq demande aux expéditeurs XMR de fournir ces informations aux médiateurs et aux arbitres en cas de litige.\nPlus de détails sont dans Bisq Wiki: [HYPERLINK:https://bisq.wiki/Trading_Monero#Auto-confirming_trades]
####################################################################
# Account
####################################################################
@ -1161,9 +1178,19 @@ account.menu.paymentAccount=Comptes en devise nationale
account.menu.altCoinsAccountView=Compte Altcoins
account.menu.password=Mot de passe du portefeuille
account.menu.seedWords=Seed du portefeuille
account.menu.walletInfo=Wallet info
account.menu.backup=Sauvegarde
account.menu.notifications=Notifications
account.menu.walletInfo.balance.headLine=Wallet balances
account.menu.walletInfo.balance.info=This shows the internal wallet balance including unconfirmed transactions.\nFor BTC, the internal wallet balance shown below should match the sum of the 'Available' and 'Reserved' balances shown in the top right of this window.
account.menu.walletInfo.xpub.headLine=Watch keys (xpub keys)
account.menu.walletInfo.walletSelector={0} {1} wallet
account.menu.walletInfo.path.headLine=HD keychain paths
account.menu.walletInfo.path.info=If you import seed words into another wallet (like Electrum), you'll need to define the path. This should only be done in emergency cases when you lose access to the Bisq wallet and data directory.\nKeep in mind that spending funds from a non-Bisq wallet can bungle the internal Bisq data structures associated with the wallet data, which can lead to failed trades.\n\nNEVER send BSQ from a non-Bisq wallet, as it will probably lead to an invalid BSQ transaction and losing your BSQ.
account.menu.walletInfo.openDetails=Show raw wallet details and private keys
## TODO should we rename the following to a gereric name?
account.arbitratorRegistration.pubKey=Clé publique
@ -1181,23 +1208,23 @@ account.altcoin.yourAltcoinAccounts=Vos comptes altcoin
account.altcoin.popup.wallet.msg=Veuillez vous assurer que vous respectez les exigences relatives à l''utilisation des {0} portefeuilles, selon les conditions présentées sur la page {1} du site.\nL''utilisation des portefeuilles provenant de plateformes de trading centralisées où (a) vous ne contrôlez pas vos clés ou (b) qui ne disposent pas d''un portefeuille compatible est risquée : cela peut entraîner la perte des fonds échangés!\nLe médiateur et l''arbitre ne sont pas des spécialistes {2} et ne pourront pas intervenir dans ce cas.
account.altcoin.popup.wallet.confirm=Je comprends et confirme que je sais quel portefeuille je dois utiliser.
# suppress inspection "UnusedProperty"
account.altcoin.popup.upx.msg=Trading UPX on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending UPX, you need to use either the official uPlexa GUI wallet or uPlexa CLI wallet with the store-tx-info flag enabled (default in new versions). Please be sure you can access the tx key as that would be required in case of a dispute.\nuplexa-wallet-cli (use the command get_tx_key)\nuplexa-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nAt normal block explorers the transfer is not verifiable.\n\nYou need to provide the arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The UPX sender is responsible for providing verification of the UPX transfer to the arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process visit uPlexa discord channel (https://discord.gg/vhdNSrV) or the uPlexa Telegram Chat (https://t.me/uplexaOfficial) to find more information.
account.altcoin.popup.upx.msg=Pour échanger UPX sur Bisq, vous devez comprendre et respecter les exigences suivantes: \n\nPour envoyer UPX, vous devez utiliser le portefeuille officiel UPXmA GUI ou le portefeuille UPXmA CLI avec le logo store-tx-info activé (valeur par défaut dans la nouvelle version) . Assurez-vous d'avoir accès à la clé tx, car elle est nécessaire dans l'état du litige. monero-wallet-cli (à l'aide de la commande get_Tx_key) monero-wallet-gui: sur la page Avancé> Preuve / Vérification. \n\nCes transactions ne sont pas vérifiables dans le navigateur blockchain ordinaire. \n\nEn cas de litige, vous devez fournir à l'arbitre les informations suivantes: \n\n- Clé privée Tx- hachage de transaction- adresse publique du destinataire \n\nSi vous ne fournissez pas les informations ci-dessus ou si vous utilisez un portefeuille incompatible, vous perdrez le litige. En cas de litige, l'expéditeur UPX est responsable de fournir la vérification du transfert UPX à l'arbitre. \n\nAucun paiement d'identité n'est requis, juste une adresse publique commune. \n\nSi vous n'êtes pas sûr du processus, veuillez visiter le canal UPXmA Discord (https://discord.gg/vhdNSrV) ou le groupe d'échanges Telegram (https://t.me/uplexaOfficial) pour plus d'informations.
# suppress inspection "UnusedProperty"
account.altcoin.popup.arq.msg=Le trading d'ARQ sur Bisq exige que vous compreniez et remplissiez les exigences suivantes:\n\nPour envoyer des ARQ, vous devez utiliser soit le portefeuille officiel ArQmA GUI soit le portefeuille ArQmA CLI avec le flag store-tx-info activé (par défaut dans les nouvelles versions). Veuillez vous assurer que vous pouvez accéder à la tx key car cela pourrait être nécessaire en cas de litige.\narqma-wallet-cli (utiliser la commande get_tx_key)\narqma-wallet-gui (allez dans l'onglet historique et cliquez sur le bouton (P) pour accéder à la preuve de paiement).\n\nAvec un l'explorateur de bloc normal, le transfert n'est pas vérifiable.\n\nVous devez fournir au médiateur ou à l'arbitre les données suivantes en cas de litige:\n- Le tx de la clé privée\n- Le hash de la transaction\n- L'adresse publique du destinataire\n\nSi vous manquez de communiquer les données ci-dessus ou si vous utilisez un portefeuille incompatible, vous perdrez le litige. L'expéditeur des ARQ est responsable de la transmission au médiateur ou à l'arbitre de la vérification du transfert ces informations relatives au litige.\n\nIl n'est pas nécessaire de fournir l'ID du paiement, seulement l'adresse publique normale.\nSi vous n'êtes pas sûr de ce processus, visitez le canal discord ArQmA (https://discord.gg/s9BQpJT) ou le forum ArQmA (https://labs.arqma.com) pour obtenir plus d'informations.
# suppress inspection "UnusedProperty"
account.altcoin.popup.xmr.msg=Trading XMR on Bisq requires that you understand the following requirement.\n\nIf selling XMR, you must be able to provide the following information to a mediator or arbitrator in case of a dispute:\n- the transaction key (Tx Key, Tx Secret Key or Tx Private Key)\n- the transaction ID (Tx ID or Tx Hash)\n- the destination address (recipient's address)\n\nSee the wiki for details on where to find this information on popular Monero wallets [HYPERLINK:https://bisq.wiki/Trading_Monero#Proving_payments].\nFailure to provide the required transaction data will result in losing disputes.\n\nAlso note that Bisq now offers automatic confirming for XMR transactions to make trades quicker, but you need to enable it in Settings.\n\nSee the wiki for more information about the auto-confirm feature: [HYPERLINK:https://bisq.wiki/Trading_Monero#Auto-confirming_trades].
account.altcoin.popup.xmr.msg=Pour échanger XMR sur Bisq, vous devez comprendre et respecter les exigences suivantes: \n\nSi vous vendez XMR, en cas de litige, vous devez fournir au médiateur ou à l'arbitre les informations suivantes: - clé de transaction (clé publique Tx, clé Tx, clé privée Tx) - ID de transaction (ID Tx Ou hachage Tx) - Adresse de destination de la transaction (adresse du destinataire) \n\nConsultez plus d'informations sur le portefeuille Monero dans le wiki: https: //bisq.wiki/Trading_Monero#Proving_payments \n\nSi vous ne fournissez pas les données de transaction requises, vous serez directement jugé échoue dans le litige. \n\nNotez également que Bisq fournit désormais la fonction de confirmation automatique des transactions XMR pour effectuer plus rapidement des transactions, mais vous devez l'activer dans les paramètres. \n\nPour plus d'informations sur la fonction de confirmation automatique, veuillez consulter le Wiki: [HYPERLINK:https://bisq.wiki/Trading_Monero#Auto-confirming_trades]
# suppress inspection "UnusedProperty"
account.altcoin.popup.msr.msg=Trading MSR on Bisq requires that you understand and fulfill the following requirements:\n\nFor sending MSR, you need to use either the official Masari GUI wallet, Masari CLI wallet with the store-tx-info flag enabled (enabled by default) or the Masari web wallet (https://wallet.getmasari.org). Please be sure you can access the tx key as that would be required in case of a dispute.\nmasari-wallet-cli (use the command get_tx_key)\nmasari-wallet-gui (go to history tab and click on the (P) button for payment proof)\n\nMasari Web Wallet (goto Account -> transaction history and view details on your sent transaction)\n\nVerification can be accomplished in-wallet.\nmasari-wallet-cli : using command (check_tx_key).\nmasari-wallet-gui : on the Advanced > Prove/Check page.\nVerification can be accomplished in the block explorer \nOpen block explorer (https://explorer.getmasari.org), use the search bar to find your transaction hash.\nOnce transaction is found, scroll to bottom to the 'Prove Sending' area and fill in details as needed.\nYou need to provide the mediator or arbitrator the following data in case of a dispute:\n- The tx private key\n- The transaction hash\n- The recipient's public address\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The MSR sender is responsible for providing verification of the MSR transfer to the mediator or arbitrator in case of a dispute.\n\nThere is no payment ID required, just the normal public address.\nIf you are not sure about that process, ask for help on the Official Masari Discord (https://discord.gg/sMCwMqs).
account.altcoin.popup.msr.msg=Le navigateur blockchain pour échanger MSR sur Bisq vous oblige à comprendre et à respecter les exigences suivantes: \n\nLors de l'envoi de MSR, vous devez utiliser le portefeuille officiel Masari GUI, le portefeuille Masari CLI avec le logo store-tx-info activé (activé par défaut) ou le portefeuille web Masari (https://wallet.getmasari.org). Assurez-vous d'avoir accès à la clé tx, car cela est nécessaire en cas de litige. monero-wallet-cli (à l'aide de la commande get_Tx_key) monero-wallet-gui: sur la page Avancé> Preuve / Vérification. \n\nLe portefeuille web Masari (accédez à Compte-> Historique des transactions et vérifiez les détails de la transaction que vous avez envoyés) \n\nLa vérification peut être effectuée dans le portefeuille. monero-wallet-cli: utilisez la commande (check_tx_key). monero-wallet-gui: sur la page Avancé> Preuve / Vérification La vérification peut être effectuée dans le navigateur blockchain. Ouvrez le navigateur blockchain (https://explorer.getmasari.org) et utilisez la barre de recherche pour trouver votre hachage de transaction. Une fois que vous avez trouvé la transaction, faites défiler jusqu'à la zone «certificat à envoyer» en bas et remplissez les détails requis. En cas de litige, vous devez fournir les informations suivantes au médiateur ou à l'arbitre: - Clé privée Tx- Hachage de transaction- Adresse publique du destinataire \n\nAucun ID de transaction n'est requis, seule une adresse publique normale est requise. Si vous ne fournissez pas les informations ci-dessus ou si vous utilisez un portefeuille incompatible, vous perdrez le litige. En cas de litige, l'expéditeur XMR est responsable de fournir la vérification du transfert XMR au médiateur ou un arbitre. \n\nSi vous n'êtes pas sûr du processus, veuillez visiter le Masari Discord officiel (https://discord.gg/sMCwMqs) pour obtenir de l'aide.
# suppress inspection "UnusedProperty"
account.altcoin.popup.blur.msg=Trading BLUR on Bisq requires that you understand and fulfill the following requirements:\n\nTo send BLUR you must use the Blur Network CLI or GUI Wallet. \n\nIf you are using the CLI wallet, a transaction hash (tx ID) will be displayed after a transfer is sent. You must save this information. Immediately after sending the transfer, you must use the command 'get_tx_key' to retrieve the transaction private key. If you fail to perform this step, you may not be able to retrieve the key later. \n\nIf you are using the Blur Network GUI Wallet, the transaction private key and transaction ID can be found conveniently in the "History" tab. Immediately after sending, locate the transaction of interest. Click the "?" symbol in the lower-right corner of the box containing the transaction. You must save this information. \n\nIn the event that arbitration is necessary, you must present the following to an mediator or arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The mediator or arbitrator will then verify the BLUR transfer using the Blur Transaction Viewer (https://blur.cash/#tx-viewer).\n\nFailure to provide the required information to the mediator or arbitrator will result in losing the dispute case. In all cases of dispute, the BLUR sender bears 100% of the burden of responsibility in verifying transactions to an mediator or arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Blur Network Discord (https://discord.gg/dMWaqVW).
account.altcoin.popup.blur.msg=ntes: \n\nPour envoyer des informations anonymes, vous devez utiliser un portefeuille CLI ou GUI de réseau anonyme. Si vous utilisez un portefeuille CLI, le hachage de la transaction (tx ID) sera affiché après la transmission. Vous devez enregistrer ces informations. Après l'envoi de la transmission, vous devez immédiatement utiliser la commande «get_tx_key» pour récupérer la clé privée de la transaction. Si vous ne parvenez pas à effectuer cette étape, vous ne pourrez peut-être pas récupérer la clé ultérieurement. \n\nSi vous utilisez le portefeuille Blur Network GUI, vous pouvez facilement trouver la clé privée de transaction et l'ID de transaction dans l'onglet «Historique». Localisez la transaction d'intérêt immédiatement après l'envoi. Cliquez sur le symbole «?» dans le coin inférieur droit de la boîte contenant la transaction. Vous devez enregistrer ces informations. \n\nSi un arbitrage est nécessaire, vous devez fournir les informations suivantes au médiateur ou à l'arbitre: 1.) ID de transaction, 2.) clé privée de transaction, 3.) adresse du destinataire. Le processus de médiation ou d'arbitrage utilisera le visualiseur de transactions BLUR (https://blur.cash/#tx-viewer) pour vérifier les transferts BLUR. \n\nLe défaut de fournir les informations nécessaires au médiateur ou à l'arbitre entraînera la perte du litige. Dans tous les litiges, l'expéditeur anonyme porte à 100% la responsabilité de vérifier la transaction avec le médiateur ou l'arbitre. \n\nSi vous ne comprenez pas ces exigences, n'échangez pas sur Bisq. Tout d'abord, demandez de l'aide dans Blur Network Discord (https://discord.gg/dMWaqVW).
# suppress inspection "UnusedProperty"
account.altcoin.popup.solo.msg=Trading Solo on Bisq requires that you understand and fulfill the following requirements:\n\nTo send Solo you must use the Solo Network CLI Wallet. \n\nIf you are using the CLI wallet, a transaction hash (tx ID) will be displayed after a transfer is sent. You must save this information. Immediately after sending the transfer, you must use the command 'get_tx_key' to retrieve the transaction private key. If you fail to perform this step, you may not be able to retrieve the key later. \n\nIn the event that arbitration is necessary, you must present the following to an mediator or arbitrator: 1.) the transaction ID, 2.) the transaction private key, and 3.) the recipient's address. The mediator or arbitrator will then verify the Solo transfer using the Solo Block Explorer by searching for the transaction and then using the "Prove sending" function (https://explorer.minesolo.com/).\n\nfailure to provide the required information to the mediator or arbitrator will result in losing the dispute case. In all cases of dispute, the Solo sender bears 100% of the burden of responsibility in verifying transactions to an mediator or arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Solo Network Discord (https://discord.minesolo.com/).
account.altcoin.popup.solo.msg=Echanger Solo sur Bisq nécessite que vous compreniez et remplissiez les conditions suivantes: \n\nPour envoyer Solo, vous devez utiliser la version 5.1.3 ou supérieure du portefeuille Web Solo CLI. \n\nSi vous utilisez un portefeuille CLI, après l'envoi de la transaction, ID de transaction sera affiché. Vous devez enregistrer ces informations. Après avoir envoyé la transaction, vous devez immédiatement utiliser la commande «get_tx_key» pour récupérer la clé de transaction. Si vous ne parvenez pas à effectuer cette étape, vous ne pourrez peut-être pas récupérer la clé ultérieurement. \n\nSi un arbitrage est nécessaire, vous devez fournir les informations suivantes au médiateur ou à l'arbitre: 1) ID de transaction, 2) clé de transaction, 3) adresse du destinataire. Le médiateur ou l'arbitre utilisera lexplorateur de blocs Solo (https://explorer.Solo.org) pour rechercher des transactions puis utilisera la fonction «envoyer une preuve» (https://explorer.minesolo.com/). \n\nLe défaut de fournir les informations nécessaires au médiateur ou à l'arbitre entraînera la perte de l'affaire. Dans tous les cas de litige, l'expéditeur de QWC assume à 100% la responsabilité lors de la vérification de la transaction avec le médiateur ou l'arbitre. \n\nSi vous ne comprenez pas ces exigences, n'échangez pas sur Bisq. Tout d'abord, demandez de l'aide dans Solo Discord (https://discord.minesolo.com/).
# suppress inspection "UnusedProperty"
account.altcoin.popup.cash2.msg=Trading CASH2 on Bisq requires that you understand and fulfill the following requirements:\n\nTo send CASH2 you must use the Cash2 Wallet version 3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'getTxKey' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an mediator or arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's Cash2 address. The mediator or arbitrator will then verify the CASH2 transfer using the Cash2 Block Explorer (https://blocks.cash2.org).\n\nFailure to provide the required information to the mediator or arbitrator will result in losing the dispute case. In all cases of dispute, the CASH2 sender bears 100% of the burden of responsibility in verifying transactions to an mediator or arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the Cash2 Discord (https://discord.gg/FGfXAYN).
account.altcoin.popup.cash2.msg=Pour échanger CASH2 sur Bisq, vous devez comprendre et respecter les exigences suivantes: \n\nPour envoyer CASH2, vous devez utiliser la version 3 ou supérieure du portefeuille CASH2. \n\nAprès l'envoi de la transaction, ID de la transaction s'affiche. Vous devez enregistrer ces informations. Après avoir envoyé la transaction, vous devez utiliser la commande «getTxKey» dans simplewallet pour récupérer immédiatement la clé de transaction.\n\nSi un arbitrage est nécessaire, vous devez fournir les informations suivantes au médiateur ou à l'arbitre: 1) ID de transaction, 2) clé de transaction, 3) adresse CASH2 du destinataire. Le médiateur ou l'arbitre utilisera lexplorateur de blocs CASH2 (https://blocks.cash2.org) pour vérifier le transfert CASH2. \n\nLe défaut de fournir les informations nécessaires au médiateur ou à l'arbitre entraînera la perte de l'affaire. Dans tous les cas de litige, l'expéditeur de CASH2 assume à 100% la responsabilité lors de la vérification de la transaction avec le médiateur ou l'arbitre. \n\nSi vous ne comprenez pas ces exigences, n'échangez pas sur Bisq. Tout d'abord, demandez de l'aide dans le Discord Cash2 (https://discord.gg/FGfXAYN).
# suppress inspection "UnusedProperty"
account.altcoin.popup.qwertycoin.msg=Trading Qwertycoin on Bisq requires that you understand and fulfill the following requirements:\n\nTo send QWC you must use the official QWC Wallet version 5.1.3 or higher. \n\nAfter a transaction is sent, the transaction ID will be displayed. You must save this information. Immediately after sending the transaction, you must use the command 'get_Tx_Key' in simplewallet to retrieve the transaction secret key. \n\nIn the event that arbitration is necessary, you must present the following to an mediator or arbitrator: 1) the transaction ID, 2) the transaction secret key, and 3) the recipient's QWC address. The mediator or arbitrator will then verify the QWC transfer using the QWC Block Explorer (https://explorer.qwertycoin.org).\n\nFailure to provide the required information to the mediator or arbitrator will result in losing the dispute case. In all cases of dispute, the QWC sender bears 100% of the burden of responsibility in verifying transactions to an mediator or arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the QWC Discord (https://discord.gg/rUkfnpC).
account.altcoin.popup.qwertycoin.msg=Pour échanger Qwertycoin sur Bisq, vous devez comprendre et respecter les exigences suivantes: \n\nPour envoyer Qwertycoin, vous devez utiliser la version 5.1.3 ou supérieure du portefeuille Qwertycoin. \n\nAprès l'envoi de la transaction, ID de la transaction s'affiche. Vous devez enregistrer ces informations. Après avoir envoyé la transaction, vous devez utiliser la commande «get_Tx_Key» dans simplewallet pour récupérer immédiatement la clé de transaction. \n\nSi un arbitrage est nécessaire, vous devez fournir les informations suivantes au médiateur ou à l'arbitre: 1) ID de transaction, 2) clé de transaction, 3) adresse QWC du destinataire. Le médiateur ou l'arbitre utilisera lexplorateur de blocs QWC (https://explorer.qwertycoin.org) pour vérifier les transferts QWC. \n\nLe défaut de fournir les informations nécessaires au médiateur ou à l'arbitre entraînera la perte de l'affaire. Dans tous les cas de litige, l'expéditeur de QWC assume à 100% la responsabilité lors de la vérification de la transaction avec le médiateur ou l'arbitre. \n\nSi vous ne comprenez pas ces exigences, n'échangez pas sur Bisq. Tout d'abord, demandez de l'aide dans QWC Discord (https://discord.gg/rUkfnpC).
# suppress inspection "UnusedProperty"
account.altcoin.popup.drgl.msg=Trading Dragonglass on Bisq requires that you understand and fulfill the following requirements:\n\nBecause of the privacy Dragonglass provides, a transaction is not verifiable on the public blockchain. If required, you can prove your payment through the use of your TXN-Private-Key.\nThe TXN-Private Key is a one-time key automatically generated for every transaction that can only be accessed from within your DRGL wallet.\nEither by DRGL-wallet GUI (inside transaction details dialog) or by the Dragonglass CLI simplewallet (using command "get_tx_key").\n\nDRGL version 'Oathkeeper' and higher are REQUIRED for both.\n\nIn case of a dispute, you must provide the mediator or arbitrator the following data:\n- The TXN-Private key\n- The transaction hash\n- The recipient's public address\n\nVerification of payment can be made using the above data as inputs at (http://drgl.info/#check_txn).\n\nFailure to provide the above data, or if you used an incompatible wallet, will result in losing the dispute case. The Dragonglass sender is responsible for providing verification of the DRGL transfer to the mediator or arbitrator in case of a dispute. Use of PaymentID is not required.\n\nIf you are unsure about any part of this process, visit Dragonglass on Discord (http://discord.drgl.info) for help.
account.altcoin.popup.drgl.msg=Echanger Dragonglass sur Bisq vous oblige à comprendre et à respecter les exigences suivantes: ~\n\nComme Dragonglass offre une protection de la confidentialité, les transactions ne peuvent pas être vérifiées sur la blockchain publique. Si nécessaire, vous pouvez prouver votre paiement en utilisant votre TXN-Private-Key. TXN-Private est une clé d'un temps générée automatiquement, utilisée pour chaque transaction qui est accessible uniquement à partir du portefeuille DESP. Soit via DRGL-wallet GUI (boîte de dialogue des détails de transaction interne), soit via Dragonglass CLI simplewallet (en utilisant la commande "get_tx_key"). \n\nLes deux nécessitent la version DRGL de «Oathkeeper» ou supérieure. \n\nEn cas de litige, vous devez fournir les informations suivantes au médiateur ou à l'arbitre: \n\n- txn-Privite-ket- hachage de transaction- adresse publique du destinataire ~\n\nLa vérification du paiement peut utiliser les données ci-dessus comme entrée http://drgl.info/#check_txn.\n\nSi vous ne fournissez pas les informations ci-dessus ou si vous utilisez un portefeuille incompatible, vous perdrez le litige. L'expéditeur Dragonglass est responsable de fournir la vérification de transfert DRGL au médiateur ou à l'arbitre en cas de litige. Aucun ID de paiement n'est requis. \n\nSi vous n'êtes pas sûr d'une partie de ce processus, veuillez visiter Dragonglass sur (http://discord.drgl.info) pour obtenir de l'aide.
# suppress inspection "UnusedProperty"
account.altcoin.popup.ZEC.msg=Lors de l'utilisation de Zcash, vous ne pouvez utiliser que les adresses transparentes (commençant par t), et non les z-adresses (privées), car le médiateur ou l'arbitre ne seraient pas en mesure de vérifier la transaction avec les z-adresses.
# suppress inspection "UnusedProperty"
@ -1207,13 +1234,13 @@ account.altcoin.popup.grin.msg=GRIN nécessite un échange interactif entre l'é
# suppress inspection "UnusedProperty"
account.altcoin.popup.beam.msg=BEAM nécessite un processus interactif entre l'émetteur et le récepteur pour créer la transaction.\n\nAssurez-vous de suivre les instructions de la page Web du projet BEAM pour envoyer et recevoir les BEAM de façon fiable (le récepteur doit être en ligne pendant au moins un certain temps).\n\nL'expéditeur de BEAM est tenu de fournir la preuve qu'il a envoyé BEAM avec succès. Assurez-vous d'utiliser un portefeuille qui peut produire une telle preuve. Si le portefeuille ne peut fournir la preuve, un litige potentiel sera résolu en faveur du récepteur des BEAM.
# suppress inspection "UnusedProperty"
account.altcoin.popup.pars.msg=Trading ParsiCoin on Bisq requires that you understand and fulfill the following requirements:\n\nTo send PARS you must use the official ParsiCoin Wallet version 3.0.0 or higher. \n\nYou can Check your Transaction Hash and Transaction Key on Transactions Section on your GUI Wallet (ParsiPay) You need to right Click on the Transaction and then click on show details. \n\nIn the event that arbitration is necessary, you must present the following to an mediator or arbitrator: 1) the Transaction Hash, 2) the Transaction Key, and 3) the recipient's PARS address. The mediator or arbitrator will then verify the PARS transfer using the ParsiCoin Block Explorer (http://explorer.parsicoin.net/#check_payment).\n\nFailure to provide the required information to the mediator or arbitrator will result in losing the dispute case. In all cases of dispute, the ParsiCoin sender bears 100% of the burden of responsibility in verifying transactions to an mediator or arbitrator. \n\nIf you do not understand these requirements, do not trade on Bisq. First, seek help at the ParsiCoin Discord (https://discord.gg/c7qmFNh).
account.altcoin.popup.pars.msg=Echanger ParsiCoin sur Bisq nécessite que vous compreniez et remplissiez les conditions suivantes: \n\nPour envoyer PARS, vous devez utiliser la version 3.0.0 ou supérieure du portefeuille ParsiCoin officiel. \n\nVous pouvez vérifier votre hachage de transaction et votre clé de transaction dans la section transaction du portefeuille GUI (ParsiPay). Vous devez cliquer avec le bouton droit de la souris sur «Transaction» puis cliquer sur «Afficher les détails». \n\nSi l'arbitrage est à 100% nécessaire, vous devez fournir au médiateur ou à l'arbitre les éléments suivants: 1) hachage de transaction, 2) clé de transaction et 3) adresse PARS du destinataire. Le médiateur ou l'arbitre utilisera lexplorateur de blocs ParsiCoin (http://explorer.parsicoin.net/#check_payment) pour vérifier les transmissions PARS. \n\nSi vous ne comprenez pas ces exigences, n'échangez pas sur Bisq. Tout d'abord, demandez de l'aide sur le ParsiCoin Discord (https://discord.gg/c7qmFNh).
# suppress inspection "UnusedProperty"
account.altcoin.popup.blk-burnt.msg=To trade burnt blackcoins, you need to know the following:\n\nBurnt blackcoins are unspendable. To trade them on Bisq, output scripts need to be in the form: OP_RETURN OP_PUSHDATA, followed by associated data bytes which, after being hex-encoded, constitute addresses. For example, burnt blackcoins with an address 666f6f (“foo” in UTF-8) will have the following script:\n\nOP_RETURN OP_PUSHDATA 666f6f\n\nTo create burnt blackcoins, one may use the “burn” RPC command available in some wallets.\n\nFor possible use cases, one may look at https://ibo.laboratorium.ee .\n\nAs burnt blackcoins are unspendable, they can not be reselled. “Selling” burnt blackcoins means burning ordinary blackcoins (with associated data equal to the destination address).\n\nIn case of a dispute, the BLK seller needs to provide the transaction hash.
account.altcoin.popup.blk-burnt.msg=Pour échanger les monnaies brûlées, vous devez savoir ce qui suit: \n\nLes monnaies brûlées ne peuvent pas être dépensée. Pour les échanger sur Bisq, le script de sortie doit prendre la forme suivante: OP_RETURN OP_PUSHDATA, suivi des octets de données pertinents, ces octets forment l'adresse après le codage hexadécimal. Par exemple, une devise brûlée avec l'adresse 666f6f ("foo" en UTF-8) aura le script suivant: \n\nOP_RETURN OP_PUSHDATA 666f6f \n\nPour créer de la monnaie brûlée, vous pouvez utiliser la commande RPC «brûler», disponible dans certains portefeuilles. \n\nPour d'éventuelles situations, vous pouvez vérifier https://ibo.laboratorium.ee \n\nPuisque la monnaie brûlée ne peut pas être utilisée, elle ne peut pas être revendue. «Vendre» une devise brûlée signifie brûler la devise d'origine (données associées à l'adresse de destination). \n\nEn cas de litige, le vendeur BLK doit fournir le hachage de la transaction.
# suppress inspection "UnusedProperty"
account.altcoin.popup.liquidbitcoin.msg=Trading L-BTC on Bisq requires that you understand the following:\n\nWhen receiving L-BTC for a trade on Bisq, you cannot use the mobile Blockstream Green Wallet app or a custodial/exchange wallet. You must only receive L-BTC into the Liquid Elements Core wallet, or another L-BTC wallet which allows you to obtain the blinding key for your blinded L-BTC address.\n\nIn the event mediation is necessary, or if a trade dispute arises, you must disclose the blinding key for your receiving L-BTC address to the Bisq mediator or refund agent so they can verify the details of your Confidential Transaction on their own Elements Core full node.\n\nFailure to provide the required information to the mediator or refund agent will result in losing the dispute case. In all cases of dispute, the L-BTC receiver bears 100% of the burden of responsibility in providing cryptographic proof to the mediator or refund agent.\n\nIf you do not understand these requirements, do not trade L-BTC on Bisq.
account.altcoin.popup.liquidbitcoin.msg=Pour échanger L-BTC sur Bisq, vous devez comprendre les termes suivants: \n\nLorsque vous acceptez des transactions L-BTC sur Bisq, vous ne pouvez pas utiliser Blockstream Green Wallet sur le téléphone mobile ou un portefeuille de dépôt / commercial. Vous ne devez recevoir du L-BTC que dans le portefeuille Liquid Elements Core ou un autre portefeuille L-BTC avec une adresse L-BTC et une clé de sécurité qui vous permettre d'être anonyme. \n\nEn cas de médiation ou en cas de litige de transaction, vous devez divulguer la clé de sécurité de l'adresse L-BTC au médiateur Bisq ou à l'agent de remboursement afin qu'ils puissent vérifier les détails de votre transaction anonyme sur leur propre nœud complet Elements Core. \n\nSi vous ne comprenez pas ou ne comprenez pas ces exigences, n'échangez pas de L-BTC sur Bisq.
account.fiat.yourFiatAccounts=Vos comptes en devise nationale
@ -1235,7 +1262,7 @@ account.password.info=Avec la protection par mot de passe, vous devrez entrer vo
account.seed.backup.title=Sauvegarder les mots composant la seed de votre portefeuille
account.seed.info=Veuillez noter les mots de la seed du portefeuille ainsi que la date! Vous pouvez récupérer votre portefeuille à tout moment avec les mots de la seed et la date.\nLes mêmes mots-clés de la seed sont utilisés pour les portefeuilles BTC et BSQ.\n\nVous devriez écrire les mots de la seed sur une feuille de papier. Ne les enregistrez pas sur votre ordinateur.\n\nVeuillez noter que les mots de la seed ne remplacent PAS une sauvegarde.\nVous devez créer une sauvegarde de l'intégralité du répertoire de l'application à partir de l'écran \"Compte/Sauvergarde\" pour restaurer correctement les données de l'application.\nL'importation de mots de la seed n'est recommandée qu'en cas d'urgence. L'application ne sera pas fonctionnelle sans une sauvegarde adéquate des fichiers et des clés de la base de données !
account.seed.backup.warning=Please note that the seed words are NOT a replacement for a backup.\nYou need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\nImporting seed words is only recommended for emergency cases. The application will not be functional without a proper backup of the database files and keys!\n\nSee the wiki page [HYPERLINK:https://bisq.wiki/Backing_up_application_data] for extended info.
account.seed.backup.warning=Veuillez noter que les mots de départ ne peuvent pas remplacer les sauvegardes. Vous devez sauvegarder tout le répertoire de l'application (dans l'onglet «Compte / Sauvegarde») pour restaurer l'état et les données de l'application. L'importation de mots de départ n'est recommandée qu'en cas d'urgence. Si le fichier de base de données et la clé ne sont pas correctement sauvegardés, l'application ne fonctionnera pas! \n\nVoir plus d'informations sur le wiki Bisq: [HYPERLINK:https://bisq.wiki/Backing_up_application_data]
account.seed.warn.noPw.msg=Vous n'avez pas configuré un mot de passe de portefeuille qui protégerait l'affichage des mots composant la seed.\n\nVoulez-vous afficher les mots composant la seed?
account.seed.warn.noPw.yes=Oui, et ne me le demander plus à l'avenir
account.seed.enterPw=Entrer le mot de passe afficher les mots composant la seed
@ -1796,7 +1823,7 @@ dao.wallet.send.setDestinationAddress=Remplissez votre adresse de destination
dao.wallet.send.send=Envoyer des fonds en BSQ
dao.wallet.send.sendBtc=Envoyer des fonds en BTC
dao.wallet.send.sendFunds.headline=Confirmer la demande de retrait
dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequired transaction fee is: {2} ({3} satoshis/vbyte)\nTransaction vsize: {4} vKb\n\nThe recipient will receive: {5}\n\nAre you sure you want to withdraw that amount?
dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequired mining fee is: {2} ({3} satoshis/vbyte)\nTransaction vsize: {4} vKb\n\nThe recipient will receive: {5}\n\nAre you sure you want to withdraw that amount?
dao.wallet.chainHeightSynced=Dernier bloc vérifié: {0}
dao.wallet.chainHeightSyncing=En attente des blocs.... {0} Blocs vérifiés sur {1}.
dao.wallet.tx.type=Type
@ -1928,9 +1955,9 @@ dao.factsAndFigures.menuItem.transactions=Transactions BSQ
dao.factsAndFigures.dashboard.avgPrice90=Moyenne sur 90 jours du prix d'échange BSQ/BTC
dao.factsAndFigures.dashboard.avgPrice30=Moyenne sur 30 jours du prix d'échange BSQ/BTC
dao.factsAndFigures.dashboard.avgUSDPrice90=90 days volume weighted average USD/BSQ trade price
dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average USD/BSQ trade price
dao.factsAndFigures.dashboard.marketCap=Capitalisation boursière (basé sur la valeur d'échange)
dao.factsAndFigures.dashboard.avgUSDPrice90=90 days volume weighted average USD/BSQ price
dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average USD/BSQ price
dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on 30 days average USD/BSQ price)
dao.factsAndFigures.dashboard.availableAmount=BSQ disponible au total
dao.factsAndFigures.supply.issuedVsBurnt=BSQ issued v. BSQ burnt
@ -1989,9 +2016,9 @@ displayUpdateDownloadWindow.button.downloadLater=Télécharger plus tard
displayUpdateDownloadWindow.button.ignoreDownload=Ignorer cette version
displayUpdateDownloadWindow.headline=Une nouvelle mise à jour Bisq est disponible !
displayUpdateDownloadWindow.download.failed.headline=Echec du téléchargement
displayUpdateDownloadWindow.download.failed=Download failed.\nPlease download and verify manually at [HYPERLINK:https://bisq.network/downloads]
displayUpdateDownloadWindow.installer.failed=Unable to determine the correct installer. Please download and verify manually at [HYPERLINK:https://bisq.network/downloads]
displayUpdateDownloadWindow.verify.failed=Verification failed.\nPlease download and verify manually at [HYPERLINK:https://bisq.network/downloads]
displayUpdateDownloadWindow.download.failed=Téléchargement échoué. Veuillez télécharger et vérifier via [HYPERLINK:https://bisq.network/downloads]
displayUpdateDownloadWindow.installer.failed=Impossible de déterminer le bon programme d'installation. Veuillez télécharger et vérifier manuellement via [HYPERLINK:https://bisq.network/downloads] .
displayUpdateDownloadWindow.verify.failed=Vérification échouée. Veuillez télécharger et vérifier manuellement via [HYPERLINK:https://bisq.network/downloads]
displayUpdateDownloadWindow.success=La nouvelle version a été téléchargée avec succès et la signature vérifiée.\n\nVeuillez ouvrir le répertoire de téléchargement, fermer l'application et installer la nouvelle version.
displayUpdateDownloadWindow.download.openDir=Ouvrir le répertoire de téléchargement
@ -2000,7 +2027,7 @@ disputeSummaryWindow.openDate=Date d'ouverture du ticket
disputeSummaryWindow.role=Rôle du trader
disputeSummaryWindow.payout=Versement du montant de l'opération
disputeSummaryWindow.payout.getsTradeAmount=BTC {0} obtient le montant du versement de la transaction
disputeSummaryWindow.payout.getsAll=BTC {0} à reçu l''intégralité
disputeSummaryWindow.payout.getsAll=Max. payout to BTC {0}
disputeSummaryWindow.payout.custom=Versement personnalisé
disputeSummaryWindow.payoutAmount.buyer=Montant du versement de l'acheteur
disputeSummaryWindow.payoutAmount.seller=Montant du versement au vendeur
@ -2023,15 +2050,15 @@ disputeSummaryWindow.reason.OTHER=Autre
# suppress inspection "UnusedProperty"
disputeSummaryWindow.reason.BANK_PROBLEMS=Banque
# suppress inspection "UnusedProperty"
disputeSummaryWindow.reason.OPTION_TRADE=Option trade
disputeSummaryWindow.reason.OPTION_TRADE=Transaction facultative
# suppress inspection "UnusedProperty"
disputeSummaryWindow.reason.SELLER_NOT_RESPONDING=Seller not responding
disputeSummaryWindow.reason.SELLER_NOT_RESPONDING=Le vendeur n'a pas répondu.
# suppress inspection "UnusedProperty"
disputeSummaryWindow.reason.WRONG_SENDER_ACCOUNT=Wrong sender account
disputeSummaryWindow.reason.WRONG_SENDER_ACCOUNT=Mauvais compte d'expéditeur
# suppress inspection "UnusedProperty"
disputeSummaryWindow.reason.PEER_WAS_LATE=Peer was late
disputeSummaryWindow.reason.PEER_WAS_LATE=Le partenaire commercial a expiré.
# suppress inspection "UnusedProperty"
disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=Trade already settled
disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=La transaction s'est stabilisée.
disputeSummaryWindow.summaryNotes=Notes de synthèse
disputeSummaryWindow.addSummaryNotes=Ajouter des notes de synthèse
@ -2039,13 +2066,13 @@ disputeSummaryWindow.close.button=Fermer le ticket
# Do no change any line break or order of tokens as the structure is used for signature verification
# suppress inspection "TrailingSpacesInProperty"
disputeSummaryWindow.close.msg=Ticket closed on {0}\n{1} node address: {2}\n\nSummary:\nTrade ID: {3}\nCurrency: {4}\nTrade amount: {5}\nPayout amount for BTC buyer: {6}\nPayout amount for BTC seller: {7}\n\nReason for dispute: {8}\n\nSummary notes:\n{9}\n
disputeSummaryWindow.close.msg=Le ticket a été fermé {0}\n {1} Adresse du nœud: {2} \n\nRésumé: \nID de transaction: {3} \nMonnaie: {4} \n Montant de la transaction: {5} \nMontant du paiement de l'acheteur BTC: {6} \nMontant du paiement du vendeur BTC: {7} \n\nRaison du litige: {8} \n\nRésumé: {9} \n\n
# Do no change any line break or order of tokens as the structure is used for signature verification
disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3}
disputeSummaryWindow.close.nextStepsForMediation=\nNext steps:\nOpen trade and accept or reject suggestion from mediator
disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\nNext steps:\nNo further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions
disputeSummaryWindow.close.nextStepsForMediation=\n\nÉtape suivante:\nOuvrez la transaction inachevée, acceptez ou rejetez la suggestion du médiateur
disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\n\nÉtape suivante: \nAucune autre action n'est requise de votre part. Si l'arbitre rend une décision en votre faveur, vous verrez la transaction «Remboursement d'arbitrage» sur la page Fonds / Transactions
disputeSummaryWindow.close.closePeer=Vous devez également clore le ticket des pairs de trading !
disputeSummaryWindow.close.txDetails.headline=Publier la transaction de remboursement
# suppress inspection "TrailingSpacesInProperty"
@ -2054,8 +2081,8 @@ disputeSummaryWindow.close.txDetails.buyer=L''acheteur reçoit {0} à l''adresse
disputeSummaryWindow.close.txDetails.seller=Le vendeur reçoit {0} à l''adresse: {1}\n
disputeSummaryWindow.close.txDetails=Spending: {0}\n{1}{2}Transaction fee: {3} ({4} satoshis/vbyte)\nTransaction vsize: {5} vKb\n\nAre you sure you want to publish this transaction?
disputeSummaryWindow.close.noPayout.headline=Close without any payout
disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any payout?
disputeSummaryWindow.close.noPayout.headline=Fermé sans paiement
disputeSummaryWindow.close.noPayout.text=Voulez-vous fermer sans paiement ?
emptyWalletWindow.headline={0} Outil de secours du portefeuille
emptyWalletWindow.info=Veuillez utiliser ceci qu'en cas d'urgence si vous ne pouvez pas accéder à vos fonds à partir de l'interface utilisateur.\n\nVeuillez remarquer que touts les ordres en attente seront automatiquement fermés lors de l'utilisation de cet outil.\n\nAvant d'utiliser cet outil, veuillez sauvegarder votre répertoire de données. Vous pouvez le faire sur \"Compte/sauvegarde\".\n\nVeuillez nous signaler votre problème et déposer un rapport de bug sur GitHub ou sur le forum Bisq afin que nous puissions enquêter sur la source du problème.
@ -2076,8 +2103,8 @@ filterWindow.onions=Adresses onion filtrées (virgule de sep.)
filterWindow.accounts=Données filtrées du compte de trading:\nFormat: séparer par une virgule liste des [ID du mode de paiement | champ de données | valeur].
filterWindow.bannedCurrencies=Codes des devises filtrées (séparer avec une virgule.)
filterWindow.bannedPaymentMethods=IDs des modes de paiements filtrés (séparer avec une virgule.)
filterWindow.bannedAccountWitnessSignerPubKeys=Filtered account witness signer pub keys (comma sep. hex of pub keys)
filterWindow.bannedPrivilegedDevPubKeys=Filtered privileged dev pub keys (comma sep. hex of pub keys)
filterWindow.bannedAccountWitnessSignerPubKeys=Clé publique filtrée du signataire du témoin de compte (clé publique hexadécimale séparée par des virgules)
filterWindow.bannedPrivilegedDevPubKeys=Clé publique filtrée de développeur privilégiée (clé publique hexadécimale séparée par des virgules)
filterWindow.arbitrators=Arbitres filtrés (adresses onion séparées par une virgule)
filterWindow.mediators=Médiateurs filtrés (adresses onion sep. par une virgule)
filterWindow.refundAgents=Agents de remboursement filtrés (adresses onion sep. par virgule)
@ -2086,13 +2113,13 @@ filterWindow.priceRelayNode=Nœuds relais avec prix filtrés (adresses onion sé
filterWindow.btcNode=Nœuds Bitcoin filtrés (adresses séparées par une virgule + port)
filterWindow.preventPublicBtcNetwork=Empêcher l'utilisation du réseau public Bitcoin
filterWindow.disableDao=Désactiver la DAO
filterWindow.disableAutoConf=Disable auto-confirm
filterWindow.disableAutoConf=Désactiver la confirmation automatique
filterWindow.autoConfExplorers=Filtered auto-confirm explorers (comma sep. addresses)
filterWindow.disableDaoBelowVersion=Version minimale requise pour la DAO
filterWindow.disableTradeBelowVersion=Version min. nécessaire pour pouvoir échanger
filterWindow.add=Ajouter le filtre
filterWindow.remove=Retirer le filtre
filterWindow.btcFeeReceiverAddresses=BTC fee receiver addresses
filterWindow.btcFeeReceiverAddresses=Adresse de réception des frais Bitcoin
offerDetailsWindow.minBtcAmount=Montant BTC min.
offerDetailsWindow.min=(min. {0})
@ -2111,7 +2138,7 @@ offerDetailsWindow.creationDate=Date de création
offerDetailsWindow.makersOnion=Adresse onion du maker
qRCodeWindow.headline=QR Code
qRCodeWindow.msg=Please use this QR code for funding your Bisq wallet from your external wallet.
qRCodeWindow.msg=Veuillez utiliser le code QR pour recharger du portefeuille externe au portefeuille Bisq.
qRCodeWindow.request=Demande de paiement:\n{0}
selectDepositTxWindow.headline=Sélectionner la transaction de dépôt en cas de litige
@ -2134,10 +2161,10 @@ sendPrivateNotificationWindow.send=Envoyer une notification privée
showWalletDataWindow.walletData=Données du portefeuille
showWalletDataWindow.includePrivKeys=Inclure les clés privées
setXMRTxKeyWindow.headline=Prove sending of XMR
setXMRTxKeyWindow.note=Adding tx info below enables auto-confirm for quicker trades. See more: https://bisq.wiki/Trading_Monero
setXMRTxKeyWindow.txHash=Transaction ID (optional)
setXMRTxKeyWindow.txKey=Transaction key (optional)
setXMRTxKeyWindow.headline=La preuve XMR a été envoyée.
setXMRTxKeyWindow.note=Ajoutez les informations tx au-dessous pour confirmer automatiquement les transactions plus rapidement. Plus d'informations: https://bisq.wiki/Trading_Monero
setXMRTxKeyWindow.txHash=ID de transaction (en option)
setXMRTxKeyWindow.txKey=Clé de transaction (en option)
# We do not translate the tac because of the legal nature. We would need translations checked by lawyers
# in each language which is too expensive atm.
@ -2151,9 +2178,10 @@ tradeDetailsWindow.disputedPayoutTxId=ID de la transaction de versement contest
tradeDetailsWindow.tradeDate=Date de l'échange
tradeDetailsWindow.txFee=Frais de minage
tradeDetailsWindow.tradingPeersOnion=Adresse onion du pair de trading
tradeDetailsWindow.tradingPeersPubKeyHash=Trading peers pubkey hash
tradeDetailsWindow.tradingPeersPubKeyHash=Valeur de hachage de la clé publique du partenaire commercial
tradeDetailsWindow.tradeState=État du trade
tradeDetailsWindow.agentAddresses=Arbitre/Médiateur
tradeDetailsWindow.detailData=Detail data
walletPasswordWindow.headline=Entrer le mot de passe pour déverouiller
@ -2183,6 +2211,8 @@ feeOptionWindow.info=Vous pouvez choisir de payer les frais de transaction en BS
feeOptionWindow.optionsLabel=Choisissez la devise pour le paiement des frais de transaction
feeOptionWindow.useBTC=Utiliser BTC
feeOptionWindow.fee={0} (≈ {1})
feeOptionWindow.btcFeeWithFiatAndPercentage={0} (≈ {1} / {2})
feeOptionWindow.btcFeeWithPercentage={0} ({1})
####################################################################
@ -2213,9 +2243,9 @@ error.closedTradeWithUnconfirmedDepositTx=La transaction de dépôt de l''échan
error.closedTradeWithNoDepositTx=La transaction de dépôt de l'échange fermé avec l''ID d'échange {0} est nulle.\n\nVeuillez redémarrer l''application pour nettoyer la liste des transactions fermées.
popup.warning.walletNotInitialized=Le portefeuille n'est pas encore initialisé
popup.warning.osxKeyLoggerWarning=Due to stricter security measures in macOS 10.14 and above, launching a Java application (Bisq uses Java) causes a popup warning in macOS ('Bisq would like to receive keystrokes from any application').\n\nTo avoid that issue please open your 'macOS Settings' and go to 'Security & Privacy' -> 'Privacy' -> 'Input Monitoring' and Remove 'Bisq' from the list on the right side.\n\nBisq will upgrade to a newer Java version to avoid that issue as soon the technical limitations (Java packager for the required Java version is not shipped yet) are resolved.
popup.warning.osxKeyLoggerWarning=En raison de mesures de sécurité plus strictes dans MacOS 10.14 et dans la version supérieure, le lancement d'une application Java (Bisq utilise Java) provoquera un avertissement pop-up dans MacOS (« Bisq souhaite recevoir les frappes de toute application »). \n\nPour éviter ce problème, veuillez ouvrir «Paramètres MacOS», puis allez dans «Sécurité et confidentialité» -> «Confidentialité» -> «Surveillance des entrées», puis supprimez «Bisq» de la liste de droite. \n\nUne fois les limitations techniques résolues (le packager Java de la version Java requise n'a pas été livré), Bisq effectuera une mise à niveau vers la nouvelle version Java pour éviter ce problème.
popup.warning.wrongVersion=Vous avez probablement une mauvaise version de Bisq sur cet ordinateur.\nL''architecture de votre ordinateur est: {0}.\nLa binary Bisq que vous avez installé est: {1}.\nVeuillez éteindre et réinstaller une bonne 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: [HYPERLINK:https://bisq.network/downloads].\n\nPlease restart the application.
popup.warning.incompatibleDB=Nous avons détecté un fichier de base de données incompatible!\n\nCes fichiers de base de données ne sont pas compatibles avec notre base de code actuelle: {0}\n\nNous avons sauvegardé les fichiers endommagés et appliqué les valeurs par défaut à la nouvelle version de la base de données.\n\nLa sauvegarde se trouve dans: \n\n{1} / db / backup_of_corrupted_data. \n\nVeuillez vérifier si vous avez installé la dernière version de Bisq. \n\nVous pouvez télécharger: \n\n[HYPERLINK:https://bisq.network/downloads] \n\nVeuillez redémarrer l'application.
popup.warning.startupFailed.twoInstances=Bisq est déjà lancé. Vous ne pouvez pas lancer deux instances de bisq.
popup.warning.tradePeriod.halfReached=Votre transaction avec ID {0} a atteint la moitié de la période de trading maximale autorisée et n''est toujours pas terminée.\n\nLa période de trade se termine le {1}.\n\nVeuillez vérifier l''état de votre transaction dans \"Portfolio/échanges en cours\" pour obtenir de plus amples informations.
popup.warning.tradePeriod.ended=Votre échange avec l''ID {0} a atteint la période de trading maximale autorisée et n''est pas terminé.\n\nLa période d''échange s''est terminée le {1}.\n\nVeuillez vérifier votre transaction sur \"Portfolio/Echanges en cours\" pour contacter le médiateur.
@ -2226,6 +2256,7 @@ popup.warning.noMediatorsAvailable=Il n'y a pas de médiateurs disponibles.
popup.warning.notFullyConnected=Vous devez attendre d'être complètement connecté au réseau.\nCela peut prendre jusqu'à 2 minutes au démarrage.
popup.warning.notSufficientConnectionsToBtcNetwork=Vous devez attendre d''avoir au minimum {0} connexions au réseau Bitcoin.
popup.warning.downloadNotComplete=Vous devez attendre que le téléchargement des blocs Bitcoin manquants soit terminé.
popup.warning.chainNotSynced=The Bisq wallet blockchain height is not synced correctly. If you recently started the application, please wait until one Bitcoin block has been published.\n\nYou can check the blockchain height in Settings/Network Info. If more than one block passes and this problem persists it may be stalled, in which case you should do an SPV resync. [HYPERLINK:https://bisq.wiki/Resyncing_SPV_file]
popup.warning.removeOffer=Vous êtes certain de vouloir retirer cet ordre?\nLes frais du maker de {0} seront perdus si vous retirez cet ordre.
popup.warning.tooLargePercentageValue=Vous ne pouvez pas définir un pourcentage de 100% ou plus grand.
popup.warning.examplePercentageValue=Merci de saisir un nombre sous la forme d'un pourcentage tel que \"5.4\" pour 5.4%
@ -2235,7 +2266,7 @@ popup.warning.insufficientBtcFundsForBsqTx=Vous ne disposez pas de suffisamment
popup.warning.bsqChangeBelowDustException=Cette transaction crée une BSQ change output qui est inférieure à la dust limit (5,46 BSQ) et serait rejetée par le réseau Bitcoin.\n\nVous devez soit envoyer un montant plus élevé pour éviter la change output (par exemple en ajoutant le montant de dust à votre montant d''envoi), soit ajouter plus de fonds BSQ à votre portefeuille pour éviter de générer une dust output.\n\nLa dust output est {0}.
popup.warning.btcChangeBelowDustException=Cette transaction crée une change output qui est inférieure à la dust limit (546 Satoshi) et serait rejetée par le réseau Bitcoin.\n\nVous devez ajouter la quantité de dust à votre montant envoyé pour éviter de générer une dust output.\n\nLa dust output est {0}.
popup.warning.insufficientBsqFundsForBtcFeePayment=You''ll need more BSQ to do this transaction—the last 5.46 BSQ in your wallet cannot be used to pay trade fees because of dust limits in the Bitcoin protocol.\n\nYou can either buy more BSQ or pay trade fees with BTC.\n\nMissing funds: {0}
popup.warning.insufficientBsqFundsForBtcFeePayment=Vous avez besoin de plus de BSQ pour effectuer cette transaction - le dernier 5,46 BSQ restant dans le portefeuille ne sera pas utilisé pour payer les frais de transaction en raison de la limite fractionnaire dans l'accord BTC. \n\nVous pouvez acheter plus de BSQ ou utiliser BTC pour payer les frais de transaction\n\nManque de fonds BSQ: {0}
popup.warning.noBsqFundsForBtcFeePayment=Votre portefeuille BSQ ne dispose pas de suffisamment de fonds pour payer les frais de transaction en BSQ.
popup.warning.messageTooLong=Votre message dépasse la taille maximale autorisée. Veuillez l'envoyer en plusieurs parties ou le télécharger depuis un service comme https://pastebin.com.
popup.warning.lockedUpFunds=Vous avez des fonds bloqués d''une transaction qui a échoué.\nSolde bloqué: {0}\nAdresse de la tx de dépôt: {1}\nID de l''échange: {2}.\n\nVeuillez ouvrir un ticket de support en sélectionnant la transaction dans l'écran des transactions ouvertes et en appuyant sur \"alt + o\" ou \"option + o\".
@ -2254,7 +2285,7 @@ popup.warning.openOffer.makerFeeTxRejected=La transaction de frais de maker pour
popup.warning.trade.txRejected.tradeFee=frais de transaction
popup.warning.trade.txRejected.deposit=dépôt
popup.warning.trade.txRejected=La transaction {0} pour l''échange avec ID {1} a été rejetée par le réseau Bitcoin.\nID de transaction={2}.\nL''échange a été déplacé vers les échanges en échec.\nAllez dans \"Paramètres/Info sur le réseau réseau\" et faites une resynchronisation SPV.\nPour obtenir de l''aide, le canal support de l''équipe Bisq est disponible sur Keybase.
popup.warning.trade.txRejected=The {0} transaction for trade with ID {1} was rejected by the Bitcoin network.\nTransaction ID={2}\nThe trade has been moved to failed trades.\nPlease go to \"Settings/Network info\" and do a SPV resync.\nFor further help please contact the Bisq support channel at the Bisq Keybase team.
popup.warning.openOfferWithInvalidMakerFeeTx=La transaction de frais de maker pour l''offre avec ID {0} n''est pas valide.\nID de transaction={1}.\nAllez dans \"Paramètres/Info sur le réseau réseau\" et faites une resynchronisation SPV.\nPour obtenir de l''aide, le canal support de l''équipe Bisq est disponible sur Keybase.
@ -2264,14 +2295,15 @@ popup.info.cashDepositInfo=Veuillez vous assurer d''avoir une succursale de l''
popup.info.cashDepositInfo.confirm=Je confirme que je peux effectuer le dépôt.
popup.info.shutDownWithOpenOffers=Bisq est en cours de fermeture, mais des ordres sont en attente.\n\nCes ordres ne seront pas disponibles sur le réseau P2P si Bisq est éteint, mais ils seront republiés sur le réseau P2P la prochaine fois que vous lancerez Bisq.\n\nPour garder vos ordres en ligne, laissez Bisq en marche et assurez-vous que cet ordinateur reste aussi en ligne (pour cela, assurez-vous qu'il ne passe pas en mode veille...la veille du moniteur ne pose aucun problème).
popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\nPlease make sure your Bisq qube is setup according to our Setup Guide at [HYPERLINK:https://bisq.wiki/Running_Bisq_on_Qubes].
popup.warn.downGradePrevention=Downgrade from version {0} to version {1} is not supported. Please use the latest Bisq version.
popup.warn.daoRequiresRestart=There was a problem with synchronizing the DAO state. You have to restart the application to fix the issue.
popup.privateNotification.headline=Notification privée importante!
popup.securityRecommendation.headline=Recommendation de sécurité importante
popup.securityRecommendation.msg=Nous vous rappelons d'envisager d'utiliser la protection par mot de passe pour votre portefeuille si vous ne l'avez pas déjà activé.\n\nIl est également fortement recommandé d'écrire les mots de la seed de portefeuille. Ces mots de la seed sont comme un mot de passe principal pour récupérer votre portefeuille Bitcoin.\nVous trouverez plus d'informations à ce sujet dans l'onglet \"seed du portefeuille\".\n\nDe plus, il est recommandé de sauvegarder le dossier complet des données de l'application dans l'onglet \"Sauvegarde".
popup.bitcoinLocalhostNode.msg=Bisq a détecté un noeud Bitcoin Core fonctionnant localement (sur localhost).\nVeuillez vous assurer que ce nœud est entièrement synchronisé avant de lancer Bisq et qu'il ne fonctionne pas en mode restreint.
popup.bitcoinLocalhostNode.additionalRequirements=\n\nFor a well configured node, the requirements are for the node to have pruning disabled and bloom filters enabled.
popup.bitcoinLocalhostNode.msg=Bisq detected a Bitcoin Core node running on this machine (at localhost).\n\nPlease ensure:\n- the node is fully synced before starting Bisq\n- pruning is disabled ('prune=0' in bitcoin.conf)\n- bloom filters are enabled ('peerbloomfilters=1' in bitcoin.conf)
popup.shutDownInProgress.headline=Fermeture en cours
popup.shutDownInProgress.msg=La fermeture de l'application nécessite quelques secondes.\nVeuillez ne pas interrompre ce processus.
@ -2303,15 +2335,12 @@ popup.accountSigning.signedByPeer=Un de vos comptes de paiement a été vérifi
popup.accountSigning.peerLimitLifted=La limite initiale pour l''un de vos comptes a été levée.\n\n{0}
popup.accountSigning.peerSigner=Un de vos comptes est suffisamment mature pour signer d'autres comptes de paiement et la limite initiale pour un de vos comptes a été levée.\n\n{0}
popup.accountSigning.singleAccountSelect.headline=Select account age witness
popup.accountSigning.singleAccountSelect.description=Search for account age witness.
popup.accountSigning.singleAccountSelect.datePicker=Select point of time for signing
popup.accountSigning.singleAccountSelect.headline=Import unsigned account age witness
popup.accountSigning.confirmSingleAccount.headline=Confirm selected account age witness
popup.accountSigning.confirmSingleAccount.selectedHash=Selected witness hash
popup.accountSigning.confirmSingleAccount.button=Sign account age witness
popup.accountSigning.successSingleAccount.description=Witness {0} was signed
popup.accountSigning.successSingleAccount.success.headline=Success
popup.accountSigning.successSingleAccount.signError=Failed to sign witness, {0}
popup.accountSigning.unsignedPubKeys.headline=Unsigned Pubkeys
popup.accountSigning.unsignedPubKeys.sign=Sign Pubkeys
@ -2447,8 +2476,8 @@ navigation.dao.wallet.receive=\"DAO/BSQ Portefeuille/Recevoir\"
formatter.formatVolumeLabel={0} montant{1}
formatter.makerTaker=Maker comme {0} {1} / Taker comme {2} {3}
formatter.youAreAsMaker=Vous êtes {0} {1} en tant que maker / Le taker est {2} {3}
formatter.youAreAsTaker=Vous êtes {0} {1} en tant que taker/ le maker est {2} {3}.
formatter.youAreAsMaker=You are: {1} {0} (maker) / Taker is: {3} {2}
formatter.youAreAsTaker=You are: {1} {0} (taker) / Maker is: {3} {2}
formatter.youAre=Vous êtes {0} {1} ({2} {3})
formatter.youAreCreatingAnOffer.fiat=Vous êtes en train de créer un ordre pour {0} {1}
formatter.youAreCreatingAnOffer.altcoin=Vous êtes en train de créer un ordre pour {0} {1} ({2} {3})
@ -2514,7 +2543,7 @@ seed.warn.walletNotEmpty.msg=Your Bitcoin wallet is not empty.\n\nYou must empty
seed.warn.walletNotEmpty.restore=Je veux quand même restaurer.
seed.warn.walletNotEmpty.emptyWallet=Je viderai mes portefeuilles en premier.
seed.warn.notEncryptedAnymore=Vos portefeuilles sont cryptés.\n\nAprès la restauration, les portefeuilles ne seront plus cryptés et vous devrez définir un nouveau mot de passe.\n\nSouhaitez-vous continuer ?
seed.warn.walletDateEmpty=As you have not specified a wallet date, bisq will have to scan the blockchain from 2013.10.09 (the BIP39 epoch date).\n\nBIP39 wallets were first introduced in bisq on 2017.06.28 (release v0.5). So you could save time by using that date.\n\nIdeally you should specify the date your wallet seed was created.\n\n\nAre you sure you want to go ahead without specifying a wallet date?
seed.warn.walletDateEmpty=Puisque vous n'avez pas spécifié la date du portefeuille, Bisq devra scanner la blockchain après le 09/10/2013 (date de création du BIP39). \n\nLe portefeuille BIP39 a été lancé pour la première fois sur Bisq le 28/06/2017 (version v0.5). Par conséquent, vous pouvez utiliser cette date pour gagner du temps. \n\nIdéalement, vous devez indiquer la date à laquelle la graine de départ du portefeuille est créée. \n\n\nÊtes-vous sûr de vouloir continuer sans spécifier la date du portefeuille?
seed.restore.success=Portefeuilles restaurés avec succès grâce aux nouveaux mots de la seed.\n\nVous devez arrêter et redémarrer l'application.
seed.restore.error=Une erreur est survenue lors de la restauration des portefeuilles avec les mots composant la seed.{0}
seed.restore.openOffers.warn=You have open offers which will be removed if you restore from seed words.\nAre you sure that you want to continue?
@ -2561,6 +2590,7 @@ payment.venmo.venmoUserName=Nom d'utilisateur Venmo
payment.popmoney.accountId=Email ou N° de téléphone
payment.promptPay.promptPayId=N° de carte d'identité/d'identification du contribuable ou numéro de téléphone
payment.supportedCurrencies=Devises acceptées
payment.supportedCurrenciesForReceiver=Currencies for receiving funds
payment.limitations=Restrictions
payment.salt=Salage de la vérification de l'âge des comptes
payment.error.noHexSalt=The salt needs to be in HEX format.\nIt is only recommended to edit the salt field if you want to transfer the salt from an old account to keep your account age. The account age is verified by using the account salt and the identifying account data (e.g. IBAN).
@ -2597,8 +2627,8 @@ payment.accountType=Type de compte
payment.checking=Vérification
payment.savings=Épargne
payment.personalId=Pièce d'identité
payment.clearXchange.info=Zelle is a money transfer service that works best *through* another bank.\n\n1. Check this page to see if (and how) your bank works with Zelle: [HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Take special note of your transfer limits—sending limits vary by bank, and banks often specify separate daily, weekly, and monthly limits.\n\n3. If your bank does not work with Zelle, you can still use it through the Zelle mobile app, but your transfer limits will be much lower.\n\n4. The name specified on your Bisq account MUST match the name on your Zelle/bank account. \n\nIf you cannot complete a Zelle transaction as specified in your trade contract, you may lose some (or all) of your security deposit.\n\nBecause of Zelle''s somewhat higher chargeback risk, sellers are advised to contact unsigned buyers through email or SMS to verify that the buyer really owns the Zelle account specified in Bisq.
payment.fasterPayments.newRequirements.info=Some banks have started verifying the receiver''s full name for Faster Payments transfers. Your current Faster Payments account does not specify a full name.\n\nPlease consider recreating your Faster Payments account in Bisq to provide future {0} buyers with a full name.\n\nWhen you recreate the account, make sure to copy the precise sort code, account number and account age verification salt values from your old account to your new account. This will ensure your existing account''s age and signing status are preserved.
payment.clearXchange.info=Zelle est un service de transfert d'argent, qui fonctionne bien pour transférer de l'argent vers d'autres banques. \n\n1. Consultez cette page pour voir si (et comment) votre banque coopère avec Zelle: \n[HYPERLINK:https://www.zellepay.com/get-started]\n\n2. Faites particulièrement attention à votre limite de transfert - les limites de versement varient d'une banque à l'autre, et les banques spécifient généralement des limites quotidiennes, hebdomadaires et mensuelles. \n\n3. Si votre banque ne peut pas utiliser Zelle, vous pouvez toujours l'utiliser via l'application mobile Zelle, mais votre limite de transfert sera bien inférieure. \n\n4. Le nom indiqué sur votre compte Bisq doit correspondre à celui du compte Zelle / bancaire. \n\nSi vous ne parvenez pas à réaliser la transaction Zelle comme stipulé dans le contrat commercial, vous risquez de perdre une partie (ou la totalité) de votre marge.\n\nComme Zelle présente un risque élevé de rétrofacturation, il est recommandé aux vendeurs de contacter les acheteurs non signés par e-mail ou SMS pour confirmer que les acheteurs ont le compte Zelle spécifié dans Bisq.
payment.fasterPayments.newRequirements.info=Certaines banques ont déjà commencé à vérifier le nom complet du destinataire du paiement rapide. Votre compte de paiement rapide actuel ne remplit pas le nom complet. \n\nPensez à recréer votre compte de paiement rapide dans Bisq pour fournir un nom complet aux futurs {0} acheteurs. \n\nLors de la recréation d'un compte, assurez-vous de copier l'indicatif bancaire, le numéro de compte et le sel de vérification de l'âge de l'ancien compte vers le nouveau compte. Cela garantira que votre âge du compte et état de signature existant sont conservés.
payment.moneyGram.info=When using MoneyGram the BTC buyer has to send the Authorisation number and a photo of the receipt by email to the BTC seller. The receipt must clearly show the seller's full name, country, state and the amount. The seller's email will be displayed to the buyer during the trade process.
payment.westernUnion.info=When using Western Union the BTC buyer has to send the MTCN (tracking number) and a photo of the receipt by email to the BTC seller. The receipt must clearly show the seller's full name, city, country and the amount. The seller's email will be displayed to the buyer during the trade process.
payment.halCash.info=Lors de l'utilisation de HalCash, l'acheteur de BTC doit envoyer au vendeur de BTC le code HalCash par SMS depuis son téléphone portable.\n\nVeuillez vous assurer de ne pas dépasser le montant maximum que votre banque vous permet d'envoyer avec HalCash. Le montant minimum par retrait est de 10 EUR et le montant maximum est de 600 EUR. Pour les retraits récurrents, il est de 3000 EUR par destinataire par jour et 6000 EUR par destinataire par mois. Veuillez vérifier ces limites auprès de votre banque pour vous assurer qu'elles utilisent les mêmes limites que celles indiquées ici.\n\nLe montant du retrait doit être un multiple de 10 EUR car vous ne pouvez pas retirer d'autres montants à un distributeur automatique. Pendant les phases de create-offer et take-offer l'affichage de l'interface utilisateur ajustera le montant en BTC afin que le montant en euros soit correct. Vous ne pouvez pas utiliser le prix basé sur le marché, car le montant en euros varierait en fonction de l'évolution des prix.\n\nEn cas de litige, l'acheteur de BTC doit fournir la preuve qu'il a envoyé la somme en EUR.
@ -2613,7 +2643,7 @@ payment.revolut.info=Revolut requires the 'User name' as account ID not the phon
payment.account.revolut.addUserNameInfo={0}\nYour existing Revolut account ({1}) does not have a ''User name''.\nPlease enter your Revolut ''User name'' to update your account data.\nThis will not affect your account age signing status.
payment.revolut.addUserNameInfo.headLine=Update Revolut account
payment.usPostalMoneyOrder.info=Trading using US Postal Money Orders (USPMO) on Bisq requires that you understand the following:\n\n- BTC buyers must write the BTC Sellers name in both the Payer and the Payees fields & take a high-resolution photo of the USPMO and envelope with proof of tracking before sending.\n- BTC buyers must send the USPMO to the BTC seller with Delivery Confirmation.\n\nIn the event mediation is necessary, or if there is a trade dispute, you will be required to send the photos to the Bisq mediator or refund agent, together with the USPMO Serial Number, Post Office Number, and dollar amount, so they can verify the details on the US Post Office website.\n\nFailure to provide the required information to the Mediator or Arbitrator will result in losing the dispute case.\n\nIn all dispute cases, the USPMO sender bears 100% of the burden of responsibility in providing evidence/proof to the Mediator or Arbitrator.\n\nIf you do not understand these requirements, do not trade using USPMO on Bisq.
payment.usPostalMoneyOrder.info=Pour échanger US Postal Money Orders (USPMO) sur Bisq, vous devez comprendre les termes suivants: \n\n- L'acheteur BTC doit écrire le nom du vendeur BTC dans les champs expéditeur et bénéficiaire, et prendre une photo à haute résolution de USPMO et de l'enveloppe avec une preuve de suivi avant l'envoi. \n\n- L'acheteur BTC doit envoyer USPMO avec la confirmation de livraison au vendeur BTC. \n\nSi une médiation est nécessaire, ou s'il y a un différend de transaction, vous devrez envoyer la photo avec le numéro USPMO, le numéro du bureau de poste et le montant de la transaction au médiateur Bisq ou à l'agent de remboursement afin qu'ils puissent vérifier les détails sur le site web de la poste américaine. \n\nSi vous ne fournissez pas les données de transaction requises, vous perdrez directement dans le différend. \n\nDans tous les cas de litige, l'expéditeur de l'USPMO assume à 100% la responsabilité lors de la fourniture de preuves / certification au médiateur ou à l'arbitre. \n\nSi vous ne comprenez pas ces exigences, veuillez ne pas échanger USPMO sur Bisq.
payment.f2f.contact=information de contact
payment.f2f.contact.prompt=How would you like to be contacted by the trading peer? (email address, phone number,...)
@ -2635,7 +2665,8 @@ payment.japan.recipient=Nom
payment.australia.payid=PayID
payment.payid=PayID linked to financial institution. Like email address or mobile phone.
payment.payid.info=A PayID like a phone number, email address or an Australian Business Number (ABN), that you can securely link to your bank, credit union or building society account. You need to have already created a PayID with your Australian financial institution. Both sending and receiving financial institutions must support PayID. For more information please check [HYPERLINK:https://payid.com.au/faqs/]
payment.amazonGiftCard.info=To pay with Amazon eGift Card you need to purchase an Amazon eGift Card at your Amazon account and use the BTC seller''s email or mobile nr. as receiver. Amazon sends then an email or text message to the receiver. Use the trade ID for the message field.\n\nAmazon eGift Cards can only be redeemed by Amazon accounts with the same currency.\n\nFor more information visit the Amazon eGift Card webpage. [HYPERLINK:https://www.amazon.com/Amazon-1_US_Email-eGift-Card/dp/B004LLIKVU]
payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the BTC seller via your Amazon account. \n\nBisq will show the BTC seller''s email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card''s message field. Please see the wiki [HYPERLINK:https://bisq.wiki/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it)
# We use constants from the code so we do not use our normal naming convention
# dynamic values are not recognized by IntelliJ

View File

@ -71,6 +71,7 @@ shared.amountWithCur=Importo in {0}
shared.volumeWithCur=Volume in {0}
shared.currency=Valuta
shared.market=Mercato
shared.deviation=Deviation
shared.paymentMethod=Metodo di pagamento
shared.tradeCurrency=Valuta di scambio
shared.offerType=Tipo di offerta
@ -104,7 +105,6 @@ shared.selectTradingAccount=Seleziona conto di trading
shared.fundFromSavingsWalletButton=Trasferisci fondi dal portafoglio Bisq
shared.fundFromExternalWalletButton=Apri il tuo portafoglio esterno per aggiungere fondi
shared.openDefaultWalletFailed=Failed to open a Bitcoin wallet application. Are you sure you have one installed?
shared.distanceInPercent=Distanza in % dal prezzo di mercato
shared.belowInPercent=Sotto % del prezzo di mercato
shared.aboveInPercent=Sopra % del prezzo di mercato
shared.enterPercentageValue=Immetti il valore %
@ -191,7 +191,7 @@ shared.tradeWalletBalance=Saldo del portafogli per gli scambi
shared.makerTxFee=Maker: {0}
shared.takerTxFee=Taker: {0}
shared.iConfirm=Confermo
shared.tradingFeeInBsqInfo=equivalente a {0} utilizzato come commissione di negoziazione
shared.tradingFeeInBsqInfo=≈ {0}
shared.openURL=Aperti {0}
shared.fiat=Fiat
shared.crypto=Crypto
@ -218,6 +218,9 @@ shared.refundAgentForSupportStaff=Agente di rimborso
shared.delayedPayoutTxId=Delayed payout transaction ID
shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to
shared.unconfirmedTransactionsLimitReached=Al momento, hai troppe transazioni non confermate. Per favore riprova più tardi.
shared.numItemsLabel=Number of entries: {0}
shared.filter=Filter
shared.enabled=Enabled
####################################################################
@ -248,14 +251,14 @@ mainView.balance.locked=Bloccati in scambi
mainView.balance.reserved.short=Riservati
mainView.balance.locked.short=Bloccati
mainView.footer.usingTor=(usando Tor)
mainView.footer.usingTor=(via Tor)
mainView.footer.localhostBitcoinNode=(localhost)
mainView.footer.btcInfo={0} {1} {2}
mainView.footer.btcFeeRate=/ Current fee rate: {0} sat/vB
mainView.footer.btcInfo={0} {1}
mainView.footer.btcFeeRate=/ Fee rate: {0} sat/vB
mainView.footer.btcInfo.initializing=Connessione alla rete Bitcoin
mainView.footer.bsqInfo.synchronizing=/ Sincronizzando DAO
mainView.footer.btcInfo.synchronizingWith=Sincronizzazione con
mainView.footer.btcInfo.synchronizedWith=Synced with
mainView.footer.btcInfo.synchronizingWith=Synchronizing with {0} at block: {1} / {2}
mainView.footer.btcInfo.synchronizedWith=Synced with {0} at block {1}
mainView.footer.btcInfo.connectingTo=Connessione a
mainView.footer.btcInfo.connectionFailed=Connessione fallita
mainView.footer.p2pInfo=Bitcoin network peers: {0} / Bisq network peers: {1}
@ -336,10 +339,10 @@ offerbook.offerersAcceptedBankSeats=Sede accettata dei paesi bancari (acquirente
offerbook.availableOffers=Offerte disponibili
offerbook.filterByCurrency=Filtra per valuta
offerbook.filterByPaymentMethod=Filtra per metodo di pagamento
offerbook.timeSinceSigning=Signed since
offerbook.timeSinceSigning=Account info
offerbook.timeSinceSigning.info=Questo account è stato verificato e {0}
offerbook.timeSinceSigning.info.arbitrator=firmato da un arbitro e può firmare account peer
offerbook.timeSinceSigning.info.peer=firmato da un peer, in attesa che i limiti vengano alzati
offerbook.timeSinceSigning.info.peer=signed by a peer, waiting %d days for limits to be lifted
offerbook.timeSinceSigning.info.peerLimitLifted=firmato da un peer e i limiti sono stati alzati
offerbook.timeSinceSigning.info.signer=firmato da un peer e può firmare account peer (limiti alzati)
offerbook.timeSinceSigning.info.banned= \nl'account è stato bannato
@ -349,9 +352,12 @@ offerbook.xmrAutoConf=Is auto-confirm enabled
offerbook.timeSinceSigning.help=Quando completi correttamente un'operazione con un peer che ha un account di pagamento firmato, il tuo account di pagamento viene firmato.\n{0} giorni dopo, il limite iniziale di {1} viene alzato e il tuo account può firmare account di pagamento di altri peer.
offerbook.timeSinceSigning.notSigned=Non ancora firmato
offerbook.timeSinceSigning.notSigned.ageDays={0} giorni
offerbook.timeSinceSigning.notSigned.noNeed=N/A
shared.notSigned=Questo account non è stato ancora firmato
shared.notSigned.noNeed=Questo tipo di account non utilizza la firma
shared.notSigned=This account has not been signed yet and was created {0} days ago
shared.notSigned.noNeed=This account type does not require signing
shared.notSigned.noNeedDays=This account type does not require signing and was created {0} days ago
shared.notSigned.noNeedAlts=Altcoin accounts do not feature signing or aging
offerbook.nrOffers=N. di offerte: {0}
offerbook.volume={0} (min - max)
@ -435,11 +441,15 @@ createOffer.warning.sellBelowMarketPrice=Otterrai sempre il {0}% in meno rispett
createOffer.warning.buyAboveMarketPrice=Pagherai sempre il {0}% in più rispetto al prezzo di mercato corrente poiché il prezzo della tua offerta verrà costantemente aggiornato.
createOffer.tradeFee.descriptionBTCOnly=Commissione di scambio
createOffer.tradeFee.descriptionBSQEnabled=Seleziona la valuta della commissione di scambio
createOffer.tradeFee.fiatAndPercent=≈ {0} / {1} dell'importo di scambio
createOffer.triggerPrice.prompt=Set optional trigger price
createOffer.triggerPrice.label=Deactivate offer if market price is {0}
createOffer.triggerPrice.tooltip=As protecting against drastic price movements you can set a trigger price which deactivates the offer if the market price reaches that value.
createOffer.triggerPrice.invalid.tooLow=Value must be higher than {0}
createOffer.triggerPrice.invalid.tooHigh=Value must be lower than {0}
# new entries
createOffer.placeOfferButton=Revisione: piazza l'offerta a {0} bitcoin
createOffer.alreadyFunded=Hai già finanziato quell'offerta.\nI tuoi fondi sono stati spostati nel tuo portafoglio Bisq locale e sono disponibili per il prelievo nella schermata \"Fondi/Invia fondi\".
createOffer.createOfferFundWalletInfo.headline=Finanzia la tua offerta
# suppress inspection "TrailingSpacesInProperty"
createOffer.createOfferFundWalletInfo.tradeAmount=- Importo di scambio: {0} \n
@ -496,7 +506,6 @@ takeOffer.error.message=Si è verificato un errore durante l'accettazione dell'o
# new entries
takeOffer.takeOfferButton=Rivedi: Accetta l'offerta per {0} bitcoin
takeOffer.noPriceFeedAvailable=Non puoi accettare questa offerta poiché utilizza un prezzo in percentuale basato sul prezzo di mercato ma non è disponibile alcun feed di prezzi.
takeOffer.alreadyFunded.movedFunds=Hai già finanziato quell'offerta.\nI tuoi fondi sono stati spostati nel tuo portafoglio Bisq locale e sono disponibili per il prelievo nella schermata \"Fondi/Invia fondi\".
takeOffer.takeOfferFundWalletInfo.headline=Finanzia il tuo scambio
# suppress inspection "TrailingSpacesInProperty"
takeOffer.takeOfferFundWalletInfo.tradeAmount=- Importo di scambio: {0} \n
@ -524,6 +533,10 @@ takeOffer.tac=Accettando questa offerta, accetto le condizioni commerciali defin
# Offerbook / Edit offer
####################################################################
openOffer.header.triggerPrice=Prezzo di attivazione
openOffer.triggerPrice=Trigger price {0}
openOffer.triggered=The offer has been deactivated because the market price reached your trigger price.\nPlease edit the offer to define a new trigger price
editOffer.setPrice=Imposta prezzo
editOffer.confirmEdit=Conferma: modifica offerta
editOffer.publishOffer=Pubblica la tua offerta.
@ -541,6 +554,8 @@ portfolio.tab.history=Storia
portfolio.tab.failed=Fallita
portfolio.tab.editOpenOffer=Modifica offerta
portfolio.closedTrades.deviation.help=Percentage price deviation from market
portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\nPlease do NOT send the fiat or altcoin payment. Contact Bisq developers on Keybase [HYPERLINK:https://keybase.io/team/bisq] or on the forum [HYPERLINK:https://bisq.community] for further assistance.\n\nError message: {0}
portfolio.pending.step1.waitForConf=Attendi la conferma della blockchain
@ -886,13 +901,12 @@ funds.tx.noTxAvailable=Nessuna transazione disponibile
funds.tx.revert=Storna
funds.tx.txSent=Transazione inviata con successo ad un nuovo indirizzo nel portafoglio Bisq locale.
funds.tx.direction.self=Invia a te stesso
funds.tx.daoTxFee=Commissione al miner per la tx verso la DAO
funds.tx.daoTxFee=Commissione di mining per transazioni BSQ
funds.tx.reimbursementRequestTxFee=Richiesta di rimborso
funds.tx.compensationRequestTxFee=Richiesta di compenso
funds.tx.dustAttackTx=Polvere ricevuta
funds.tx.dustAttackTx.popup=Questa transazione sta inviando un importo BTC molto piccolo al tuo portafoglio e potrebbe essere un tentativo da parte delle società di chain analysis per spiare il tuo portafoglio.\n\nSe usi quell'output della transazione in una transazione di spesa, scopriranno che probabilmente sei anche il proprietario dell'altro indirizzo (combinazione di monete).\n\nPer proteggere la tua privacy, il portafoglio Bisq ignora tali output di polvere a fini di spesa e nella visualizzazione del saldo. È possibile impostare la soglia al di sotto della quale un output è considerato polvere.\n 
####################################################################
# Support
####################################################################
@ -943,6 +957,7 @@ support.error=Il destinatario non ha potuto elaborare il messaggio. Errore: {0}
support.buyerAddress=Indirizzo BTC dell'acquirente
support.sellerAddress=Indirizzo BTC del venditore
support.role=Ruolo
support.agent=Support agent
support.state=Stato
support.closed=Chiuso
support.open=Aperto
@ -1052,6 +1067,7 @@ settings.net.creationDateColumn=Stabilito
settings.net.connectionTypeColumn=Dentro/Fuori
settings.net.sentDataLabel=Sent data statistics
settings.net.receivedDataLabel=Received data statistics
settings.net.chainHeightLabel=Latest BTC block height
settings.net.roundTripTimeColumn=Ritorno
settings.net.sentBytesColumn=Inviato
settings.net.receivedBytesColumn=Ricevuto
@ -1066,6 +1082,7 @@ settings.net.needRestart=È necessario riavviare l'applicazione per applicare ta
settings.net.notKnownYet=Non ancora noto...
settings.net.sentData=Sent data: {0}, {1} messages, {2} messages/sec
settings.net.receivedData=Received data: {0}, {1} messages, {2} messages/sec
settings.net.chainHeight=Bisq: {0} | Peers: {1}
settings.net.ips=[Indirizzo IP:porta | hostname:porta | indirizzo onion:porta] (separato da una virgola). La porta può essere omessa se è usata quella predefinita (8333).
settings.net.seedNode=Nodo seme
settings.net.directPeer=Peer (diretto)
@ -1074,7 +1091,7 @@ settings.net.inbound=in entrata
settings.net.outbound=in uscita
settings.net.reSyncSPVChainLabel=Risincronizza la catena SPV
settings.net.reSyncSPVChainButton=Elimina il file SPV e risincronizza
settings.net.reSyncSPVSuccess=Il file della catena SPV verrà eliminato al successivo avvio. È necessario riavviare l'applicazione ora.\n\nDopo il riavvio, la risincronizzazione con la rete può richiedere del tempo e vedrai tutte le transazioni solo una volta completata la risincronizzazione.\n\nA seconda del numero di transazioni e dell'età del tuo portafoglio, la risincronizzazione può richiedere fino a qualche ora e consuma il 100% della CPU. Non interrompere il processo, altrimenti sarà necessario ripeterlo.
settings.net.reSyncSPVSuccess=Are you sure you want to do an SPV resync? If you proceed, the SPV chain file will be deleted on the next startup.\n\nAfter the restart it can take a while to resync with the network and you will only see all transactions once the resync is completed.\n\nDepending on the number of transactions and the age of your wallet the resync can take up to a few hours and consumes 100% of CPU. Do not interrupt the process otherwise you have to repeat it.
settings.net.reSyncSPVAfterRestart=Il file della catena SPV è stato eliminato. Per favore sii paziente. La risincronizzazione con la rete può richiedere del tempo.
settings.net.reSyncSPVAfterRestartCompleted=La risincronizzazione è ora completata. Si prega di riavviare l'applicazione.
settings.net.reSyncSPVFailed=Impossibile eliminare il file della catena SPV.\nErrore: {0}
@ -1161,9 +1178,19 @@ account.menu.paymentAccount=Conti in valuta nazionale
account.menu.altCoinsAccountView=Conti altcoin
account.menu.password=Password portafoglio
account.menu.seedWords=Seme portafoglio
account.menu.walletInfo=Wallet info
account.menu.backup=Backup
account.menu.notifications=Notifiche
account.menu.walletInfo.balance.headLine=Wallet balances
account.menu.walletInfo.balance.info=This shows the internal wallet balance including unconfirmed transactions.\nFor BTC, the internal wallet balance shown below should match the sum of the 'Available' and 'Reserved' balances shown in the top right of this window.
account.menu.walletInfo.xpub.headLine=Watch keys (xpub keys)
account.menu.walletInfo.walletSelector={0} {1} wallet
account.menu.walletInfo.path.headLine=HD keychain paths
account.menu.walletInfo.path.info=If you import seed words into another wallet (like Electrum), you'll need to define the path. This should only be done in emergency cases when you lose access to the Bisq wallet and data directory.\nKeep in mind that spending funds from a non-Bisq wallet can bungle the internal Bisq data structures associated with the wallet data, which can lead to failed trades.\n\nNEVER send BSQ from a non-Bisq wallet, as it will probably lead to an invalid BSQ transaction and losing your BSQ.
account.menu.walletInfo.openDetails=Show raw wallet details and private keys
## TODO should we rename the following to a gereric name?
account.arbitratorRegistration.pubKey=Chiave pubblica
@ -1796,7 +1823,7 @@ dao.wallet.send.setDestinationAddress=Inserisci il tuo indirizzo di destinazione
dao.wallet.send.send=Invia fondi BSQ
dao.wallet.send.sendBtc=Invia fondi BTC
dao.wallet.send.sendFunds.headline=Conferma richiesta di prelievo
dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequired transaction fee is: {2} ({3} satoshis/vbyte)\nTransaction vsize: {4} vKb\n\nThe recipient will receive: {5}\n\nAre you sure you want to withdraw that amount?
dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequired mining fee is: {2} ({3} satoshis/vbyte)\nTransaction vsize: {4} vKb\n\nThe recipient will receive: {5}\n\nAre you sure you want to withdraw that amount?
dao.wallet.chainHeightSynced=Ultimo blocco verificato: {0}
dao.wallet.chainHeightSyncing=In attesa di blocchi... Verificati {0} blocchi su {1}
dao.wallet.tx.type=Tipo
@ -1928,9 +1955,9 @@ dao.factsAndFigures.menuItem.transactions=Transazioni BSQ
dao.factsAndFigures.dashboard.avgPrice90=Prezzo di scambio medio BSQ/BTC di 90 giorni
dao.factsAndFigures.dashboard.avgPrice30=Prezzo di scambio medio BSQ/BTC di 30 giorni
dao.factsAndFigures.dashboard.avgUSDPrice90=Prezzo medio ponderato USD/BSQ degli ultimi 90 giorni
dao.factsAndFigures.dashboard.avgUSDPrice30=Prezzo medio ponderato USD / BSQ degli ultimi 30 giorni
dao.factsAndFigures.dashboard.marketCap=Capitalizzazione di mercato (basata sul prezzo di scambio)
dao.factsAndFigures.dashboard.avgUSDPrice90=90 days volume weighted average USD/BSQ price
dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average USD/BSQ price
dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on 30 days average USD/BSQ price)
dao.factsAndFigures.dashboard.availableAmount=BSQ totale disponibile
dao.factsAndFigures.supply.issuedVsBurnt=BSQ emessi v. BSQ bruciati
@ -2000,7 +2027,7 @@ disputeSummaryWindow.openDate=Data di apertura del ticket
disputeSummaryWindow.role=Ruolo del trader
disputeSummaryWindow.payout=Pagamento dell'importo di scambio
disputeSummaryWindow.payout.getsTradeAmount=BTC {0} ottiene il pagamento dell'importo commerciale
disputeSummaryWindow.payout.getsAll=BTC {0} ottiene tutto
disputeSummaryWindow.payout.getsAll=Max. payout to BTC {0}
disputeSummaryWindow.payout.custom=Pagamento personalizzato
disputeSummaryWindow.payoutAmount.buyer=Importo pagamento dell'acquirente
disputeSummaryWindow.payoutAmount.seller=Importo pagamento del venditore
@ -2154,6 +2181,7 @@ tradeDetailsWindow.tradingPeersOnion=Indirizzi onion peer di trading
tradeDetailsWindow.tradingPeersPubKeyHash=Trading peers pubkey hash
tradeDetailsWindow.tradeState=Stato di scambio
tradeDetailsWindow.agentAddresses=Arbitro/Mediatore
tradeDetailsWindow.detailData=Detail data
walletPasswordWindow.headline=Inserisci la password per sbloccare
@ -2183,6 +2211,8 @@ feeOptionWindow.info=Puoi scegliere di pagare la commissione commerciale in BSQ
feeOptionWindow.optionsLabel=Scegli la valuta per il pagamento delle commissioni commerciali
feeOptionWindow.useBTC=Usa BTC
feeOptionWindow.fee={0} (≈ {1})
feeOptionWindow.btcFeeWithFiatAndPercentage={0} (≈ {1} / {2})
feeOptionWindow.btcFeeWithPercentage={0} ({1})
####################################################################
@ -2226,6 +2256,7 @@ popup.warning.noMediatorsAvailable=Non ci sono mediatori disponibili.
popup.warning.notFullyConnected=È necessario attendere fino a quando non si è completamente connessi alla rete.\nQuesto potrebbe richiedere fino a circa 2 minuti all'avvio.
popup.warning.notSufficientConnectionsToBtcNetwork=Devi aspettare fino a quando non hai almeno {0} connessioni alla rete Bitcoin.
popup.warning.downloadNotComplete=Devi aspettare fino al completamento del download dei blocchi Bitcoin mancanti.
popup.warning.chainNotSynced=The Bisq wallet blockchain height is not synced correctly. If you recently started the application, please wait until one Bitcoin block has been published.\n\nYou can check the blockchain height in Settings/Network Info. If more than one block passes and this problem persists it may be stalled, in which case you should do an SPV resync. [HYPERLINK:https://bisq.wiki/Resyncing_SPV_file]
popup.warning.removeOffer=Sei sicuro di voler rimuovere quell'offerta?\nLa commissione del maker di {0} andrà persa se rimuovi quell'offerta.
popup.warning.tooLargePercentageValue=Non è possibile impostare una percentuale del 100% o superiore.
popup.warning.examplePercentageValue=Inserisci un numero percentuale come \"5.4\" per il 5,4%
@ -2254,7 +2285,7 @@ popup.warning.openOffer.makerFeeTxRejected=La commissione della transazione del
popup.warning.trade.txRejected.tradeFee=commissione di scambio
popup.warning.trade.txRejected.deposit=deposita
popup.warning.trade.txRejected=La transazione {0} per lo scambio con ID {1} è stata rifiutata dalla rete Bitcoin.\nTransazione ID={2}}\nLo scambio è stato trasferito nella sezione scambi falliti.\nVai su \"Impostazioni/Informazioni di rete\" ed esegui una risincronizzazione SPV.\nPer ulteriore assistenza, contattare il canale di supporto Bisq nel team di Bisq Keybase.
popup.warning.trade.txRejected=The {0} transaction for trade with ID {1} was rejected by the Bitcoin network.\nTransaction ID={2}\nThe trade has been moved to failed trades.\nPlease go to \"Settings/Network info\" and do a SPV resync.\nFor further help please contact the Bisq support channel at the Bisq Keybase team.
popup.warning.openOfferWithInvalidMakerFeeTx=La commissione della transazione del creatore dell'offerta con ID {0} non è valida.\nTransazione ID={1}.\nVai su \"Impostazioni/Informazioni di rete\" ed esegui una risincronizzazione SPV.\nPer ulteriore assistenza, contattare il canale di supporto Bisq nel team di Bisq Keybase.
@ -2264,14 +2295,15 @@ popup.info.cashDepositInfo=Assicurati di avere una filiale bancaria nella tua zo
popup.info.cashDepositInfo.confirm=Confermo di poter effettuare il deposito
popup.info.shutDownWithOpenOffers=Bisq viene chiuso, ma ci sono offerte aperte.\n\nQueste offerte non saranno disponibili sulla rete P2P mentre Bisq rimane chiuso, ma verranno ripubblicate sulla rete P2P al prossimo avvio di Bisq.\n\nPer mantenere le tue offerte attive è necessario che Bisq rimanga in funzione ed il computer online (assicurati che non vada in modalità standby. Il solo monitor in standby non è un problema).
popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\nPlease make sure your Bisq qube is setup according to our Setup Guide at [HYPERLINK:https://bisq.wiki/Running_Bisq_on_Qubes].
popup.warn.downGradePrevention=Downgrade from version {0} to version {1} is not supported. Please use the latest Bisq version.
popup.warn.daoRequiresRestart=There was a problem with synchronizing the DAO state. You have to restart the application to fix the issue.
popup.privateNotification.headline=Notifica privata importante!
popup.securityRecommendation.headline=Raccomandazione di sicurezza importante
popup.securityRecommendation.msg=Vorremmo ricordarti di prendere in considerazione l'utilizzo della protezione con password per il tuo portafoglio se non l'avessi già abilitato.\n\nSi consiglia inoltre di annotare le parole seme del portafoglio. Le parole seme sono come una password principale per recuperare il tuo portafoglio Bitcoin.\nNella sezione \"Wallet Seed\" trovi ulteriori informazioni.\n\nInoltre, è necessario eseguire il backup della cartella completa dei dati dell'applicazione nella sezione \"Backup\".
popup.bitcoinLocalhostNode.msg=Bisq ha rilevato un nodo Bitcoin Core in esecuzione localmente (su localhost).\nAssicurarsi che questo nodo sia completamente sincronizzato prima di avviare Bisq e che non sia in esecuzione in modalità di eliminazione.
popup.bitcoinLocalhostNode.additionalRequirements=\n\nPer un nodo ben configurato, i requisiti necessari sono che il nodo abbia il pruning disabilitato e i filtri bloom abilitati.
popup.bitcoinLocalhostNode.msg=Bisq detected a Bitcoin Core node running on this machine (at localhost).\n\nPlease ensure:\n- the node is fully synced before starting Bisq\n- pruning is disabled ('prune=0' in bitcoin.conf)\n- bloom filters are enabled ('peerbloomfilters=1' in bitcoin.conf)
popup.shutDownInProgress.headline=Arresto in corso
popup.shutDownInProgress.msg=La chiusura dell'applicazione può richiedere un paio di secondi.\nNon interrompere il processo.
@ -2282,7 +2314,7 @@ popup.info.multiplePaymentAccounts.headline=Disponibili più conti di pagamento
popup.info.multiplePaymentAccounts.msg=Hai più account di pagamento disponibili per questa offerta. Assicurati di aver scelto quello giusto.
popup.accountSigning.selectAccounts.headline=Seleziona conti di pagamento
popup.accountSigning.selectAccounts.description=In base al metodo di pagamento e al momento in cui verranno selezionati tutti i conti di pagamento collegati a una controversia in cui si è verificato un pagamento
popup.accountSigning.selectAccounts.description=In base al metodo di pagamento e al momento in cui verranno selezionati tutti i conti di pagamento collegati a una controversia in cui si è verificato un pagamento
popup.accountSigning.selectAccounts.signAll=Firma tutti i metodi di pagamento
popup.accountSigning.selectAccounts.datePicker=Seleziona il momento in cui verranno firmati gli account
@ -2303,15 +2335,12 @@ popup.accountSigning.signedByPeer=Uno dei tuoi conti di pagamento è stato verif
popup.accountSigning.peerLimitLifted=Il limite iniziale per uno dei tuoi account è stato revocato.\n\n{0}
popup.accountSigning.peerSigner=Uno dei tuoi account è abbastanza maturo per firmare altri account di pagamento e il limite iniziale per uno dei tuoi account è stato revocato.\n\n{0}
popup.accountSigning.singleAccountSelect.headline=Select account age witness
popup.accountSigning.singleAccountSelect.description=Search for account age witness.
popup.accountSigning.singleAccountSelect.datePicker=Select point of time for signing
popup.accountSigning.singleAccountSelect.headline=Import unsigned account age witness
popup.accountSigning.confirmSingleAccount.headline=Confirm selected account age witness
popup.accountSigning.confirmSingleAccount.selectedHash=Selected witness hash
popup.accountSigning.confirmSingleAccount.button=Sign account age witness
popup.accountSigning.successSingleAccount.description=Witness {0} was signed
popup.accountSigning.successSingleAccount.success.headline=Success
popup.accountSigning.successSingleAccount.signError=Failed to sign witness, {0}
popup.accountSigning.unsignedPubKeys.headline=Unsigned Pubkeys
popup.accountSigning.unsignedPubKeys.sign=Sign Pubkeys
@ -2447,8 +2476,8 @@ navigation.dao.wallet.receive=\"Portafoglio DAO/BSQ/Ricevi\"
formatter.formatVolumeLabel={0} importo{1}
formatter.makerTaker=Maker come {0} {1} / Taker come {2} {3}
formatter.youAreAsMaker=Sei {0} {1} come maker / Taker è {2} {3}
formatter.youAreAsTaker=Sei {0} {1} come Taker / make è {2} {3}
formatter.youAreAsMaker=You are: {1} {0} (maker) / Taker is: {3} {2}
formatter.youAreAsTaker=You are: {1} {0} (taker) / Maker is: {3} {2}
formatter.youAre=Sei {0} {1} ({2} {3})
formatter.youAreCreatingAnOffer.fiat=Stai creando un'offerta per {0} {1}
formatter.youAreCreatingAnOffer.altcoin=Stai creando un'offerta per {0} {1} ({2} {3})
@ -2561,6 +2590,7 @@ payment.venmo.venmoUserName=Nome utente Venmo
payment.popmoney.accountId=Email o numero di telefono fisso
payment.promptPay.promptPayId=Codice fiscale/P.IVA o n. di telefono
payment.supportedCurrencies=Valute supportate
payment.supportedCurrenciesForReceiver=Currencies for receiving funds
payment.limitations=Limitazioni
payment.salt=Sale per la verifica dell'età dell'account
payment.error.noHexSalt=The salt needs to be in HEX format.\nIt is only recommended to edit the salt field if you want to transfer the salt from an old account to keep your account age. The account age is verified by using the account salt and the identifying account data (e.g. IBAN).
@ -2635,7 +2665,8 @@ payment.japan.recipient=Nome
payment.australia.payid=PayID
payment.payid=PayID linked to financial institution. Like email address or mobile phone.
payment.payid.info=A PayID like a phone number, email address or an Australian Business Number (ABN), that you can securely link to your bank, credit union or building society account. You need to have already created a PayID with your Australian financial institution. Both sending and receiving financial institutions must support PayID. For more information please check [HYPERLINK:https://payid.com.au/faqs/]
payment.amazonGiftCard.info=To pay with Amazon eGift Card you need to purchase an Amazon eGift Card at your Amazon account and use the BTC seller''s email or mobile nr. as receiver. Amazon sends then an email or text message to the receiver. Use the trade ID for the message field.\n\nAmazon eGift Cards can only be redeemed by Amazon accounts with the same currency.\n\nFor more information visit the Amazon eGift Card webpage. [HYPERLINK:https://www.amazon.com/Amazon-1_US_Email-eGift-Card/dp/B004LLIKVU]
payment.amazonGiftCard.info=To pay with Amazon eGift Card, you will need to send an Amazon eGift Card to the BTC seller via your Amazon account. \n\nBisq will show the BTC seller''s email address or phone number where the gift card should be sent, and you must include the trade ID in the gift card''s message field. Please see the wiki [HYPERLINK:https://bisq.wiki/Amazon_eGift_card] for further details and best practices. \n\nThree important notes:\n- try to send gift cards with amounts of 100 USD or smaller, as Amazon is known to flag larger gift cards as fraudulent\n- try to use creative, believable text for the gift card''s message (e.g., "Happy birthday Susan!") along with the trade ID (and use trader chat to tell your trading peer the reference text you picked so they can verify your payment)\n- Amazon eGift Cards can only be redeemed on the Amazon website they were purchased on (e.g., a gift card purchased on amazon.it can only be redeemed on amazon.it)
# We use constants from the code so we do not use our normal naming convention
# dynamic values are not recognized by IntelliJ

Some files were not shown because too many files have changed in this diff Show More