diff --git a/.gitignore b/.gitignore index b408ac7647..ec6bf87ec4 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,3 @@ deploy /monitor/monitor-tor/* .java-version .localnet -/apitest/src/main/resources/dao-setup* diff --git a/Makefile b/Makefile index 641a37348f..26eb1897b5 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # See docs/installing.md -build: nodes localnet build-haveno +build: nodes localnet build-haveno clean: ./gradlew clean @@ -9,7 +9,7 @@ clean-localnet: rm -rf .localnet localnet: - mkdir -p .localnet + mkdir -p .localnet nodes: localnet ./scripts/xmr_btc_deps.sh @@ -39,7 +39,6 @@ seednode: --useDevPrivilegeKeys=true \ --nodePort=2002 \ --appName=haveno-XMR_STAGENET_Seed_2002 \ - --daoActivated=false arbitrator-desktop: # Arbitrator and mediator need to be registerd in the UI after launching it. @@ -49,7 +48,6 @@ arbitrator-desktop: --useDevPrivilegeKeys=true \ --nodePort=4444 \ --appName=haveno-XMR_STAGENET_arbitrator \ - --daoActivated=false \ --apiPassword=apitest \ --apiPort=9998 @@ -60,7 +58,6 @@ alice-desktop: --useDevPrivilegeKeys=true \ --nodePort=5555 \ --appName=haveno-XMR_STAGENET_Alice \ - --daoActivated=false \ --apiPassword=apitest \ --apiPort=9999 @@ -71,7 +68,6 @@ alice-daemon: --useDevPrivilegeKeys=true \ --nodePort=5555 \ --appName=haveno-XMR_STAGENET_Alice \ - --daoActivated=false \ --apiPassword=apitest \ --apiPort=9999 @@ -82,7 +78,6 @@ bob-desktop: --useDevPrivilegeKeys=true \ --nodePort=6666 \ --appName=haveno-XMR_STAGENET_Bob \ - --daoActivated=false \ --apiPassword=apitest \ --apiPort=10000 @@ -93,7 +88,6 @@ bob-daemon: --useDevPrivilegeKeys=true \ --nodePort=6666 \ --appName=haveno-XMR_STAGENET_Bob \ - --daoActivated=false \ --apiPassword=apitest \ --apiPort=10000 diff --git a/apitest/dao-setup.gradle b/apitest/dao-setup.gradle deleted file mode 100644 index 833f5e7355..0000000000 --- a/apitest/dao-setup.gradle +++ /dev/null @@ -1,83 +0,0 @@ -// This gradle file contains tasks to install and clean dao-setup files downloaded from -// https://github.com/bisq-network/bisq/raw/master/docs/dao-setup.zip -// These tasks are not run by the default build, but they can can be run during a full -// or partial builds, or by themselves. -// To run a full Bisq clean build, test, and install dao-setup files: -// ./gradlew clean build :apitest:installDaoSetup -// To install or re-install dao-setup file only: -// ./gradlew :apitest:installDaoSetup -x test -// To clean installed dao-setup files: -// ./gradlew :apitest:cleanDaoSetup -x test -// -// The :apitest subproject will not run on Windows, and these tasks have not been -// tested on Windows. -def buildResourcesDir = project(":apitest").buildDir.path + '/resources/main' - -// This task requires ant in the system $PATH. -task installDaoSetup(dependsOn: 'cleanDaoSetup') { - doLast { - println "Installing dao-setup directories in build dir $buildResourcesDir ..." - def src = 'https://github.com/bisq-network/bisq/raw/master/docs/dao-setup.zip' - def destfile = project.rootDir.path + '/apitest/src/main/resources/dao-setup.zip' - def url = new URL(src) - def f = new File(destfile) - if (f.exists()) { - println "File $destfile already exists, skipping download." - } else { - if (!f.parentFile.exists()) - mkdir "$buildResourcesDir" - - println "Downloading $url to $buildResourcesDir ..." - url.withInputStream { i -> f.withOutputStream { it << i } } - } - - // We need an ant task for unzipping the dao-setup.zip file. - println "Unzipping $destfile to $buildResourcesDir ..." - ant.unzip(src: 'src/main/resources/dao-setup.zip', - dest: 'src/main/resources', - overwrite: "true") { - // Warning: overwrite: "true" does not work if empty dirs exist, so the - // cleanDaoSetup task should be run before trying to re-install fresh - // dao-setup files. - patternset() { - include(name: '**') - exclude(name: '**/bitcoin.conf') // installed at runtime with correct blocknotify script path - exclude(name: '**/blocknotify') // installed from src/main/resources to allow port configs - } - mapper(type: "identity") - } - - // Copy files from unzip target dir 'dao-setup' to build/resources/main. - def daoSetupSrc = project.rootDir.path + '/apitest/src/main/resources/dao-setup' - def daoSetupDest = buildResourcesDir + '/dao-setup' - println "Copying $daoSetupSrc to $daoSetupDest ..." - copy { - from daoSetupSrc - into daoSetupDest - } - - // Move dao-setup files from build/resources/main/dao-setup to build/resources/main - file(buildResourcesDir + '/dao-setup/Bitcoin-regtest') - .renameTo(file(buildResourcesDir + '/Bitcoin-regtest')) - file(buildResourcesDir + '/dao-setup/bisq-XMR_STAGENET_Alice_dao') - .renameTo(file(buildResourcesDir + '/bisq-XMR_STAGENET_Alice_dao')) - file(buildResourcesDir + '/dao-setup/bisq-XMR_STAGENET_Bob_dao') - .renameTo(file(buildResourcesDir + '/bisq-XMR_STAGENET_Bob_dao')) - delete file(buildResourcesDir + '/dao-setup') - } -} - -task cleanDaoSetup { - doLast { - // When re-installing dao-setup files before re-running tests, the bitcoin - // datadir and dao-setup dirs have to be cleaned first. This task allows - // you to re-install dao-setup files and re-run tests without having to - // re-compile any code. - println "Deleting dao-setup directories in build dir $buildResourcesDir ..." - delete file(buildResourcesDir + '/Bitcoin-regtest') - delete file(buildResourcesDir + '/bisq-XMR_STAGENET_Seed_2002') - delete file(buildResourcesDir + '/bisq-XMR_STAGENET_Arb_dao') - delete file(buildResourcesDir + '/bisq-XMR_STAGENET_Alice_dao') - delete file(buildResourcesDir + '/bisq-XMR_STAGENET_Bob_dao') - } -} diff --git a/apitest/docs/api-beta-test-guide.md b/apitest/docs/api-beta-test-guide.md index 4a110a303a..89fe788c58 100644 --- a/apitest/docs/api-beta-test-guide.md +++ b/apitest/docs/api-beta-test-guide.md @@ -33,14 +33,6 @@ called `api-beta-test`. $ git clone https://github.com/bisq-network/bisq.git api-beta-test ``` -Change your current working directory to `api-beta-test`, build the source, and download / install Bisq’s -pre-configured DAO / dev / regtest setup files. -``` -$ cd api-beta-test -$ ./gradlew clean build :apitest:installDaoSetup -x test # if you want to skip Bisq tests -$ ./gradlew clean build :apitest:installDaoSetup # if you want to run Bisq tests -``` - ## Running Api Test Harness If your bitcoin-core binaries are in your system `PATH`, start bitcoind in regtest-mode, Bisq seednode and arbitration @@ -131,7 +123,7 @@ The script takes four options: This simulation creates US / USD face-to-face payment accounts for Bob and Alice. Alice (always the trade maker) creates a SELL / USD offer for the amount of 0.1 BTC, at a price 2% below the current market price. Bob (always the taker), will use his face-to-face account to take the offer, then the two sides will complete -the trade, checking their trade status along the way, and their BSQ / BTC balances when the trade is closed. +the trade, checking their trade status along the way, and their BTC balances when the trade is closed. ``` $ apitest/scripts/trade-simulation.sh -d sell -c us -m 2.00 -a 0.1 ``` @@ -175,7 +167,7 @@ method help will be returned from the server. Also note an api password is requ There is no need to secure your regtest Bisq wallet with an encryption password when running these examples, but you should encrypt your mainnet wallet as you probably already do when using the Bisq UI to transact in real BTC. This section explains how to encrypt your Bisq wallet with the CLI, and unlock it before performing wallet -related operations such as creating and taking offers, checking balances, and sending BSQ and BTC to external wallets. +related operations such as creating and taking offers, checking balances, and sending BTC to external wallets. Encrypt your wallet with a password: ``` @@ -201,17 +193,6 @@ $ ./bisq-cli --password=xyz lockwallet ### Checking Balances -Show full BSQ and BTC wallet balance information: -``` -$ ./bisq-cli --password=xyz --port=9998 getbalance -``` - -Show full BSQ wallet balance information: -``` -$ ./bisq-cli --password=xyz --port=9999 getbalance --currency-code=bsq -``` -_Note: The example above is asking for Bob’s balance (using port `9999`), not Alice’s balance._ - Show Bob’s full BTC wallet balance information: ``` $ ./bisq-cli --password=xyz --port=9999 getbalance --currency-code=btc @@ -230,33 +211,9 @@ You can check a block explorer for the status of a transaction, or you can check $ ./bisq-cli --password=xyz --port=9998 getaddressbalance --address= ``` -#### Receiving BSQ -To receive BSQ from an external wallet, find an unused BSQ address: -``` -$ ./bisq-cli --password=xyz --port=9998 getunusedbsqaddress -``` +### Sending BTC to External Wallets -Give the public address to the sender. After the BSQ is sent, you can check block explorers for the status of -the transaction. There is no support (yet) to check the balance of an individual BSQ address in your wallet, -but you can check your BSQ wallet’s balance to determine if the new funds have arrived: -``` -$ ./bisq-cli --password=xyz --port=9999 getbalance --currency-code=bsq -``` - -### Sending BSQ and BTC to External Wallets - -Below are commands for sending BSQ and BTC to external wallets. - -Send BSQ: -``` -$ ./bisq-cli --password=xyz --port=9998 sendbsq --address= --amount= -``` -_Note: Sending BSQ to non-Bisq wallets is not supported and highly discouraged._ - -Send BSQ with a withdrawal transaction fee of 10 sats/byte: -``` -$ ./bisq-cli --password=xyz --port=9998 sendbsq --address= --amount= --tx-fee-rate=10 -``` +Below are commands for sending BTC to external wallets. Send BTC: ``` @@ -272,7 +229,7 @@ $ ./bisq-cli --password=xyz --port=9998 sendbtc --address= --amount If you have traded using the Bisq UI, you are probably aware of the default network bitcoin withdrawal transaction fee and custom withdrawal transaction fee user preference in the UI’s setting view. The Api uses these same withdrawal transaction fee rates, and affords a third – as mentioned in the previous section -- withdrawal -transaction fee option in the `sendbsq` and `sendbtc` commands. The `sendbsq` and `sendbtc` commands' +transaction fee option in the `sendbtc` commands. The `sendbtc` commands' `--tx-fee-rate=` options override both the default network fee rate, and your custom transaction fee setting for the execution of those commands. @@ -341,7 +298,7 @@ $ ./bisq-cli --password=xyz --port=9998 createoffer --help The `trade-simulation.sh` script described above is an easy way to figure out how to use this command. In a previous example, Alice created a BUY/ EUR offer to buy 0.125 BTC at a fixed price of 30,800 EUR, -and pay the Bisq maker fee in BSQ. Alice had already created an EUR face-to-face payment account with id +and pay the Bisq maker fee in BTC. Alice had already created an EUR face-to-face payment account with id `f3c1ec8b-9761-458d-b13d-9039c6892413`, and used this `createoffer` command: ``` $ ./bisq-cli --password=xyz --port=9998 createoffer \ @@ -351,7 +308,7 @@ $ ./bisq-cli --password=xyz --port=9998 createoffer \ --amount=0.125 \ --fixed-price=30800 \ --security-deposit=15.0 \ - --fee-currency=BSQ + --fee-currency=BTC ``` If Alice was in Japan, and wanted to create an offer to sell 0.125 BTC at 0.5% above the current market JPY price, @@ -364,7 +321,7 @@ $ ./bisq-cli --password=xyz --port=9998 createoffer \ --amount=0.125 \ --market-price-margin=0.5 \ --security-deposit=15.0 \ - --fee-currency=BSQ + --fee-currency=BTC ``` The `trade-simulation.sh` script options that would generate the previous `createoffer` example is: diff --git a/apitest/docs/build-run.md b/apitest/docs/build-run.md index ba9149e238..6d3a0656b9 100644 --- a/apitest/docs/build-run.md +++ b/apitest/docs/build-run.md @@ -26,31 +26,6 @@ with specific command line options, i.e., unique appDatadir and ports, but this The API test harness uses the GNU Bourne-Again SHell `bash`, and is not supported on Windows. -### Predefined DAO / Regtest Setup - -The API test harness depends on the contents of https://github.com/bisq-network/bisq/raw/master/docs/dao-setup.zip. -The files contained in dao-setup.zip include a bitcoin-core wallet, a regtest genesis tx and chain of 111 blocks, plus -data directories for Bob and Alice Bisq instances. Bob & Alice wallets are pre-configured with 10 BTC each, and the -equivalent of 2.5 BTC in BSQ distributed among Bob & Alice's BSQ wallets. - -See https://github.com/bisq-network/bisq/blob/master/docs/dao-setup.md for details. - -### Install DAO / Regtest Setup Files - -Bisq's gradle build file defines a task for downloading dao-setup.zip and extracting its contents to the -`apitest/src/main/resources` folder, and the test harness will install a fresh set of data files to the -`apitest/build/resources/main` folder during a test case's scaffold setup phase -- normally a static `@BeforeAll` method. - -The dao-setup files can be downloaded during a normal build: - - $ ./gradlew clean build :apitest:installDaoSetup - -Or by running a single task: - - $ ./gradlew :apitest:installDaoSetup - -The `:apitest:installDaoSetup` task does not need to be run again until after the next time you run the gradle `clean` task. - ### Run API Tests The API test harness supports narrow & broad functional and full end to end test cases requiring diff --git a/apitest/scripts/limit-order-simulation.sh b/apitest/scripts/limit-order-simulation.sh index db37d8ab94..2f7b1479bb 100755 --- a/apitest/scripts/limit-order-simulation.sh +++ b/apitest/scripts/limit-order-simulation.sh @@ -125,7 +125,6 @@ else CMD+=" --market-price-margin=$MKT_PRICE_MARGIN" fi CMD+=" --security-deposit=50.0" -CMD+=" --fee-currency=BSQ" printdate "ALICE CLI: $CMD" OFFER_ID=$(createoffer "$CMD") exitoncommandalert $? diff --git a/apitest/scripts/mainnet-test.sh b/apitest/scripts/mainnet-test.sh index 8f2815d4d0..9edf53f158 100755 --- a/apitest/scripts/mainnet-test.sh +++ b/apitest/scripts/mainnet-test.sh @@ -154,11 +154,6 @@ [ "$status" -eq 0 ] } -@test "test getunusedbsqaddress" { - run ./bisq-cli --password=xyz getunusedbsqaddress - [ "$status" -eq 0 ] -} - @test "test getaddressbalance missing address argument" { run ./bisq-cli --password=xyz getaddressbalance [ "$status" -eq 1 ] diff --git a/apitest/scripts/trade-simulation-env.sh b/apitest/scripts/trade-simulation-env.sh index c8d4bfa312..3dbe93235d 100755 --- a/apitest/scripts/trade-simulation-env.sh +++ b/apitest/scripts/trade-simulation-env.sh @@ -226,14 +226,14 @@ checkseednoderunning() { checkarbnoderunning() { if [[ "$LINUX" == "TRUE" ]]; then - if pgrep -f "bisq.daemon.app.BisqDaemonMain --appName=bisq-XMR_STAGENET_Arb_dao" > /dev/null ; then + if pgrep -f "bisq.daemon.app.BisqDaemonMain --appName=bisq-XMR_STAGENET_Arb" > /dev/null ; then printdate "The arbitration node is running on host." else printdate "Error: arbitration node is not running on host, exiting." apitestusage fi elif [[ "$DARWIN" == "TRUE" ]]; then - if ps -A | awk '/[b]isq.daemon.app.BisqDaemonMain --appName=bisq-XMR_STAGENET_Arb_dao/ {print $1}' > /dev/null ; then + if ps -A | awk '/[b]isq.daemon.app.BisqDaemonMain --appName=bisq-XMR_STAGENET_Arb/ {print $1}' > /dev/null ; then printdate "The arbitration node is running on host." else printdate "Error: arbitration node is not running on host, exiting." @@ -247,14 +247,14 @@ checkarbnoderunning() { checkalicenoderunning() { if [[ "$LINUX" == "TRUE" ]]; then - if pgrep -f "bisq.daemon.app.BisqDaemonMain --appName=bisq-XMR_STAGENET_Alice_dao" > /dev/null ; then + if pgrep -f "bisq.daemon.app.BisqDaemonMain --appName=bisq-XMR_STAGENET_Alice" > /dev/null ; then printdate "Alice's node is running on host." else printdate "Error: Alice's node is not running on host, exiting." apitestusage fi elif [[ "$DARWIN" == "TRUE" ]]; then - if ps -A | awk '/[b]isq.daemon.app.BisqDaemonMain --appName=bisq-XMR_STAGENET_Alice_dao/ {print $1}' > /dev/null ; then + if ps -A | awk '/[b]isq.daemon.app.BisqDaemonMain --appName=bisq-XMR_STAGENET_Alice/ {print $1}' > /dev/null ; then printdate "Alice's node node is running on host." else printdate "Error: Alice's node is not running on host, exiting." @@ -268,14 +268,14 @@ checkalicenoderunning() { checkbobnoderunning() { if [[ "$LINUX" == "TRUE" ]]; then - if pgrep -f "bisq.daemon.app.BisqDaemonMain --appName=bisq-XMR_STAGENET_Alice_dao" > /dev/null ; then + if pgrep -f "bisq.daemon.app.BisqDaemonMain --appName=bisq-XMR_STAGENET_Alice" > /dev/null ; then printdate "Bob's node is running on host." else printdate "Error: Bob's node is not running on host, exiting." apitestusage fi elif [[ "$DARWIN" == "TRUE" ]]; then - if ps -A | awk '/[b]isq.daemon.app.BisqDaemonMain --appName=bisq-XMR_STAGENET_Alice_dao/ {print $1}' > /dev/null ; then + if ps -A | awk '/[b]isq.daemon.app.BisqDaemonMain --appName=bisq-XMR_STAGENET_Alice/ {print $1}' > /dev/null ; then printdate "Bob's node node is running on host." else printdate "Error: Bob's node is not running on host, exiting." diff --git a/apitest/scripts/trade-simulation-utils.sh b/apitest/scripts/trade-simulation-utils.sh index 3ed7d3488b..406d4fa8f6 100755 --- a/apitest/scripts/trade-simulation-utils.sh +++ b/apitest/scripts/trade-simulation-utils.sh @@ -193,7 +193,7 @@ gencreateoffercommand() { CMD+=" --market-price-margin=$MKT_PRICE_MARGIN" fi CMD+=" --security-deposit=15.0" - CMD+=" --fee-currency=BSQ" + CMD+=" --fee-currency=BTC" echo "$CMD" } @@ -477,7 +477,7 @@ executetrade() { printdate "First offer found: $OFFER_ID" # Take Alice's offer. - CMD="$CLI_BASE --port=$BOB_PORT takeoffer --offer-id=$OFFER_ID --payment-account=$BOB_ACCT_ID --fee-currency=bsq" + CMD="$CLI_BASE --port=$BOB_PORT takeoffer --offer-id=$OFFER_ID --payment-account=$BOB_ACCT_ID" printdate "BOB CLI: $CMD" TRADE=$($CMD) commandalert $? "Could not take offer." diff --git a/apitest/src/main/java/bisq/apitest/ApiTestMain.java b/apitest/src/main/java/bisq/apitest/ApiTestMain.java index 20a40850ec..ea5b47c52e 100644 --- a/apitest/src/main/java/bisq/apitest/ApiTestMain.java +++ b/apitest/src/main/java/bisq/apitest/ApiTestMain.java @@ -38,7 +38,7 @@ import bisq.apitest.config.ApiTestConfig; * * It can be used to smoke test your bitcoind environment: bisq-apitest. * - * It can be used to run the regtest/dao environment for release testing: + * It can be used to run the regtest environment for release testing: * bisq-test --shutdownAfterTests=false * * All method, scenario and end to end tests are found in the test sources folder. diff --git a/apitest/src/main/java/bisq/apitest/Scaffold.java b/apitest/src/main/java/bisq/apitest/Scaffold.java index d518c0bcb0..57ebdc2c13 100644 --- a/apitest/src/main/java/bisq/apitest/Scaffold.java +++ b/apitest/src/main/java/bisq/apitest/Scaffold.java @@ -139,7 +139,6 @@ public class Scaffold { public Scaffold setUp() throws IOException, InterruptedException, ExecutionException { - installDaoSetupDirectories(); // Start each background process from an executor, then add a shutdown hook. CountDownLatch countdownLatch = new CountDownLatch(config.supportingApps.size()); @@ -161,7 +160,6 @@ public class Scaffold { try { log.info("Shutting down executor service ..."); executor.shutdownNow(); - //noinspection ResultOfMethodCallIgnored executor.awaitTermination(config.supportingApps.size() * 2000L, MILLISECONDS); SetupTask[] orderedTasks = new SetupTask[]{ @@ -208,84 +206,6 @@ public class Scaffold { return firstException; } - public void installDaoSetupDirectories() { - cleanDaoSetupDirectories(); - - String daoSetupDir = Paths.get(config.baseSrcResourcesDir, "dao-setup").toFile().getAbsolutePath(); - String buildDataDir = config.rootAppDataDir.getAbsolutePath(); - try { - if (!new File(daoSetupDir).exists()) - throw new FileNotFoundException( - format("Dao setup dir '%s' not found. Run gradle :apitest:installDaoSetup" - + " to download dao-setup.zip and extract contents to resources folder", - daoSetupDir)); - - BashCommand copyBitcoinRegtestDir = new BashCommand( - "cp -rf " + daoSetupDir + "/Bitcoin-regtest/regtest" - + " " + config.bitcoinDatadir); - if (copyBitcoinRegtestDir.run().getExitStatus() != 0) - throw new IllegalStateException("Could not install bitcoin regtest dir"); - - String aliceDataDir = daoSetupDir + "/" + alicedaemon.appName; - BashCommand copyAliceDataDir = new BashCommand( - "cp -rf " + aliceDataDir + " " + config.rootAppDataDir); - if (copyAliceDataDir.run().getExitStatus() != 0) - throw new IllegalStateException("Could not install alice data dir"); - - String bobDataDir = daoSetupDir + "/" + bobdaemon.appName; - BashCommand copyBobDataDir = new BashCommand( - "cp -rf " + bobDataDir + " " + config.rootAppDataDir); - if (copyBobDataDir.run().getExitStatus() != 0) - throw new IllegalStateException("Could not install bob data dir"); - - log.info("Installed dao-setup files into {}", buildDataDir); - - if (!config.callRateMeteringConfigPath.isEmpty()) { - installCallRateMeteringConfiguration(aliceDataDir); - installCallRateMeteringConfiguration(bobDataDir); - } - - // Copy the blocknotify script from the src resources dir to the build - // resources dir. Users may want to edit comment out some lines when all - // of the default block notifcation ports being will not be used (to avoid - // seeing rpc notifcation warnings in log files). - installBitcoinBlocknotify(); - - } catch (IOException | InterruptedException ex) { - throw new IllegalStateException("Could not install dao-setup files from " + daoSetupDir, ex); - } - } - - private void cleanDaoSetupDirectories() { - String buildDataDir = config.rootAppDataDir.getAbsolutePath(); - log.info("Cleaning dao-setup data in {}", buildDataDir); - - try { - BashCommand rmBobDataDir = new BashCommand("rm -rf " + config.rootAppDataDir + "/" + bobdaemon.appName); - if (rmBobDataDir.run().getExitStatus() != 0) - throw new IllegalStateException("Could not delete bob data dir"); - - BashCommand rmAliceDataDir = new BashCommand("rm -rf " + config.rootAppDataDir + "/" + alicedaemon.appName); - if (rmAliceDataDir.run().getExitStatus() != 0) - throw new IllegalStateException("Could not delete alice data dir"); - - BashCommand rmArbNodeDataDir = new BashCommand("rm -rf " + config.rootAppDataDir + "/" + arbdaemon.appName); - if (rmArbNodeDataDir.run().getExitStatus() != 0) - throw new IllegalStateException("Could not delete arbitrator data dir"); - - BashCommand rmSeedNodeDataDir = new BashCommand("rm -rf " + config.rootAppDataDir + "/" + seednode.appName); - if (rmSeedNodeDataDir.run().getExitStatus() != 0) - throw new IllegalStateException("Could not delete seednode data dir"); - - BashCommand rmBitcoinRegtestDir = new BashCommand("rm -rf " + config.bitcoinDatadir + "/regtest"); - if (rmBitcoinRegtestDir.run().getExitStatus() != 0) - throw new IllegalStateException("Could not clean bitcoind regtest dir"); - - } catch (IOException | InterruptedException ex) { - throw new IllegalStateException("Could not clean dao-setup files from " + buildDataDir, ex); - } - } - private void installBitcoinBlocknotify() { // gradle is not working for this try { diff --git a/apitest/src/main/java/bisq/apitest/config/ApiTestConfig.java b/apitest/src/main/java/bisq/apitest/config/ApiTestConfig.java index 0a779f4781..1ca4180b1a 100644 --- a/apitest/src/main/java/bisq/apitest/config/ApiTestConfig.java +++ b/apitest/src/main/java/bisq/apitest/config/ApiTestConfig.java @@ -55,7 +55,6 @@ import static joptsimple.internal.Strings.EMPTY; public class ApiTestConfig { // Global constants - public static final String BSQ = "BSQ"; public static final String BTC = "BTC"; public static final String XMR = "XMR"; public static final String ARBITRATOR = "arbitrator"; diff --git a/apitest/src/main/java/bisq/apitest/config/BisqAppConfig.java b/apitest/src/main/java/bisq/apitest/config/BisqAppConfig.java index a34c7e7a2d..b92f87d70c 100644 --- a/apitest/src/main/java/bisq/apitest/config/BisqAppConfig.java +++ b/apitest/src/main/java/bisq/apitest/config/BisqAppConfig.java @@ -28,7 +28,6 @@ import bisq.daemon.app.BisqDaemonMain; /** Some non user configurable Bisq seednode, arb node, bob and alice daemon option values. @see dev-setup.md - @see dao-setup.md */ public enum BisqAppConfig { @@ -40,7 +39,7 @@ public enum BisqAppConfig { 5120, -1, 49996), - arbdaemon("bisq-XMR_STAGENET_Arb_dao", + arbdaemon("bisq-XMR_STAGENET_Arb", "bisq-daemon", "-XX:MaxRAM=2g -Dlogback.configurationFile=apitest/build/resources/main/logback.xml", BisqDaemonMain.class.getName(), @@ -48,7 +47,7 @@ public enum BisqAppConfig { 5121, 9997, 49997), - arbdesktop("bisq-XMR_STAGENET_Arb_dao", + arbdesktop("bisq-XMR_STAGENET_Arb", "bisq-desktop", "-XX:MaxRAM=3g -Dlogback.configurationFile=apitest/build/resources/main/logback.xml", BisqAppMain.class.getName(), @@ -56,7 +55,7 @@ public enum BisqAppConfig { 5121, -1, 49997), - alicedaemon("bisq-XMR_STAGENET_Alice_dao", + alicedaemon("bisq-XMR_STAGENET_Alice", "bisq-daemon", "-XX:MaxRAM=2g -Dlogback.configurationFile=apitest/build/resources/main/logback.xml", BisqDaemonMain.class.getName(), @@ -64,7 +63,7 @@ public enum BisqAppConfig { 5122, 9998, 49998), - alicedesktop("bisq-XMR_STAGENET_Alice_dao", + alicedesktop("bisq-XMR_STAGENET_Alice", "bisq-desktop", "-XX:MaxRAM=4g -Dlogback.configurationFile=apitest/build/resources/main/logback.xml", BisqAppMain.class.getName(), @@ -72,7 +71,7 @@ public enum BisqAppConfig { 5122, -1, 49998), - bobdaemon("bisq-XMR_STAGENET_Bob_dao", + bobdaemon("bisq-XMR_STAGENET_Bob", "bisq-daemon", "-XX:MaxRAM=2g -Dlogback.configurationFile=apitest/build/resources/main/logback.xml", BisqDaemonMain.class.getName(), @@ -80,7 +79,7 @@ public enum BisqAppConfig { 5123, 9999, 49999), - bobdesktop("bisq-XMR_STAGENET_Bob_dao", + bobdesktop("bisq-XMR_STAGENET_Bob", "bisq-desktop", "-XX:MaxRAM=4g -Dlogback.configurationFile=apitest/build/resources/main/logback.xml", BisqAppMain.class.getName(), diff --git a/apitest/src/main/java/bisq/apitest/linux/BisqProcess.java b/apitest/src/main/java/bisq/apitest/linux/BisqProcess.java index 04bbd38a38..203a63205a 100644 --- a/apitest/src/main/java/bisq/apitest/linux/BisqProcess.java +++ b/apitest/src/main/java/bisq/apitest/linux/BisqProcess.java @@ -48,8 +48,6 @@ public class BisqProcess extends AbstractLinuxProcess implements LinuxProcess { private final String genesisTxId; private final int genesisBlockHeight; private final String seedNodes; - private final boolean daoActivated; - private final boolean fullDaoNode; private final boolean useLocalhostForP2P; public final boolean useDevPrivilegeKeys; private final String findBisqPidScript; @@ -62,8 +60,6 @@ public class BisqProcess extends AbstractLinuxProcess implements LinuxProcess { this.genesisTxId = "30af0050040befd8af25068cc697e418e09c2d8ebd8d411d2240591b9ec203cf"; this.genesisBlockHeight = 111; this.seedNodes = "localhost:2002"; - this.daoActivated = true; - this.fullDaoNode = true; this.useLocalhostForP2P = true; this.useDevPrivilegeKeys = true; this.findBisqPidScript = (config.isRunningTest ? "." : "./apitest") @@ -224,8 +220,6 @@ public class BisqProcess extends AbstractLinuxProcess implements LinuxProcess { add("--rpcUser=" + config.bitcoinRpcUser); add("--rpcPassword=" + config.bitcoinRpcPassword); add("--rpcPort=" + config.bitcoinRpcPort); - add("--daoActivated=" + daoActivated); - add("--fullDaoNode=" + fullDaoNode); add("--seedNodes=" + seedNodes); add("--baseCurrencyNetwork=" + baseCurrencyNetwork); add("--useDevPrivilegeKeys=" + useDevPrivilegeKeys); diff --git a/apitest/src/test/java/bisq/apitest/ApiTestCase.java b/apitest/src/test/java/bisq/apitest/ApiTestCase.java index 1ecd7e53c3..9e14c3dea9 100644 --- a/apitest/src/test/java/bisq/apitest/ApiTestCase.java +++ b/apitest/src/test/java/bisq/apitest/ApiTestCase.java @@ -59,7 +59,7 @@ import bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig; *

* Those documents contain information about the configurations used by this test harness: * bitcoin-core's bitcoin.conf and blocknotify values, bisq instance options, the DAO genesis - * transaction id, initial BSQ and BTC balances for Bob & Alice accounts, and Bob and + * transaction id, initial BTC balances for Bob & Alice accounts, and Bob and * Alice's default payment accounts. *

* During a build, the @@ -68,9 +68,9 @@ import bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig; * method, the DAO setup files are re-installed into the run time's data directories * (each test case runs on a refreshed DAO/regtest environment setup). *

- * Initial Alice balances & accounts: 10.0 BTC, 1000000.00 BSQ, USD PerfectMoney dummy + * Initial Alice balances & accounts: 10.0 BTC, USD PerfectMoney dummy *

- * Initial Bob balances & accounts: 10.0 BTC, 1500000.00 BSQ, USD PerfectMoney dummy + * Initial Bob balances & accounts: 10.0 BTC, USD PerfectMoney dummy */ @Slf4j public class ApiTestCase { diff --git a/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java index f0e95dd25f..3ee143b954 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java @@ -32,7 +32,6 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind; -import static bisq.apitest.config.ApiTestConfig.BSQ; import static bisq.apitest.config.BisqAppConfig.alicedaemon; import static bisq.apitest.config.BisqAppConfig.arbdaemon; import static bisq.apitest.config.BisqAppConfig.bobdaemon; @@ -52,9 +51,6 @@ public abstract class AbstractOfferTest extends MethodTest { @Setter protected static boolean isLongRunningTest; - protected static PaymentAccount alicesBsqAcct; - protected static PaymentAccount bobsBsqAcct; - @BeforeAll public static void setUp() { startSupportingApps(true, @@ -66,18 +62,6 @@ public abstract class AbstractOfferTest extends MethodTest { bobdaemon); } - - public static void createBsqPaymentAccounts() { - alicesBsqAcct = aliceClient.createCryptoCurrencyPaymentAccount("Alice's BSQ Account", - BSQ, - aliceClient.getUnusedBsqAddress(), - false); - bobsBsqAcct = bobClient.createCryptoCurrencyPaymentAccount("Bob's BSQ Account", - BSQ, - bobClient.getUnusedBsqAddress(), - false); - } - protected double getScaledOfferPrice(double offerPrice, String currencyCode) { int precision = isCryptoCurrency(currencyCode) ? Altcoin.SMALLEST_UNIT_EXPONENT : Fiat.SMALLEST_UNIT_EXPONENT; return scaleDownByPowerOf10(offerPrice, precision); diff --git a/apitest/src/test/java/bisq/apitest/method/offer/CancelOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/CancelOfferTest.java index fe21e4aa8f..5897fde57a 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/CancelOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/CancelOfferTest.java @@ -32,7 +32,6 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; -import static bisq.apitest.config.ApiTestConfig.BSQ; import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static org.junit.jupiter.api.Assertions.assertEquals; import static protobuf.OfferPayload.Direction.BUY; @@ -53,8 +52,7 @@ public class CancelOfferTest extends AbstractOfferTest { 10000000L, 0.00, getDefaultBuyerSecurityDepositAsPercent(), - paymentAccountId, - BSQ); + paymentAccountId); }; @Test diff --git a/apitest/src/test/java/bisq/apitest/method/offer/CreateBSQOffersTest.java b/apitest/src/test/java/bisq/apitest/method/offer/CreateBSQOffersTest.java deleted file mode 100644 index ba4f8ce47b..0000000000 --- a/apitest/src/test/java/bisq/apitest/method/offer/CreateBSQOffersTest.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.apitest.method.offer; - -import bisq.proto.grpc.OfferInfo; - -import java.util.List; - -import lombok.extern.slf4j.Slf4j; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; - -import static bisq.apitest.config.ApiTestConfig.BSQ; -import static bisq.apitest.config.ApiTestConfig.BTC; -import static bisq.cli.TableFormat.formatBalancesTbls; -import static bisq.cli.TableFormat.formatOfferTable; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; -import static java.util.Collections.singletonList; -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 protobuf.OfferPayload.Direction.BUY; -import static protobuf.OfferPayload.Direction.SELL; - -@Disabled -@Slf4j -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class CreateBSQOffersTest extends AbstractOfferTest { - - private static final String MAKER_FEE_CURRENCY_CODE = BSQ; - - @BeforeAll - public static void setUp() { - AbstractOfferTest.setUp(); - createBsqPaymentAccounts(); - } - - @Test - @Order(1) - public void testCreateBuy1BTCFor20KBSQOffer() { - // Remember alt coin trades are BTC trades. When placing an offer, you are - // offering to buy or sell BTC, not BSQ, XMR, etc. In this test case, - // Alice places an offer to BUY BTC with BSQ. - var newOffer = aliceClient.createFixedPricedOffer(BUY.name(), - BSQ, - 100_000_000L, - 100_000_000L, - "0.00005", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ - getDefaultBuyerSecurityDepositAsPercent(), - alicesBsqAcct.getId(), - MAKER_FEE_CURRENCY_CODE); - log.info("Sell BSQ (Buy BTC) OFFER:\n{}", formatOfferTable(singletonList(newOffer), BSQ)); - String newOfferId = newOffer.getId(); - assertNotEquals("", newOfferId); - assertEquals(BUY.name(), newOffer.getDirection()); - assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); - assertEquals(100_000_000L, newOffer.getAmount()); - assertEquals(100_000_000L, newOffer.getMinAmount()); - assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); - assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); - assertEquals(BSQ, newOffer.getBaseCurrencyCode()); - assertEquals(BTC, newOffer.getCounterCurrencyCode()); - assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); - - genBtcBlockAndWaitForOfferPreparation(); - - newOffer = aliceClient.getMyOffer(newOfferId); - assertEquals(newOfferId, newOffer.getId()); - assertEquals(BUY.name(), newOffer.getDirection()); - assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); - assertEquals(100_000_000L, newOffer.getAmount()); - assertEquals(100_000_000L, newOffer.getMinAmount()); - assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); - assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); - assertEquals(BSQ, newOffer.getBaseCurrencyCode()); - assertEquals(BTC, newOffer.getCounterCurrencyCode()); - assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); - } - - @Test - @Order(2) - public void testCreateSell1BTCFor20KBSQOffer() { - // Alice places an offer to SELL BTC for BSQ. - var newOffer = aliceClient.createFixedPricedOffer(SELL.name(), - BSQ, - 100_000_000L, - 100_000_000L, - "0.00005", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ - getDefaultBuyerSecurityDepositAsPercent(), - alicesBsqAcct.getId(), - MAKER_FEE_CURRENCY_CODE); - log.info("SELL 20K BSQ OFFER:\n{}", formatOfferTable(singletonList(newOffer), BSQ)); - String newOfferId = newOffer.getId(); - assertNotEquals("", newOfferId); - assertEquals(SELL.name(), newOffer.getDirection()); - assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); - assertEquals(100_000_000L, newOffer.getAmount()); - assertEquals(100_000_000L, newOffer.getMinAmount()); - assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); - assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); - assertEquals(BSQ, newOffer.getBaseCurrencyCode()); - assertEquals(BTC, newOffer.getCounterCurrencyCode()); - assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); - - genBtcBlockAndWaitForOfferPreparation(); - - newOffer = aliceClient.getMyOffer(newOfferId); - assertEquals(newOfferId, newOffer.getId()); - assertEquals(SELL.name(), newOffer.getDirection()); - assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); - assertEquals(100_000_000L, newOffer.getAmount()); - assertEquals(100_000_000L, newOffer.getMinAmount()); - assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit()); - assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); - assertEquals(BSQ, newOffer.getBaseCurrencyCode()); - assertEquals(BTC, newOffer.getCounterCurrencyCode()); - assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); - } - - @Test - @Order(3) - public void testCreateBuyBTCWith1To2KBSQOffer() { - // Alice places an offer to BUY 0.05 - 0.10 BTC with BSQ. - var newOffer = aliceClient.createFixedPricedOffer(BUY.name(), - BSQ, - 10_000_000L, - 5_000_000L, - "0.00005", // FIXED PRICE IN BTC sats FOR 1 BSQ - getDefaultBuyerSecurityDepositAsPercent(), - alicesBsqAcct.getId(), - MAKER_FEE_CURRENCY_CODE); - log.info("BUY 1-2K BSQ OFFER:\n{}", formatOfferTable(singletonList(newOffer), BSQ)); - String newOfferId = newOffer.getId(); - assertNotEquals("", newOfferId); - assertEquals(BUY.name(), newOffer.getDirection()); - assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); - assertEquals(10_000_000L, newOffer.getAmount()); - assertEquals(5_000_000L, newOffer.getMinAmount()); - assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); - assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); - assertEquals(BSQ, newOffer.getBaseCurrencyCode()); - assertEquals(BTC, newOffer.getCounterCurrencyCode()); - assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); - - genBtcBlockAndWaitForOfferPreparation(); - - newOffer = aliceClient.getMyOffer(newOfferId); - assertEquals(newOfferId, newOffer.getId()); - assertEquals(BUY.name(), newOffer.getDirection()); - assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); - assertEquals(10_000_000L, newOffer.getAmount()); - assertEquals(5_000_000L, newOffer.getMinAmount()); - assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit()); - assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); - assertEquals(BSQ, newOffer.getBaseCurrencyCode()); - assertEquals(BTC, newOffer.getCounterCurrencyCode()); - assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); - } - - @Test - @Order(4) - public void testCreateSellBTCFor5To10KBSQOffer() { - // Alice places an offer to SELL 0.25 - 0.50 BTC for BSQ. - var newOffer = aliceClient.createFixedPricedOffer(SELL.name(), - BSQ, - 50_000_000L, - 25_000_000L, - "0.00005", // FIXED PRICE IN BTC sats FOR 1 BSQ - getDefaultBuyerSecurityDepositAsPercent(), - alicesBsqAcct.getId(), - MAKER_FEE_CURRENCY_CODE); - log.info("SELL 5-10K BSQ OFFER:\n{}", formatOfferTable(singletonList(newOffer), BSQ)); - String newOfferId = newOffer.getId(); - assertNotEquals("", newOfferId); - assertEquals(SELL.name(), newOffer.getDirection()); - assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); - assertEquals(50_000_000L, newOffer.getAmount()); - assertEquals(25_000_000L, newOffer.getMinAmount()); - assertEquals(7_500_000, newOffer.getBuyerSecurityDeposit()); - assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); - assertEquals(BSQ, newOffer.getBaseCurrencyCode()); - assertEquals(BTC, newOffer.getCounterCurrencyCode()); - assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); - - genBtcBlockAndWaitForOfferPreparation(); - - newOffer = aliceClient.getMyOffer(newOfferId); - assertEquals(newOfferId, newOffer.getId()); - assertEquals(SELL.name(), newOffer.getDirection()); - assertFalse(newOffer.getUseMarketBasedPrice()); - assertEquals(5_000, newOffer.getPrice()); - assertEquals(50_000_000L, newOffer.getAmount()); - assertEquals(25_000_000L, newOffer.getMinAmount()); - assertEquals(7_500_000, newOffer.getBuyerSecurityDeposit()); - assertEquals(alicesBsqAcct.getId(), newOffer.getPaymentAccountId()); - assertEquals(BSQ, newOffer.getBaseCurrencyCode()); - assertEquals(BTC, newOffer.getCounterCurrencyCode()); - assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); - } - - @Test - @Order(5) - public void testGetAllMyBsqOffers() { - List offers = aliceClient.getMyBsqOffersSortedByDate(); - log.info("ALL ALICE'S BSQ OFFERS:\n{}", formatOfferTable(offers, BSQ)); - assertEquals(4, offers.size()); - log.info("ALICE'S BALANCES\n{}", formatBalancesTbls(aliceClient.getBalances())); - } - - @Test - @Order(6) - public void testGetAvailableBsqOffers() { - List offers = bobClient.getBsqOffersSortedByDate(); - log.info("ALL BOB'S AVAILABLE BSQ OFFERS:\n{}", formatOfferTable(offers, BSQ)); - assertEquals(4, offers.size()); - log.info("BOB'S BALANCES\n{}", formatBalancesTbls(bobClient.getBalances())); - } - - private void genBtcBlockAndWaitForOfferPreparation() { - // Extra time is needed for the OfferUtils#isBsqForMakerFeeAvailable, which - // can sometimes return an incorrect false value if the BsqWallet's - // available confirmed balance is temporarily = zero during BSQ offer prep. - genBtcBlocksThenWait(1, 5000); - } -} diff --git a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingFixedPriceTest.java b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingFixedPriceTest.java index e5f4346280..08c9af087f 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingFixedPriceTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingFixedPriceTest.java @@ -27,7 +27,6 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; -import static bisq.apitest.config.ApiTestConfig.BSQ; import static bisq.apitest.config.ApiTestConfig.XMR; import static bisq.cli.TableFormat.formatOfferTable; import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; @@ -43,8 +42,6 @@ import static protobuf.OfferPayload.Direction.SELL; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { - private static final String MAKER_FEE_CURRENCY_CODE = BSQ; - @Test @Order(1) public void testCreateAUDXMRBuyOfferUsingFixedPrice16000() { @@ -55,8 +52,7 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { 10_000_000L, "36000", getDefaultBuyerSecurityDepositAsPercent(), - audAccount.getId(), - MAKER_FEE_CURRENCY_CODE); + audAccount.getId()); log.info("OFFER #1:\n{}", formatOfferTable(singletonList(newOffer), "AUD")); String newOfferId = newOffer.getId(); assertNotEquals("", newOfferId); @@ -69,7 +65,6 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { assertEquals(audAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(XMR, newOffer.getBaseCurrencyCode()); assertEquals("AUD", newOffer.getCounterCurrencyCode()); - assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); newOffer = aliceClient.getMyOffer(newOfferId); assertEquals(newOfferId, newOffer.getId()); @@ -82,7 +77,6 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { assertEquals(audAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(XMR, newOffer.getBaseCurrencyCode()); assertEquals("AUD", newOffer.getCounterCurrencyCode()); - assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); } @Test @@ -95,8 +89,7 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { 10_000_000L, "30000.1234", getDefaultBuyerSecurityDepositAsPercent(), - usdAccount.getId(), - MAKER_FEE_CURRENCY_CODE); + usdAccount.getId()); log.info("OFFER #2:\n{}", formatOfferTable(singletonList(newOffer), "USD")); String newOfferId = newOffer.getId(); assertNotEquals("", newOfferId); @@ -109,7 +102,6 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(XMR, newOffer.getBaseCurrencyCode()); assertEquals("USD", newOffer.getCounterCurrencyCode()); - assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); newOffer = aliceClient.getMyOffer(newOfferId); assertEquals(newOfferId, newOffer.getId()); @@ -122,7 +114,6 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(XMR, newOffer.getBaseCurrencyCode()); assertEquals("USD", newOffer.getCounterCurrencyCode()); - assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); } @Test @@ -135,8 +126,7 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { 5_000_000L, "29500.1234", getDefaultBuyerSecurityDepositAsPercent(), - eurAccount.getId(), - MAKER_FEE_CURRENCY_CODE); + eurAccount.getId()); log.info("OFFER #3:\n{}", formatOfferTable(singletonList(newOffer), "EUR")); String newOfferId = newOffer.getId(); assertNotEquals("", newOfferId); @@ -149,7 +139,6 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { assertEquals(eurAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(XMR, newOffer.getBaseCurrencyCode()); assertEquals("EUR", newOffer.getCounterCurrencyCode()); - assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); newOffer = aliceClient.getMyOffer(newOfferId); assertEquals(newOfferId, newOffer.getId()); @@ -162,6 +151,5 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest { assertEquals(eurAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(XMR, newOffer.getBaseCurrencyCode()); assertEquals("EUR", newOffer.getCounterCurrencyCode()); - assertFalse(newOffer.getIsCurrencyForMakerFeeBtc()); } } diff --git a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java index c8c42b7c69..177da0ad22 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/CreateOfferUsingMarketPriceMarginTest.java @@ -54,8 +54,6 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { private static final double MKT_PRICE_MARGIN_ERROR_TOLERANCE = 0.0050; // 0.50% private static final double MKT_PRICE_MARGIN_WARNING_TOLERANCE = 0.0001; // 0.01% - private static final String MAKER_FEE_CURRENCY_CODE = XMR; - @Test @Order(1) public void testCreateUSDXMRBuyOffer5PctPriceMargin() { @@ -67,8 +65,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { 10_000_000L, priceMarginPctInput, getDefaultBuyerSecurityDepositAsPercent(), - usdAccount.getId(), - MAKER_FEE_CURRENCY_CODE); + usdAccount.getId()); log.info("OFFER #1:\n{}", formatOfferTable(singletonList(newOffer), "usd")); String newOfferId = newOffer.getId(); assertNotEquals("", newOfferId); @@ -80,7 +77,6 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(XMR, newOffer.getBaseCurrencyCode()); assertEquals("USD", newOffer.getCounterCurrencyCode()); - assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); newOffer = aliceClient.getMyOffer(newOfferId); assertEquals(newOfferId, newOffer.getId()); @@ -92,7 +88,6 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(XMR, newOffer.getBaseCurrencyCode()); assertEquals("USD", newOffer.getCounterCurrencyCode()); - assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); assertCalculatedPriceIsCorrect(newOffer, priceMarginPctInput); } @@ -108,8 +103,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { 10_000_000L, priceMarginPctInput, getDefaultBuyerSecurityDepositAsPercent(), - nzdAccount.getId(), - MAKER_FEE_CURRENCY_CODE); + nzdAccount.getId()); log.info("OFFER #2:\n{}", formatOfferTable(singletonList(newOffer), "nzd")); String newOfferId = newOffer.getId(); assertNotEquals("", newOfferId); @@ -121,7 +115,6 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { assertEquals(nzdAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(XMR, newOffer.getBaseCurrencyCode()); assertEquals("NZD", newOffer.getCounterCurrencyCode()); - assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); newOffer = aliceClient.getMyOffer(newOfferId); assertEquals(newOfferId, newOffer.getId()); @@ -133,7 +126,6 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { assertEquals(nzdAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(XMR, newOffer.getBaseCurrencyCode()); assertEquals("NZD", newOffer.getCounterCurrencyCode()); - assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); assertCalculatedPriceIsCorrect(newOffer, priceMarginPctInput); } @@ -149,8 +141,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { 5_000_000L, priceMarginPctInput, getDefaultBuyerSecurityDepositAsPercent(), - gbpAccount.getId(), - MAKER_FEE_CURRENCY_CODE); + gbpAccount.getId()); log.info("OFFER #3:\n{}", formatOfferTable(singletonList(newOffer), "gbp")); String newOfferId = newOffer.getId(); assertNotEquals("", newOfferId); @@ -162,7 +153,6 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { assertEquals(gbpAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(XMR, newOffer.getBaseCurrencyCode()); assertEquals("GBP", newOffer.getCounterCurrencyCode()); - assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); newOffer = aliceClient.getMyOffer(newOfferId); assertEquals(newOfferId, newOffer.getId()); @@ -174,7 +164,6 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { assertEquals(gbpAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(XMR, newOffer.getBaseCurrencyCode()); assertEquals("GBP", newOffer.getCounterCurrencyCode()); - assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); assertCalculatedPriceIsCorrect(newOffer, priceMarginPctInput); } @@ -190,8 +179,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { 5_000_000L, priceMarginPctInput, getDefaultBuyerSecurityDepositAsPercent(), - brlAccount.getId(), - MAKER_FEE_CURRENCY_CODE); + brlAccount.getId()); log.info("OFFER #4:\n{}", formatOfferTable(singletonList(newOffer), "brl")); String newOfferId = newOffer.getId(); assertNotEquals("", newOfferId); @@ -203,7 +191,6 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { assertEquals(brlAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(XMR, newOffer.getBaseCurrencyCode()); assertEquals("BRL", newOffer.getCounterCurrencyCode()); - assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); newOffer = aliceClient.getMyOffer(newOfferId); assertEquals(newOfferId, newOffer.getId()); @@ -215,7 +202,6 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest { assertEquals(brlAccount.getId(), newOffer.getPaymentAccountId()); assertEquals(XMR, newOffer.getBaseCurrencyCode()); assertEquals("BRL", newOffer.getCounterCurrencyCode()); - assertTrue(newOffer.getIsCurrencyForMakerFeeBtc()); assertCalculatedPriceIsCorrect(newOffer, priceMarginPctInput); } diff --git a/apitest/src/test/java/bisq/apitest/method/offer/ValidateCreateOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/ValidateCreateOfferTest.java index 33626ee6c3..0db546b74d 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/ValidateCreateOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/ValidateCreateOfferTest.java @@ -29,7 +29,6 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; -import static bisq.apitest.config.ApiTestConfig.BSQ; import static bisq.apitest.config.ApiTestConfig.BTC; import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static java.lang.String.format; @@ -54,8 +53,7 @@ public class ValidateCreateOfferTest extends AbstractOfferTest { 100000000000L, "10000.0000", getDefaultBuyerSecurityDepositAsPercent(), - usdAccount.getId(), - BSQ)); + usdAccount.getId())); assertEquals("UNKNOWN: An error occurred at task: ValidateOffer", exception.getMessage()); } @@ -71,8 +69,7 @@ public class ValidateCreateOfferTest extends AbstractOfferTest { 10000000L, "40000.0000", getDefaultBuyerSecurityDepositAsPercent(), - chfAccount.getId(), - BTC)); + chfAccount.getId())); String expectedError = format("UNKNOWN: cannot create EUR offer with payment account %s", chfAccount.getId()); assertEquals(expectedError, exception.getMessage()); } @@ -89,8 +86,7 @@ public class ValidateCreateOfferTest extends AbstractOfferTest { 10000000L, "63000.0000", getDefaultBuyerSecurityDepositAsPercent(), - audAccount.getId(), - BTC)); + audAccount.getId())); String expectedError = format("UNKNOWN: cannot create CAD offer with payment account %s", audAccount.getId()); assertEquals(expectedError, exception.getMessage()); } diff --git a/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java b/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java index 4c4a6b3453..3b8a69808e 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java @@ -9,7 +9,6 @@ import org.slf4j.Logger; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.TestInfo; -import static bisq.cli.CurrencyFormat.formatBsqAmount; import static bisq.cli.TradeFormat.format; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -35,16 +34,14 @@ public class AbstractTradeTest extends AbstractOfferTest { } protected final TradeInfo takeAlicesOffer(String offerId, - String paymentAccountId, - String takerFeeCurrencyCode) { - return bobClient.takeOffer(offerId, paymentAccountId, takerFeeCurrencyCode); + String paymentAccountId) { + return bobClient.takeOffer(offerId, paymentAccountId); } @SuppressWarnings("unused") protected final TradeInfo takeBobsOffer(String offerId, - String paymentAccountId, - String takerFeeCurrencyCode) { - return aliceClient.takeOffer(offerId, paymentAccountId, takerFeeCurrencyCode); + String paymentAccountId) { + return aliceClient.takeOffer(offerId, paymentAccountId); } protected final void verifyExpectedProtocolStatus(TradeInfo trade) { @@ -62,40 +59,6 @@ public class AbstractTradeTest extends AbstractOfferTest { assertEquals(EXPECTED_PROTOCOL_STATUS.isWithdrawn, trade.getIsWithdrawn()); } - protected final void sendBsqPayment(Logger log, - GrpcClient grpcClient, - TradeInfo trade) { - var contract = trade.getContract(); - String receiverAddress = contract.getIsBuyerMakerAndSellerTaker() - ? contract.getTakerPaymentAccountPayload().getAddress() - : contract.getMakerPaymentAccountPayload().getAddress(); - String sendBsqAmount = formatBsqAmount(trade.getOffer().getVolume()); - log.info("Sending {} BSQ to address {}", sendBsqAmount, receiverAddress); - grpcClient.sendBsq(receiverAddress, sendBsqAmount, ""); - } - - protected final void verifyBsqPaymentHasBeenReceived(Logger log, - GrpcClient grpcClient, - TradeInfo trade) { - var contract = trade.getContract(); - var bsqSats = trade.getOffer().getVolume(); - var receiveAmountAsString = formatBsqAmount(bsqSats); - var address = contract.getIsBuyerMakerAndSellerTaker() - ? contract.getTakerPaymentAccountPayload().getAddress() - : contract.getMakerPaymentAccountPayload().getAddress(); - boolean receivedBsqSatoshis = grpcClient.verifyBsqSentToAddress(address, receiveAmountAsString); - if (receivedBsqSatoshis) - log.info("Payment of {} BSQ was received to address {} for trade with id {}.", - receiveAmountAsString, - address, - trade.getTradeId()); - else - fail(String.format("Payment of %s BSQ was was not sent to address %s for trade with id %s.", - receiveAmountAsString, - address, - trade.getTradeId())); - } - protected final void logTrade(Logger log, TestInfo testInfo, String description, diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBSQOfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBSQOfferTest.java deleted file mode 100644 index 5b327ccb29..0000000000 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBSQOfferTest.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.apitest.method.trade; - -import bisq.proto.grpc.TradeInfo; - -import io.grpc.StatusRuntimeException; - -import java.util.function.Predicate; - -import lombok.extern.slf4j.Slf4j; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.TestMethodOrder; - -import static bisq.apitest.config.ApiTestConfig.BSQ; -import static bisq.cli.TableFormat.formatBalancesTbls; -import static bisq.cli.TableFormat.formatOfferTable; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; -import static bisq.core.trade.Trade.Phase.DEPOSIT_CONFIRMED; -import static bisq.core.trade.Trade.Phase.FIAT_SENT; -import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED; -import static bisq.core.trade.Trade.State.BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG; -import static bisq.core.trade.Trade.State.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN; -import static bisq.core.trade.Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG; -import static bisq.core.trade.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG; -import static java.lang.String.format; -import static java.util.Collections.singletonList; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.fail; -import static protobuf.Offer.State.OFFER_FEE_PAID; -import static protobuf.OfferPayload.Direction.SELL; - - - -import bisq.apitest.method.offer.AbstractOfferTest; - -// https://github.com/ghubstan/bisq/blob/master/cli/src/main/java/bisq/cli/TradeFormat.java - -@Disabled -@Slf4j -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class TakeBuyBSQOfferTest extends AbstractTradeTest { - - // Alice is maker / bsq buyer (btc seller), Bob is taker / bsq seller (btc buyer). - - // Maker and Taker fees are in BSQ. - private static final String TRADE_FEE_CURRENCY_CODE = BSQ; - - @BeforeAll - public static void setUp() { - AbstractOfferTest.setUp(); - createBsqPaymentAccounts(); - EXPECTED_PROTOCOL_STATUS.init(); - } - - @Test - @Order(1) - public void testTakeAlicesSellBTCForBSQOffer(final TestInfo testInfo) { - try { - // Alice is going to BUY BSQ, but the Offer direction = SELL because it is a - // BTC trade; Alice will SELL BTC for BSQ. Bob will send Alice BSQ. - // Confused me, but just need to remember there are only BTC offers. - var btcTradeDirection = SELL.name(); - var alicesOffer = aliceClient.createFixedPricedOffer(btcTradeDirection, - BSQ, - 15_000_000L, - 7_500_000L, - "0.000035", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ - getDefaultBuyerSecurityDepositAsPercent(), - alicesBsqAcct.getId(), - TRADE_FEE_CURRENCY_CODE); - log.info("ALICE'S BUY BSQ (SELL BTC) OFFER:\n{}", formatOfferTable(singletonList(alicesOffer), BSQ)); - genBtcBlocksThenWait(1, 5000); - var offerId = alicesOffer.getId(); - assertFalse(alicesOffer.getIsCurrencyForMakerFeeBtc()); - - var alicesBsqOffers = aliceClient.getMyCryptoCurrencyOffers(btcTradeDirection, BSQ); - assertEquals(1, alicesBsqOffers.size()); - - var trade = takeAlicesOffer(offerId, bobsBsqAcct.getId(), TRADE_FEE_CURRENCY_CODE); - assertNotNull(trade); - assertEquals(offerId, trade.getTradeId()); - // Cache the trade id for the other tests. - tradeId = trade.getTradeId(); - - genBtcBlocksThenWait(1, 6000); - alicesBsqOffers = aliceClient.getMyBsqOffersSortedByDate(); - assertEquals(0, alicesBsqOffers.size()); - - for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { - trade = bobClient.getTrade(trade.getTradeId()); - - if (!trade.getIsDepositConfirmed()) { - log.warn("Bob still waiting on trade {} maker tx {} taker tx {}: DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN, attempt # {}", - trade.getShortId(), - trade.getMakerDepositTxId(), - trade.getTakerDepositTxId(), - i); - genBtcBlocksThenWait(1, 4000); - continue; - } else { - EXPECTED_PROTOCOL_STATUS.setState(DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN) - .setPhase(DEPOSIT_CONFIRMED) - .setDepositPublished(true) - .setDepositConfirmed(true); - verifyExpectedProtocolStatus(trade); - logTrade(log, testInfo, "Bob's view after taking offer and deposit confirmed", trade); - break; - } - } - - genBtcBlocksThenWait(1, 2500); - - if (!trade.getIsDepositConfirmed()) { - fail(format("INVALID_PHASE for Bob's trade %s in STATE=%s PHASE=%s, deposit tx was never confirmed.", - trade.getShortId(), - trade.getState(), - trade.getPhase())); - } - - logTrade(log, testInfo, "Alice's Maker/Buyer View", aliceClient.getTrade(tradeId), true); - logTrade(log, testInfo, "Bob's Taker/Seller View", bobClient.getTrade(tradeId), true); - - } catch (StatusRuntimeException e) { - fail(e); - } - } - - @Test - @Order(2) - public void testBobsConfirmPaymentStarted(final TestInfo testInfo) { - try { - var trade = bobClient.getTrade(tradeId); - - Predicate tradeStateAndPhaseCorrect = (t) -> - t.getState().equals(DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN.name()) - && t.getPhase().equals(DEPOSIT_CONFIRMED.name()); - - for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { - if (!tradeStateAndPhaseCorrect.test(trade)) { - log.warn("INVALID_PHASE for Bob's trade {} in STATE={} PHASE={}, cannot send payment started msg yet.", - trade.getShortId(), - trade.getState(), - trade.getPhase()); - sleep(10_000); - trade = bobClient.getTrade(tradeId); - continue; - } else { - break; - } - } - - if (!tradeStateAndPhaseCorrect.test(trade)) { - fail(format("INVALID_PHASE for Bob's trade %s in STATE=%s PHASE=%s, could not send payment started msg.", - trade.getShortId(), - trade.getState(), - trade.getPhase())); - } - - sendBsqPayment(log, bobClient, trade); - genBtcBlocksThenWait(1, 2500); - bobClient.confirmPaymentStarted(trade.getTradeId()); - sleep(6000); - - for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { - trade = aliceClient.getTrade(tradeId); - - if (!trade.getIsFiatSent()) { - log.warn("Alice still waiting for trade {} SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG, attempt # {}", - trade.getShortId(), - i); - sleep(5000); - continue; - } else { - // Warning: trade.getOffer().getState() might be AVAILABLE, not OFFER_FEE_PAID. - EXPECTED_PROTOCOL_STATUS.setState(SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG) - .setPhase(FIAT_SENT) - .setFiatSent(true); - verifyExpectedProtocolStatus(trade); - logTrade(log, testInfo, "Alice's view after confirming fiat payment received", trade); - break; - } - } - - logTrade(log, testInfo, "Alice's Maker/Buyer View (Payment Sent)", aliceClient.getTrade(tradeId), true); - logTrade(log, testInfo, "Bob's Taker/Seller View (Payment Sent)", bobClient.getTrade(tradeId), true); - - } catch (StatusRuntimeException e) { - fail(e); - } - } - - @Test - @Order(3) - public void testAlicesConfirmPaymentReceived(final TestInfo testInfo) { - try { - var trade = aliceClient.getTrade(tradeId); - - Predicate tradeStateAndPhaseCorrect = (t) -> - t.getState().equals(SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG.name()) - && (t.getPhase().equals(PAYOUT_PUBLISHED.name()) || t.getPhase().equals(FIAT_SENT.name())); - - for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { - if (!tradeStateAndPhaseCorrect.test(trade)) { - log.warn("INVALID_PHASE for Alice's trade {} in STATE={} PHASE={}, cannot confirm payment received yet.", - trade.getShortId(), - trade.getState(), - trade.getPhase()); - sleep(1000 * 10); - trade = aliceClient.getTrade(tradeId); - continue; - } else { - break; - } - } - - if (!tradeStateAndPhaseCorrect.test(trade)) { - fail(format("INVALID_PHASE for Alice's trade %s in STATE=%s PHASE=%s, cannot confirm payment received.", - trade.getShortId(), - trade.getState(), - trade.getPhase())); - } - - sleep(2000); - verifyBsqPaymentHasBeenReceived(log, aliceClient, trade); - - aliceClient.confirmPaymentReceived(trade.getTradeId()); - sleep(3000); - - trade = aliceClient.getTrade(tradeId); - assertEquals(OFFER_FEE_PAID.name(), trade.getOffer().getState()); - EXPECTED_PROTOCOL_STATUS.setState(SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG) - .setPhase(PAYOUT_PUBLISHED) - .setPayoutPublished(true) - .setFiatReceived(true); - verifyExpectedProtocolStatus(trade); - logTrade(log, testInfo, "Alice's view after confirming fiat payment received", trade); - - logTrade(log, testInfo, "Alice's Maker/Buyer View (Payment Received)", aliceClient.getTrade(tradeId), true); - logTrade(log, testInfo, "Bob's Taker/Seller View (Payment Received)", bobClient.getTrade(tradeId), true); - - } catch (StatusRuntimeException e) { - fail(e); - } - } - - @Test - @Order(4) - public void testBobsKeepFunds(final TestInfo testInfo) { - try { - genBtcBlocksThenWait(1, 1000); - - var trade = bobClient.getTrade(tradeId); - logTrade(log, testInfo, "Alice's view before keeping funds", trade); - - bobClient.keepFunds(tradeId); - genBtcBlocksThenWait(1, 1000); - - trade = bobClient.getTrade(tradeId); - EXPECTED_PROTOCOL_STATUS.setState(BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG) - .setPhase(PAYOUT_PUBLISHED); - verifyExpectedProtocolStatus(trade); - logTrade(log, testInfo, "Alice's view after keeping funds", trade); - - logTrade(log, testInfo, "Alice's Maker/Buyer View (Done)", aliceClient.getTrade(tradeId), true); - logTrade(log, testInfo, "Bob's Taker/Seller View (Done)", bobClient.getTrade(tradeId), true); - - var alicesBalances = aliceClient.getBalances(); - log.info("{} Alice's Current Balance:\n{}", - testName(testInfo), - formatBalancesTbls(alicesBalances)); - var bobsBalances = bobClient.getBalances(); - log.info("{} Bob's Current Balance:\n{}", - testName(testInfo), - formatBalancesTbls(bobsBalances)); - - } catch (StatusRuntimeException e) { - fail(e); - } - } -} diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java index 0f0091fc9f..2fa55abf3d 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java @@ -34,7 +34,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.TestMethodOrder; -import static bisq.apitest.config.ApiTestConfig.BSQ; import static bisq.cli.TableFormat.formatBalancesTbls; import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static bisq.core.trade.Trade.Phase.DEPOSIT_CONFIRMED; @@ -57,9 +56,6 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest { // Alice is maker/buyer, Bob is taker/seller. - // Maker and Taker fees are in BSQ. - private static final String TRADE_FEE_CURRENCY_CODE = BSQ; - @Test @Order(1) public void testTakeAlicesBuyOffer(final TestInfo testInfo) { @@ -71,10 +67,8 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest { 12_500_000L, // min-amount = amount 0.00, getDefaultBuyerSecurityDepositAsPercent(), - alicesUsdAccount.getId(), - TRADE_FEE_CURRENCY_CODE); + alicesUsdAccount.getId()); var offerId = alicesOffer.getId(); - assertFalse(alicesOffer.getIsCurrencyForMakerFeeBtc()); // Wait for Alice's AddToOfferBook task. // Wait times vary; my logs show >= 2 second delay. @@ -83,7 +77,7 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest { assertEquals(1, alicesUsdOffers.size()); PaymentAccount bobsUsdAccount = createDummyF2FAccount(bobClient, "US"); - var trade = takeAlicesOffer(offerId, bobsUsdAccount.getId(), TRADE_FEE_CURRENCY_CODE); + var trade = takeAlicesOffer(offerId, bobsUsdAccount.getId()); assertNotNull(trade); assertEquals(offerId, trade.getTradeId()); // Cache the trade id for the other tests. diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBSQOfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBSQOfferTest.java deleted file mode 100644 index 1ffa318afb..0000000000 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBSQOfferTest.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.apitest.method.trade; - -import bisq.proto.grpc.TradeInfo; - -import io.grpc.StatusRuntimeException; - -import java.util.function.Predicate; - -import lombok.extern.slf4j.Slf4j; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.TestMethodOrder; - -import static bisq.apitest.config.ApiTestConfig.BSQ; -import static bisq.apitest.config.ApiTestConfig.BTC; -import static bisq.cli.TableFormat.formatBalancesTbls; -import static bisq.cli.TableFormat.formatOfferTable; -import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; -import static bisq.core.trade.Trade.Phase.DEPOSIT_CONFIRMED; -import static bisq.core.trade.Trade.Phase.FIAT_SENT; -import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED; -import static bisq.core.trade.Trade.Phase.WITHDRAWN; -import static bisq.core.trade.Trade.State.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN; -import static bisq.core.trade.Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG; -import static bisq.core.trade.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG; -import static bisq.core.trade.Trade.State.WITHDRAW_COMPLETED; -import static java.lang.String.format; -import static java.util.Collections.singletonList; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; -import static protobuf.OfferPayload.Direction.BUY; - - - -import bisq.apitest.method.offer.AbstractOfferTest; - -@Disabled -@Slf4j -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class TakeSellBSQOfferTest extends AbstractTradeTest { - - // Alice is maker / bsq seller (btc buyer), Bob is taker / bsq buyer (btc seller). - - // Maker and Taker fees are in BTC. - private static final String TRADE_FEE_CURRENCY_CODE = BTC; - - private static final String WITHDRAWAL_TX_MEMO = "Bob's trade withdrawal"; - - @BeforeAll - public static void setUp() { - AbstractOfferTest.setUp(); - createBsqPaymentAccounts(); - EXPECTED_PROTOCOL_STATUS.init(); - } - - @Test - @Order(1) - public void testTakeAlicesBuyBTCForBSQOffer(final TestInfo testInfo) { - try { - // Alice is going to SELL BSQ, but the Offer direction = BUY because it is a - // BTC trade; Alice will BUY BTC for BSQ. Alice will send Bob BSQ. - // Confused me, but just need to remember there are only BTC offers. - var btcTradeDirection = BUY.name(); - var alicesOffer = aliceClient.createFixedPricedOffer(btcTradeDirection, - BSQ, - 15_000_000L, - 7_500_000L, - "0.000035", // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ - getDefaultBuyerSecurityDepositAsPercent(), - alicesBsqAcct.getId(), - TRADE_FEE_CURRENCY_CODE); - log.info("ALICE'S SELL BSQ (BUY BTC) OFFER:\n{}", formatOfferTable(singletonList(alicesOffer), BSQ)); - genBtcBlocksThenWait(1, 4000); - var offerId = alicesOffer.getId(); - assertTrue(alicesOffer.getIsCurrencyForMakerFeeBtc()); - - var alicesBsqOffers = aliceClient.getMyCryptoCurrencyOffers(btcTradeDirection, BSQ); - assertEquals(1, alicesBsqOffers.size()); - - var trade = takeAlicesOffer(offerId, bobsBsqAcct.getId(), TRADE_FEE_CURRENCY_CODE); - assertNotNull(trade); - assertEquals(offerId, trade.getTradeId()); - // Cache the trade id for the other tests. - tradeId = trade.getTradeId(); - - genBtcBlocksThenWait(1, 6000); - alicesBsqOffers = aliceClient.getMyBsqOffersSortedByDate(); - assertEquals(0, alicesBsqOffers.size()); - - for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { - trade = bobClient.getTrade(trade.getTradeId()); - - if (!trade.getIsDepositConfirmed()) { - log.warn("Bob still waiting on trade {} maker tx {} taker tx {}: DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN, attempt # {}", - trade.getShortId(), - trade.getMakerDepositTxId(), - trade.getTakerDepositTxId(), - i); - genBtcBlocksThenWait(1, 4000); - continue; - } else { - EXPECTED_PROTOCOL_STATUS.setState(DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN) - .setPhase(DEPOSIT_CONFIRMED) - .setDepositPublished(true) - .setDepositConfirmed(true); - verifyExpectedProtocolStatus(trade); - logTrade(log, testInfo, "Bob's view after taking offer and deposit confirmed", trade); - break; - } - } - - genBtcBlocksThenWait(1, 2500); - - if (!trade.getIsDepositConfirmed()) { - fail(format("INVALID_PHASE for Bob's trade %s in STATE=%s PHASE=%s, deposit tx was never confirmed.", - trade.getShortId(), - trade.getState(), - trade.getPhase())); - } - - logTrade(log, testInfo, "Alice's Maker/Seller View", aliceClient.getTrade(tradeId), true); - logTrade(log, testInfo, "Bob's Taker/Buyer View", bobClient.getTrade(tradeId), true); - - } catch (StatusRuntimeException e) { - fail(e); - } - } - - @Test - @Order(2) - public void testAlicesConfirmPaymentStarted(final TestInfo testInfo) { - try { - var trade = aliceClient.getTrade(tradeId); - - Predicate tradeStateAndPhaseCorrect = (t) -> - t.getState().equals(DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN.name()) - && t.getPhase().equals(DEPOSIT_CONFIRMED.name()); - - for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { - if (!tradeStateAndPhaseCorrect.test(trade)) { - log.warn("INVALID_PHASE for Alice's trade {} in STATE={} PHASE={}, cannot send payment started msg yet.", - trade.getShortId(), - trade.getState(), - trade.getPhase()); - sleep(10_000); - trade = aliceClient.getTrade(tradeId); - continue; - } else { - break; - } - } - - if (!tradeStateAndPhaseCorrect.test(trade)) { - fail(format("INVALID_PHASE for Alice's trade %s in STATE=%s PHASE=%s, could not send payment started msg.", - trade.getShortId(), - trade.getState(), - trade.getPhase())); - } - - sendBsqPayment(log, aliceClient, trade); - genBtcBlocksThenWait(1, 2500); - aliceClient.confirmPaymentStarted(trade.getTradeId()); - sleep(6000); - - for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { - trade = bobClient.getTrade(tradeId); - - if (!trade.getIsFiatSent()) { - log.warn("Bob still waiting for trade {} SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG, attempt # {}", - trade.getShortId(), - i); - sleep(5000); - continue; - } else { - // Warning: trade.getOffer().getState() might be AVAILABLE, not OFFER_FEE_PAID. - EXPECTED_PROTOCOL_STATUS.setState(SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG) - .setPhase(FIAT_SENT) - .setFiatSent(true); - verifyExpectedProtocolStatus(trade); - logTrade(log, testInfo, "Alice's view after confirming fiat payment received", trade); - break; - } - } - - logTrade(log, testInfo, "Alice's Maker/Seller View (Payment Sent)", aliceClient.getTrade(tradeId), true); - logTrade(log, testInfo, "Bob's Taker/Buyer View (Payment Sent)", bobClient.getTrade(tradeId), true); - - } catch (StatusRuntimeException e) { - fail(e); - } - } - - @Test - @Order(3) - public void testBobsConfirmPaymentReceived(final TestInfo testInfo) { - try { - var trade = bobClient.getTrade(tradeId); - - Predicate tradeStateAndPhaseCorrect = (t) -> - t.getState().equals(SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG.name()) - && (t.getPhase().equals(PAYOUT_PUBLISHED.name()) || t.getPhase().equals(FIAT_SENT.name())); - - for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) { - if (!tradeStateAndPhaseCorrect.test(trade)) { - log.warn("INVALID_PHASE for Bob's trade {} in STATE={} PHASE={}, cannot confirm payment received yet.", - trade.getShortId(), - trade.getState(), - trade.getPhase()); - sleep(1000 * 10); - trade = bobClient.getTrade(tradeId); - continue; - } else { - break; - } - } - - if (!tradeStateAndPhaseCorrect.test(trade)) { - fail(format("INVALID_PHASE for Bob's trade %s in STATE=%s PHASE=%s, cannot confirm payment received.", - trade.getShortId(), - trade.getState(), - trade.getPhase())); - } - - sleep(2000); - verifyBsqPaymentHasBeenReceived(log, bobClient, trade); - - bobClient.confirmPaymentReceived(trade.getTradeId()); - sleep(3000); - - trade = bobClient.getTrade(tradeId); - // Warning: trade.getOffer().getState() might be AVAILABLE, not OFFER_FEE_PAID. - EXPECTED_PROTOCOL_STATUS.setState(SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG) - .setPhase(PAYOUT_PUBLISHED) - .setPayoutPublished(true) - .setFiatReceived(true); - verifyExpectedProtocolStatus(trade); - logTrade(log, testInfo, "Alice's view after confirming fiat payment received", trade); - - logTrade(log, testInfo, "Alice's Maker/Seller View (Payment Received)", aliceClient.getTrade(tradeId), true); - logTrade(log, testInfo, "Bob's Taker/Buyer View (Payment Received)", bobClient.getTrade(tradeId), true); - - } catch (StatusRuntimeException e) { - fail(e); - } - } - - @Test - @Order(4) - public void testAlicesBtcWithdrawalToExternalAddress(final TestInfo testInfo) { - try { - genBtcBlocksThenWait(1, 1000); - - var trade = aliceClient.getTrade(tradeId); - logTrade(log, testInfo, "Alice's view before withdrawing BTC funds to external wallet", trade); - - String toAddress = bitcoinCli.getNewBtcAddress(); - aliceClient.withdrawFunds(tradeId, toAddress, WITHDRAWAL_TX_MEMO); - - genBtcBlocksThenWait(1, 1000); - - trade = aliceClient.getTrade(tradeId); - EXPECTED_PROTOCOL_STATUS.setState(WITHDRAW_COMPLETED) - .setPhase(WITHDRAWN) - .setWithdrawn(true); - verifyExpectedProtocolStatus(trade); - logTrade(log, testInfo, "Alice's view after withdrawing funds to external wallet", trade); - - - logTrade(log, testInfo, "Alice's Maker/Seller View (Done)", aliceClient.getTrade(tradeId), true); - logTrade(log, testInfo, "Bob's Taker/Buyer View (Done)", bobClient.getTrade(tradeId), true); - - var alicesBalances = aliceClient.getBalances(); - log.info("{} Alice's Current Balance:\n{}", - testName(testInfo), - formatBalancesTbls(alicesBalances)); - var bobsBalances = bobClient.getBalances(); - log.info("{} Bob's Current Balance:\n{}", - testName(testInfo), - formatBalancesTbls(bobsBalances)); - - } catch (StatusRuntimeException e) { - fail(e); - } - } -} diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java index 1e5b95448b..9bebf7246a 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeSellBTCOfferTest.java @@ -58,9 +58,6 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest { // Alice is maker/seller, Bob is taker/buyer. - // Maker and Taker fees are in BTC. - private static final String TRADE_FEE_CURRENCY_CODE = BTC; - private static final String WITHDRAWAL_TX_MEMO = "Bob's trade withdrawal"; @Test @@ -74,10 +71,8 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest { 12_500_000L, // min-amount = amount 0.00, getDefaultBuyerSecurityDepositAsPercent(), - alicesUsdAccount.getId(), - TRADE_FEE_CURRENCY_CODE); + alicesUsdAccount.getId()); var offerId = alicesOffer.getId(); - assertTrue(alicesOffer.getIsCurrencyForMakerFeeBtc()); // Wait for Alice's AddToOfferBook task. // Wait times vary; my logs show >= 2 second delay, but taking sell offers @@ -87,7 +82,7 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest { assertEquals(1, alicesUsdOffers.size()); PaymentAccount bobsUsdAccount = createDummyF2FAccount(bobClient, "US"); - var trade = takeAlicesOffer(offerId, bobsUsdAccount.getId(), TRADE_FEE_CURRENCY_CODE); + var trade = takeAlicesOffer(offerId, bobsUsdAccount.getId()); assertNotNull(trade); assertEquals(offerId, trade.getTradeId()); // Cache the trade id for the other tests. diff --git a/apitest/src/test/java/bisq/apitest/method/wallet/BsqWalletTest.java b/apitest/src/test/java/bisq/apitest/method/wallet/BsqWalletTest.java deleted file mode 100644 index 6be9dd6654..0000000000 --- a/apitest/src/test/java/bisq/apitest/method/wallet/BsqWalletTest.java +++ /dev/null @@ -1,203 +0,0 @@ -package bisq.apitest.method.wallet; - -import bisq.proto.grpc.BsqBalanceInfo; - -import org.bitcoinj.core.LegacyAddress; -import org.bitcoinj.core.NetworkParameters; - -import lombok.extern.slf4j.Slf4j; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.TestMethodOrder; - -import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind; -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.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation; - - - -import bisq.apitest.config.BisqAppConfig; -import bisq.apitest.method.MethodTest; -import bisq.cli.GrpcClient; - -@Disabled -@Slf4j -@TestMethodOrder(OrderAnnotation.class) -public class BsqWalletTest extends MethodTest { - - private static final String SEND_BSQ_AMOUNT = "25000.50"; - - @BeforeAll - public static void setUp() { - startSupportingApps(false, - true, - bitcoind, - seednode, - arbdaemon, - alicedaemon, - bobdaemon); - } - - @Test - @Order(1) - public void testGetUnusedBsqAddress() { - var address = aliceClient.getUnusedBsqAddress(); - assertFalse(address.isEmpty()); - assertTrue(address.startsWith("B")); - - NetworkParameters networkParameters = LegacyAddress.getParametersFromAddress(address.substring(1)); - String addressNetwork = networkParameters.getPaymentProtocolId(); - assertNotEquals(PAYMENT_PROTOCOL_ID_MAINNET, addressNetwork); - // TODO Fix bug causing the regtest bsq address network to be evaluated as 'testnet' here. - assertTrue(addressNetwork.equals(PAYMENT_PROTOCOL_ID_TESTNET) - || addressNetwork.equals(PAYMENT_PROTOCOL_ID_REGTEST)); - } - - @Test - @Order(2) - public void testInitialBsqBalances(final TestInfo testInfo) { - BsqBalanceInfo alicesBsqBalances = aliceClient.getBsqBalances(); - log.debug("{} -> Alice's BSQ Initial Balances -> \n{}", - testName(testInfo), - formatBsqBalanceInfoTbl(alicesBsqBalances)); - verifyBsqBalances(ALICES_INITIAL_BSQ_BALANCES, alicesBsqBalances); - - BsqBalanceInfo bobsBsqBalances = bobClient.getBsqBalances(); - log.debug("{} -> Bob's BSQ Initial Balances -> \n{}", - testName(testInfo), - formatBsqBalanceInfoTbl(bobsBsqBalances)); - verifyBsqBalances(BOBS_INITIAL_BSQ_BALANCES, bobsBsqBalances); - } - - @Test - @Order(3) - public void testSendBsqAndCheckBalancesBeforeGeneratingBtcBlock(final TestInfo testInfo) { - String bobsBsqAddress = bobClient.getUnusedBsqAddress(); - aliceClient.sendBsq(bobsBsqAddress, SEND_BSQ_AMOUNT, "100"); - sleep(2000); - - BsqBalanceInfo alicesBsqBalances = aliceClient.getBsqBalances(); - BsqBalanceInfo bobsBsqBalances = waitForNonZeroBsqUnverifiedBalance(bobClient); - - log.debug("BSQ Balances Before BTC Block Gen..."); - printBobAndAliceBsqBalances(testInfo, - bobsBsqBalances, - alicesBsqBalances, - alicedaemon); - - verifyBsqBalances(bsqBalanceModel(150000000, - 2500050, - 0, - 0, - 0, - 0), - bobsBsqBalances); - - verifyBsqBalances(bsqBalanceModel(97499950, - 97499950, - 97499950, - 0, - 0, - 0), - alicesBsqBalances); - } - - @Test - @Order(4) - public void testBalancesAfterSendingBsqAndGeneratingBtcBlock(final TestInfo testInfo) { - // There is a wallet persist delay; we have to - // wait for both wallets to be saved to disk. - genBtcBlocksThenWait(1, 4000); - - BsqBalanceInfo alicesBsqBalances = aliceClient.getBalances().getBsq(); - BsqBalanceInfo bobsBsqBalances = waitForBsqNewAvailableConfirmedBalance(bobClient, 150000000); - - log.debug("See Available Confirmed BSQ Balances..."); - printBobAndAliceBsqBalances(testInfo, - bobsBsqBalances, - alicesBsqBalances, - alicedaemon); - - verifyBsqBalances(bsqBalanceModel(152500050, - 0, - 0, - 0, - 0, - 0), - bobsBsqBalances); - - verifyBsqBalances(bsqBalanceModel(97499950, - 0, - 0, - 0, - 0, - 0), - alicesBsqBalances); - } - - @AfterAll - public static void tearDown() { - tearDownScaffold(); - } - - private BsqBalanceInfo waitForNonZeroBsqUnverifiedBalance(GrpcClient grpcClient) { - // 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. - BsqBalanceInfo bsqBalance = grpcClient.getBsqBalances(); - for (int numRequests = 1; numRequests <= 15 && bsqBalance.getUnverifiedBalance() == 0; numRequests++) { - sleep(1000); - bsqBalance = grpcClient.getBsqBalances(); - } - return bsqBalance; - } - - private BsqBalanceInfo waitForBsqNewAvailableConfirmedBalance(GrpcClient grpcClient, - long staleBalance) { - BsqBalanceInfo bsqBalance = grpcClient.getBsqBalances(); - for (int numRequests = 1; - numRequests <= 15 && bsqBalance.getAvailableConfirmedBalance() == staleBalance; - numRequests++) { - sleep(1000); - bsqBalance = grpcClient.getBsqBalances(); - } - return bsqBalance; - } - - @SuppressWarnings("SameParameterValue") - private void printBobAndAliceBsqBalances(final TestInfo testInfo, - BsqBalanceInfo bobsBsqBalances, - BsqBalanceInfo alicesBsqBalances, - BisqAppConfig senderApp) { - log.debug("{} -> Bob's BSQ Balances After {} {} BSQ-> \n{}", - testName(testInfo), - senderApp.equals(bobdaemon) ? "Sending" : "Receiving", - SEND_BSQ_AMOUNT, - formatBsqBalanceInfoTbl(bobsBsqBalances)); - - log.debug("{} -> Alice's Balances After {} {} BSQ-> \n{}", - testName(testInfo), - senderApp.equals(alicedaemon) ? "Sending" : "Receiving", - SEND_BSQ_AMOUNT, - formatBsqBalanceInfoTbl(alicesBsqBalances)); - } -} diff --git a/apitest/src/test/java/bisq/apitest/method/wallet/WalletTestUtil.java b/apitest/src/test/java/bisq/apitest/method/wallet/WalletTestUtil.java index 85b9f04e84..3c2da836dc 100644 --- a/apitest/src/test/java/bisq/apitest/method/wallet/WalletTestUtil.java +++ b/apitest/src/test/java/bisq/apitest/method/wallet/WalletTestUtil.java @@ -1,6 +1,5 @@ package bisq.apitest.method.wallet; -import bisq.proto.grpc.BsqBalanceInfo; import bisq.proto.grpc.BtcBalanceInfo; import lombok.extern.slf4j.Slf4j; @@ -10,7 +9,7 @@ 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 + // All api tests depend on the 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, @@ -19,49 +18,6 @@ public class WalletTestUtil { 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()); diff --git a/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java b/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java index 5c531a9399..c83b264c84 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java +++ b/apitest/src/test/java/bisq/apitest/scenario/OfferTest.java @@ -29,7 +29,6 @@ import org.junit.jupiter.api.TestMethodOrder; import bisq.apitest.method.offer.AbstractOfferTest; import bisq.apitest.method.offer.CancelOfferTest; -import bisq.apitest.method.offer.CreateBSQOffersTest; import bisq.apitest.method.offer.CreateOfferUsingFixedPriceTest; import bisq.apitest.method.offer.CreateOfferUsingMarketPriceMarginTest; import bisq.apitest.method.offer.ValidateCreateOfferTest; @@ -72,17 +71,4 @@ public class OfferTest extends AbstractOfferTest { test.testCreateGBPXMRSellOfferMinus1Point5PctPriceMargin(); test.testCreateBRLXMRSellOffer6Point55PctPriceMargin(); } - - @Test - @Order(5) - public void testCreateBSQOffersTest() { - CreateBSQOffersTest test = new CreateBSQOffersTest(); - CreateBSQOffersTest.createBsqPaymentAccounts(); - test.testCreateBuy1BTCFor20KBSQOffer(); - test.testCreateSell1BTCFor20KBSQOffer(); - test.testCreateBuyBTCWith1To2KBSQOffer(); - test.testCreateSellBTCFor5To10KBSQOffer(); - test.testGetAllMyBsqOffers(); - test.testGetAvailableBsqOffers(); - } } diff --git a/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java b/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java index f4e93ad35a..4c07452abc 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java +++ b/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java @@ -29,9 +29,7 @@ import org.junit.jupiter.api.TestMethodOrder; import bisq.apitest.method.trade.AbstractTradeTest; -import bisq.apitest.method.trade.TakeBuyBSQOfferTest; import bisq.apitest.method.trade.TakeBuyBTCOfferTest; -import bisq.apitest.method.trade.TakeSellBSQOfferTest; import bisq.apitest.method.trade.TakeSellBTCOfferTest; @@ -63,26 +61,4 @@ public class TradeTest extends AbstractTradeTest { test.testAlicesConfirmPaymentReceived(testInfo); test.testBobsBtcWithdrawalToExternalAddress(testInfo); } - - @Test - @Order(3) - public void testTakeBuyBSQOffer(final TestInfo testInfo) { - TakeBuyBSQOfferTest test = new TakeBuyBSQOfferTest(); - TakeBuyBSQOfferTest.createBsqPaymentAccounts(); - test.testTakeAlicesSellBTCForBSQOffer(testInfo); - test.testBobsConfirmPaymentStarted(testInfo); - test.testAlicesConfirmPaymentReceived(testInfo); - test.testBobsKeepFunds(testInfo); - } - - @Test - @Order(4) - public void testTakeSellBSQOffer(final TestInfo testInfo) { - TakeSellBSQOfferTest test = new TakeSellBSQOfferTest(); - TakeSellBSQOfferTest.createBsqPaymentAccounts(); - test.testTakeAlicesBuyBTCForBSQOffer(testInfo); - test.testAlicesConfirmPaymentStarted(testInfo); - test.testBobsConfirmPaymentReceived(testInfo); - test.testAlicesBtcWithdrawalToExternalAddress(testInfo); - } } diff --git a/apitest/src/test/java/bisq/apitest/scenario/WalletTest.java b/apitest/src/test/java/bisq/apitest/scenario/WalletTest.java index 73a7e8ab16..9540a04bd0 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/WalletTest.java +++ b/apitest/src/test/java/bisq/apitest/scenario/WalletTest.java @@ -36,7 +36,6 @@ import static bisq.apitest.config.BisqAppConfig.seednode; import bisq.apitest.method.MethodTest; -import bisq.apitest.method.wallet.BsqWalletTest; import bisq.apitest.method.wallet.BtcTxFeeRateTest; import bisq.apitest.method.wallet.BtcWalletTest; import bisq.apitest.method.wallet.WalletProtectionTest; @@ -70,17 +69,6 @@ public class WalletTest extends MethodTest { btcWalletTest.testAliceSendBTCToBob(testInfo); } - @Test - @Order(2) - public void testBsqWalletFunding(final TestInfo testInfo) { - BsqWalletTest bsqWalletTest = new BsqWalletTest(); - - bsqWalletTest.testGetUnusedBsqAddress(); - bsqWalletTest.testInitialBsqBalances(testInfo); - bsqWalletTest.testSendBsqAndCheckBalancesBeforeGeneratingBtcBlock(testInfo); - bsqWalletTest.testBalancesAfterSendingBsqAndGeneratingBtcBlock(testInfo); - } - @Test @Order(3) public void testWalletProtection() { diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java b/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java index c34dc14d28..750d0ba917 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java +++ b/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java @@ -58,7 +58,7 @@ public class BotClient { } /** - * Returns current BSQ and BTC balance information. + * Returns current balance information. * @return BalancesInfo */ public BalancesInfo getBalance() { @@ -124,7 +124,6 @@ public class BotClient { * @param minAmountInSatoshis * @param priceMarginAsPercent * @param securityDepositAsPercent - * @param feeCurrency * @return OfferInfo */ public OfferInfo createOfferAtMarketBasedPrice(PaymentAccount paymentAccount, @@ -133,16 +132,14 @@ public class BotClient { long amountInSatoshis, long minAmountInSatoshis, double priceMarginAsPercent, - double securityDepositAsPercent, - String feeCurrency) { + double securityDepositAsPercent) { return grpcClient.createMarketBasedPricedOffer(direction, currencyCode, amountInSatoshis, minAmountInSatoshis, priceMarginAsPercent, securityDepositAsPercent, - paymentAccount.getId(), - feeCurrency); + paymentAccount.getId()); } /** @@ -154,7 +151,6 @@ public class BotClient { * @param minAmountInSatoshis * @param fixedOfferPriceAsString * @param securityDepositAsPercent - * @param feeCurrency * @return OfferInfo */ public OfferInfo createOfferAtFixedPrice(PaymentAccount paymentAccount, @@ -163,20 +159,18 @@ public class BotClient { long amountInSatoshis, long minAmountInSatoshis, String fixedOfferPriceAsString, - double securityDepositAsPercent, - String feeCurrency) { + double securityDepositAsPercent) { return grpcClient.createFixedPricedOffer(direction, currencyCode, amountInSatoshis, minAmountInSatoshis, fixedOfferPriceAsString, securityDepositAsPercent, - paymentAccount.getId(), - feeCurrency); + paymentAccount.getId()); } - public TradeInfo takeOffer(String offerId, PaymentAccount paymentAccount, String feeCurrency) { - return grpcClient.takeOffer(offerId, paymentAccount.getId(), feeCurrency); + public TradeInfo takeOffer(String offerId, PaymentAccount paymentAccount) { + return grpcClient.takeOffer(offerId, paymentAccount.getId()); } /** diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java b/apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java index 1942f8ad07..73adff74db 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java +++ b/apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java @@ -95,8 +95,6 @@ public class RandomOffer { private final boolean useMarketBasedPrice; @Getter private final double priceMargin; - @Getter - private final String feeCurrency; @Getter private String fixedOfferPrice = "0"; @@ -114,7 +112,6 @@ public class RandomOffer { this.minAmount = nextMinAmount.get(); this.useMarketBasedPrice = RANDOM.nextBoolean(); this.priceMargin = nextPriceMargin.get(); - this.feeCurrency = RANDOM.nextBoolean() ? "BSQ" : "BTC"; } public RandomOffer create() throws InvalidRandomOfferException { @@ -127,8 +124,7 @@ public class RandomOffer { amount, minAmount, priceMargin, - getDefaultBuyerSecurityDepositAsPercent(), - feeCurrency); + getDefaultBuyerSecurityDepositAsPercent()); } else { this.offer = botClient.createOfferAtFixedPrice(paymentAccount, direction, @@ -136,8 +132,7 @@ public class RandomOffer { amount, minAmount, fixedOfferPrice, - getDefaultBuyerSecurityDepositAsPercent(), - feeCurrency); + getDefaultBuyerSecurityDepositAsPercent()); } this.id = offer.getId(); return this; diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/TakerBotProtocol.java b/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/TakerBotProtocol.java index 63b700824f..a7b3117226 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/TakerBotProtocol.java +++ b/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/TakerBotProtocol.java @@ -102,13 +102,12 @@ public class TakerBotProtocol extends BotProtocol { private final Function takeOffer = (offer) -> { initProtocolStep.accept(TAKE_OFFER); checkIfShutdownCalled("Interrupted before taking offer."); - String feeCurrency = RANDOM.nextBoolean() ? "BSQ" : "BTC"; - return botClient.takeOffer(offer.getId(), paymentAccount, feeCurrency); + return botClient.takeOffer(offer.getId(), paymentAccount); }; private void createMakeOfferScript() { String direction = RANDOM.nextBoolean() ? "BUY" : "SELL"; - String feeCurrency = RANDOM.nextBoolean() ? "BSQ" : "BTC"; + String feeCurrency = "BTC"; boolean createMarginPricedOffer = RANDOM.nextBoolean(); // If not using an F2F account, don't go over possible 0.01 BTC // limit if account is not signed. diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/script/BashScriptGenerator.java b/apitest/src/test/java/bisq/apitest/scenario/bot/script/BashScriptGenerator.java index d41e8a1acd..fe94fb4107 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/script/BashScriptGenerator.java +++ b/apitest/src/test/java/bisq/apitest/scenario/bot/script/BashScriptGenerator.java @@ -130,7 +130,7 @@ public class BashScriptGenerator { cliBase, offer.getDirection(), offer.getCounterCurrencyCode()); - String takeOfferCmd = format("%s takeoffer --offer-id=%s --payment-account=%s --fee-currency=BSQ", + String takeOfferCmd = format("%s takeoffer --offer-id=%s --payment-account=%s", cliBase, offer.getId(), this.getPaymentAccountId()); diff --git a/assets/src/main/java/bisq/asset/PrintTool.java b/assets/src/main/java/bisq/asset/PrintTool.java index ba3cbf4f84..0ea4132dfc 100644 --- a/assets/src/main/java/bisq/asset/PrintTool.java +++ b/assets/src/main/java/bisq/asset/PrintTool.java @@ -27,7 +27,6 @@ public class PrintTool { StringBuilder sb = new StringBuilder(); new AssetRegistry().stream() .sorted(Comparator.comparing(o -> o.getName().toLowerCase())) - .filter(e -> !e.getTickerSymbol().equals("BSQ")) // BSQ is not out yet... .filter(e -> !e.getTickerSymbol().equals("BTC")) .map(e -> new Pair(e.getName(), e.getTickerSymbol())) // We want to get rid of duplicated entries for regtest/testnet... .distinct() diff --git a/assets/src/main/java/bisq/asset/coins/BSQ.java b/assets/src/main/java/bisq/asset/coins/BSQ.java deleted file mode 100644 index 47a1966666..0000000000 --- a/assets/src/main/java/bisq/asset/coins/BSQ.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.asset.coins; - -import bisq.asset.AddressValidationResult; -import bisq.asset.Base58AddressValidator; -import bisq.asset.Coin; - -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.params.MainNetParams; -import org.bitcoinj.params.RegTestParams; -import org.bitcoinj.params.TestNet3Params; - -public class BSQ extends Coin { - - public BSQ(Network network, NetworkParameters networkParameters) { - super("BSQ", "BSQ", new BSQAddressValidator(networkParameters), network); - } - - - public static class Mainnet extends BSQ { - - public Mainnet() { - super(Network.MAINNET, MainNetParams.get()); - } - } - - - public static class Testnet extends BSQ { - - public Testnet() { - super(Network.TESTNET, TestNet3Params.get()); - } - } - - - public static class Regtest extends BSQ { - - public Regtest() { - super(Network.STAGENET, RegTestParams.get()); - } - } - - - public static class BSQAddressValidator extends Base58AddressValidator { - - public BSQAddressValidator(NetworkParameters networkParameters) { - super(networkParameters); - } - - @Override - public AddressValidationResult validate(String address) { - if (!address.startsWith("B")) - return AddressValidationResult.invalidAddress("BSQ address must start with 'B'"); - - String addressAsBtc = address.substring(1, address.length()); - - return super.validate(addressAsBtc); - } - } -} diff --git a/assets/src/main/resources/META-INF/services/bisq.asset.Asset b/assets/src/main/resources/META-INF/services/bisq.asset.Asset index 80a6168463..66b37c4bb7 100644 --- a/assets/src/main/resources/META-INF/services/bisq.asset.Asset +++ b/assets/src/main/resources/META-INF/services/bisq.asset.Asset @@ -19,9 +19,6 @@ bisq.asset.coins.BitDaric bisq.asset.coins.Bitmark bisq.asset.coins.Bitzec bisq.asset.coins.Blur -bisq.asset.coins.BSQ$Mainnet -bisq.asset.coins.BSQ$Regtest -bisq.asset.coins.BSQ$Testnet bisq.asset.coins.BurntBlackCoin bisq.asset.coins.Cash2 bisq.asset.coins.Chaucha diff --git a/assets/src/test/java/bisq/asset/coins/BSQTest.java b/assets/src/test/java/bisq/asset/coins/BSQTest.java deleted file mode 100644 index 9bb9764b8a..0000000000 --- a/assets/src/test/java/bisq/asset/coins/BSQTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.asset.coins; - -import bisq.asset.AbstractAssetTest; - -import org.junit.Test; - -public class BSQTest extends AbstractAssetTest { - - public BSQTest() { - super(new BSQ.Mainnet()); - } - - @Test - public void testValidAddresses() { - assertValidAddress("B17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"); - assertValidAddress("B3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX"); - assertValidAddress("B1111111111111111111114oLvT2"); - assertValidAddress("B1BitcoinEaterAddressDontSendf59kuE"); - } - - @Test - public void testInvalidAddresses() { - assertInvalidAddress("B17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhemqq"); - assertInvalidAddress("B17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYheO"); - assertInvalidAddress("B17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhek#"); - } -} diff --git a/build.gradle b/build.gradle index 4b3e0adc03..b2f7a493b5 100644 --- a/build.gradle +++ b/build.gradle @@ -631,34 +631,12 @@ configure(project(':inventory')) { configure(project(':apitest')) { mainClassName = 'bisq.apitest.ApiTestMain' - // The external dao-setup.gradle file contains tasks to install and clean dao-setup - // files downloaded from - // https://github.com/bisq-network/bisq/raw/master/docs/dao-setup.zip - // These tasks are not run by the default build, but they can can be run during - // full or partial builds, or by themselves. - // To run the regular clean + build + test (non api), and install dao-setup files: - // ./gradlew clean build :apitest:installDaoSetup - // To install or re-install dao-setup file only: - // ./gradlew :apitest:installDaoSetup -x test - // To clean installed dao-setup files: - // ./gradlew :apitest:cleanDaoSetup -x test - apply from: 'dao-setup.gradle' - // We have to disable the :apitest 'test' task by default because we do not want // to interfere with normal builds. To run JUnit tests in this subproject: // Run a normal build and install dao-setup files first, then run: // 'gradle :apitest:test -DrunApiTests=true' test.enabled = System.getProperty("runApiTests") == "true" - sourceSets { - main { - resources { - exclude 'dao-setup' - exclude 'dao-setup.zip' - } - } - } - test { useJUnitPlatform() outputs.upToDateWhen { false } // Don't use previously cached test outputs. diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java index a08065269a..718097da4c 100644 --- a/cli/src/main/java/bisq/cli/CliMain.java +++ b/cli/src/main/java/bisq/cli/CliMain.java @@ -69,14 +69,12 @@ import bisq.cli.opts.GetTradeOptionParser; import bisq.cli.opts.GetTransactionOptionParser; import bisq.cli.opts.RegisterDisputeAgentOptionParser; import bisq.cli.opts.RemoveWalletPasswordOptionParser; -import bisq.cli.opts.SendBsqOptionParser; import bisq.cli.opts.SendBtcOptionParser; import bisq.cli.opts.SetTxFeeRateOptionParser; import bisq.cli.opts.SetWalletPasswordOptionParser; import bisq.cli.opts.SimpleMethodOptionParser; import bisq.cli.opts.TakeOfferOptionParser; import bisq.cli.opts.UnlockWalletOptionParser; -import bisq.cli.opts.VerifyBsqSentToAddressOptionParser; import bisq.cli.opts.WithdrawFundsOptionParser; /** @@ -168,9 +166,6 @@ public class CliMain { var currencyCode = opts.getCurrencyCode(); var balances = client.getBalances(currencyCode); switch (currencyCode.toUpperCase()) { - case "BSQ": - out.println(formatBsqBalanceInfoTbl(balances.getBsq())); - break; case "BTC": out.println(formatBtcBalanceInfoTbl(balances.getBtc())); break; @@ -215,36 +210,6 @@ public class CliMain { out.println(formatAddressBalanceTbl(fundingAddresses)); return; } - case getunusedbsqaddress: { - if (new SimpleMethodOptionParser(args).parse().isForHelp()) { - out.println(client.getMethodHelp(method)); - return; - } - var address = client.getUnusedBsqAddress(); - out.println(address); - return; - } - case sendbsq: { - var opts = new SendBsqOptionParser(args).parse(); - if (opts.isForHelp()) { - out.println(client.getMethodHelp(method)); - return; - } - var address = opts.getAddress(); - var amount = opts.getAmount(); - verifyStringIsValidDecimal(OPT_AMOUNT, amount); - - var txFeeRate = opts.getFeeRate(); - if (!txFeeRate.isEmpty()) - verifyStringIsValidLong(OPT_TX_FEE_RATE, txFeeRate); - - var txInfo = client.sendBsq(address, amount, txFeeRate); - out.printf("%s bsq sent to %s in tx %s%n", - amount, - address, - txInfo.getTxId()); - return; - } case sendbtc: { var opts = new SendBtcOptionParser(args).parse(); if (opts.isForHelp()) { @@ -268,23 +233,6 @@ public class CliMain { txInfo.getTxId()); return; } - case verifybsqsenttoaddress: { - var opts = new VerifyBsqSentToAddressOptionParser(args).parse(); - if (opts.isForHelp()) { - out.println(client.getMethodHelp(method)); - return; - } - var address = opts.getAddress(); - var amount = opts.getAmount(); - verifyStringIsValidDecimal(OPT_AMOUNT, amount); - - var bsqWasSent = client.verifyBsqSentToAddress(address, amount); - out.printf("%s bsq %s sent to address %s%n", - amount, - bsqWasSent ? "has been" : "has not been", - address); - return; - } case gettxfeerate: { if (new SimpleMethodOptionParser(args).parse().isForHelp()) { out.println(client.getMethodHelp(method)); @@ -339,7 +287,6 @@ public class CliMain { var fixedPrice = opts.getFixedPrice(); var marketPriceMargin = opts.getMktPriceMarginAsBigDecimal(); var securityDeposit = toSecurityDepositAsPct(opts.getSecurityDeposit()); - var makerFeeCurrencyCode = opts.getMakerFeeCurrencyCode(); var offer = client.createOffer(direction, currencyCode, amount, @@ -348,8 +295,7 @@ public class CliMain { fixedPrice, marketPriceMargin.doubleValue(), securityDeposit, - paymentAcctId, - makerFeeCurrencyCode); + paymentAcctId); out.println(formatOfferTable(singletonList(offer), currencyCode)); return; } @@ -426,8 +372,7 @@ public class CliMain { } var offerId = opts.getOfferId(); var paymentAccountId = opts.getPaymentAccountId(); - var takerFeeCurrencyCode = opts.getTakerFeeCurrencyCode(); - var trade = client.takeOffer(offerId, paymentAccountId, takerFeeCurrencyCode); + var trade = client.takeOffer(offerId, paymentAccountId); out.printf("trade %s successfully taken%n", trade.getTradeId()); return; } @@ -721,7 +666,7 @@ public class CliMain { stream.format(rowFormat, "------", "------", "------------"); stream.format(rowFormat, getversion.name(), "", "Get server version"); stream.println(); - stream.format(rowFormat, getbalance.name(), "[--currency-code=]", "Get server wallet balances"); + stream.format(rowFormat, getbalance.name(), "[--currency-code=]", "Get server wallet balances"); stream.println(); stream.format(rowFormat, getaddressbalance.name(), "--address=", "Get server wallet address balance"); stream.println(); @@ -729,17 +674,10 @@ public class CliMain { stream.println(); stream.format(rowFormat, getfundingaddresses.name(), "", "Get BTC funding addresses"); stream.println(); - stream.format(rowFormat, getunusedbsqaddress.name(), "", "Get unused BSQ address"); - stream.println(); - stream.format(rowFormat, sendbsq.name(), "--address= --amount= \\", "Send BSQ"); - stream.format(rowFormat, "", "[--tx-fee-rate=]", ""); - stream.println(); stream.format(rowFormat, sendbtc.name(), "--address= --amount= \\", "Send BTC"); stream.format(rowFormat, "", "[--tx-fee-rate=]", ""); stream.format(rowFormat, "", "[--memo=<\"memo\">]", ""); stream.println(); - stream.format(rowFormat, verifybsqsenttoaddress.name(), "--address= --amount=", - "Verify amount was sent to BSQ wallet address"); stream.println(); stream.format(rowFormat, gettxfeerate.name(), "", "Get current tx fee rate in sats/byte"); stream.println(); @@ -756,7 +694,7 @@ public class CliMain { stream.format(rowFormat, "", "[--min-amount=] \\", ""); stream.format(rowFormat, "", "--fixed-price= | --market-price=margin= \\", ""); stream.format(rowFormat, "", "--security-deposit= \\", ""); - stream.format(rowFormat, "", "[--fee-currency=]", ""); + stream.format(rowFormat, "", "[--fee-currency=]", ""); stream.println(); stream.format(rowFormat, canceloffer.name(), "--offer-id=", "Cancel offer with id"); stream.println(); @@ -772,7 +710,7 @@ public class CliMain { stream.println(); stream.format(rowFormat, takeoffer.name(), "--offer-id= \\", "Take offer with id"); stream.format(rowFormat, "", "--payment-account=", ""); - stream.format(rowFormat, "", "[--fee-currency=]", ""); + stream.format(rowFormat, "", "[--fee-currency=]", ""); stream.println(); stream.format(rowFormat, gettrade.name(), "--trade-id= \\", "Get trade summary or full contract"); stream.format(rowFormat, "", "[--show-contract=]", ""); @@ -794,8 +732,8 @@ public class CliMain { stream.format(rowFormat, createpaymentacct.name(), "--payment-account-form=", "Create a new payment account"); stream.println(); stream.format(rowFormat, createcryptopaymentacct.name(), "--account-name= \\", "Create a new cryptocurrency payment account"); - stream.format(rowFormat, "", "--currency-code= \\", ""); - stream.format(rowFormat, "", "--address=", ""); + stream.format(rowFormat, "", "--currency-code= \\", ""); + stream.format(rowFormat, "", "--address=

", ""); stream.format(rowFormat, "", "--trade-instant=", ""); stream.println(); stream.format(rowFormat, getpaymentaccts.name(), "", "Get user payment accounts"); diff --git a/cli/src/main/java/bisq/cli/ColumnHeaderConstants.java b/cli/src/main/java/bisq/cli/ColumnHeaderConstants.java index fdb2c999e9..e47c874df3 100644 --- a/cli/src/main/java/bisq/cli/ColumnHeaderConstants.java +++ b/cli/src/main/java/bisq/cli/ColumnHeaderConstants.java @@ -54,7 +54,6 @@ class ColumnHeaderConstants { static final String COL_HEADER_PRICE = "Price in %-3s for 1 BTC"; static final String COL_HEADER_PRICE_OF_ALTCOIN = "Price in BTC for 1 %-3s"; static final String COL_HEADER_TRADE_AMOUNT = padStart("Amount(%-3s)", 12, ' '); - static final String COL_HEADER_TRADE_BSQ_BUYER_ADDRESS = "BSQ Buyer Address"; static final String COL_HEADER_TRADE_BUYER_COST = padEnd("Buyer Cost(%-3s)", 15, ' '); static final String COL_HEADER_TRADE_DEPOSIT_CONFIRMED = "Deposit Confirmed"; static final String COL_HEADER_TRADE_DEPOSIT_PUBLISHED = "Deposit Published"; diff --git a/cli/src/main/java/bisq/cli/CryptoCurrencyUtil.java b/cli/src/main/java/bisq/cli/CryptoCurrencyUtil.java index cb503d7c07..9f74e18976 100644 --- a/cli/src/main/java/bisq/cli/CryptoCurrencyUtil.java +++ b/cli/src/main/java/bisq/cli/CryptoCurrencyUtil.java @@ -28,7 +28,6 @@ class CryptoCurrencyUtil { public static List getSupportedCryptoCurrencies() { final List result = new ArrayList<>(); - result.add("BSQ"); result.sort(String::compareTo); return result; } diff --git a/cli/src/main/java/bisq/cli/CurrencyFormat.java b/cli/src/main/java/bisq/cli/CurrencyFormat.java index 22022ceb58..e3f599ef67 100644 --- a/cli/src/main/java/bisq/cli/CurrencyFormat.java +++ b/cli/src/main/java/bisq/cli/CurrencyFormat.java @@ -46,12 +46,8 @@ public class CurrencyFormat { static final DecimalFormat BTC_FORMAT = new DecimalFormat("###,##0.00000000"); static final DecimalFormat BTC_TX_FEE_FORMAT = new DecimalFormat("###,###,##0"); - static final BigDecimal BSQ_SATOSHI_DIVISOR = new BigDecimal(100); - static final DecimalFormat BSQ_FORMAT = new DecimalFormat("###,###,###,##0.00"); - static final DecimalFormat SEND_BSQ_FORMAT = new DecimalFormat("###########0.00"); - static final BigDecimal SECURITY_DEPOSIT_MULTIPLICAND = new BigDecimal("0.01"); - + // TODO: (woodser): replace formatSatoshis(), formatBsq() with formatXmr() @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") @@ -59,22 +55,10 @@ public class CurrencyFormat { return BTC_FORMAT.format(BigDecimal.valueOf(sats).divide(SATOSHI_DIVISOR)); } - @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") - public static String formatBsq(long sats) { - return BSQ_FORMAT.format(BigDecimal.valueOf(sats).divide(BSQ_SATOSHI_DIVISOR)); - } - public static String formatXmr(BigInteger amount) { return "" + MoneroUtils.atomicUnitsToXmr(amount); } - public static String formatBsqAmount(long bsqSats) { - // BSQ sats = trade.getOffer().getVolume() - NUMBER_FORMAT.setMinimumFractionDigits(2); - NUMBER_FORMAT.setMaximumFractionDigits(2); - NUMBER_FORMAT.setRoundingMode(HALF_UP); - return SEND_BSQ_FORMAT.format((double) bsqSats / SATOSHI_DIVISOR.doubleValue()); - } public static String formatTxFeeRateInfo(TxFeeRateInfo txFeeRateInfo) { if (txFeeRateInfo.getUseCustomTxFeeRate()) diff --git a/cli/src/main/java/bisq/cli/GrpcClient.java b/cli/src/main/java/bisq/cli/GrpcClient.java index a14a753120..8669ea19cf 100644 --- a/cli/src/main/java/bisq/cli/GrpcClient.java +++ b/cli/src/main/java/bisq/cli/GrpcClient.java @@ -19,7 +19,6 @@ package bisq.cli; import bisq.proto.grpc.AddressBalanceInfo; import bisq.proto.grpc.BalancesInfo; -import bisq.proto.grpc.BsqBalanceInfo; import bisq.proto.grpc.BtcBalanceInfo; import bisq.proto.grpc.CancelOfferRequest; import bisq.proto.grpc.ConfirmPaymentReceivedRequest; @@ -42,7 +41,6 @@ import bisq.proto.grpc.GetPaymentMethodsRequest; import bisq.proto.grpc.GetTradeRequest; import bisq.proto.grpc.GetTransactionRequest; import bisq.proto.grpc.GetTxFeeRateRequest; -import bisq.proto.grpc.GetUnusedBsqAddressRequest; import bisq.proto.grpc.GetVersionRequest; import bisq.proto.grpc.KeepFundsRequest; import bisq.proto.grpc.LockWalletRequest; @@ -50,7 +48,6 @@ import bisq.proto.grpc.MarketPriceRequest; import bisq.proto.grpc.OfferInfo; import bisq.proto.grpc.RegisterDisputeAgentRequest; import bisq.proto.grpc.RemoveWalletPasswordRequest; -import bisq.proto.grpc.SendBsqRequest; import bisq.proto.grpc.SendBtcRequest; import bisq.proto.grpc.SetTxFeeRatePreferenceRequest; import bisq.proto.grpc.SetWalletPasswordRequest; @@ -62,7 +59,6 @@ import bisq.proto.grpc.TxFeeRateInfo; import bisq.proto.grpc.TxInfo; import bisq.proto.grpc.UnlockWalletRequest; import bisq.proto.grpc.UnsetTxFeeRatePreferenceRequest; -import bisq.proto.grpc.VerifyBsqSentToAddressRequest; import bisq.proto.grpc.WithdrawFundsRequest; import bisq.proto.grpc.XmrBalanceInfo; @@ -100,10 +96,6 @@ public final class GrpcClient { return getBalances(""); } - public BsqBalanceInfo getBsqBalances() { - return getBalances("BSQ").getBsq(); - } - public BtcBalanceInfo getBtcBalances() { return getBalances("BTC").getBtc(); } @@ -137,11 +129,6 @@ public final class GrpcClient { return grpcStubs.walletsService.getFundingAddresses(request).getAddressBalanceInfoList(); } - public String getUnusedBsqAddress() { - var request = GetUnusedBsqAddressRequest.newBuilder().build(); - return grpcStubs.walletsService.getUnusedBsqAddress(request).getAddress(); - } - public String getUnusedBtcAddress() { var request = GetFundingAddressesRequest.newBuilder().build(); var addressBalances = grpcStubs.walletsService.getFundingAddresses(request) @@ -154,15 +141,6 @@ public final class GrpcClient { .getAddress(); } - public TxInfo sendBsq(String address, String amount, String txFeeRate) { - var request = SendBsqRequest.newBuilder() - .setAddress(address) - .setAmount(amount) - .setTxFeeRate(txFeeRate) - .build(); - return grpcStubs.walletsService.sendBsq(request).getTxInfo(); - } - public TxInfo sendBtc(String address, String amount, String txFeeRate, String memo) { var request = SendBtcRequest.newBuilder() .setAddress(address) @@ -173,14 +151,6 @@ public final class GrpcClient { return grpcStubs.walletsService.sendBtc(request).getTxInfo(); } - public boolean verifyBsqSentToAddress(String address, String amount) { - var request = VerifyBsqSentToAddressRequest.newBuilder() - .setAddress(address) - .setAmount(amount) - .build(); - return grpcStubs.walletsService.verifyBsqSentToAddress(request).getIsAmountReceived(); - } - public TxFeeRateInfo getTxFeeRate() { var request = GetTxFeeRateRequest.newBuilder().build(); return grpcStubs.walletsService.getTxFeeRate(request).getTxFeeRateInfo(); @@ -211,8 +181,7 @@ public final class GrpcClient { long minAmount, String fixedPrice, double securityDeposit, - String paymentAcctId, - String makerFeeCurrencyCode) { + String paymentAcctId) { return createOffer(direction, currencyCode, amount, @@ -221,8 +190,7 @@ public final class GrpcClient { fixedPrice, 0.00, securityDeposit, - paymentAcctId, - makerFeeCurrencyCode); + paymentAcctId); } public OfferInfo createMarketBasedPricedOffer(String direction, @@ -231,8 +199,7 @@ public final class GrpcClient { long minAmount, double marketPriceMargin, double securityDeposit, - String paymentAcctId, - String makerFeeCurrencyCode) { + String paymentAcctId) { return createOffer(direction, currencyCode, amount, @@ -241,8 +208,7 @@ public final class GrpcClient { "0", marketPriceMargin, securityDeposit, - paymentAcctId, - makerFeeCurrencyCode); + paymentAcctId); } public OfferInfo createOffer(String direction, @@ -253,8 +219,7 @@ public final class GrpcClient { String fixedPrice, double marketPriceMargin, double securityDeposit, - String paymentAcctId, - String makerFeeCurrencyCode) { + String paymentAcctId) { var request = CreateOfferRequest.newBuilder() .setDirection(direction) .setCurrencyCode(currencyCode) @@ -265,7 +230,6 @@ public final class GrpcClient { .setMarketPriceMargin(marketPriceMargin) .setBuyerSecurityDeposit(securityDeposit) .setPaymentAccountId(paymentAcctId) - .setMakerFeeCurrencyCode(makerFeeCurrencyCode) .build(); return grpcStubs.offersService.createOffer(request).getOffer(); } @@ -321,13 +285,6 @@ public final class GrpcClient { return offers.isEmpty() ? offers : sortOffersByDate(offers); } - public List getBsqOffersSortedByDate() { - ArrayList offers = new ArrayList<>(); - offers.addAll(getCryptoCurrencyOffers(BUY.name(), "BSQ")); - offers.addAll(getCryptoCurrencyOffers(SELL.name(), "BSQ")); - return sortOffersByDate(offers); - } - public List getMyOffers(String direction, String currencyCode) { if (isSupportedCryptoCurrency(currencyCode)) { return getMyCryptoCurrencyOffers(direction, currencyCode); @@ -358,13 +315,6 @@ public final class GrpcClient { return sortOffersByDate(offers); } - public List getMyBsqOffersSortedByDate() { - ArrayList offers = new ArrayList<>(); - offers.addAll(getMyCryptoCurrencyOffers(BUY.name(), "BSQ")); - offers.addAll(getMyCryptoCurrencyOffers(SELL.name(), "BSQ")); - return sortOffersByDate(offers); - } - public OfferInfo getMostRecentOffer(String direction, String currencyCode) { List offers = getOffersSortedByDate(direction, currencyCode); return offers.isEmpty() ? null : offers.get(offers.size() - 1); @@ -376,17 +326,16 @@ public final class GrpcClient { .collect(toList()); } - public TakeOfferReply getTakeOfferReply(String offerId, String paymentAccountId, String takerFeeCurrencyCode) { + public TakeOfferReply getTakeOfferReply(String offerId, String paymentAccountId) { var request = TakeOfferRequest.newBuilder() .setOfferId(offerId) .setPaymentAccountId(paymentAccountId) - .setTakerFeeCurrencyCode(takerFeeCurrencyCode) .build(); return grpcStubs.tradesService.takeOffer(request); } - public TradeInfo takeOffer(String offerId, String paymentAccountId, String takerFeeCurrencyCode) { - var reply = getTakeOfferReply(offerId, paymentAccountId, takerFeeCurrencyCode); + public TradeInfo takeOffer(String offerId, String paymentAccountId) { + var reply = getTakeOfferReply(offerId, paymentAccountId); if (reply.hasTrade()) return reply.getTrade(); else diff --git a/cli/src/main/java/bisq/cli/Method.java b/cli/src/main/java/bisq/cli/Method.java index cf8b1d7df5..24084128b0 100644 --- a/cli/src/main/java/bisq/cli/Method.java +++ b/cli/src/main/java/bisq/cli/Method.java @@ -41,15 +41,12 @@ public enum Method { gettrade, gettransaction, gettxfeerate, - getunusedbsqaddress, getversion, keepfunds, lockwallet, registerdisputeagent, removewalletpassword, - sendbsq, sendbtc, - verifybsqsenttoaddress, settxfeerate, setwalletpassword, takeoffer, diff --git a/cli/src/main/java/bisq/cli/TableFormat.java b/cli/src/main/java/bisq/cli/TableFormat.java index 73afcf40ce..bd18b6b642 100644 --- a/cli/src/main/java/bisq/cli/TableFormat.java +++ b/cli/src/main/java/bisq/cli/TableFormat.java @@ -19,7 +19,6 @@ package bisq.cli; import bisq.proto.grpc.AddressBalanceInfo; import bisq.proto.grpc.BalancesInfo; -import bisq.proto.grpc.BsqBalanceInfo; import bisq.proto.grpc.BtcBalanceInfo; import bisq.proto.grpc.OfferInfo; import bisq.proto.grpc.XmrBalanceInfo; @@ -75,31 +74,7 @@ public class TableFormat { public static String formatBalancesTbls(BalancesInfo balancesInfo) { return "XMR" + "\n" - + formatBtcBalanceInfoTbl(balancesInfo.getBtc()) + "\n" - + "BSQ" + "\n" - + formatBsqBalanceInfoTbl(balancesInfo.getBsq()); - } - - public static String formatBsqBalanceInfoTbl(BsqBalanceInfo bsqBalanceInfo) { - String headerLine = COL_HEADER_AVAILABLE_CONFIRMED_BALANCE + COL_HEADER_DELIMITER - + COL_HEADER_UNVERIFIED_BALANCE + COL_HEADER_DELIMITER - + COL_HEADER_UNCONFIRMED_CHANGE_BALANCE + COL_HEADER_DELIMITER - + COL_HEADER_LOCKED_FOR_VOTING_BALANCE + COL_HEADER_DELIMITER - + COL_HEADER_LOCKUP_BONDS_BALANCE + COL_HEADER_DELIMITER - + COL_HEADER_UNLOCKING_BONDS_BALANCE + COL_HEADER_DELIMITER + "\n"; - String colDataFormat = "%" + COL_HEADER_AVAILABLE_CONFIRMED_BALANCE.length() + "s" // rt justify - + " %" + (COL_HEADER_UNVERIFIED_BALANCE.length() + 1) + "s" // rt justify - + " %" + (COL_HEADER_UNCONFIRMED_CHANGE_BALANCE.length() + 1) + "s" // rt justify - + " %" + (COL_HEADER_LOCKED_FOR_VOTING_BALANCE.length() + 1) + "s" // rt justify - + " %" + (COL_HEADER_LOCKUP_BONDS_BALANCE.length() + 1) + "s" // rt justify - + " %" + (COL_HEADER_UNLOCKING_BONDS_BALANCE.length() + 1) + "s"; // rt justify - return headerLine + format(colDataFormat, - formatBsq(bsqBalanceInfo.getAvailableConfirmedBalance()), - formatBsq(bsqBalanceInfo.getUnverifiedBalance()), - formatBsq(bsqBalanceInfo.getUnconfirmedChangeBalance()), - formatBsq(bsqBalanceInfo.getLockedForVotingBalance()), - formatBsq(bsqBalanceInfo.getLockupBondsBalance()), - formatBsq(bsqBalanceInfo.getUnlockingBondsBalance())); + + formatBtcBalanceInfoTbl(balancesInfo.getBtc()); } public static String formatBtcBalanceInfoTbl(BtcBalanceInfo btcBalanceInfo) { diff --git a/cli/src/main/java/bisq/cli/TradeFormat.java b/cli/src/main/java/bisq/cli/TradeFormat.java index 97167a3546..86d271aa97 100644 --- a/cli/src/main/java/bisq/cli/TradeFormat.java +++ b/cli/src/main/java/bisq/cli/TradeFormat.java @@ -61,9 +61,6 @@ public class TradeFormat { "%" + (COL_HEADER_TRADE_TAKER_FEE.length() + 2) + "s" : ""; - boolean showBsqBuyerAddress = shouldShowBsqBuyerAddress(tradeInfo, isTaker); - Supplier bsqBuyerAddressHeader = () -> showBsqBuyerAddress ? COL_HEADER_TRADE_BSQ_BUYER_ADDRESS : ""; - Supplier bsqBuyerAddressHeaderSpec = () -> showBsqBuyerAddress ? "%s" : ""; String headersFormat = padEnd(COL_HEADER_TRADE_SHORT_ID, shortIdColWidth, ' ') + COL_HEADER_DELIMITER + padEnd(COL_HEADER_TRADE_ROLE, roleColWidth, ' ') + COL_HEADER_DELIMITER @@ -80,7 +77,6 @@ public class TradeFormat { + COL_HEADER_TRADE_PAYMENT_RECEIVED + COL_HEADER_DELIMITER + COL_HEADER_TRADE_PAYOUT_PUBLISHED + COL_HEADER_DELIMITER + COL_HEADER_TRADE_WITHDRAWN + COL_HEADER_DELIMITER - + bsqBuyerAddressHeader.get() + "%n"; String counterCurrencyCode = tradeInfo.getOffer().getCounterCurrencyCode(); @@ -108,16 +104,14 @@ public class TradeFormat { + " %-" + (COL_HEADER_TRADE_PAYMENT_SENT.length() - 1) + "s" // left + " %-" + (COL_HEADER_TRADE_PAYMENT_RECEIVED.length() - 1) + "s" // left + " %-" + COL_HEADER_TRADE_PAYOUT_PUBLISHED.length() + "s" // lt justify - + " %-" + (COL_HEADER_TRADE_WITHDRAWN.length() + 2) + "s" - + bsqBuyerAddressHeaderSpec.get(); + + " %-" + (COL_HEADER_TRADE_WITHDRAWN.length() + 2) + "s"; - return headerLine + formatTradeData(colDataFormat, tradeInfo, isTaker, showBsqBuyerAddress); + return headerLine + formatTradeData(colDataFormat, tradeInfo, isTaker); } private static String formatTradeData(String format, TradeInfo tradeInfo, - boolean isTaker, - boolean showBsqBuyerAddress) { + boolean isTaker) { return String.format(format, tradeInfo.getShortId(), tradeInfo.getRole(), @@ -131,8 +125,7 @@ public class TradeFormat { tradeInfo.getIsFiatSent() ? YES : NO, tradeInfo.getIsFiatReceived() ? YES : NO, tradeInfo.getIsPayoutPublished() ? YES : NO, - tradeInfo.getIsWithdrawn() ? YES : NO, - bsqReceiveAddress.apply(tradeInfo, showBsqBuyerAddress)); + tradeInfo.getIsWithdrawn() ? YES : NO); } private static final Function priceHeader = (t) -> @@ -181,31 +174,4 @@ public class TradeFormat { ? formatOfferVolume(t.getOffer().getVolume()) : formatXmr(ParsingUtils.centinerosToAtomicUnits(t.getTradeAmountAsLong())); - private static final BiFunction bsqReceiveAddress = (t, showBsqBuyerAddress) -> { - if (showBsqBuyerAddress) { - ContractInfo contract = t.getContract(); - boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker(); - return isBuyerMakerAndSellerTaker // (is XMR buyer / maker) - ? contract.getTakerPaymentAccountPayload().getAddress() - : contract.getMakerPaymentAccountPayload().getAddress(); - } else { - return ""; - } - }; - - private static boolean shouldShowBsqBuyerAddress(TradeInfo tradeInfo, boolean isTaker) { - if (tradeInfo.getOffer().getBaseCurrencyCode().equals("XMR")) { - return false; - } else { - ContractInfo contract = tradeInfo.getContract(); - // Do not forget buyer and seller refer to XMR buyer and seller, not BSQ - // buyer and seller. If you are buying BSQ, you are the XMR seller. - boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker(); - if (isTaker) { - return !isBuyerMakerAndSellerTaker; - } else { - return isBuyerMakerAndSellerTaker; - } - } - } } diff --git a/cli/src/main/java/bisq/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java b/cli/src/main/java/bisq/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java index a37a9f109b..b659a888e9 100644 --- a/cli/src/main/java/bisq/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java @@ -30,10 +30,10 @@ public class CreateCryptoCurrencyPaymentAcctOptionParser extends AbstractMethodO final OptionSpec accountNameOpt = parser.accepts(OPT_ACCOUNT_NAME, "crypto currency account name") .withRequiredArg(); - final OptionSpec currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "crypto currency code (bsq only)") + final OptionSpec currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "crypto currency code") .withRequiredArg(); - final OptionSpec addressOpt = parser.accepts(OPT_ADDRESS, "bsq address") + final OptionSpec addressOpt = parser.accepts(OPT_ADDRESS, "address") .withRequiredArg(); final OptionSpec tradeInstantOpt = parser.accepts(OPT_TRADE_INSTANT, "create trade instant account") @@ -54,16 +54,9 @@ public class CreateCryptoCurrencyPaymentAcctOptionParser extends AbstractMethodO if (!options.has(accountNameOpt) || options.valueOf(accountNameOpt).isEmpty()) throw new IllegalArgumentException("no payment account name specified"); - + if (!options.has(currencyCodeOpt) || options.valueOf(currencyCodeOpt).isEmpty()) throw new IllegalArgumentException("no currency code specified"); - - if (!options.valueOf(currencyCodeOpt).equalsIgnoreCase("bsq")) - throw new IllegalArgumentException("api only supports bsq crypto currency payment accounts"); - - if (!options.has(addressOpt) || options.valueOf(addressOpt).isEmpty()) - throw new IllegalArgumentException("no bsq address specified"); - return this; } diff --git a/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java index 42cf8ad155..6ba53272db 100644 --- a/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java @@ -55,10 +55,6 @@ public class CreateOfferOptionParser extends AbstractMethodOptionParser implemen final OptionSpec securityDepositOpt = parser.accepts(OPT_SECURITY_DEPOSIT, "maker security deposit (%)") .withRequiredArg(); - final OptionSpec makerFeeCurrencyCodeOpt = parser.accepts(OPT_FEE_CURRENCY, "maker fee currency code (bsq|btc)") - .withOptionalArg() - .defaultsTo("btc"); - public CreateOfferOptionParser(String[] args) { super(args); } @@ -137,8 +133,4 @@ public class CreateOfferOptionParser extends AbstractMethodOptionParser implemen public String getSecurityDeposit() { return options.valueOf(securityDepositOpt); } - - public String getMakerFeeCurrencyCode() { - return options.has(makerFeeCurrencyCodeOpt) ? options.valueOf(makerFeeCurrencyCodeOpt) : "btc"; - } } diff --git a/cli/src/main/java/bisq/cli/opts/GetBalanceOptionParser.java b/cli/src/main/java/bisq/cli/opts/GetBalanceOptionParser.java index 206e590c3d..480a85aa16 100644 --- a/cli/src/main/java/bisq/cli/opts/GetBalanceOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/GetBalanceOptionParser.java @@ -25,7 +25,7 @@ import static joptsimple.internal.Strings.EMPTY; public class GetBalanceOptionParser extends AbstractMethodOptionParser implements MethodOpts { - final OptionSpec currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "wallet currency code (bsq|btc)") + final OptionSpec currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "wallet currency code (btc)") .withOptionalArg() .defaultsTo(EMPTY); diff --git a/cli/src/main/java/bisq/cli/opts/SendBsqOptionParser.java b/cli/src/main/java/bisq/cli/opts/SendBsqOptionParser.java deleted file mode 100644 index ad9ab87cbb..0000000000 --- a/cli/src/main/java/bisq/cli/opts/SendBsqOptionParser.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.cli.opts; - - -import joptsimple.OptionSpec; - -import static bisq.cli.opts.OptLabel.OPT_ADDRESS; -import static bisq.cli.opts.OptLabel.OPT_AMOUNT; -import static bisq.cli.opts.OptLabel.OPT_TX_FEE_RATE; -import static joptsimple.internal.Strings.EMPTY; - -public class SendBsqOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec addressOpt = parser.accepts(OPT_ADDRESS, "destination bsq address") - .withRequiredArg(); - - final OptionSpec amountOpt = parser.accepts(OPT_AMOUNT, "amount of bsq to send") - .withRequiredArg(); - - final OptionSpec feeRateOpt = parser.accepts(OPT_TX_FEE_RATE, "optional tx fee rate (sats/byte)") - .withOptionalArg() - .defaultsTo(EMPTY); - - public SendBsqOptionParser(String[] args) { - super(args); - } - - public SendBsqOptionParser parse() { - super.parse(); - - // Short circuit opt validation if user just wants help. - if (options.has(helpOpt)) - return this; - - if (!options.has(addressOpt) || options.valueOf(addressOpt).isEmpty()) - throw new IllegalArgumentException("no bsq address specified"); - - if (!options.has(amountOpt) || options.valueOf(amountOpt).isEmpty()) - throw new IllegalArgumentException("no bsq amount specified"); - - return this; - } - - public String getAddress() { - return options.valueOf(addressOpt); - } - - public String getAmount() { - return options.valueOf(amountOpt); - } - - public String getFeeRate() { - return options.has(feeRateOpt) ? options.valueOf(feeRateOpt) : ""; - } -} diff --git a/cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java index 67fbdd8c86..90e740351b 100644 --- a/cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java @@ -32,10 +32,6 @@ public class TakeOfferOptionParser extends AbstractMethodOptionParser implements final OptionSpec paymentAccountIdOpt = parser.accepts(OPT_PAYMENT_ACCOUNT, "id of payment account used for trade") .withRequiredArg(); - final OptionSpec takerFeeCurrencyCodeOpt = parser.accepts(OPT_FEE_CURRENCY, "taker fee currency code (bsq|btc)") - .withOptionalArg() - .defaultsTo("btc"); - public TakeOfferOptionParser(String[] args) { super(args); } @@ -63,8 +59,4 @@ public class TakeOfferOptionParser extends AbstractMethodOptionParser implements public String getPaymentAccountId() { return options.valueOf(paymentAccountIdOpt); } - - public String getTakerFeeCurrencyCode() { - return options.has(takerFeeCurrencyCodeOpt) ? options.valueOf(takerFeeCurrencyCodeOpt) : "btc"; - } } diff --git a/cli/src/main/java/bisq/cli/opts/VerifyBsqSentToAddressOptionParser.java b/cli/src/main/java/bisq/cli/opts/VerifyBsqSentToAddressOptionParser.java deleted file mode 100644 index c2d5ea2b35..0000000000 --- a/cli/src/main/java/bisq/cli/opts/VerifyBsqSentToAddressOptionParser.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.cli.opts; - - -import joptsimple.OptionSpec; - -import static bisq.cli.opts.OptLabel.OPT_ADDRESS; -import static bisq.cli.opts.OptLabel.OPT_AMOUNT; - -public class VerifyBsqSentToAddressOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec addressOpt = parser.accepts(OPT_ADDRESS, "receiving bsq address") - .withRequiredArg(); - - final OptionSpec amountOpt = parser.accepts(OPT_AMOUNT, "amount of bsq received") - .withRequiredArg(); - - public VerifyBsqSentToAddressOptionParser(String[] args) { - super(args); - } - - public VerifyBsqSentToAddressOptionParser parse() { - super.parse(); - - // Short circuit opt validation if user just wants help. - if (options.has(helpOpt)) - return this; - - if (!options.has(addressOpt) || options.valueOf(addressOpt).isEmpty()) - throw new IllegalArgumentException("no bsq address specified"); - - if (!options.has(amountOpt) || options.valueOf(amountOpt).isEmpty()) - throw new IllegalArgumentException("no bsq amount specified"); - - return this; - } - - public String getAddress() { - return options.valueOf(addressOpt); - } - - public String getAmount() { - return options.valueOf(amountOpt); - } -} diff --git a/cli/src/test/java/bisq/cli/opt/OptionParsersTest.java b/cli/src/test/java/bisq/cli/opt/OptionParsersTest.java index 951b56a5e3..983e3b39fb 100644 --- a/cli/src/test/java/bisq/cli/opt/OptionParsersTest.java +++ b/cli/src/test/java/bisq/cli/opt/OptionParsersTest.java @@ -184,84 +184,4 @@ public class OptionParsersTest { exception.getMessage()); } - // createcryptopaymentacct parser tests - - @Test - public void testCreateCryptoCurrencyPaymentAcctOptionParserWithMissingAcctNameOptShouldThrowException() { - String[] args = new String[]{ - PASSWORD_OPT, - createcryptopaymentacct.name() - }; - Throwable exception = assertThrows(RuntimeException.class, () -> - new CreateCryptoCurrencyPaymentAcctOptionParser(args).parse()); - assertEquals("no payment account name specified", exception.getMessage()); - } - - @Test - public void testCreateCryptoCurrencyPaymentAcctOptionParserWithEmptyAcctNameOptShouldThrowException() { - String[] args = new String[]{ - PASSWORD_OPT, - createcryptopaymentacct.name(), - "--" + OPT_ACCOUNT_NAME - }; - Throwable exception = assertThrows(RuntimeException.class, () -> - new CreateCryptoCurrencyPaymentAcctOptionParser(args).parse()); - assertEquals("account-name requires an argument", exception.getMessage()); - } - - @Test - public void testCreateCryptoCurrencyPaymentAcctOptionParserWithMissingCurrencyCodeOptShouldThrowException() { - String[] args = new String[]{ - PASSWORD_OPT, - createcryptopaymentacct.name(), - "--" + OPT_ACCOUNT_NAME + "=" + "bsq payment account" - }; - Throwable exception = assertThrows(RuntimeException.class, () -> - new CreateCryptoCurrencyPaymentAcctOptionParser(args).parse()); - assertEquals("no currency code specified", exception.getMessage()); - } - - @Test - public void testCreateCryptoCurrencyPaymentAcctOptionParserWithInvalidCurrencyCodeOptShouldThrowException() { - String[] args = new String[]{ - PASSWORD_OPT, - createcryptopaymentacct.name(), - "--" + OPT_ACCOUNT_NAME + "=" + "bsq payment account", - "--" + OPT_CURRENCY_CODE + "=" + "xmr" - }; - Throwable exception = assertThrows(RuntimeException.class, () -> - new CreateCryptoCurrencyPaymentAcctOptionParser(args).parse()); - assertEquals("api only supports bsq crypto currency payment accounts", exception.getMessage()); - } - - @Test - public void testCreateCryptoCurrencyPaymentAcctOptionParserWithMissingAddressOptShouldThrowException() { - String[] args = new String[]{ - PASSWORD_OPT, - createcryptopaymentacct.name(), - "--" + OPT_ACCOUNT_NAME + "=" + "bsq payment account", - "--" + OPT_CURRENCY_CODE + "=" + "bsq" - }; - Throwable exception = assertThrows(RuntimeException.class, () -> - new CreateCryptoCurrencyPaymentAcctOptionParser(args).parse()); - assertEquals("no bsq address specified", exception.getMessage()); - } - - @Test - public void testCreateCryptoCurrencyPaymentAcctOptionParser() { - var acctName = "bsq payment account"; - var currencyCode = "bsq"; - var address = "B1nXyZ"; // address is validated on server - String[] args = new String[]{ - PASSWORD_OPT, - createcryptopaymentacct.name(), - "--" + OPT_ACCOUNT_NAME + "=" + acctName, - "--" + OPT_CURRENCY_CODE + "=" + currencyCode, - "--" + OPT_ADDRESS + "=" + address - }; - var parser = new CreateCryptoCurrencyPaymentAcctOptionParser(args).parse(); - assertEquals(acctName, parser.getAccountName()); - assertEquals(currencyCode, parser.getCurrencyCode()); - assertEquals(address, parser.getAddress()); - } } diff --git a/common/src/main/java/bisq/common/app/Capabilities.java b/common/src/main/java/bisq/common/app/Capabilities.java index d0b3e50a3f..fa06e958f1 100644 --- a/common/src/main/java/bisq/common/app/Capabilities.java +++ b/common/src/main/java/bisq/common/app/Capabilities.java @@ -47,8 +47,7 @@ public class Capabilities { // Defines which most recent capability any node need to support. // This helps to clean network from very old inactive but still running nodes. - @SuppressWarnings("deprecation") - private static final Capability MANDATORY_CAPABILITY = Capability.DAO_STATE; + private static final Capability MANDATORY_CAPABILITY = Capability.TRADE_STATISTICS_3; protected final Set capabilities = new HashSet<>(); diff --git a/common/src/main/java/bisq/common/app/Capability.java b/common/src/main/java/bisq/common/app/Capability.java index 1c9aebd889..c522240ff9 100644 --- a/common/src/main/java/bisq/common/app/Capability.java +++ b/common/src/main/java/bisq/common/app/Capability.java @@ -28,11 +28,11 @@ public enum Capability { @Deprecated TRADE_STATISTICS_2, // Not required anymore as no old clients out there not having that support @Deprecated ACCOUNT_AGE_WITNESS, // Not required anymore as no old clients out there not having that support SEED_NODE, // Node is a seed node - DAO_FULL_NODE, // DAO full node can deliver BSQ blocks + @Deprecated DAO_FULL_NODE, // DAO full node can deliver BSQ blocks @Deprecated PROPOSAL, // Not required anymore as no old clients out there not having that support @Deprecated BLIND_VOTE, // Not required anymore as no old clients out there not having that support @Deprecated ACK_MSG, // Not required anymore as no old clients out there not having that support - RECEIVE_BSQ_BLOCK, // Signaling that node which wants to receive BSQ blocks (DAO lite node) + @Deprecated RECEIVE_BSQ_BLOCK, // Signaling that node which wants to receive BSQ blocks (DAO lite node) @Deprecated DAO_STATE, // Not required anymore as no old clients out there not having that support @Deprecated BUNDLE_OF_ENVELOPES, // Supports bundling of messages if many messages are sent in short interval diff --git a/common/src/main/java/bisq/common/app/DevEnv.java b/common/src/main/java/bisq/common/app/DevEnv.java index 9bf852d97b..eb79c535f1 100644 --- a/common/src/main/java/bisq/common/app/DevEnv.java +++ b/common/src/main/java/bisq/common/app/DevEnv.java @@ -34,7 +34,6 @@ public class DevEnv { public static void setup(Config config) { DevEnv.setDevMode(config.useDevMode); - DevEnv.setDaoActivated(config.daoActivated); } // If set to true we ignore several UI behavior like confirmation popups as well dummy accounts are created and @@ -49,23 +48,10 @@ public class DevEnv { DevEnv.devMode = devMode; } - private static boolean daoActivated = true; - - public static boolean isDaoActivated() { - return daoActivated; - } - - public static void setDaoActivated(boolean daoActivated) { - DevEnv.daoActivated = daoActivated; - } - public static void logErrorAndThrowIfDevMode(String msg) { log.error(msg); if (devMode) throw new RuntimeException(msg); } - public static boolean isDaoTradingActivated() { - return true; - } } diff --git a/common/src/main/java/bisq/common/app/Version.java b/common/src/main/java/bisq/common/app/Version.java index 68ad28ed10..05eecfc15c 100644 --- a/common/src/main/java/bisq/common/app/Version.java +++ b/common/src/main/java/bisq/common/app/Version.java @@ -102,8 +102,6 @@ public class Version { public static final int TRADE_PROTOCOL_VERSION = 3; private static int p2pMessageVersion; - public static final String BSQ_TX_VERSION = "1"; - public static int getP2PMessageVersion() { return p2pMessageVersion; } diff --git a/common/src/main/java/bisq/common/config/BaseCurrencyNetwork.java b/common/src/main/java/bisq/common/config/BaseCurrencyNetwork.java index a44ac4b265..13c5ad20bb 100644 --- a/common/src/main/java/bisq/common/config/BaseCurrencyNetwork.java +++ b/common/src/main/java/bisq/common/config/BaseCurrencyNetwork.java @@ -28,10 +28,7 @@ import lombok.Getter; public enum BaseCurrencyNetwork { XMR_MAINNET(new XmrMainNetParams(), "XMR", "MAINNET", "Monero"), // TODO (woodser): network params are part of bitcoinj and shouldn't be needed. only used to get MonetaryFormat? replace with MonetaryFormat if so XMR_TESTNET(new XmrTestNet3Params(), "XMR", "TESTNET", "Monero"), - XMR_STAGENET(new XmrRegTestParams(), "XMR", "STAGENET", "Monero"), - BTC_DAO_TESTNET(RegTestParams.get(), "XMR", "STAGENET", "Monero"), - BTC_DAO_BETANET(MainNetParams.get(), "XMR", "MAINNET", "Monero"), // mainnet test genesis - BTC_DAO_REGTEST(RegTestParams.get(), "XMR", "STAGENET", "Monero"); + XMR_STAGENET(new XmrRegTestParams(), "XMR", "STAGENET", "Monero"); @Getter private final NetworkParameters parameters; @@ -57,18 +54,6 @@ public enum BaseCurrencyNetwork { return "XMR_TESTNET".equals(name()); } - public boolean isDaoTestNet() { - return "BTC_DAO_TESTNET".equals(name()); - } - - public boolean isDaoRegTest() { - return "BTC_DAO_REGTEST".equals(name()); - } - - public boolean isDaoBetaNet() { - return "BTC_DAO_BETANET".equals(name()); - } - public boolean isStagenet() { return "XMR_STAGENET".equals(name()); } diff --git a/common/src/main/java/bisq/common/config/Config.java b/common/src/main/java/bisq/common/config/Config.java index f03bd2537c..4b13fbd86c 100644 --- a/common/src/main/java/bisq/common/config/Config.java +++ b/common/src/main/java/bisq/common/config/Config.java @@ -103,18 +103,6 @@ public class Config { public static final String USE_ALL_PROVIDED_NODES = "useAllProvidedNodes"; public static final String USER_AGENT = "userAgent"; public static final String NUM_CONNECTIONS_FOR_BTC = "numConnectionsForBtc"; - public static final String RPC_USER = "rpcUser"; - public static final String RPC_PASSWORD = "rpcPassword"; - public static final String RPC_HOST = "rpcHost"; - public static final String RPC_PORT = "rpcPort"; - public static final String RPC_BLOCK_NOTIFICATION_PORT = "rpcBlockNotificationPort"; - public static final String RPC_BLOCK_NOTIFICATION_HOST = "rpcBlockNotificationHost"; - public static final String DUMP_BLOCKCHAIN_DATA = "dumpBlockchainData"; - public static final String FULL_DAO_NODE = "fullDaoNode"; - public static final String GENESIS_TX_ID = "genesisTxId"; - public static final String GENESIS_BLOCK_HEIGHT = "genesisBlockHeight"; - public static final String GENESIS_TOTAL_SUPPLY = "genesisTotalSupply"; - public static final String DAO_ACTIVATED = "daoActivated"; public static final String DUMP_DELAYED_PAYOUT_TXS = "dumpDelayedPayoutTxs"; public static final String ALLOW_FAULTY_DELAYED_TXS = "allowFaultyDelayedTxs"; public static final String API_PASSWORD = "apiPassword"; @@ -130,7 +118,6 @@ public class Config { public static final int UNSPECIFIED_PORT = -1; public static final String DEFAULT_REGTEST_HOST = "localhost"; public static final int DEFAULT_NUM_CONNECTIONS_FOR_BTC = 9; // down from BitcoinJ default of 12 - public static final boolean DEFAULT_FULL_DAO_NODE = false; static final String DEFAULT_CONFIG_FILE_NAME = "bisq.properties"; // Static fields that provide access to Config properties in locations where injecting @@ -163,7 +150,6 @@ public class Config { public final NetworkParameters networkParameters; public final boolean ignoreLocalBtcNode; public final String bitcoinRegtestHost; - public final boolean daoActivated; public final String referralId; public final boolean useDevMode; public final boolean useDevModeHeader; @@ -195,18 +181,6 @@ public class Config { public final boolean useAllProvidedNodes; public final String userAgent; public final int numConnectionsForBtc; - public final String rpcUser; - public final String rpcPassword; - public final String rpcHost; - public final int rpcPort; - public final int rpcBlockNotificationPort; - public final String rpcBlockNotificationHost; - public final boolean dumpBlockchainData; - public final boolean fullDaoNode; - public final boolean fullDaoNodeOptionSetExplicitly; - public final String genesisTxId; - public final int genesisBlockHeight; - public final long genesisTotalSupply; public final boolean dumpDelayedPayoutTxs; public final boolean allowFaultyDelayedTxs; public final String apiPassword; @@ -552,78 +526,6 @@ public class Config { .ofType(int.class) .defaultsTo(DEFAULT_NUM_CONNECTIONS_FOR_BTC); - ArgumentAcceptingOptionSpec rpcUserOpt = - parser.accepts(RPC_USER, "Bitcoind rpc username") - .withRequiredArg() - .defaultsTo(""); - - ArgumentAcceptingOptionSpec rpcPasswordOpt = - parser.accepts(RPC_PASSWORD, "Bitcoind rpc password") - .withRequiredArg() - .defaultsTo(""); - - ArgumentAcceptingOptionSpec rpcHostOpt = - parser.accepts(RPC_HOST, "Bitcoind rpc host") - .withRequiredArg() - .defaultsTo(""); - - ArgumentAcceptingOptionSpec rpcPortOpt = - parser.accepts(RPC_PORT, "Bitcoind rpc port") - .withRequiredArg() - .ofType(int.class) - .defaultsTo(UNSPECIFIED_PORT); - - ArgumentAcceptingOptionSpec rpcBlockNotificationPortOpt = - parser.accepts(RPC_BLOCK_NOTIFICATION_PORT, "Bitcoind rpc port for block notifications") - .withRequiredArg() - .ofType(int.class) - .defaultsTo(UNSPECIFIED_PORT); - - ArgumentAcceptingOptionSpec rpcBlockNotificationHostOpt = - parser.accepts(RPC_BLOCK_NOTIFICATION_HOST, - "Bitcoind rpc accepted incoming host for block notifications") - .withRequiredArg() - .defaultsTo(""); - - ArgumentAcceptingOptionSpec dumpBlockchainDataOpt = - parser.accepts(DUMP_BLOCKCHAIN_DATA, "If set to true the blockchain data " + - "from RPC requests to Bitcoin Core are stored as json file in the data dir.") - .withRequiredArg() - .ofType(boolean.class) - .defaultsTo(false); - - ArgumentAcceptingOptionSpec fullDaoNodeOpt = - parser.accepts(FULL_DAO_NODE, "If set to true the node requests the blockchain data via RPC requests " + - "from Bitcoin Core and provide the validated BSQ txs to the network. It requires that the " + - "other RPC properties are set as well.") - .withRequiredArg() - .ofType(Boolean.class) - .defaultsTo(DEFAULT_FULL_DAO_NODE); - - ArgumentAcceptingOptionSpec genesisTxIdOpt = - parser.accepts(GENESIS_TX_ID, "Genesis transaction ID when not using the hard coded one") - .withRequiredArg() - .defaultsTo(""); - - ArgumentAcceptingOptionSpec genesisBlockHeightOpt = - parser.accepts(GENESIS_BLOCK_HEIGHT, - "Genesis transaction block height when not using the hard coded one") - .withRequiredArg() - .ofType(int.class) - .defaultsTo(-1); - - ArgumentAcceptingOptionSpec genesisTotalSupplyOpt = - parser.accepts(GENESIS_TOTAL_SUPPLY, "Genesis total supply when not using the hard coded one") - .withRequiredArg() - .ofType(long.class) - .defaultsTo(-1L); - - ArgumentAcceptingOptionSpec daoActivatedOpt = - parser.accepts(DAO_ACTIVATED, "Developer flag. If true it enables dao phase 2 features.") - .withRequiredArg() - .ofType(boolean.class) - .defaultsTo(true); - ArgumentAcceptingOptionSpec dumpDelayedPayoutTxsOpt = parser.accepts(DUMP_DELAYED_PAYOUT_TXS, "Dump delayed payout transactions to file") .withRequiredArg() @@ -767,19 +669,7 @@ public class Config { this.useAllProvidedNodes = options.valueOf(useAllProvidedNodesOpt); this.userAgent = options.valueOf(userAgentOpt); this.numConnectionsForBtc = options.valueOf(numConnectionsForBtcOpt); - this.rpcUser = options.valueOf(rpcUserOpt); - this.rpcPassword = options.valueOf(rpcPasswordOpt); - this.rpcHost = options.valueOf(rpcHostOpt); - this.rpcPort = options.valueOf(rpcPortOpt); - this.rpcBlockNotificationPort = options.valueOf(rpcBlockNotificationPortOpt); - this.rpcBlockNotificationHost = options.valueOf(rpcBlockNotificationHostOpt); - this.dumpBlockchainData = options.valueOf(dumpBlockchainDataOpt); - this.fullDaoNode = options.valueOf(fullDaoNodeOpt); - this.fullDaoNodeOptionSetExplicitly = options.has(fullDaoNodeOpt); - this.genesisTxId = options.valueOf(genesisTxIdOpt); - this.genesisBlockHeight = options.valueOf(genesisBlockHeightOpt); - this.genesisTotalSupply = options.valueOf(genesisTotalSupplyOpt); - this.daoActivated = options.valueOf(daoActivatedOpt); + this.dumpDelayedPayoutTxs = options.valueOf(dumpDelayedPayoutTxsOpt); this.allowFaultyDelayedTxs = options.valueOf(allowFaultyDelayedTxsOpt); this.apiPassword = options.valueOf(apiPasswordOpt); diff --git a/common/src/main/java/bisq/common/persistence/PersistenceManager.java b/common/src/main/java/bisq/common/persistence/PersistenceManager.java index 04b5a95ac2..9d76bc043e 100644 --- a/common/src/main/java/bisq/common/persistence/PersistenceManager.java +++ b/common/src/main/java/bisq/common/persistence/PersistenceManager.java @@ -67,8 +67,7 @@ import static com.google.common.base.Preconditions.checkNotNull; * with the modified model that data is written at shut down we eliminate frequent and expensive disk I/O. Risks of * deadlock or data inconsistency and a more complex model have been a further argument for that model. In fact * previously we wasted a lot of resources as way too many threads have been created without doing actual work as well - * the write operations got triggered way too often specially for the very frequent changes at SequenceNumberMap and - * the very large DaoState (at dao blockchain sync that slowed down sync). + * the write operations got triggered way too often specially for the very frequent changes at SequenceNumberMap * * * @param The type of the {@link PersistableEnvelope} to be written or read from disk diff --git a/common/src/test/java/bisq/common/app/CapabilitiesTest.java b/common/src/test/java/bisq/common/app/CapabilitiesTest.java index f939ff9cd8..0dc107c976 100644 --- a/common/src/test/java/bisq/common/app/CapabilitiesTest.java +++ b/common/src/test/java/bisq/common/app/CapabilitiesTest.java @@ -23,7 +23,6 @@ import java.util.HashSet; import org.junit.Test; -import static bisq.common.app.Capability.DAO_FULL_NODE; import static bisq.common.app.Capability.SEED_NODE; import static bisq.common.app.Capability.TRADE_STATISTICS; import static bisq.common.app.Capability.TRADE_STATISTICS_2; @@ -46,17 +45,12 @@ public class CapabilitiesTest { assertTrue(new Capabilities().hasLess(new Capabilities(SEED_NODE))); assertFalse(new Capabilities().hasLess(new Capabilities())); assertFalse(new Capabilities(SEED_NODE).hasLess(new Capabilities())); - assertTrue(new Capabilities(SEED_NODE).hasLess(new Capabilities(DAO_FULL_NODE))); - assertFalse(new Capabilities(DAO_FULL_NODE).hasLess(new Capabilities(SEED_NODE))); Capabilities all = new Capabilities( TRADE_STATISTICS, TRADE_STATISTICS_2, Capability.ACCOUNT_AGE_WITNESS, Capability.ACK_MSG, - Capability.PROPOSAL, - Capability.BLIND_VOTE, - Capability.DAO_STATE, Capability.BUNDLE_OF_ENVELOPES, Capability.MEDIATION, Capability.SIGNED_ACCOUNT_AGE_WITNESS, @@ -68,9 +62,6 @@ public class CapabilitiesTest { TRADE_STATISTICS_2, Capability.ACCOUNT_AGE_WITNESS, Capability.ACK_MSG, - Capability.PROPOSAL, - Capability.BLIND_VOTE, - Capability.DAO_STATE, Capability.BUNDLE_OF_ENVELOPES, Capability.MEDIATION, Capability.SIGNED_ACCOUNT_AGE_WITNESS, diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java index 20261a5b2e..54672ad82f 100644 --- a/core/src/main/java/bisq/core/api/CoreApi.java +++ b/core/src/main/java/bisq/core/api/CoreApi.java @@ -20,7 +20,6 @@ package bisq.core.api; import bisq.core.api.model.AddressBalanceInfo; import bisq.core.api.model.BalancesInfo; import bisq.core.api.model.TxFeeRateInfo; -import bisq.core.btc.wallet.TxBroadcaster; import bisq.core.monetary.Price; import bisq.core.offer.Offer; import bisq.core.offer.OfferPayload; @@ -148,7 +147,6 @@ public class CoreApi { double buyerSecurityDeposit, long triggerPrice, String paymentAccountId, - String makerFeeCurrencyCode, Consumer resultHandler) { coreOffersService.createAndPlaceOffer(currencyCode, directionAsString, @@ -160,7 +158,6 @@ public class CoreApi { buyerSecurityDeposit, triggerPrice, paymentAccountId, - makerFeeCurrencyCode, resultHandler); } @@ -238,13 +235,11 @@ public class CoreApi { public void takeOffer(String offerId, String paymentAccountId, - String takerFeeCurrencyCode, Consumer resultHandler, ErrorMessageHandler errorMessageHandler) { Offer offer = coreOffersService.getOffer(offerId); coreTradesService.takeOffer(offer, paymentAccountId, - takerFeeCurrencyCode, resultHandler, errorMessageHandler); } @@ -297,17 +292,6 @@ public class CoreApi { return walletsService.getFundingAddresses(); } - public String getUnusedBsqAddress() { - return walletsService.getUnusedBsqAddress(); - } - - public void sendBsq(String address, - String amount, - String txFeeRate, - TxBroadcaster.Callback callback) { - walletsService.sendBsq(address, amount, txFeeRate, callback); - } - public void sendBtc(String address, String amount, String txFeeRate, @@ -316,9 +300,6 @@ public class CoreApi { walletsService.sendBtc(address, amount, txFeeRate, memo, callback); } - public boolean verifyBsqSentToAddress(String address, String amount) { - return walletsService.verifyBsqSentToAddress(address, amount); - } public void getTxFeeRate(ResultHandler resultHandler) { walletsService.getTxFeeRate(resultHandler); diff --git a/core/src/main/java/bisq/core/api/CoreDisputeAgentsService.java b/core/src/main/java/bisq/core/api/CoreDisputeAgentsService.java index 7b060834a9..79339e93aa 100644 --- a/core/src/main/java/bisq/core/api/CoreDisputeAgentsService.java +++ b/core/src/main/java/bisq/core/api/CoreDisputeAgentsService.java @@ -82,7 +82,6 @@ class CoreDisputeAgentsService { throw new IllegalStateException("p2p service is not bootstrapped yet"); if (config.baseCurrencyNetwork.isMainnet() - || config.baseCurrencyNetwork.isDaoBetaNet() || !config.useLocalhostForP2P) throw new IllegalStateException("dispute agents must be registered in a Bisq UI"); diff --git a/core/src/main/java/bisq/core/api/CoreHelpService.java b/core/src/main/java/bisq/core/api/CoreHelpService.java index 6a4605f748..26644baa0c 100644 --- a/core/src/main/java/bisq/core/api/CoreHelpService.java +++ b/core/src/main/java/bisq/core/api/CoreHelpService.java @@ -72,7 +72,6 @@ class CoreHelpService { out.println(coreHelpService.getMethodHelp("getversion")); // out.println(coreHelpService.getMethodHelp("getfundingaddresses")); // out.println(coreHelpService.getMethodHelp("getfundingaddresses")); - // out.println(coreHelpService.getMethodHelp("getunusedbsqaddress")); // out.println(coreHelpService.getMethodHelp("unsettxfeerate")); // out.println(coreHelpService.getMethodHelp("getpaymentmethods")); // out.println(coreHelpService.getMethodHelp("getpaymentaccts")); diff --git a/core/src/main/java/bisq/core/api/CoreOffersService.java b/core/src/main/java/bisq/core/api/CoreOffersService.java index 6580e9592d..6fc3bb6ebf 100644 --- a/core/src/main/java/bisq/core/api/CoreOffersService.java +++ b/core/src/main/java/bisq/core/api/CoreOffersService.java @@ -200,11 +200,9 @@ class CoreOffersService { double buyerSecurityDeposit, long triggerPrice, String paymentAccountId, - String makerFeeCurrencyCode, Consumer resultHandler) { coreWalletsService.verifyWalletsAreAvailable(); coreWalletsService.verifyEncryptedWalletIsUnlocked(); - offerUtil.maybeSetFeePaymentCurrencyPreference(makerFeeCurrencyCode); PaymentAccount paymentAccount = user.getPaymentAccount(paymentAccountId); if (paymentAccount == null) diff --git a/core/src/main/java/bisq/core/api/CorePaymentAccountsService.java b/core/src/main/java/bisq/core/api/CorePaymentAccountsService.java index 0843e20ab7..2d0dc59e04 100644 --- a/core/src/main/java/bisq/core/api/CorePaymentAccountsService.java +++ b/core/src/main/java/bisq/core/api/CorePaymentAccountsService.java @@ -19,7 +19,6 @@ package bisq.core.api; import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.api.model.PaymentAccountForm; -import bisq.core.locale.CryptoCurrency; import bisq.core.payment.CryptoCurrencyAccount; import bisq.core.payment.InstantCryptoCurrencyAccount; import bisq.core.payment.PaymentAccount; @@ -34,13 +33,11 @@ import java.io.File; import java.util.Comparator; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; -import static bisq.core.locale.CurrencyUtil.getCryptoCurrency; import static java.lang.String.format; @Singleton @@ -103,21 +100,12 @@ class CorePaymentAccountsService { String currencyCode, String address, boolean tradeInstant) { - String bsqCode = currencyCode.toUpperCase(); - if (!bsqCode.equals("BSQ")) - throw new IllegalArgumentException("api does not currently support " + currencyCode + " accounts"); - - // Validate the BSQ address string but ignore the return value. - coreWalletsService.getValidBsqLegacyAddress(address); - var cryptoCurrencyAccount = tradeInstant ? (InstantCryptoCurrencyAccount) PaymentAccountFactory.getPaymentAccount(PaymentMethod.BLOCK_CHAINS_INSTANT) : (CryptoCurrencyAccount) PaymentAccountFactory.getPaymentAccount(PaymentMethod.BLOCK_CHAINS); cryptoCurrencyAccount.init(); cryptoCurrencyAccount.setAccountName(accountName); cryptoCurrencyAccount.setAddress(address); - Optional cryptoCurrency = getCryptoCurrency(bsqCode); - cryptoCurrency.ifPresent(cryptoCurrencyAccount::setSingleTradeCurrency); user.addPaymentAccount(cryptoCurrencyAccount); accountAgeWitnessService.publishMyAccountAgeWitness(cryptoCurrencyAccount.getPaymentAccountPayload()); log.info("Saved crypto payment account with id {} and payment method {}.", diff --git a/core/src/main/java/bisq/core/api/CoreTradesService.java b/core/src/main/java/bisq/core/api/CoreTradesService.java index 9d93994faa..145b128464 100644 --- a/core/src/main/java/bisq/core/api/CoreTradesService.java +++ b/core/src/main/java/bisq/core/api/CoreTradesService.java @@ -87,14 +87,11 @@ class CoreTradesService { void takeOffer(Offer offer, String paymentAccountId, - String takerFeeCurrencyCode, Consumer resultHandler, ErrorMessageHandler errorMessageHandler) { coreWalletsService.verifyWalletsAreAvailable(); coreWalletsService.verifyEncryptedWalletIsUnlocked(); - offerUtil.maybeSetFeePaymentCurrencyPreference(takerFeeCurrencyCode); - var paymentAccount = user.getPaymentAccount(paymentAccountId); if (paymentAccount == null) throw new IllegalArgumentException(format("payment account with id '%s' not found", paymentAccountId)); diff --git a/core/src/main/java/bisq/core/api/CoreWalletsService.java b/core/src/main/java/bisq/core/api/CoreWalletsService.java index 8a096ee1df..077d67fbdc 100644 --- a/core/src/main/java/bisq/core/api/CoreWalletsService.java +++ b/core/src/main/java/bisq/core/api/CoreWalletsService.java @@ -19,22 +19,17 @@ package bisq.core.api; import bisq.core.api.model.AddressBalanceInfo; import bisq.core.api.model.BalancesInfo; -import bisq.core.api.model.BsqBalanceInfo; import bisq.core.api.model.BtcBalanceInfo; import bisq.core.api.model.TxFeeRateInfo; import bisq.core.api.model.XmrBalanceInfo; import bisq.core.app.AppStartupState; import bisq.core.btc.Balances; import bisq.core.btc.exceptions.AddressEntryException; -import bisq.core.btc.exceptions.BsqChangeBelowDustException; import bisq.core.btc.exceptions.InsufficientFundsException; import bisq.core.btc.exceptions.TransactionVerificationException; import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.model.AddressEntry; -import bisq.core.btc.model.BsqTransferModel; import bisq.core.btc.setup.WalletsSetup; -import bisq.core.btc.wallet.BsqTransferService; -import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.TxBroadcaster; import bisq.core.btc.wallet.WalletsManager; @@ -42,7 +37,6 @@ import bisq.core.btc.wallet.XmrWalletService; import bisq.core.provider.fee.FeeService; import bisq.core.user.Preferences; import bisq.core.util.FormattingUtils; -import bisq.core.util.coin.BsqFormatter; import bisq.core.util.coin.CoinFormatter; import bisq.common.Timer; @@ -100,9 +94,6 @@ class CoreWalletsService { private final Balances balances; private final WalletsManager walletsManager; private final WalletsSetup walletsSetup; - private final BsqWalletService bsqWalletService; - private final BsqTransferService bsqTransferService; - private final BsqFormatter bsqFormatter; private final BtcWalletService btcWalletService; private final XmrWalletService xmrWalletService; private final CoinFormatter btcFormatter; @@ -123,9 +114,6 @@ class CoreWalletsService { Balances balances, WalletsManager walletsManager, WalletsSetup walletsSetup, - BsqWalletService bsqWalletService, - BsqTransferService bsqTransferService, - BsqFormatter bsqFormatter, BtcWalletService btcWalletService, XmrWalletService xmrWalletService, @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter, @@ -136,9 +124,6 @@ class CoreWalletsService { this.balances = balances; this.walletsManager = walletsManager; this.walletsSetup = walletsSetup; - this.bsqWalletService = bsqWalletService; - this.bsqTransferService = bsqTransferService; - this.bsqFormatter = bsqFormatter; this.btcWalletService = btcWalletService; this.xmrWalletService = xmrWalletService; this.btcFormatter = btcFormatter; @@ -164,14 +149,12 @@ class CoreWalletsService { throw new IllegalStateException("balance is not yet available"); switch (currencyCode.trim().toUpperCase()) { - case "BSQ": - return new BalancesInfo(getBsqBalances(), BtcBalanceInfo.EMPTY, XmrBalanceInfo.EMPTY); case "BTC": - return new BalancesInfo(BsqBalanceInfo.EMPTY, getBtcBalances(), XmrBalanceInfo.EMPTY); + return new BalancesInfo(getBtcBalances(), XmrBalanceInfo.EMPTY); case "XMR": - return new BalancesInfo(BsqBalanceInfo.EMPTY, BtcBalanceInfo.EMPTY, getXmrBalances()); + return new BalancesInfo(BtcBalanceInfo.EMPTY, getXmrBalances()); default: - return new BalancesInfo(getBsqBalances(), getBtcBalances(), getXmrBalances()); + return new BalancesInfo(getBtcBalances(), getXmrBalances()); } } @@ -232,40 +215,6 @@ class CoreWalletsService { .collect(Collectors.toList()); } - String getUnusedBsqAddress() { - return bsqWalletService.getUnusedBsqAddressAsString(); - } - - void sendBsq(String address, - String amount, - String txFeeRate, - TxBroadcaster.Callback callback) { - verifyWalletsAreAvailable(); - verifyEncryptedWalletIsUnlocked(); - - try { - LegacyAddress legacyAddress = getValidBsqLegacyAddress(address); - Coin receiverAmount = getValidTransferAmount(amount, bsqFormatter); - Coin txFeePerVbyte = getTxFeeRateFromParamOrPreferenceOrFeeService(txFeeRate); - BsqTransferModel model = bsqTransferService.getBsqTransferModel(legacyAddress, - receiverAmount, - txFeePerVbyte); - log.info("Sending {} BSQ to {} with tx fee rate {} sats/byte.", - amount, - address, - txFeePerVbyte.value); - bsqTransferService.sendFunds(model, callback); - } catch (InsufficientMoneyException ex) { - log.error("", ex); - throw new IllegalStateException("cannot send bsq due to insufficient funds", ex); - } catch (NumberFormatException - | BsqChangeBelowDustException - | TransactionVerificationException - | WalletException ex) { - log.error("", ex); - throw new IllegalStateException(ex); - } - } void sendBtc(String address, String amount, @@ -322,40 +271,6 @@ class CoreWalletsService { } } - boolean verifyBsqSentToAddress(String address, String amount) { - Address receiverAddress = getValidBsqLegacyAddress(address); - NetworkParameters networkParameters = getNetworkParameters(); - Predicate isTxOutputAddressMatch = (txOut) -> - txOut.getScriptPubKey().getToAddress(networkParameters).equals(receiverAddress); - Coin coinValue = parseToCoin(amount, bsqFormatter); - Predicate isTxOutputValueMatch = (txOut) -> - txOut.getValue().longValue() == coinValue.longValue(); - List spendableBsqTxOutputs = bsqWalletService.getSpendableBsqTransactionOutputs(); - - log.info("Searching {} spendable tx outputs for matching address {} and value {}:", - spendableBsqTxOutputs.size(), - address, - coinValue.toPlainString()); - long numMatches = 0; - for (TransactionOutput txOut : spendableBsqTxOutputs) { - if (isTxOutputAddressMatch.test(txOut) && isTxOutputValueMatch.test(txOut)) { - log.info("\t\tTx {} output has matching address {} and value {}.", - txOut.getParentTransaction().getTxId(), - address, - txOut.getValue().toPlainString()); - numMatches++; - } - } - if (numMatches > 1) { - log.warn("{} tx outputs matched address {} and value {}, could be a" - + " false positive BSQ payment verification result.", - numMatches, - address, - coinValue.toPlainString()); - - } - return numMatches > 0; - } void getTxFeeRate(ResultHandler resultHandler) { try { @@ -561,23 +476,13 @@ class CoreWalletsService { throw new IllegalStateException("server is not fully initialized"); } - // Returns a LegacyAddress for the string, or a RuntimeException if invalid. - LegacyAddress getValidBsqLegacyAddress(String address) { - try { - return bsqFormatter.getAddressFromBsqAddress(address); - } catch (Throwable t) { - log.error("", t); - throw new IllegalStateException(format("%s is not a valid bsq address", address)); - } - } - // Throws a RuntimeException if wallet currency code is not BSQ or BTC. + // Throws a RuntimeException if wallet currency code is not BTC. private void verifyWalletCurrencyCodeIsValid(String currencyCode) { if (currencyCode == null || currencyCode.isEmpty()) return; - if (!currencyCode.equalsIgnoreCase("BSQ") - && !currencyCode.equalsIgnoreCase("BTC")) + if (!currencyCode.equalsIgnoreCase("BTC")) throw new IllegalStateException(format("wallet does not support %s", currencyCode)); } @@ -587,31 +492,13 @@ class CoreWalletsService { if (tempAesKey == null) throw new IllegalStateException("cannot use null key, unlockwallet timeout may have expired"); - if (btcWalletService.getAesKey() == null || bsqWalletService.getAesKey() == null) { + if (btcWalletService.getAesKey() == null) { KeyParameter aesKey = new KeyParameter(tempAesKey.getKey()); walletsManager.setAesKey(aesKey); walletsSetup.getWalletConfig().maybeAddSegwitKeychain(walletsSetup.getWalletConfig().btcWallet(), aesKey); } } - private BsqBalanceInfo getBsqBalances() { - verifyWalletsAreAvailable(); - verifyEncryptedWalletIsUnlocked(); - - var availableConfirmedBalance = bsqWalletService.getAvailableConfirmedBalance(); - var unverifiedBalance = bsqWalletService.getUnverifiedBalance(); - var unconfirmedChangeBalance = bsqWalletService.getUnconfirmedChangeBalance(); - var lockedForVotingBalance = bsqWalletService.getLockedForVotingBalance(); - var lockupBondsBalance = bsqWalletService.getLockupBondsBalance(); - var unlockingBondsBalance = bsqWalletService.getUnlockingBondsBalance(); - - return new BsqBalanceInfo(availableConfirmedBalance.value, - unverifiedBalance.value, - unconfirmedChangeBalance.value, - lockedForVotingBalance.value, - lockupBondsBalance.value, - unlockingBondsBalance.value); - } // TODO (woodser): delete this since it's serving XMR balances private BtcBalanceInfo getBtcBalances() { diff --git a/core/src/main/java/bisq/core/api/model/BalancesInfo.java b/core/src/main/java/bisq/core/api/model/BalancesInfo.java index fb15b0a7e9..7d36e5a16a 100644 --- a/core/src/main/java/bisq/core/api/model/BalancesInfo.java +++ b/core/src/main/java/bisq/core/api/model/BalancesInfo.java @@ -10,12 +10,10 @@ public class BalancesInfo implements Payload { // Getter names are shortened for readability's sake, i.e., // balancesInfo.getBtc().getAvailableBalance() is cleaner than // balancesInfo.getBtcBalanceInfo().getAvailableBalance(). - private final BsqBalanceInfo bsq; private final BtcBalanceInfo btc; private final XmrBalanceInfo xmr; - public BalancesInfo(BsqBalanceInfo bsq, BtcBalanceInfo btc, XmrBalanceInfo xmr) { - this.bsq = bsq; + public BalancesInfo(BtcBalanceInfo btc, XmrBalanceInfo xmr) { this.btc = btc; this.xmr = xmr; } @@ -27,14 +25,13 @@ public class BalancesInfo implements Payload { @Override public bisq.proto.grpc.BalancesInfo toProtoMessage() { return bisq.proto.grpc.BalancesInfo.newBuilder() - .setBsq(bsq.toProtoMessage()) .setBtc(btc.toProtoMessage()) .setXmr(xmr.toProtoMessage()) .build(); } public static BalancesInfo fromProto(bisq.proto.grpc.BalancesInfo proto) { - return new BalancesInfo(BsqBalanceInfo.fromProto(proto.getBsq()), + return new BalancesInfo( BtcBalanceInfo.fromProto(proto.getBtc()), XmrBalanceInfo.fromProto(proto.getXmr())); } @@ -42,8 +39,7 @@ public class BalancesInfo implements Payload { @Override public String toString() { return "BalancesInfo{" + "\n" + - " " + bsq.toString() + "\n" + - ", " + btc.toString() + "\n" + + " " + btc.toString() + "\n" + ", " + xmr.toString() + "\n" + '}'; } diff --git a/core/src/main/java/bisq/core/api/model/BsqBalanceInfo.java b/core/src/main/java/bisq/core/api/model/BsqBalanceInfo.java deleted file mode 100644 index 23324e21f3..0000000000 --- a/core/src/main/java/bisq/core/api/model/BsqBalanceInfo.java +++ /dev/null @@ -1,94 +0,0 @@ -package bisq.core.api.model; - -import bisq.common.Payload; - -import com.google.common.annotations.VisibleForTesting; - -import lombok.Getter; - -@Getter -public class BsqBalanceInfo implements Payload { - - public static final BsqBalanceInfo EMPTY = new BsqBalanceInfo(-1, - -1, - -1, - -1, - -1, - -1); - - // All balances are in BSQ satoshis. - private final long availableConfirmedBalance; - private final long unverifiedBalance; - private final long unconfirmedChangeBalance; - private final long lockedForVotingBalance; - private final long lockupBondsBalance; - private final long unlockingBondsBalance; - - public BsqBalanceInfo(long availableConfirmedBalance, - long unverifiedBalance, - long unconfirmedChangeBalance, - long lockedForVotingBalance, - long lockupBondsBalance, - long unlockingBondsBalance) { - this.availableConfirmedBalance = availableConfirmedBalance; - this.unverifiedBalance = unverifiedBalance; - this.unconfirmedChangeBalance = unconfirmedChangeBalance; - this.lockedForVotingBalance = lockedForVotingBalance; - this.lockupBondsBalance = lockupBondsBalance; - this.unlockingBondsBalance = unlockingBondsBalance; - } - - @VisibleForTesting - public static BsqBalanceInfo valueOf(long availableConfirmedBalance, - long unverifiedBalance, - long unconfirmedChangeBalance, - long lockedForVotingBalance, - long lockupBondsBalance, - long unlockingBondsBalance) { - // Convenience for creating a model instance instead of a proto. - return new BsqBalanceInfo(availableConfirmedBalance, - unverifiedBalance, - unconfirmedChangeBalance, - lockedForVotingBalance, - lockupBondsBalance, - unlockingBondsBalance); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public bisq.proto.grpc.BsqBalanceInfo toProtoMessage() { - return bisq.proto.grpc.BsqBalanceInfo.newBuilder() - .setAvailableConfirmedBalance(availableConfirmedBalance) - .setUnverifiedBalance(unverifiedBalance) - .setUnconfirmedChangeBalance(unconfirmedChangeBalance) - .setLockedForVotingBalance(lockedForVotingBalance) - .setLockupBondsBalance(lockupBondsBalance) - .setUnlockingBondsBalance(unlockingBondsBalance) - .build(); - - } - - public static BsqBalanceInfo fromProto(bisq.proto.grpc.BsqBalanceInfo proto) { - return new BsqBalanceInfo(proto.getAvailableConfirmedBalance(), - proto.getUnverifiedBalance(), - proto.getUnconfirmedChangeBalance(), - proto.getLockedForVotingBalance(), - proto.getLockupBondsBalance(), - proto.getUnlockingBondsBalance()); - } - - @Override - public String toString() { - return "BsqBalanceInfo{" + - "availableConfirmedBalance=" + availableConfirmedBalance + - ", unverifiedBalance=" + unverifiedBalance + - ", unconfirmedChangeBalance=" + unconfirmedChangeBalance + - ", lockedForVotingBalance=" + lockedForVotingBalance + - ", lockupBondsBalance=" + lockupBondsBalance + - ", unlockingBondsBalance=" + unlockingBondsBalance + - '}'; - } -} diff --git a/core/src/main/java/bisq/core/api/model/OfferInfo.java b/core/src/main/java/bisq/core/api/model/OfferInfo.java index f8501f7df1..54911251f8 100644 --- a/core/src/main/java/bisq/core/api/model/OfferInfo.java +++ b/core/src/main/java/bisq/core/api/model/OfferInfo.java @@ -51,7 +51,6 @@ public class OfferInfo implements Payload { private final long buyerSecurityDeposit; private final long sellerSecurityDeposit; private final long triggerPrice; - private final boolean isCurrencyForMakerFeeBtc; private final String paymentAccountId; private final String paymentMethodId; private final String paymentMethodShortName; @@ -79,7 +78,6 @@ public class OfferInfo implements Payload { this.buyerSecurityDeposit = builder.buyerSecurityDeposit; this.sellerSecurityDeposit = builder.sellerSecurityDeposit; this.triggerPrice = builder.triggerPrice; - this.isCurrencyForMakerFeeBtc = builder.isCurrencyForMakerFeeBtc; this.paymentAccountId = builder.paymentAccountId; this.paymentMethodId = builder.paymentMethodId; this.paymentMethodShortName = builder.paymentMethodShortName; @@ -116,7 +114,6 @@ public class OfferInfo implements Payload { .withOfferFeePaymentTxId(offer.getOfferFeePaymentTxId()) .withBuyerSecurityDeposit(offer.getBuyerSecurityDeposit().value) .withSellerSecurityDeposit(offer.getSellerSecurityDeposit().value) - .withIsCurrencyForMakerFeeBtc(offer.isCurrencyForMakerFeeBtc()) .withPaymentAccountId(offer.getMakerPaymentAccountId()) .withPaymentMethodId(offer.getPaymentMethod().getId()) .withPaymentMethodShortName(offer.getPaymentMethod().getShortName()) @@ -148,7 +145,6 @@ public class OfferInfo implements Payload { .setBuyerSecurityDeposit(buyerSecurityDeposit) .setSellerSecurityDeposit(sellerSecurityDeposit) .setTriggerPrice(triggerPrice) - .setIsCurrencyForMakerFeeBtc(isCurrencyForMakerFeeBtc) .setPaymentAccountId(paymentAccountId) .setPaymentMethodId(paymentMethodId) .setPaymentMethodShortName(paymentMethodShortName) @@ -177,7 +173,6 @@ public class OfferInfo implements Payload { .withBuyerSecurityDeposit(proto.getBuyerSecurityDeposit()) .withSellerSecurityDeposit(proto.getSellerSecurityDeposit()) .withTriggerPrice(proto.getTriggerPrice()) - .withIsCurrencyForMakerFeeBtc(proto.getIsCurrencyForMakerFeeBtc()) .withPaymentAccountId(proto.getPaymentAccountId()) .withPaymentMethodId(proto.getPaymentMethodId()) .withPaymentMethodShortName(proto.getPaymentMethodShortName()) @@ -210,7 +205,6 @@ public class OfferInfo implements Payload { private long buyerSecurityDeposit; private long sellerSecurityDeposit; private long triggerPrice; - private boolean isCurrencyForMakerFeeBtc; private String paymentAccountId; private String paymentMethodId; private String paymentMethodShortName; @@ -294,10 +288,6 @@ public class OfferInfo implements Payload { return this; } - public OfferInfoBuilder withIsCurrencyForMakerFeeBtc(boolean isCurrencyForMakerFeeBtc) { - this.isCurrencyForMakerFeeBtc = isCurrencyForMakerFeeBtc; - return this; - } public OfferInfoBuilder withPaymentAccountId(String paymentAccountId) { this.paymentAccountId = paymentAccountId; diff --git a/core/src/main/java/bisq/core/api/model/TradeInfo.java b/core/src/main/java/bisq/core/api/model/TradeInfo.java index c5c3c60c43..b9ebb9d5cf 100644 --- a/core/src/main/java/bisq/core/api/model/TradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/TradeInfo.java @@ -43,7 +43,6 @@ public class TradeInfo implements Payload { private final String shortId; private final long date; private final String role; - private final boolean isCurrencyForTakerFeeBtc; private final long txFeeAsLong; private final long takerFeeAsLong; private final String takerFeeTxId; @@ -71,7 +70,6 @@ public class TradeInfo implements Payload { this.shortId = builder.shortId; this.date = builder.date; this.role = builder.role; - this.isCurrencyForTakerFeeBtc = builder.isCurrencyForTakerFeeBtc; this.txFeeAsLong = builder.txFeeAsLong; this.takerFeeAsLong = builder.takerFeeAsLong; this.takerFeeTxId = builder.takerFeeTxId; @@ -225,7 +223,6 @@ public class TradeInfo implements Payload { private String shortId; private long date; private String role; - private boolean isCurrencyForTakerFeeBtc; private long txFeeAsLong; private long takerFeeAsLong; private String takerFeeTxId; @@ -272,11 +269,6 @@ public class TradeInfo implements Payload { return this; } - public TradeInfoBuilder withIsCurrencyForTakerFeeBtc(boolean isCurrencyForTakerFeeBtc) { - this.isCurrencyForTakerFeeBtc = isCurrencyForTakerFeeBtc; - return this; - } - public TradeInfoBuilder withTxFeeAsLong(long txFeeAsLong) { this.txFeeAsLong = txFeeAsLong; return this; @@ -296,7 +288,7 @@ public class TradeInfo implements Payload { this.makerDepositTxId = makerDepositTxId; return this; } - + public TradeInfoBuilder withTakerDepositTxId(String takerDepositTxId) { this.takerDepositTxId = takerDepositTxId; return this; @@ -389,7 +381,6 @@ public class TradeInfo implements Payload { ", shortId='" + shortId + '\'' + "\n" + ", date='" + date + '\'' + "\n" + ", role='" + role + '\'' + "\n" + - ", isCurrencyForTakerFeeBtc='" + isCurrencyForTakerFeeBtc + '\'' + "\n" + ", txFeeAsLong='" + txFeeAsLong + '\'' + "\n" + ", takerFeeAsLong='" + takerFeeAsLong + '\'' + "\n" + ", takerFeeTxId='" + takerFeeTxId + '\'' + "\n" + diff --git a/core/src/main/java/bisq/core/app/BisqExecutable.java b/core/src/main/java/bisq/core/app/BisqExecutable.java index 0016b68dab..07e21905b6 100644 --- a/core/src/main/java/bisq/core/app/BisqExecutable.java +++ b/core/src/main/java/bisq/core/app/BisqExecutable.java @@ -18,11 +18,8 @@ package bisq.core.app; import bisq.core.btc.setup.WalletsSetup; -import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.XmrWalletService; -import bisq.core.dao.DaoSetup; -import bisq.core.dao.node.full.RpcService; import bisq.core.offer.OpenOfferManager; import bisq.core.provider.price.PriceFeedService; import bisq.core.setup.CorePersistedDataHost; @@ -235,8 +232,6 @@ public abstract class BisqExecutable implements GracefulShutDownHandler, BisqSet injector.getInstance(ArbitratorManager.class).shutDown(); injector.getInstance(TradeStatisticsManager.class).shutDown(); injector.getInstance(XmrTxProofService.class).shutDown(); - injector.getInstance(RpcService.class).shutDown(); - injector.getInstance(DaoSetup.class).shutDown(); injector.getInstance(AvoidStandbyModeService.class).shutDown(); injector.getInstance(XmrWalletService.class).shutDown(); // TODO: why not shut down BtcWalletService, etc? log.info("OpenOfferManager shutdown started"); @@ -244,7 +239,6 @@ public abstract class BisqExecutable implements GracefulShutDownHandler, BisqSet log.info("OpenOfferManager shutdown completed"); injector.getInstance(BtcWalletService.class).shutDown(); - injector.getInstance(BsqWalletService.class).shutDown(); // We need to shutdown BitcoinJ before the P2PService as it uses Tor. WalletsSetup walletsSetup = injector.getInstance(WalletsSetup.class); diff --git a/core/src/main/java/bisq/core/app/BisqHeadlessApp.java b/core/src/main/java/bisq/core/app/BisqHeadlessApp.java index ae9bd25693..1eb0552755 100644 --- a/core/src/main/java/bisq/core/app/BisqHeadlessApp.java +++ b/core/src/main/java/bisq/core/app/BisqHeadlessApp.java @@ -84,12 +84,8 @@ public class BisqHeadlessApp implements HeadlessApp { bisqSetup.setDisplayUpdateHandler((alert, key) -> log.info("onDisplayUpdateHandler")); bisqSetup.setDisplayAlertHandler(alert -> log.info("onDisplayAlertHandler. alert={}", alert)); bisqSetup.setDisplayPrivateNotificationHandler(privateNotification -> log.info("onDisplayPrivateNotificationHandler. privateNotification={}", privateNotification)); - bisqSetup.setDaoErrorMessageHandler(errorMessage -> log.error("onDaoErrorMessageHandler. errorMessage={}", errorMessage)); - bisqSetup.setDaoWarnMessageHandler(warnMessage -> log.warn("onDaoWarnMessageHandler. warnMessage={}", warnMessage)); bisqSetup.setDisplaySecurityRecommendationHandler(key -> log.info("onDisplaySecurityRecommendationHandler")); - bisqSetup.setDisplayLocalhostHandler(key -> log.info("onDisplayLocalhostHandler")); bisqSetup.setWrongOSArchitectureHandler(msg -> log.error("onWrongOSArchitectureHandler. msg={}", msg)); - bisqSetup.setVoteResultExceptionHandler(voteResultException -> log.warn("voteResultException={}", voteResultException.toString())); bisqSetup.setRejectedTxErrorMessageHandler(errorMessage -> log.warn("setRejectedTxErrorMessageHandler. errorMessage={}", errorMessage)); bisqSetup.setShowPopupIfInvalidBtcConfigHandler(() -> log.error("onShowPopupIfInvalidBtcConfigHandler")); bisqSetup.setRevolutAccountsUpdateHandler(revolutAccountList -> log.info("setRevolutAccountsUpdateHandler: revolutAccountList={}", revolutAccountList)); @@ -97,12 +93,6 @@ public class BisqHeadlessApp implements HeadlessApp { bisqSetup.setQubesOSInfoHandler(() -> log.info("setQubesOSInfoHandler")); bisqSetup.setDownGradePreventionHandler(lastVersion -> log.info("Downgrade from version {} to version {} is not supported", lastVersion, Version.VERSION)); - bisqSetup.setDaoRequiresRestartHandler(() -> { - log.info("There was a problem with synchronizing the DAO state. " + - "A restart of the application is required to fix the issue."); - gracefulShutDownHandler.gracefulShutDown(() -> { - }); - }); corruptedStorageFileHandler.getFiles().ifPresent(files -> log.warn("getCorruptedDatabaseFiles. files={}", files)); tradeManager.setTakeOfferRequestErrorMessageHandler(errorMessage -> log.error("onTakeOfferRequestErrorMessageHandler")); diff --git a/core/src/main/java/bisq/core/app/BisqSetup.java b/core/src/main/java/bisq/core/app/BisqSetup.java index 8045a7b6cf..15b1500f89 100644 --- a/core/src/main/java/bisq/core/app/BisqSetup.java +++ b/core/src/main/java/bisq/core/app/BisqSetup.java @@ -30,8 +30,6 @@ import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.WalletsManager; import bisq.core.btc.wallet.XmrWalletService; import bisq.core.btc.wallet.http.MemPoolSpaceTxBroadcaster; -import bisq.core.dao.governance.voteresult.VoteResultException; -import bisq.core.dao.state.unconfirmed.UnconfirmedBsqChangeOutputListService; import bisq.core.locale.Res; import bisq.core.offer.OpenOfferManager; import bisq.core.payment.AmazonGiftCardAccount; @@ -135,7 +133,6 @@ public class BisqSetup { private final Preferences preferences; private final User user; private final AlertManager alertManager; - private final UnconfirmedBsqChangeOutputListService unconfirmedBsqChangeOutputListService; private final Config config; private final AccountAgeWitnessService accountAgeWitnessService; private final TorSetup torSetup; @@ -149,7 +146,7 @@ public class BisqSetup { @Setter @Nullable private Consumer chainFileLockedExceptionHandler, - spvFileCorruptedHandler, lockedUpFundsHandler, daoErrorMessageHandler, daoWarnMessageHandler, + spvFileCorruptedHandler, lockedUpFundsHandler, filterWarningHandler, displaySecurityRecommendationHandler, displayLocalhostHandler, wrongOSArchitectureHandler, displaySignedByArbitratorHandler, displaySignedByPeerHandler, displayPeerLimitLiftedHandler, displayPeerSignerHandler, @@ -171,9 +168,6 @@ public class BisqSetup { private BiConsumer displayUpdateHandler; @Setter @Nullable - private Consumer voteResultExceptionHandler; - @Setter - @Nullable private Consumer displayPrivateNotificationHandler; @Setter @Nullable @@ -192,9 +186,6 @@ public class BisqSetup { private Runnable qubesOSInfoHandler; @Setter @Nullable - private Runnable daoRequiresRestartHandler; - @Setter - @Nullable private Consumer downGradePreventionHandler; @Getter @@ -221,7 +212,6 @@ public class BisqSetup { Preferences preferences, User user, AlertManager alertManager, - UnconfirmedBsqChangeOutputListService unconfirmedBsqChangeOutputListService, Config config, AccountAgeWitnessService accountAgeWitnessService, TorSetup torSetup, @@ -243,7 +233,6 @@ public class BisqSetup { this.preferences = preferences; this.user = user; this.alertManager = alertManager; - this.unconfirmedBsqChangeOutputListService = unconfirmedBsqChangeOutputListService; this.config = config; this.accountAgeWitnessService = accountAgeWitnessService; this.torSetup = torSetup; @@ -334,10 +323,6 @@ public class BisqSetup { try { walletsSetup.reSyncSPVChain(); - // In case we had an unconfirmed change output we reset the unconfirmedBsqChangeOutputList so that - // after a SPV resync we do not have any dangling BSQ utxos in that list which would cause an incorrect - // BSQ balance state after the SPV resync. - unconfirmedBsqChangeOutputListService.onSpvResync(); } catch (IOException e) { log.error(e.toString()); e.printStackTrace(); @@ -461,13 +446,9 @@ public class BisqSetup { domainInitialisation.initDomainServices(rejectedTxErrorMessageHandler, displayPrivateNotificationHandler, - daoErrorMessageHandler, - daoWarnMessageHandler, filterWarningHandler, - voteResultExceptionHandler, revolutAccountsUpdateHandler, - amazonGiftCardAccountsUpdateHandler, - daoRequiresRestartHandler); + amazonGiftCardAccountsUpdateHandler); if (walletsSetup.downloadPercentageProperty().get() == 1) { checkForLockedUpFunds(); diff --git a/core/src/main/java/bisq/core/app/CoreModule.java b/core/src/main/java/bisq/core/app/CoreModule.java index 4984a84aae..36d9d85f0b 100644 --- a/core/src/main/java/bisq/core/app/CoreModule.java +++ b/core/src/main/java/bisq/core/app/CoreModule.java @@ -19,7 +19,6 @@ package bisq.core.app; 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; @@ -89,7 +88,6 @@ public class CoreModule extends AppModule { install(new OfferModule(config)); install(new P2PModule(config)); install(new BitcoinModule(config)); - install(new DaoModule(config)); install(new AlertModule(config)); install(new FilterModule(config)); install(new CorePresentationModule(config)); diff --git a/core/src/main/java/bisq/core/app/DomainInitialisation.java b/core/src/main/java/bisq/core/app/DomainInitialisation.java index b0dbd16b77..a1284d9bbc 100644 --- a/core/src/main/java/bisq/core/app/DomainInitialisation.java +++ b/core/src/main/java/bisq/core/app/DomainInitialisation.java @@ -22,10 +22,6 @@ import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.alert.PrivateNotificationManager; import bisq.core.alert.PrivateNotificationPayload; import bisq.core.btc.Balances; -import bisq.core.dao.DaoSetup; -import bisq.core.dao.governance.voteresult.VoteResultException; -import bisq.core.dao.governance.voteresult.VoteResultService; -import bisq.core.dao.state.DaoStateSnapshotService; import bisq.core.filter.FilterManager; import bisq.core.notifications.MobileNotificationService; import bisq.core.notifications.alerts.DisputeMsgEvents; @@ -37,7 +33,6 @@ import bisq.core.offer.OpenOfferManager; import bisq.core.offer.TriggerPriceService; import bisq.core.payment.AmazonGiftCardAccount; import bisq.core.payment.RevolutAccount; -import bisq.core.payment.TradeLimits; import bisq.core.provider.fee.FeeService; import bisq.core.provider.mempool.MempoolService; import bisq.core.provider.price.PriceFeedService; @@ -77,7 +72,6 @@ import java.util.stream.Collectors; */ public class DomainInitialisation { private final ClockWatcher clockWatcher; - private final TradeLimits tradeLimits; private final ArbitrationManager arbitrationManager; private final MediationManager mediationManager; private final RefundManager refundManager; @@ -95,13 +89,11 @@ public class DomainInitialisation { private final PrivateNotificationManager privateNotificationManager; private final P2PService p2PService; private final FeeService feeService; - private final DaoSetup daoSetup; private final TradeStatisticsManager tradeStatisticsManager; private final AccountAgeWitnessService accountAgeWitnessService; private final SignedWitnessService signedWitnessService; private final PriceFeedService priceFeedService; private final FilterManager filterManager; - private final VoteResultService voteResultService; private final MobileNotificationService mobileNotificationService; private final MyOfferTakenEvents myOfferTakenEvents; private final TradeEvents tradeEvents; @@ -109,13 +101,11 @@ public class DomainInitialisation { private final PriceAlert priceAlert; private final MarketAlerts marketAlerts; private final User user; - private final DaoStateSnapshotService daoStateSnapshotService; private final TriggerPriceService triggerPriceService; private final MempoolService mempoolService; @Inject public DomainInitialisation(ClockWatcher clockWatcher, - TradeLimits tradeLimits, ArbitrationManager arbitrationManager, MediationManager mediationManager, RefundManager refundManager, @@ -133,13 +123,11 @@ public class DomainInitialisation { PrivateNotificationManager privateNotificationManager, P2PService p2PService, FeeService feeService, - DaoSetup daoSetup, TradeStatisticsManager tradeStatisticsManager, AccountAgeWitnessService accountAgeWitnessService, SignedWitnessService signedWitnessService, PriceFeedService priceFeedService, FilterManager filterManager, - VoteResultService voteResultService, MobileNotificationService mobileNotificationService, MyOfferTakenEvents myOfferTakenEvents, TradeEvents tradeEvents, @@ -147,11 +135,9 @@ public class DomainInitialisation { PriceAlert priceAlert, MarketAlerts marketAlerts, User user, - DaoStateSnapshotService daoStateSnapshotService, TriggerPriceService triggerPriceService, MempoolService mempoolService) { this.clockWatcher = clockWatcher; - this.tradeLimits = tradeLimits; this.arbitrationManager = arbitrationManager; this.mediationManager = mediationManager; this.refundManager = refundManager; @@ -169,13 +155,11 @@ public class DomainInitialisation { this.privateNotificationManager = privateNotificationManager; this.p2PService = p2PService; this.feeService = feeService; - this.daoSetup = daoSetup; this.tradeStatisticsManager = tradeStatisticsManager; this.accountAgeWitnessService = accountAgeWitnessService; this.signedWitnessService = signedWitnessService; this.priceFeedService = priceFeedService; this.filterManager = filterManager; - this.voteResultService = voteResultService; this.mobileNotificationService = mobileNotificationService; this.myOfferTakenEvents = myOfferTakenEvents; this.tradeEvents = tradeEvents; @@ -183,26 +167,19 @@ public class DomainInitialisation { this.priceAlert = priceAlert; this.marketAlerts = marketAlerts; this.user = user; - this.daoStateSnapshotService = daoStateSnapshotService; this.triggerPriceService = triggerPriceService; this.mempoolService = mempoolService; } public void initDomainServices(Consumer rejectedTxErrorMessageHandler, Consumer displayPrivateNotificationHandler, - Consumer daoErrorMessageHandler, - Consumer daoWarnMessageHandler, Consumer filterWarningHandler, - Consumer voteResultExceptionHandler, Consumer> revolutAccountsUpdateHandler, - Consumer> amazonGiftCardAccountsUpdateHandler, - Runnable daoRequiresRestartHandler) { + Consumer> amazonGiftCardAccountsUpdateHandler) { clockWatcher.start(); PersistenceManager.onAllServicesInitialized(); - tradeLimits.onAllServicesInitialized(); - tradeManager.onAllServicesInitialized(); arbitrationManager.onAllServicesInitialized(); mediationManager.onAllServicesInitialized(); @@ -232,17 +209,6 @@ public class DomainInitialisation { feeService.onAllServicesInitialized(); - if (DevEnv.isDaoActivated()) { - daoSetup.onAllServicesInitialized(errorMessage -> { - if (daoErrorMessageHandler != null) - daoErrorMessageHandler.accept(errorMessage); - }, warningMessage -> { - if (daoWarnMessageHandler != null) - daoWarnMessageHandler.accept(warningMessage); - }); - - daoStateSnapshotService.setDaoRequiresRestartHandler(daoRequiresRestartHandler); - } tradeStatisticsManager.onAllServicesInitialized(); @@ -254,12 +220,6 @@ public class DomainInitialisation { filterManager.setFilterWarningHandler(filterWarningHandler); filterManager.onAllServicesInitialized(); - voteResultService.getVoteResultExceptions().addListener((ListChangeListener) c -> { - c.next(); - if (c.wasAdded() && voteResultExceptionHandler != null) { - c.getAddedSubList().forEach(voteResultExceptionHandler); - } - }); mobileNotificationService.onAllServicesInitialized(); myOfferTakenEvents.onAllServicesInitialized(); diff --git a/core/src/main/java/bisq/core/app/P2PNetworkSetup.java b/core/src/main/java/bisq/core/app/P2PNetworkSetup.java index 2643a389dc..c132dd5b2d 100644 --- a/core/src/main/java/bisq/core/app/P2PNetworkSetup.java +++ b/core/src/main/java/bisq/core/app/P2PNetworkSetup.java @@ -94,7 +94,6 @@ public class P2PNetworkSetup { walletsSetup.numPeersProperty(), hiddenServicePublished, initialP2PNetworkDataReceived, (state, warning, numP2pPeers, numBtcPeers, hiddenService, dataReceived) -> { String result; - String daoFullNode = preferences.isDaoFullNode() ? Res.get("mainView.footer.daoFullNode") + " / " : ""; int p2pPeers = (int) numP2pPeers; if (warning != null && p2pPeers == 0) { result = warning; @@ -107,7 +106,7 @@ public class P2PNetworkSetup { else result = state + " / " + p2pInfo; } - return daoFullNode + result; + return result; }); p2PNetworkInfoBinding.subscribe((observable, oldValue, newValue) -> { p2PNetworkInfo.set(newValue); diff --git a/core/src/main/java/bisq/core/app/misc/AppSetupWithP2PAndDAO.java b/core/src/main/java/bisq/core/app/misc/AppSetupWithP2PAndDAO.java index 95751832c9..e5fe89b441 100644 --- a/core/src/main/java/bisq/core/app/misc/AppSetupWithP2PAndDAO.java +++ b/core/src/main/java/bisq/core/app/misc/AppSetupWithP2PAndDAO.java @@ -19,13 +19,6 @@ package bisq.core.app.misc; import bisq.core.account.sign.SignedWitnessService; import bisq.core.account.witness.AccountAgeWitnessService; -import bisq.core.dao.DaoSetup; -import bisq.core.dao.governance.ballot.BallotListService; -import bisq.core.dao.governance.blindvote.MyBlindVoteListService; -import bisq.core.dao.governance.bond.reputation.MyReputationListService; -import bisq.core.dao.governance.myvote.MyVoteListService; -import bisq.core.dao.governance.proofofburn.MyProofOfBurnListService; -import bisq.core.dao.governance.proposal.MyProposalListService; import bisq.core.filter.FilterManager; import bisq.core.trade.statistics.TradeStatisticsManager; @@ -41,7 +34,6 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class AppSetupWithP2PAndDAO extends AppSetupWithP2P { - private final DaoSetup daoSetup; @Inject public AppSetupWithP2PAndDAO(P2PService p2PService, @@ -51,13 +43,6 @@ public class AppSetupWithP2PAndDAO extends AppSetupWithP2P { AccountAgeWitnessService accountAgeWitnessService, SignedWitnessService signedWitnessService, FilterManager filterManager, - DaoSetup daoSetup, - MyVoteListService myVoteListService, - BallotListService ballotListService, - MyBlindVoteListService myBlindVoteListService, - MyProposalListService myProposalListService, - MyReputationListService myReputationListService, - MyProofOfBurnListService myProofOfBurnListService, Config config) { super(p2PService, p2PDataStorage, @@ -68,23 +53,11 @@ public class AppSetupWithP2PAndDAO extends AppSetupWithP2P { filterManager, config); - this.daoSetup = daoSetup; - // TODO Should be refactored/removed. In the meantime keep in sync with CorePersistedDataHost - if (config.daoActivated) { - persistedDataHosts.add(myVoteListService); - persistedDataHosts.add(ballotListService); - persistedDataHosts.add(myBlindVoteListService); - persistedDataHosts.add(myProposalListService); - persistedDataHosts.add(myReputationListService); - persistedDataHosts.add(myProofOfBurnListService); - } } @Override protected void onBasicServicesInitialized() { super.onBasicServicesInitialized(); - - daoSetup.onAllServicesInitialized(log::error, log::warn); } } diff --git a/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java b/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java index 9ad33d9b2f..33482a5090 100644 --- a/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java +++ b/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java @@ -19,11 +19,8 @@ package bisq.core.app.misc; import bisq.core.app.BisqExecutable; import bisq.core.btc.setup.WalletsSetup; -import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.XmrWalletService; -import bisq.core.dao.DaoSetup; -import bisq.core.dao.node.full.RpcService; import bisq.core.offer.OpenOfferManager; import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager; @@ -88,8 +85,6 @@ public abstract class ExecutableForAppWithP2p extends BisqExecutable { try { if (injector != null) { JsonFileManager.shutDownAllInstances(); - injector.getInstance(RpcService.class).shutDown(); - injector.getInstance(DaoSetup.class).shutDown(); injector.getInstance(ArbitratorManager.class).shutDown(); injector.getInstance(OpenOfferManager.class).shutDown(() -> injector.getInstance(P2PService.class).shutDown(() -> { injector.getInstance(WalletsSetup.class).shutDownComplete.addListener((ov, o, n) -> { @@ -104,7 +99,6 @@ public abstract class ExecutableForAppWithP2p extends BisqExecutable { injector.getInstance(WalletsSetup.class).shutDown(); injector.getInstance(XmrWalletService.class).shutDown(); // TODO (woodser): this is not actually called, perhaps because WalletsSetup.class completes too quick so its listener calls System.exit(0) injector.getInstance(BtcWalletService.class).shutDown(); - injector.getInstance(BsqWalletService.class).shutDown(); })); // we wait max 5 sec. UserThread.runAfter(() -> { diff --git a/core/src/main/java/bisq/core/app/misc/ModuleForAppWithP2p.java b/core/src/main/java/bisq/core/app/misc/ModuleForAppWithP2p.java index 879c29ab69..c2a9d05d6d 100644 --- a/core/src/main/java/bisq/core/app/misc/ModuleForAppWithP2p.java +++ b/core/src/main/java/bisq/core/app/misc/ModuleForAppWithP2p.java @@ -20,7 +20,6 @@ package bisq.core.app.misc; import bisq.core.alert.AlertModule; 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; @@ -92,7 +91,6 @@ public class ModuleForAppWithP2p extends AppModule { install(new OfferModule(config)); install(new P2PModule(config)); install(new BitcoinModule(config)); - install(new DaoModule(config)); install(new AlertModule(config)); install(new FilterModule(config)); bind(PubKeyRing.class).toProvider(PubKeyRingProvider.class); diff --git a/core/src/main/java/bisq/core/btc/BitcoinModule.java b/core/src/main/java/bisq/core/btc/BitcoinModule.java index 18e4b891bf..459f30b204 100644 --- a/core/src/main/java/bisq/core/btc/BitcoinModule.java +++ b/core/src/main/java/bisq/core/btc/BitcoinModule.java @@ -22,8 +22,6 @@ import bisq.core.btc.model.XmrAddressEntryList; import bisq.core.btc.nodes.BtcNodes; import bisq.core.btc.setup.RegTestHost; import bisq.core.btc.setup.WalletsSetup; -import bisq.core.btc.wallet.BsqCoinSelector; -import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.NonBsqCoinSelector; import bisq.core.btc.wallet.TradeWalletService; @@ -60,11 +58,7 @@ public class BitcoinModule extends AppModule { // otherwise the specified host or default (localhost) String regTestHost = config.bitcoinRegtestHost; if (regTestHost.isEmpty()) { - regTestHost = config.baseCurrencyNetwork.isDaoTestNet() ? - "104.248.31.39" : - config.baseCurrencyNetwork.isDaoRegTest() ? - "134.209.242.206" : - Config.DEFAULT_REGTEST_HOST; + regTestHost = Config.DEFAULT_REGTEST_HOST; } RegTestHost.HOST = regTestHost; @@ -82,7 +76,6 @@ public class BitcoinModule extends AppModule { bindConstant().annotatedWith(named(Config.USER_AGENT)).to(config.userAgent); bindConstant().annotatedWith(named(Config.NUM_CONNECTIONS_FOR_BTC)).to(config.numConnectionsForBtc); bindConstant().annotatedWith(named(Config.USE_ALL_PROVIDED_NODES)).to(config.useAllProvidedNodes); - bindConstant().annotatedWith(named(Config.IGNORE_LOCAL_BTC_NODE)).to(config.ignoreLocalBtcNode); bindConstant().annotatedWith(named(Config.SOCKS5_DISCOVER_MODE)).to(config.socks5DiscoverMode); bind(new TypeLiteral>(){}).annotatedWith(named(PROVIDERS)).toInstance(config.providers); @@ -91,9 +84,7 @@ public class BitcoinModule extends AppModule { bind(WalletsSetup.class).in(Singleton.class); bind(XmrWalletService.class).in(Singleton.class); bind(BtcWalletService.class).in(Singleton.class); - bind(BsqWalletService.class).in(Singleton.class); bind(TradeWalletService.class).in(Singleton.class); - bind(BsqCoinSelector.class).in(Singleton.class); bind(NonBsqCoinSelector.class).in(Singleton.class); bind(BtcNodes.class).in(Singleton.class); bind(Balances.class).in(Singleton.class); diff --git a/core/src/main/java/bisq/core/btc/TxFeeEstimationService.java b/core/src/main/java/bisq/core/btc/TxFeeEstimationService.java index ecc3652838..e449c3d9ec 100644 --- a/core/src/main/java/bisq/core/btc/TxFeeEstimationService.java +++ b/core/src/main/java/bisq/core/btc/TxFeeEstimationService.java @@ -58,7 +58,6 @@ public class TxFeeEstimationService { public static int TYPICAL_TX_WITH_1_INPUT_VSIZE = 175; private static int DEPOSIT_TX_VSIZE = 233; - private static int BSQ_INPUT_INCREASE = 150; private static int MAX_ITERATIONS = 10; private final FeeService feeService; @@ -114,12 +113,6 @@ public class TxFeeEstimationService { "if the user pays from an external wallet. In that case we use an estimated tx vsize of {} vbytes.", estimatedTxVsize); } - if (!preferences.isPayFeeInBtc()) { - // If we pay the fee in BSQ we have one input more which adds about 150 bytes - // TODO: Clarify if there is always just one additional input or if there can be more. - estimatedTxVsize += BSQ_INPUT_INCREASE; - } - Coin txFee; int vsize; if (isTaker) { diff --git a/core/src/main/java/bisq/core/btc/exceptions/BsqChangeBelowDustException.java b/core/src/main/java/bisq/core/btc/exceptions/BsqChangeBelowDustException.java deleted file mode 100644 index d0bf325151..0000000000 --- a/core/src/main/java/bisq/core/btc/exceptions/BsqChangeBelowDustException.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.btc.exceptions; - -import org.bitcoinj.core.Coin; - -import lombok.Getter; - -public class BsqChangeBelowDustException extends Exception { - @Getter - private final Coin outputValue; - - public BsqChangeBelowDustException(String message, Coin outputValue) { - super(message); - - this.outputValue = outputValue; - } -} diff --git a/core/src/main/java/bisq/core/btc/exceptions/InsufficientBsqException.java b/core/src/main/java/bisq/core/btc/exceptions/InsufficientBsqException.java deleted file mode 100644 index 3b3749baf4..0000000000 --- a/core/src/main/java/bisq/core/btc/exceptions/InsufficientBsqException.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.btc.exceptions; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; - -public class InsufficientBsqException extends InsufficientMoneyException { - - public InsufficientBsqException(Coin missing) { - super(missing, "Insufficient BSQ, missing " + missing.value / 100D + " BSQ"); - } -} diff --git a/core/src/main/java/bisq/core/btc/listeners/BsqBalanceListener.java b/core/src/main/java/bisq/core/btc/listeners/BsqBalanceListener.java deleted file mode 100644 index 7923db370c..0000000000 --- a/core/src/main/java/bisq/core/btc/listeners/BsqBalanceListener.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.btc.listeners; - -import org.bitcoinj.core.Coin; - -public interface BsqBalanceListener { - void onUpdateBalances(Coin availableConfirmedBalance, - Coin availableNonBsqBalance, - Coin unverifiedBalance, - Coin unconfirmedChangeBalance, - Coin lockedForVotingBalance, - Coin lockedInBondsBalance, - Coin unlockingBondsBalance); -} diff --git a/core/src/main/java/bisq/core/btc/model/BsqTransferModel.java b/core/src/main/java/bisq/core/btc/model/BsqTransferModel.java deleted file mode 100644 index 4ee77f24af..0000000000 --- a/core/src/main/java/bisq/core/btc/model/BsqTransferModel.java +++ /dev/null @@ -1,70 +0,0 @@ -package bisq.core.btc.model; - -import bisq.core.dao.state.model.blockchain.TxType; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.LegacyAddress; -import org.bitcoinj.core.Transaction; - -import lombok.Getter; - -@Getter -public final class BsqTransferModel { - - private final LegacyAddress receiverAddress; - private final Coin receiverAmount; - private final Transaction preparedSendTx; - private final Transaction txWithBtcFee; - private final Transaction signedTx; - private final Coin miningFee; - private final int txSize; - private final TxType txType; - - public BsqTransferModel(LegacyAddress receiverAddress, - Coin receiverAmount, - Transaction preparedSendTx, - Transaction txWithBtcFee, - Transaction signedTx) { - this.receiverAddress = receiverAddress; - this.receiverAmount = receiverAmount; - this.preparedSendTx = preparedSendTx; - this.txWithBtcFee = txWithBtcFee; - this.signedTx = signedTx; - this.miningFee = signedTx.getFee(); - this.txSize = signedTx.bitcoinSerialize().length; - this.txType = TxType.TRANSFER_BSQ; - } - - public String getReceiverAddressAsString() { - return receiverAddress.toString(); - } - - public double getTxSizeInKb() { - return txSize / 1000d; - } - - public String toShortString() { - return "{" + "\n" + - " receiverAddress='" + getReceiverAddressAsString() + '\'' + "\n" + - ", receiverAmount=" + receiverAmount + "\n" + - ", txWithBtcFee.txId=" + txWithBtcFee.getTxId() + "\n" + - ", miningFee=" + miningFee + "\n" + - ", txSizeInKb=" + getTxSizeInKb() + "\n" + - '}'; - } - - @Override - public String toString() { - return "BsqTransferModel{" + "\n" + - " receiverAddress='" + getReceiverAddressAsString() + '\'' + "\n" + - ", receiverAmount=" + receiverAmount + "\n" + - ", preparedSendTx=" + preparedSendTx + "\n" + - ", txWithBtcFee=" + txWithBtcFee + "\n" + - ", signedTx=" + signedTx + "\n" + - ", miningFee=" + miningFee + "\n" + - ", txSize=" + txSize + "\n" + - ", txSizeInKb=" + getTxSizeInKb() + "\n" + - ", txType=" + txType + "\n" + - '}'; - } -} diff --git a/core/src/main/java/bisq/core/btc/nodes/LocalBitcoinNode.java b/core/src/main/java/bisq/core/btc/nodes/LocalBitcoinNode.java index 358de89a12..d8e4356c42 100644 --- a/core/src/main/java/bisq/core/btc/nodes/LocalBitcoinNode.java +++ b/core/src/main/java/bisq/core/btc/nodes/LocalBitcoinNode.java @@ -52,13 +52,7 @@ public class LocalBitcoinNode { */ public boolean shouldBeIgnored() { BaseCurrencyNetwork baseCurrencyNetwork = config.baseCurrencyNetwork; - - // For dao testnet (server side regtest) we disable the use of local bitcoin node - // to avoid confusion if local btc node is not synced with our dao testnet master - // node. Note: above comment was previously in WalletConfig::createPeerGroup. - return config.ignoreLocalBtcNode || - baseCurrencyNetwork.isDaoRegTest() || - baseCurrencyNetwork.isDaoTestNet(); + return config.ignoreLocalBtcNode; } /** diff --git a/core/src/main/java/bisq/core/btc/setup/BisqKeyChainFactory.java b/core/src/main/java/bisq/core/btc/setup/BisqKeyChainFactory.java index 7c576a3c84..e274752d94 100644 --- a/core/src/main/java/bisq/core/btc/setup/BisqKeyChainFactory.java +++ b/core/src/main/java/bisq/core/btc/setup/BisqKeyChainFactory.java @@ -32,29 +32,23 @@ import com.google.common.collect.ImmutableList; /** * Hack to convert bitcoinj 0.14 wallets to bitcoinj 0.15 format. * - * This code is required to be executed only once per user (actually twice, for btc and bsq wallets). + * This code is required to be executed only once per user (actually twice, for btc wallets). * Once all users using bitcoinj 0.14 wallets have executed this code, this class will be no longer needed. * * Since that is almost impossible to guarantee, this hack will stay until we decide to don't be * backwards compatible with pre bitcoinj 0.15 wallets. * In that scenario, users will have to migrate using this procedure: - * 1) Run pre bitcoinj 0.15 bisq and copy their seed words on a piece of paper. - * 2) Run post bitcoinj 0.15 bisq and use recover from seed. + * 1) Run pre bitcoinj 0.15 btc and copy their seed words on a piece of paper. + * 2) Run post bitcoinj 0.15 btc and use recover from seed. * */ public class BisqKeyChainFactory extends DefaultKeyChainFactory { - private boolean isBsqWallet; - - public BisqKeyChainFactory(boolean isBsqWallet) { - this.isBsqWallet = isBsqWallet; - } - @Override public DeterministicKeyChain makeKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicSeed seed, KeyCrypter crypter, boolean isMarried, Script.ScriptType outputScriptType, ImmutableList accountPath) { ImmutableList maybeUpdatedAccountPath = accountPath; if (DeterministicKeyChain.ACCOUNT_ZERO_PATH.equals(accountPath)) { // This is a bitcoinj 0.14 wallet that has no account path in the serialized mnemonic - KeyChainGroupStructure structure = new BisqKeyChainGroupStructure(isBsqWallet); + KeyChainGroupStructure structure = new BisqKeyChainGroupStructure(); maybeUpdatedAccountPath = structure.accountPathFor(outputScriptType); } diff --git a/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroupStructure.java b/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroupStructure.java index 66ec8b7cea..304b6130e6 100644 --- a/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroupStructure.java +++ b/core/src/main/java/bisq/core/btc/setup/BisqKeyChainGroupStructure.java @@ -39,43 +39,13 @@ public class BisqKeyChainGroupStructure implements KeyChainGroupStructure { new ChildNumber(0, true), ChildNumber.ONE_HARDENED); - // See https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki - // https://github.com/satoshilabs/slips/blob/master/slip-0044.md - // We have registered 142 (0x8000008E) as coin_type for BSQ - public static final ImmutableList BIP44_BSQ_NON_SEGWIT_ACCOUNT_PATH = ImmutableList.of( - new ChildNumber(44, true), - new ChildNumber(142, true), - ChildNumber.ZERO_HARDENED); - - // We don't use segwit for BSQ - // public static final ImmutableList BIP44_BSQ_SEGWIT_ACCOUNT_PATH = ImmutableList.of( - // new ChildNumber(44, true), - // new ChildNumber(142, true), - // ChildNumber.ONE_HARDENED); - - private boolean isBsqWallet; - - public BisqKeyChainGroupStructure (boolean isBsqWallet) { - this.isBsqWallet = isBsqWallet; - } - @Override public ImmutableList accountPathFor(Script.ScriptType outputScriptType) { - if (!isBsqWallet) { - if (outputScriptType == null || outputScriptType == Script.ScriptType.P2PKH) - return BIP44_BTC_NON_SEGWIT_ACCOUNT_PATH; - else if (outputScriptType == Script.ScriptType.P2WPKH) - return BIP44_BTC_SEGWIT_ACCOUNT_PATH; - else - throw new IllegalArgumentException(outputScriptType.toString()); - } else { - if (outputScriptType == null || outputScriptType == Script.ScriptType.P2PKH) - return BIP44_BSQ_NON_SEGWIT_ACCOUNT_PATH; - else if (outputScriptType == Script.ScriptType.P2WPKH) - //return BIP44_BSQ_SEGWIT_ACCOUNT_PATH; - throw new IllegalArgumentException(outputScriptType.toString()); - else - throw new IllegalArgumentException(outputScriptType.toString()); - } + if (outputScriptType == null || outputScriptType == Script.ScriptType.P2PKH) + return BIP44_BTC_NON_SEGWIT_ACCOUNT_PATH; + else if (outputScriptType == Script.ScriptType.P2WPKH) + return BIP44_BTC_SEGWIT_ACCOUNT_PATH; + else + throw new IllegalArgumentException(outputScriptType.toString()); } } diff --git a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java index 9c6ab65a24..efe2df21ec 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java @@ -142,13 +142,11 @@ public class WalletConfig extends AbstractIdleService { protected volatile MoneroDaemon vXmrDaemon; protected volatile MoneroWalletRpc vXmrWallet; protected volatile Wallet vBtcWallet; - protected volatile Wallet vBsqWallet; protected volatile PeerGroup vPeerGroup; protected final File directory; protected volatile File vXmrWalletFile; protected volatile File vBtcWalletFile; - protected volatile File vBsqWalletFile; protected PeerAddress[] peerAddresses; protected DownloadProgressTracker downloadListener; @@ -373,15 +371,10 @@ public class WalletConfig extends AbstractIdleService { String btcPrefix = "_BTC"; vBtcWalletFile = new File(directory, filePrefix + btcPrefix + ".wallet"); boolean shouldReplayWallet = (vBtcWalletFile.exists() && !chainFileExists) || restoreFromSeed != null; - vBtcWallet = createOrLoadWallet(shouldReplayWallet, vBtcWalletFile, false); + vBtcWallet = createOrLoadWallet(shouldReplayWallet, vBtcWalletFile); vBtcWallet.allowSpendingUnconfirmedTransactions(); vBtcWallet.setRiskAnalyzer(new BisqRiskAnalysis.Analyzer()); - String bsqPrefix = "_BSQ"; - vBsqWalletFile = new File(directory, filePrefix + bsqPrefix + ".wallet"); - vBsqWallet = createOrLoadWallet(shouldReplayWallet, vBsqWalletFile, true); - vBsqWallet.setRiskAnalyzer(new BisqRiskAnalysis.Analyzer()); - // Initiate Bitcoin network objects (block store, blockchain and peer group) vStore = new SPVBlockStore(params, chainFile); if (!chainFileExists || restoreFromSeed != null) { @@ -431,8 +424,6 @@ public class WalletConfig extends AbstractIdleService { } vChain.addWallet(vBtcWallet); vPeerGroup.addWallet(vBtcWallet); - vChain.addWallet(vBsqWallet); - vPeerGroup.addWallet(vBsqWallet); onSetupCompleted(); if (migratedWalletToSegwit.get()) { @@ -468,23 +459,22 @@ public class WalletConfig extends AbstractIdleService { } private Wallet createOrLoadWallet(boolean shouldReplayWallet, - File walletFile, - boolean isBsqWallet) throws Exception { + File walletFile) throws Exception { Wallet wallet; maybeMoveOldWalletOutOfTheWay(walletFile); if (walletFile.exists()) { - wallet = loadWallet(shouldReplayWallet, walletFile, isBsqWallet); + wallet = loadWallet(shouldReplayWallet, walletFile); } else { - wallet = createWallet(isBsqWallet); + wallet = createWallet(); //wallet.freshReceiveKey(); // Currently the only way we can be sure that an extension is aware of its containing wallet is by // deserializing the extension (see WalletExtension#deserializeWalletExtension(Wallet, byte[])) // Hence, we first save and then load wallet to ensure any extensions are correctly initialized. wallet.saveToFile(walletFile); - wallet = loadWallet(false, walletFile, isBsqWallet); + wallet = loadWallet(false, walletFile); } this.setupAutoSave(wallet, walletFile); @@ -496,7 +486,7 @@ public class WalletConfig extends AbstractIdleService { wallet.autosaveToFile(walletFile, 5, TimeUnit.SECONDS, null); } - private Wallet loadWallet(boolean shouldReplayWallet, File walletFile, boolean isBsqWallet) throws Exception { + private Wallet loadWallet(boolean shouldReplayWallet, File walletFile) throws Exception { Wallet wallet; try (FileInputStream walletStream = new FileInputStream(walletFile)) { WalletExtension[] extArray = new WalletExtension[]{}; @@ -504,32 +494,25 @@ public class WalletConfig extends AbstractIdleService { final WalletProtobufSerializer serializer; serializer = new WalletProtobufSerializer(); // Hack to convert bitcoinj 0.14 wallets to bitcoinj 0.15 format - serializer.setKeyChainFactory(new BisqKeyChainFactory(isBsqWallet)); + serializer.setKeyChainFactory(new BisqKeyChainFactory()); wallet = serializer.readWallet(params, extArray, proto); if (shouldReplayWallet) wallet.reset(); - if (!isBsqWallet) { - maybeAddSegwitKeychain(wallet, null); - } + maybeAddSegwitKeychain(wallet, null); } return wallet; } - protected Wallet createWallet(boolean isBsqWallet) { - Script.ScriptType preferredOutputScriptType = isBsqWallet ? Script.ScriptType.P2PKH : Script.ScriptType.P2WPKH; - KeyChainGroupStructure structure = new BisqKeyChainGroupStructure(isBsqWallet); + protected Wallet createWallet() { + Script.ScriptType preferredOutputScriptType = Script.ScriptType.P2WPKH; + KeyChainGroupStructure structure = new BisqKeyChainGroupStructure(); KeyChainGroup.Builder kcgBuilder = KeyChainGroup.builder(params, structure); if (restoreFromSeed != null) { kcgBuilder.fromSeed(restoreFromSeed, preferredOutputScriptType); } else { // new wallet - if (!isBsqWallet) { - // btc wallet uses a new random seed. - kcgBuilder.fromRandom(preferredOutputScriptType); - } else { - // bsq wallet uses btc wallet's seed created a few milliseconds ago. - kcgBuilder.fromSeed(vBtcWallet.getKeyChainSeed(), preferredOutputScriptType); - } + // btc wallet uses a new random seed. + kcgBuilder.fromRandom(preferredOutputScriptType); } return new Wallet(params, kcgBuilder.build()); } @@ -588,12 +571,6 @@ public class WalletConfig extends AbstractIdleService { vBtcWallet = null; log.info("BtcWallet saved to file"); - if (vBsqWallet != null && vBsqWalletFile != null) { - vBsqWallet.saveToFile(vBsqWalletFile); - vBsqWallet = null; - log.info("BsqWallet saved to file"); - } - vStore.close(); vStore = null; log.info("SPV file closed"); @@ -644,11 +621,6 @@ public class WalletConfig extends AbstractIdleService { return vXmrWallet; } - public Wallet bsqWallet() { - checkState(state() == State.STARTING || state() == State.RUNNING, "Cannot call until startup is complete"); - return vBsqWallet; - } - public PeerGroup peerGroup() { checkState(state() == State.STARTING || state() == State.RUNNING, "Cannot call until startup is complete"); return vPeerGroup; @@ -681,7 +653,7 @@ public class WalletConfig extends AbstractIdleService { } DeterministicKeyChain nativeSegwitKeyChain = DeterministicKeyChain.builder().seed(seed) .outputScriptType(Script.ScriptType.P2WPKH) - .accountPath(new BisqKeyChainGroupStructure(false).accountPathFor(Script.ScriptType.P2WPKH)).build(); + .accountPath(new BisqKeyChainGroupStructure().accountPathFor(Script.ScriptType.P2WPKH)).build(); if (aesKey != null) { // If wallet is encrypted, encrypt the new keychain. KeyCrypter keyCrypter = wallet.getKeyCrypter(); diff --git a/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java b/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java index 3454882c1c..fb4b1dadd7 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java @@ -120,7 +120,6 @@ public class WalletsSetup { public final BooleanProperty walletsSetupFailed = new SimpleBooleanProperty(); private static final long STARTUP_TIMEOUT = 180; - private static final String BSQ_WALLET_FILE_NAME = "haveno_BSQ.wallet"; private static final String SPV_CHAIN_FILE_NAME = "haveno.spvchain"; private final RegTestHost regTestHost; @@ -427,7 +426,6 @@ public class WalletsSetup { FileUtil.rollingBackup(walletDir, xmrWalletFileName, 20); FileUtil.rollingBackup(walletDir, xmrWalletFileName + ".keys", 20); FileUtil.rollingBackup(walletDir, xmrWalletFileName + ".address.txt", 20); - FileUtil.rollingBackup(walletDir, BSQ_WALLET_FILE_NAME, 20); } public void clearBackups() { @@ -498,11 +496,6 @@ public class WalletsSetup { return walletConfig.getXmrWallet(); } - @Nullable - public Wallet getBsqWallet() { - return walletConfig.bsqWallet(); - } - public NetworkParameters getParams() { return params; } diff --git a/core/src/main/java/bisq/core/btc/wallet/BsqCoinSelector.java b/core/src/main/java/bisq/core/btc/wallet/BsqCoinSelector.java deleted file mode 100644 index e6d122a6b8..0000000000 --- a/core/src/main/java/bisq/core/btc/wallet/BsqCoinSelector.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.btc.wallet; - -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.TxOutputKey; -import bisq.core.dao.state.unconfirmed.UnconfirmedBsqChangeOutputListService; - -import org.bitcoinj.core.Transaction; -import org.bitcoinj.core.TransactionOutput; - -import javax.inject.Inject; - -import lombok.extern.slf4j.Slf4j; - -/** - * We use a specialized version of the CoinSelector based on the DefaultCoinSelector implementation. - * We lookup for spendable outputs which matches our address of our address. - */ -@Slf4j -public class BsqCoinSelector extends BisqDefaultCoinSelector { - private final DaoStateService daoStateService; - private final UnconfirmedBsqChangeOutputListService unconfirmedBsqChangeOutputListService; - - @Inject - public BsqCoinSelector(DaoStateService daoStateService, UnconfirmedBsqChangeOutputListService unconfirmedBsqChangeOutputListService) { - // permitForeignPendingTx is not relevant here as we do not support pending foreign utxos anyway. - super(false); - this.daoStateService = daoStateService; - this.unconfirmedBsqChangeOutputListService = unconfirmedBsqChangeOutputListService; - } - - @Override - protected boolean isTxOutputSpendable(TransactionOutput output) { - // output.getParentTransaction() cannot be null as it is checked in calling method - Transaction parentTransaction = output.getParentTransaction(); - if (parentTransaction == null) - return false; - - // If it is a normal confirmed BSQ output we use the default lookup at the daoState - if (daoStateService.isTxOutputSpendable(new TxOutputKey(parentTransaction.getTxId().toString(), output.getIndex()))) - return true; - - // It might be that it is an unconfirmed change output which we allow to be used for spending without requiring a confirmation. - // We check if we have the output in the dao state, if so we have a confirmed but unspendable output (e.g. confiscated). - if (daoStateService.getTxOutput(new TxOutputKey(parentTransaction.getTxId().toString(), output.getIndex())).isPresent()) - return false; - - // Only if it's not existing yet in the dao state (unconfirmed) we use our unconfirmedBsqChangeOutputList to - // check if it is an own change output. - return unconfirmedBsqChangeOutputListService.hasTransactionOutput(output); - } - - // For BSQ we do not check for dust attack utxos as they are 5.46 BSQ and a considerable value. - // The default 546 sat dust limit is handled in the BitcoinJ side anyway. - @Override - protected boolean isDustAttackUtxo(TransactionOutput output) { - return false; - } -} diff --git a/core/src/main/java/bisq/core/btc/wallet/BsqTransferService.java b/core/src/main/java/bisq/core/btc/wallet/BsqTransferService.java deleted file mode 100644 index 4558b0acf7..0000000000 --- a/core/src/main/java/bisq/core/btc/wallet/BsqTransferService.java +++ /dev/null @@ -1,60 +0,0 @@ -package bisq.core.btc.wallet; - -import bisq.core.btc.exceptions.BsqChangeBelowDustException; -import bisq.core.btc.exceptions.TransactionVerificationException; -import bisq.core.btc.exceptions.WalletException; -import bisq.core.btc.model.BsqTransferModel; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.LegacyAddress; -import org.bitcoinj.core.Transaction; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@Singleton -public class BsqTransferService { - - private final WalletsManager walletsManager; - private final BsqWalletService bsqWalletService; - private final BtcWalletService btcWalletService; - - @Inject - public BsqTransferService(WalletsManager walletsManager, - BsqWalletService bsqWalletService, - BtcWalletService btcWalletService) { - this.walletsManager = walletsManager; - this.bsqWalletService = bsqWalletService; - this.btcWalletService = btcWalletService; - } - - public BsqTransferModel getBsqTransferModel(LegacyAddress address, - Coin receiverAmount, - Coin txFeePerVbyte) - throws TransactionVerificationException, - WalletException, - BsqChangeBelowDustException, - InsufficientMoneyException { - - Transaction preparedSendTx = bsqWalletService.getPreparedSendBsqTx(address.toString(), receiverAmount); - Transaction txWithBtcFee = btcWalletService.completePreparedSendBsqTx(preparedSendTx, txFeePerVbyte); - Transaction signedTx = bsqWalletService.signTx(txWithBtcFee); - - return new BsqTransferModel(address, - receiverAmount, - preparedSendTx, - txWithBtcFee, - signedTx); - } - - public void sendFunds(BsqTransferModel bsqTransferModel, TxBroadcaster.Callback callback) { - log.info("Publishing BSQ transfer {}", bsqTransferModel.toShortString()); - walletsManager.publishAndCommitBsqTx(bsqTransferModel.getTxWithBtcFee(), - bsqTransferModel.getTxType(), - callback); - } -} diff --git a/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java deleted file mode 100644 index e8e1afa98c..0000000000 --- a/core/src/main/java/bisq/core/btc/wallet/BsqWalletService.java +++ /dev/null @@ -1,836 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.btc.wallet; - -import bisq.core.btc.exceptions.BsqChangeBelowDustException; -import bisq.core.btc.exceptions.InsufficientBsqException; -import bisq.core.btc.exceptions.TransactionVerificationException; -import bisq.core.btc.exceptions.WalletException; -import bisq.core.btc.listeners.BsqBalanceListener; -import bisq.core.btc.setup.WalletsSetup; -import bisq.core.dao.DaoKillSwitch; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.blockchain.TxOutputKey; -import bisq.core.dao.state.model.blockchain.TxType; -import bisq.core.dao.state.unconfirmed.UnconfirmedBsqChangeOutputListService; -import bisq.core.provider.fee.FeeService; -import bisq.core.user.Preferences; - -import bisq.common.UserThread; - -import org.bitcoinj.core.Address; -import org.bitcoinj.core.AddressFormatException; -import org.bitcoinj.core.BlockChain; -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.LegacyAddress; -import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.core.Sha256Hash; -import org.bitcoinj.core.Transaction; -import org.bitcoinj.core.TransactionConfidence; -import org.bitcoinj.core.TransactionInput; -import org.bitcoinj.core.TransactionOutPoint; -import org.bitcoinj.core.TransactionOutput; -import org.bitcoinj.script.ScriptException; -import org.bitcoinj.wallet.CoinSelection; -import org.bitcoinj.wallet.CoinSelector; -import org.bitcoinj.wallet.SendRequest; - -import javax.inject.Inject; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.bitcoinj.core.TransactionConfidence.ConfidenceType.BUILDING; -import static org.bitcoinj.core.TransactionConfidence.ConfidenceType.PENDING; - -@Slf4j -public class BsqWalletService extends WalletService implements DaoStateListener { - - public interface WalletTransactionsChangeListener { - - void onWalletTransactionsChange(); - } - - private final DaoKillSwitch daoKillSwitch; - private final BsqCoinSelector bsqCoinSelector; - private final NonBsqCoinSelector nonBsqCoinSelector; - private final DaoStateService daoStateService; - private final UnconfirmedBsqChangeOutputListService unconfirmedBsqChangeOutputListService; - private final List walletTransactions = new ArrayList<>(); - private final CopyOnWriteArraySet bsqBalanceListeners = new CopyOnWriteArraySet<>(); - private final List walletTransactionsChangeListeners = new ArrayList<>(); - private boolean updateBsqWalletTransactionsPending; - - // balance of non BSQ satoshis - @Getter - private Coin availableNonBsqBalance = Coin.ZERO; - @Getter - private Coin availableConfirmedBalance = Coin.ZERO; - @Getter - private Coin unverifiedBalance = Coin.ZERO; - @Getter - private Coin unconfirmedChangeBalance = Coin.ZERO; - @Getter - private Coin lockedForVotingBalance = Coin.ZERO; - @Getter - private Coin lockupBondsBalance = Coin.ZERO; - @Getter - private Coin unlockingBondsBalance = Coin.ZERO; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public BsqWalletService(WalletsSetup walletsSetup, - BsqCoinSelector bsqCoinSelector, - NonBsqCoinSelector nonBsqCoinSelector, - DaoStateService daoStateService, - UnconfirmedBsqChangeOutputListService unconfirmedBsqChangeOutputListService, - Preferences preferences, - FeeService feeService, - DaoKillSwitch daoKillSwitch) { - super(walletsSetup, - preferences, - feeService); - - this.bsqCoinSelector = bsqCoinSelector; - this.nonBsqCoinSelector = nonBsqCoinSelector; - this.daoStateService = daoStateService; - this.unconfirmedBsqChangeOutputListService = unconfirmedBsqChangeOutputListService; - this.daoKillSwitch = daoKillSwitch; - - nonBsqCoinSelector.setPreferences(preferences); - - walletsSetup.addSetupCompletedHandler(() -> { - wallet = walletsSetup.getBsqWallet(); - if (wallet != null) { - wallet.setCoinSelector(bsqCoinSelector); - addListenersToWallet(); - } - - BlockChain chain = walletsSetup.getChain(); - if (chain != null) { - chain.addNewBestBlockListener(block -> chainHeightProperty.set(block.getHeight())); - chainHeightProperty.set(chain.getBestChainHeight()); - } - }); - - daoStateService.addDaoStateListener(this); - } - - @Override - protected void addListenersToWallet() { - super.addListenersToWallet(); - - wallet.addCoinsReceivedEventListener((wallet, tx, prevBalance, newBalance) -> - updateBsqWalletTransactions() - ); - wallet.addCoinsSentEventListener((wallet, tx, prevBalance, newBalance) -> - updateBsqWalletTransactions() - ); - wallet.addReorganizeEventListener(wallet -> { - log.warn("onReorganize "); - updateBsqWalletTransactions(); - unconfirmedBsqChangeOutputListService.onReorganize(); - }); - wallet.addTransactionConfidenceEventListener((wallet, tx) -> { - // We are only interested in updates from unconfirmed txs and confirmed txs at the - // time when it gets into a block. Otherwise we would get called - // updateBsqWalletTransactions for each tx as the block depth changes for all. - if (tx != null && tx.getConfidence() != null && tx.getConfidence().getDepthInBlocks() <= 1 && - daoStateService.isParseBlockChainComplete()) { - updateBsqWalletTransactions(); - } - unconfirmedBsqChangeOutputListService.onTransactionConfidenceChanged(tx); - }); - wallet.addKeyChainEventListener(keys -> - updateBsqWalletTransactions() - ); - wallet.addScriptsChangeEventListener((wallet, scripts, isAddingScripts) -> - updateBsqWalletTransactions() - ); - wallet.addChangeEventListener(wallet -> - updateBsqWalletTransactions() - ); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - if (isWalletReady()) { - wallet.getTransactions(false).forEach(unconfirmedBsqChangeOutputListService::onTransactionConfidenceChanged); - updateBsqWalletTransactions(); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Overridden Methods - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - String getWalletAsString(boolean includePrivKeys) { - return wallet.toString(true, includePrivKeys, this.aesKey, true, true, walletsSetup.getChain()) + "\n\n" + - "All pubKeys as hex:\n" + - wallet.printAllPubKeysAsHex(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Balance - /////////////////////////////////////////////////////////////////////////////////////////// - - private void updateBsqBalance() { - long ts = System.currentTimeMillis(); - unverifiedBalance = Coin.valueOf( - getTransactions(false).stream() - .filter(tx -> tx.getConfidence().getConfidenceType() == PENDING) - .mapToLong(tx -> { - // Sum up outputs into BSQ wallet and subtract the inputs using lockup or unlocking - // outputs since those inputs will be accounted for in lockupBondsBalance and - // unlockingBondsBalance - long outputs = tx.getOutputs().stream() - .filter(out -> out.isMine(wallet)) - .filter(TransactionOutput::isAvailableForSpending) - .mapToLong(out -> out.getValue().value) - .sum(); - // Account for spending of locked connectedOutputs - long lockedInputs = tx.getInputs().stream() - .filter(in -> { - TransactionOutput connectedOutput = in.getConnectedOutput(); - if (connectedOutput != null) { - Transaction parentTransaction = connectedOutput.getParentTransaction(); - // TODO SQ - if (parentTransaction != null/* && - parentTransaction.getConfidence().getConfidenceType() == BUILDING*/) { - TxOutputKey key = new TxOutputKey(parentTransaction.getTxId().toString(), - connectedOutput.getIndex()); - - return (connectedOutput.isMine(wallet) - && (daoStateService.isLockupOutput(key) - || daoStateService.isUnlockingAndUnspent(key))); - } - } - return false; - }) - .mapToLong(in -> in.getValue() != null ? in.getValue().value : 0) - .sum(); - return outputs - lockedInputs; - }) - .sum() - ); - - Set confirmedTxIdSet = getTransactions(false).stream() - .filter(tx -> tx.getConfidence().getConfidenceType() == BUILDING) - .map(Transaction::getTxId) - .map(Sha256Hash::toString) - .collect(Collectors.toSet()); - - lockedForVotingBalance = Coin.valueOf(daoStateService.getUnspentBlindVoteStakeTxOutputs().stream() - .filter(txOutput -> confirmedTxIdSet.contains(txOutput.getTxId())) - .mapToLong(TxOutput::getValue) - .sum()); - - lockupBondsBalance = Coin.valueOf(daoStateService.getLockupTxOutputs().stream() - .filter(txOutput -> daoStateService.isUnspent(txOutput.getKey())) - .filter(txOutput -> !daoStateService.isConfiscatedLockupTxOutput(txOutput.getTxId())) - .filter(txOutput -> confirmedTxIdSet.contains(txOutput.getTxId())) - .mapToLong(TxOutput::getValue) - .sum()); - - unlockingBondsBalance = Coin.valueOf(daoStateService.getUnspentUnlockingTxOutputsStream() - .filter(txOutput -> confirmedTxIdSet.contains(txOutput.getTxId())) - .filter(txOutput -> !daoStateService.isConfiscatedUnlockTxOutput(txOutput.getTxId())) - .mapToLong(TxOutput::getValue) - .sum()); - - availableConfirmedBalance = bsqCoinSelector.select(NetworkParameters.MAX_MONEY, - wallet.calculateAllSpendCandidates()).valueGathered; - - if (availableConfirmedBalance.isNegative()) - availableConfirmedBalance = Coin.ZERO; - - unconfirmedChangeBalance = unconfirmedBsqChangeOutputListService.getBalance(); - - availableNonBsqBalance = nonBsqCoinSelector.select(NetworkParameters.MAX_MONEY, - wallet.calculateAllSpendCandidates()).valueGathered; - - bsqBalanceListeners.forEach(e -> e.onUpdateBalances(availableConfirmedBalance, availableNonBsqBalance, unverifiedBalance, - unconfirmedChangeBalance, lockedForVotingBalance, lockupBondsBalance, unlockingBondsBalance)); - log.info("updateBsqBalance took {} ms", System.currentTimeMillis() - ts); - } - - public void addBsqBalanceListener(BsqBalanceListener listener) { - bsqBalanceListeners.add(listener); - } - - public void removeBsqBalanceListener(BsqBalanceListener listener) { - bsqBalanceListeners.remove(listener); - } - - public void addWalletTransactionsChangeListener(WalletTransactionsChangeListener listener) { - walletTransactionsChangeListeners.add(listener); - } - - public void removeWalletTransactionsChangeListener(WalletTransactionsChangeListener listener) { - walletTransactionsChangeListeners.remove(listener); - } - - public List getSpendableBsqTransactionOutputs() { - return new ArrayList<>(bsqCoinSelector.select(NetworkParameters.MAX_MONEY, - wallet.calculateAllSpendCandidates()).gathered); - } - - public List getSpendableNonBsqTransactionOutputs() { - return new ArrayList<>(nonBsqCoinSelector.select(NetworkParameters.MAX_MONEY, - wallet.calculateAllSpendCandidates()).gathered); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // BSQ TransactionOutputs and Transactions - /////////////////////////////////////////////////////////////////////////////////////////// - - public List getClonedWalletTransactions() { - return new ArrayList<>(walletTransactions); - } - - public Stream getPendingWalletTransactionsStream() { - return walletTransactions.stream() - .filter(transaction -> transaction.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.PENDING); - } - - private void updateBsqWalletTransactions() { - if (daoStateService.isParseBlockChainComplete()) { - // We get called updateBsqWalletTransactions multiple times from onWalletChanged, onTransactionConfidenceChanged - // and from onParseBlockCompleteAfterBatchProcessing. But as updateBsqBalance is an expensive operation we do - // not want to call it in a short interval series so we use a flag and a delay to not call it multiple times - // in a 100 ms period. - if (!updateBsqWalletTransactionsPending) { - updateBsqWalletTransactionsPending = true; - UserThread.runAfter(() -> { - walletTransactions.clear(); - walletTransactions.addAll(getTransactions(false)); - walletTransactionsChangeListeners.forEach(WalletTransactionsChangeListener::onWalletTransactionsChange); - updateBsqBalance(); - updateBsqWalletTransactionsPending = false; - }, 100, TimeUnit.MILLISECONDS); - } - } - } - - private Set getBsqWalletTransactions() { - return getTransactions(false).stream() - .filter(transaction -> transaction.getConfidence().getConfidenceType() == PENDING || - daoStateService.containsTx(transaction.getTxId().toString())) - .collect(Collectors.toSet()); - } - - public Set getUnverifiedBsqTransactions() { - Set bsqWalletTransactions = getBsqWalletTransactions(); - Set walletTxs = new HashSet<>(getTransactions(false)); - checkArgument(walletTxs.size() >= bsqWalletTransactions.size(), - "We cannot have more txsWithOutputsFoundInBsqTxo than walletTxs"); - if (walletTxs.size() == bsqWalletTransactions.size()) { - // As expected - return new HashSet<>(); - } else { - Map map = walletTxs.stream() - .collect(Collectors.toMap(t -> t.getTxId().toString(), Function.identity())); - - Set walletTxIds = walletTxs.stream() - .map(Transaction::getTxId).map(Sha256Hash::toString).collect(Collectors.toSet()); - Set bsqTxIds = bsqWalletTransactions.stream() - .map(Transaction::getTxId).map(Sha256Hash::toString).collect(Collectors.toSet()); - - walletTxIds.stream() - .filter(bsqTxIds::contains) - .forEach(map::remove); - return new HashSet<>(map.values()); - } - } - - @Override - public Coin getValueSentFromMeForTransaction(Transaction transaction) throws ScriptException { - Coin result = Coin.ZERO; - // We check all our inputs and get the connected outputs. - for (int i = 0; i < transaction.getInputs().size(); i++) { - TransactionInput input = transaction.getInputs().get(i); - // We grab the connected output for that input - TransactionOutput connectedOutput = input.getConnectedOutput(); - if (connectedOutput != null) { - // We grab the parent tx of the connected output - final Transaction parentTransaction = connectedOutput.getParentTransaction(); - final boolean isConfirmed = parentTransaction != null && - parentTransaction.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING; - if (connectedOutput.isMineOrWatched(wallet)) { - if (isConfirmed) { - // We lookup if we have a BSQ tx matching the parent tx - // We cannot make that findTx call outside of the loop as the parent tx can change at each iteration - Optional txOptional = daoStateService.getTx(parentTransaction.getTxId().toString()); - if (txOptional.isPresent()) { - TxOutput txOutput = txOptional.get().getTxOutputs().get(connectedOutput.getIndex()); - if (daoStateService.isBsqTxOutputType(txOutput)) { - //TODO check why values are not the same - if (txOutput.getValue() != connectedOutput.getValue().value) - log.warn("getValueSentToMeForTransaction: Value of BSQ output do not match BitcoinJ tx output. " + - "txOutput.getValue()={}, output.getValue().value={}, txId={}", - txOutput.getValue(), connectedOutput.getValue().value, txOptional.get().getId()); - - // If it is a valid BSQ output we add it - result = result.add(Coin.valueOf(txOutput.getValue())); - } - } - } /*else { - // TODO atm we don't display amounts of unconfirmed txs but that might change so we leave that code - // if it will be required - // If the tx is not confirmed yet we add the value and assume it is a valid BSQ output. - result = result.add(connectedOutput.getValue()); - }*/ - } - } - } - return result; - } - - @Override - public Coin getValueSentToMeForTransaction(Transaction transaction) throws ScriptException { - Coin result = Coin.ZERO; - final String txId = transaction.getTxId().toString(); - // We check if we have a matching BSQ tx. We do that call here to avoid repeated calls in the loop. - Optional txOptional = daoStateService.getTx(txId); - // We check all the outputs of our tx - for (int i = 0; i < transaction.getOutputs().size(); i++) { - TransactionOutput output = transaction.getOutputs().get(i); - final boolean isConfirmed = output.getParentTransaction() != null && - output.getParentTransaction().getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING; - if (output.isMineOrWatched(wallet)) { - if (isConfirmed) { - if (txOptional.isPresent()) { - // The index of the BSQ tx outputs are the same like the bitcoinj tx outputs - TxOutput txOutput = txOptional.get().getTxOutputs().get(i); - if (daoStateService.isBsqTxOutputType(txOutput)) { - //TODO check why values are not the same - if (txOutput.getValue() != output.getValue().value) { - log.warn("getValueSentToMeForTransaction: Value of BSQ output do not match BitcoinJ tx output. " + - "txOutput.getValue()={}, output.getValue().value={}, txId={}", - txOutput.getValue(), output.getValue().value, txId); - } - - // If it is a valid BSQ output we add it - result = result.add(Coin.valueOf(txOutput.getValue())); - } - } - } /*else { - // TODO atm we don't display amounts of unconfirmed txs but that might change so we leave that code - // if it will be required - // If the tx is not confirmed yet we add the value and assume it is a valid BSQ output. - result = result.add(output.getValue()); - }*/ - } - } - return result; - } - - public Optional isWalletTransaction(String txId) { - return walletTransactions.stream().filter(e -> e.getTxId().toString().equals(txId)).findAny(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Sign tx - /////////////////////////////////////////////////////////////////////////////////////////// - - public Transaction signTx(Transaction tx) throws WalletException, TransactionVerificationException { - for (int i = 0; i < tx.getInputs().size(); i++) { - TransactionInput txIn = tx.getInputs().get(i); - TransactionOutput connectedOutput = txIn.getConnectedOutput(); - if (connectedOutput != null && connectedOutput.isMine(wallet)) { - signTransactionInput(wallet, aesKey, tx, txIn, i); - checkScriptSig(tx, txIn, i); - } - } - - for (TransactionOutput txo : tx.getOutputs()) { - Coin value = txo.getValue(); - // OpReturn outputs have value 0 - if (value.isPositive()) { - checkArgument(Restrictions.isAboveDust(txo.getValue()), - "An output value is below dust limit. Transaction=" + tx); - } - } - - checkWalletConsistency(wallet); - verifyTransaction(tx); - printTx("BSQ wallet: Signed Tx", tx); - return tx; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Commit tx - /////////////////////////////////////////////////////////////////////////////////////////// - - public void commitTx(Transaction tx, TxType txType) { - wallet.commitTx(tx); - //printTx("BSQ commit Tx", tx); - - unconfirmedBsqChangeOutputListService.onCommitTx(tx, txType, wallet); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Send BSQ with BTC fee - /////////////////////////////////////////////////////////////////////////////////////////// - - public Transaction getPreparedSendBsqTx(String receiverAddress, Coin receiverAmount) - throws AddressFormatException, InsufficientBsqException, WalletException, - TransactionVerificationException, BsqChangeBelowDustException { - return getPreparedSendTx(receiverAddress, receiverAmount, bsqCoinSelector, false); - } - - public Transaction getPreparedSendBsqTx(String receiverAddress, - Coin receiverAmount, - @Nullable Set utxoCandidates) - throws AddressFormatException, InsufficientBsqException, WalletException, - TransactionVerificationException, BsqChangeBelowDustException { - if (utxoCandidates != null) { - bsqCoinSelector.setUtxoCandidates(utxoCandidates); - } - return getPreparedSendTx(receiverAddress, receiverAmount, bsqCoinSelector, false); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Send BTC (non-BSQ) with BTC fee (e.g. the issuance output from a lost comp. request) - /////////////////////////////////////////////////////////////////////////////////////////// - - public Transaction getPreparedSendBtcTx(String receiverAddress, Coin receiverAmount) - throws AddressFormatException, InsufficientBsqException, WalletException, - TransactionVerificationException, BsqChangeBelowDustException { - return getPreparedSendTx(receiverAddress, receiverAmount, nonBsqCoinSelector, true); - } - - public Transaction getPreparedSendBtcTx(String receiverAddress, - Coin receiverAmount, - @Nullable Set utxoCandidates) - throws AddressFormatException, InsufficientBsqException, WalletException, - TransactionVerificationException, BsqChangeBelowDustException { - if (utxoCandidates != null) { - nonBsqCoinSelector.setUtxoCandidates(utxoCandidates); - } - return getPreparedSendTx(receiverAddress, receiverAmount, nonBsqCoinSelector, true); - } - - private Transaction getPreparedSendTx(String receiverAddress, Coin receiverAmount, CoinSelector coinSelector, - boolean allowSegwitOuput) - throws AddressFormatException, InsufficientBsqException, WalletException, TransactionVerificationException, BsqChangeBelowDustException { - daoKillSwitch.assertDaoIsNotDisabled(); - Transaction tx = new Transaction(params); - checkArgument(Restrictions.isAboveDust(receiverAmount), - "The amount is too low (dust limit)."); - if (allowSegwitOuput) { - tx.addOutput(receiverAmount, Address.fromString(params, receiverAddress)); - } else { - tx.addOutput(receiverAmount, LegacyAddress.fromBase58(params, receiverAddress)); - } - SendRequest sendRequest = SendRequest.forTx(tx); - sendRequest.fee = Coin.ZERO; - sendRequest.feePerKb = Coin.ZERO; - sendRequest.ensureMinRequiredFee = false; - sendRequest.aesKey = aesKey; - sendRequest.shuffleOutputs = false; - sendRequest.signInputs = false; - sendRequest.changeAddress = getChangeAddress(); - sendRequest.coinSelector = coinSelector; - try { - wallet.completeTx(sendRequest); - checkWalletConsistency(wallet); - verifyTransaction(tx); - // printTx("prepareSendTx", tx); - - // Tx has as first output BSQ and an optional second BSQ change output. - // At that stage we do not have added the BTC inputs so there is no BTC change output here. - if (tx.getOutputs().size() == 2) { - Coin bsqChangeOutputValue = tx.getOutputs().get(1).getValue(); - if (!Restrictions.isAboveDust(bsqChangeOutputValue)) { - String msg = "BSQ change output is below dust limit. outputValue=" + bsqChangeOutputValue.value / 100 + " BSQ"; - log.warn(msg); - throw new BsqChangeBelowDustException(msg, bsqChangeOutputValue); - } - } - - return tx; - } catch (InsufficientMoneyException e) { - log.error("getPreparedSendTx: tx={}", tx.toString()); - log.error(e.toString()); - throw new InsufficientBsqException(e.missing); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Burn fee txs - /////////////////////////////////////////////////////////////////////////////////////////// - - public Transaction getPreparedTradeFeeTx(Coin fee) throws InsufficientBsqException { - daoKillSwitch.assertDaoIsNotDisabled(); - - Transaction tx = new Transaction(params); - addInputsAndChangeOutputForTx(tx, fee, bsqCoinSelector); - return tx; - } - - // We create a tx with Bsq inputs for the fee and optional BSQ change output. - // As the fee amount will be missing in the output those BSQ fees are burned. - public Transaction getPreparedProposalTx(Coin fee) throws InsufficientBsqException { - return getPreparedTxWithMandatoryBsqChangeOutput(fee); - } - - public Transaction getPreparedIssuanceTx(Coin fee) throws InsufficientBsqException { - return getPreparedTxWithMandatoryBsqChangeOutput(fee); - } - - public Transaction getPreparedProofOfBurnTx(Coin fee) throws InsufficientBsqException { - return getPreparedTxWithMandatoryBsqChangeOutput(fee); - } - - public Transaction getPreparedBurnFeeTxForAssetListing(Coin fee) throws InsufficientBsqException { - return getPreparedTxWithMandatoryBsqChangeOutput(fee); - } - - // We need to require one BSQ change output as we could otherwise not be able to distinguish between 2 - // structurally same transactions where only the BSQ fee is different. In case of asset listing fee and proof of - // burn it is a user input, so it is not known to the parser, instead we derive the burned fee from the parser. - - // In case of proposal fee we could derive it from the params. - - // For issuance txs we also require a BSQ change output before the issuance output gets added. There was a - // minor bug with the old version that multiple inputs would have caused an exception in case there was no - // change output (e.g. inputs of 21 and 6 BSQ for BSQ fee of 21 BSQ would have caused that only 1 input was used - // and then caused an error as we enforced a change output. This new version handles such cases correctly. - - // Examples for the structurally indistinguishable transactions: - // Case 1: 10 BSQ fee to burn - // In: 17 BSQ - // Out: BSQ change 7 BSQ -> valid BSQ - // Out: OpReturn - // Miner fee: 1000 sat (10 BSQ burned) - - // Case 2: 17 BSQ fee to burn - // In: 17 BSQ - // Out: burned BSQ change 7 BSQ -> BTC (7 BSQ burned) - // Out: OpReturn - // Miner fee: 1000 sat (10 BSQ burned) - - private Transaction getPreparedTxWithMandatoryBsqChangeOutput(Coin fee) throws InsufficientBsqException { - daoKillSwitch.assertDaoIsNotDisabled(); - - Transaction tx = new Transaction(params); - // We look for inputs covering out BSQ fee we want to pay. - CoinSelection coinSelection = bsqCoinSelector.select(fee, wallet.calculateAllSpendCandidates()); - try { - Coin change = bsqCoinSelector.getChange(fee, coinSelection); - if (change.isZero() || Restrictions.isDust(change)) { - // If change is zero or below dust we increase required input amount to enforce a BSQ change output. - // All outputs after that are considered BTC and therefore would be burned BSQ if BSQ is left from what - // we use for miner fee. - - Coin minDustThreshold = Coin.valueOf(preferences.getIgnoreDustThreshold()); - Coin increasedRequiredInput = fee.add(minDustThreshold); - coinSelection = bsqCoinSelector.select(increasedRequiredInput, wallet.calculateAllSpendCandidates()); - change = bsqCoinSelector.getChange(fee, coinSelection); - - log.warn("We increased required input as change output was zero or dust: New change value={}", change); - String info = "Available BSQ balance=" + coinSelection.valueGathered.value / 100 + " BSQ. " + - "Intended fee to burn=" + fee.value / 100 + " BSQ. " + - "Please increase your balance to at least " + (coinSelection.valueGathered.value + minDustThreshold.value) / 100 + " BSQ."; - checkArgument(coinSelection.valueGathered.compareTo(fee) > 0, - "This transaction require a change output of at least " + minDustThreshold.value / 100 + " BSQ (dust limit). " + - info); - - checkArgument(!Restrictions.isDust(change), - "This transaction would create a dust output of " + change.value / 100 + " BSQ. " + - "It requires a change output of at least " + minDustThreshold.value / 100 + " BSQ (dust limit). " + - info); - } - - coinSelection.gathered.forEach(tx::addInput); - tx.addOutput(change, getChangeAddress()); - - return tx; - - } catch (InsufficientMoneyException e) { - log.error("coinSelection.gathered={}", coinSelection.gathered); - throw new InsufficientBsqException(e.missing); - } - } - - private void addInputsAndChangeOutputForTx(Transaction tx, - Coin fee, - BsqCoinSelector bsqCoinSelector) - throws InsufficientBsqException { - Coin requiredInput; - // If our fee is less then dust limit we increase it so we are sure to not get any dust output. - if (Restrictions.isDust(fee)) { - requiredInput = fee.add(Restrictions.getMinNonDustOutput()); - } else { - requiredInput = fee; - } - - CoinSelection coinSelection = bsqCoinSelector.select(requiredInput, wallet.calculateAllSpendCandidates()); - coinSelection.gathered.forEach(tx::addInput); - try { - Coin change = bsqCoinSelector.getChange(fee, coinSelection); - // Change can be ZERO, then no change output is created so don't rely on a BSQ change output - if (change.isPositive()) { - checkArgument(Restrictions.isAboveDust(change), - "The change output of " + change.value / 100d + " BSQ is below the min. dust value of " - + Restrictions.getMinNonDustOutput().value / 100d + - ". At least " + Restrictions.getMinNonDustOutput().add(fee).value / 100d + - " BSQ is needed for this transaction"); - tx.addOutput(change, getChangeAddress()); - } - } catch (InsufficientMoneyException e) { - log.error(tx.toString()); - throw new InsufficientBsqException(e.missing); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Blind vote tx - /////////////////////////////////////////////////////////////////////////////////////////// - - // We create a tx with Bsq inputs for the fee, one output for the stake and optional one BSQ change output. - // As the fee amount will be missing in the output those BSQ fees are burned. - public Transaction getPreparedBlindVoteTx(Coin fee, Coin stake) throws InsufficientBsqException { - daoKillSwitch.assertDaoIsNotDisabled(); - Transaction tx = new Transaction(params); - tx.addOutput(new TransactionOutput(params, tx, stake, getUnusedAddress())); - addInputsAndChangeOutputForTx(tx, fee.add(stake), bsqCoinSelector); - //printTx("getPreparedBlindVoteTx", tx); - return tx; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // MyVote reveal tx - /////////////////////////////////////////////////////////////////////////////////////////// - - public Transaction getPreparedVoteRevealTx(TxOutput stakeTxOutput) { - daoKillSwitch.assertDaoIsNotDisabled(); - Transaction tx = new Transaction(params); - final Coin stake = Coin.valueOf(stakeTxOutput.getValue()); - Transaction blindVoteTx = getTransaction(stakeTxOutput.getTxId()); - checkNotNull(blindVoteTx, "blindVoteTx must not be null"); - TransactionOutPoint outPoint = new TransactionOutPoint(params, stakeTxOutput.getIndex(), blindVoteTx); - // Input is not signed yet so we use new byte[]{} - tx.addInput(new TransactionInput(params, tx, new byte[]{}, outPoint, stake)); - tx.addOutput(new TransactionOutput(params, tx, stake, getUnusedAddress())); - // printTx("getPreparedVoteRevealTx", tx); - return tx; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Lockup bond tx - /////////////////////////////////////////////////////////////////////////////////////////// - - public Transaction getPreparedLockupTx(Coin lockupAmount) throws AddressFormatException, InsufficientBsqException { - daoKillSwitch.assertDaoIsNotDisabled(); - Transaction tx = new Transaction(params); - checkArgument(Restrictions.isAboveDust(lockupAmount), "The amount is too low (dust limit)."); - tx.addOutput(new TransactionOutput(params, tx, lockupAmount, getUnusedAddress())); - addInputsAndChangeOutputForTx(tx, lockupAmount, bsqCoinSelector); - printTx("prepareLockupTx", tx); - return tx; - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Unlock bond tx - /////////////////////////////////////////////////////////////////////////////////////////// - - public Transaction getPreparedUnlockTx(TxOutput lockupTxOutput) throws AddressFormatException { - daoKillSwitch.assertDaoIsNotDisabled(); - Transaction tx = new Transaction(params); - // Unlocking means spending the full value of the locked txOutput to another txOutput with the same value - Coin amountToUnlock = Coin.valueOf(lockupTxOutput.getValue()); - checkArgument(Restrictions.isAboveDust(amountToUnlock), "The amount is too low (dust limit)."); - Transaction lockupTx = getTransaction(lockupTxOutput.getTxId()); - checkNotNull(lockupTx, "lockupTx must not be null"); - TransactionOutPoint outPoint = new TransactionOutPoint(params, lockupTxOutput.getIndex(), lockupTx); - // Input is not signed yet so we use new byte[]{} - tx.addInput(new TransactionInput(params, tx, new byte[]{}, outPoint, amountToUnlock)); - tx.addOutput(new TransactionOutput(params, tx, amountToUnlock, getUnusedAddress())); - printTx("prepareUnlockTx", tx); - return tx; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Addresses - /////////////////////////////////////////////////////////////////////////////////////////// - - private LegacyAddress getChangeAddress() { - return getUnusedAddress(); - } - - public LegacyAddress getUnusedAddress() { - return (LegacyAddress) wallet.getIssuedReceiveAddresses().stream() - .filter(this::isAddressUnused) - .findAny() - .orElse(wallet.freshReceiveAddress()); - } - - public String getUnusedBsqAddressAsString() { - return "B" + getUnusedAddress().toString(); - } - - // For BSQ we do not check for dust attack utxos as they are 5.46 BSQ and a considerable value. - // The default 546 sat dust limit is handled in the BitcoinJ side anyway. - @Override - protected boolean isDustAttackUtxo(TransactionOutput output) { - return false; - } -} diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java index 76752a87f6..081dcacac1 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -147,444 +147,6 @@ public class BtcWalletService extends WalletService { wallet.printAllPubKeysAsHex(); } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Public Methods - /////////////////////////////////////////////////////////////////////////////////////////// - - /////////////////////////////////////////////////////////////////////////////////////////// - // Burn BSQ txs (some proposal txs, asset listing fee tx, proof of burn tx) - /////////////////////////////////////////////////////////////////////////////////////////// - - public Transaction completePreparedBurnBsqTx(Transaction preparedBurnFeeTx, byte[] opReturnData) - throws WalletException, InsufficientMoneyException, TransactionVerificationException { - return completePreparedProposalTx(preparedBurnFeeTx, opReturnData, null, null); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Proposal txs - /////////////////////////////////////////////////////////////////////////////////////////// - - public Transaction completePreparedReimbursementRequestTx(Coin issuanceAmount, - Address issuanceAddress, - Transaction feeTx, - byte[] opReturnData) - throws TransactionVerificationException, WalletException, InsufficientMoneyException { - return completePreparedProposalTx(feeTx, opReturnData, issuanceAmount, issuanceAddress); - } - - public Transaction completePreparedCompensationRequestTx(Coin issuanceAmount, - Address issuanceAddress, - Transaction feeTx, - byte[] opReturnData) - throws TransactionVerificationException, WalletException, InsufficientMoneyException { - return completePreparedProposalTx(feeTx, opReturnData, issuanceAmount, issuanceAddress); - } - - private Transaction completePreparedProposalTx(Transaction feeTx, byte[] opReturnData, - @Nullable Coin issuanceAmount, @Nullable Address issuanceAddress) - throws TransactionVerificationException, WalletException, InsufficientMoneyException { - - // (BsqFee)tx has following structure: - // inputs [1-n] BSQ inputs (fee) - // outputs [0-1] BSQ request fee change output (>= 546 Satoshi) - - // preparedCompensationRequestTx has following structure: - // inputs [1-n] BSQ inputs for request fee - // inputs [1-n] BTC inputs for BSQ issuance and miner fee - // outputs [1] Mandatory BSQ request fee change output (>= 546 Satoshi) - // outputs [1] Potentially BSQ issuance output (>= 546 Satoshi) - in case of a issuance tx, otherwise that output does not exist - // outputs [0-1] BTC change output from issuance and miner fee inputs (>= 546 Satoshi) - // outputs [1] OP_RETURN with opReturnData and amount 0 - // mining fee: BTC mining fee + burned BSQ fee - - Transaction preparedTx = new Transaction(params); - // Copy inputs from BSQ fee tx - feeTx.getInputs().forEach(preparedTx::addInput); - int indexOfBtcFirstInput = feeTx.getInputs().size(); - - // Need to be first because issuance is not guaranteed to be valid and would otherwise burn change output! - // BSQ change outputs from BSQ fee inputs. - feeTx.getOutputs().forEach(preparedTx::addOutput); - - // For generic proposals there is no issuance output, for compensation and reimburse requests there is - if (issuanceAmount != null && issuanceAddress != null) { - // BSQ issuance output - preparedTx.addOutput(issuanceAmount, issuanceAddress); - } - - // safety check counter to avoid endless loops - int counter = 0; - // estimated size of input sig - int sigSizePerInput = 106; - // typical size for a tx with 3 inputs - int txVsizeWithUnsignedInputs = 300; - Coin txFeePerVbyte = feeService.getTxFeePerVbyte(); - - Address changeAddress = getFreshAddressEntry().getAddress(); - checkNotNull(changeAddress, "changeAddress must not be null"); - - BtcCoinSelector coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE), - preferences.getIgnoreDustThreshold()); - List preparedBsqTxInputs = preparedTx.getInputs(); - List preparedBsqTxOutputs = preparedTx.getOutputs(); - Tuple2 numInputs = getNumInputs(preparedTx); - int numLegacyInputs = numInputs.first; - int numSegwitInputs = numInputs.second; - Transaction resultTx = null; - boolean isFeeOutsideTolerance; - do { - counter++; - if (counter >= 10) { - checkNotNull(resultTx, "resultTx must not be null"); - log.error("Could not calculate the fee. Tx=" + resultTx); - break; - } - - Transaction tx = new Transaction(params); - preparedBsqTxInputs.forEach(tx::addInput); - preparedBsqTxOutputs.forEach(tx::addOutput); - - SendRequest sendRequest = SendRequest.forTx(tx); - sendRequest.shuffleOutputs = false; - sendRequest.aesKey = aesKey; - // signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet) - sendRequest.signInputs = false; - - sendRequest.fee = txFeePerVbyte.multiply(txVsizeWithUnsignedInputs + - sigSizePerInput * numLegacyInputs + - sigSizePerInput * numSegwitInputs / 4); - - sendRequest.feePerKb = Coin.ZERO; - sendRequest.ensureMinRequiredFee = false; - - sendRequest.coinSelector = coinSelector; - sendRequest.changeAddress = changeAddress; - wallet.completeTx(sendRequest); - - resultTx = sendRequest.tx; - - // add OP_RETURN output - resultTx.addOutput(new TransactionOutput(params, resultTx, Coin.ZERO, ScriptBuilder.createOpReturnScript(opReturnData).getProgram())); - - numInputs = getNumInputs(resultTx); - numLegacyInputs = numInputs.first; - numSegwitInputs = numInputs.second; - txVsizeWithUnsignedInputs = resultTx.getVsize(); - long estimatedFeeAsLong = txFeePerVbyte.multiply(txVsizeWithUnsignedInputs + - sigSizePerInput * numLegacyInputs + - sigSizePerInput * numSegwitInputs / 4).value; - - // calculated fee must be inside of a tolerance range with tx fee - isFeeOutsideTolerance = Math.abs(resultTx.getFee().value - estimatedFeeAsLong) > 1000; - } - while (isFeeOutsideTolerance); - - // Sign all BTC inputs - signAllBtcInputs(indexOfBtcFirstInput, resultTx); - - checkWalletConsistency(wallet); - verifyTransaction(resultTx); - - // printTx("BTC wallet: Signed tx", resultTx); - return resultTx; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Blind vote tx - /////////////////////////////////////////////////////////////////////////////////////////// - - // We add BTC inputs to pay miner fees and sign the BTC tx inputs - - // (BsqFee)tx has following structure: - // inputs [1-n] BSQ inputs (fee + stake) - // outputs [1] BSQ stake - // outputs [0-1] BSQ change output (>= 546 Satoshi) - - // preparedVoteTx has following structure: - // inputs [1-n] BSQ inputs (fee + stake) - // inputs [1-n] BTC inputs for miner fee - // outputs [1] BSQ stake - // outputs [0-1] BSQ change output (>= 546 Satoshi) - // outputs [0-1] BTC change output from miner fee inputs (>= 546 Satoshi) - // outputs [1] OP_RETURN with opReturnData and amount 0 - // mining fee: BTC mining fee + burned BSQ fee - public Transaction completePreparedBlindVoteTx(Transaction preparedTx, byte[] opReturnData) - throws TransactionVerificationException, WalletException, InsufficientMoneyException { - // First input index for btc inputs (they get added after bsq inputs) - return completePreparedBsqTxWithBtcFee(preparedTx, opReturnData); - } - - private Transaction completePreparedBsqTxWithBtcFee(Transaction preparedTx, - byte[] opReturnData) throws InsufficientMoneyException, TransactionVerificationException, WalletException { - // Remember index for first BTC input - int indexOfBtcFirstInput = preparedTx.getInputs().size(); - - Transaction tx = addInputsForMinerFee(preparedTx, opReturnData); - signAllBtcInputs(indexOfBtcFirstInput, tx); - - checkWalletConsistency(wallet); - verifyTransaction(tx); - - // printTx("BTC wallet: Signed tx", tx); - return tx; - } - - private Transaction addInputsForMinerFee(Transaction preparedTx, - byte[] opReturnData) throws InsufficientMoneyException { - // safety check counter to avoid endless loops - int counter = 0; - // estimated size of input sig - int sigSizePerInput = 106; - // typical size for a tx with 3 inputs - int txVsizeWithUnsignedInputs = 300; - Coin txFeePerVbyte = feeService.getTxFeePerVbyte(); - - Address changeAddress = getFreshAddressEntry().getAddress(); - checkNotNull(changeAddress, "changeAddress must not be null"); - - BtcCoinSelector coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE), - preferences.getIgnoreDustThreshold()); - List preparedBsqTxInputs = preparedTx.getInputs(); - List preparedBsqTxOutputs = preparedTx.getOutputs(); - Tuple2 numInputs = getNumInputs(preparedTx); - int numLegacyInputs = numInputs.first; - int numSegwitInputs = numInputs.second; - Transaction resultTx = null; - boolean isFeeOutsideTolerance; - do { - counter++; - if (counter >= 10) { - checkNotNull(resultTx, "resultTx must not be null"); - log.error("Could not calculate the fee. Tx=" + resultTx); - break; - } - - Transaction tx = new Transaction(params); - preparedBsqTxInputs.forEach(tx::addInput); - preparedBsqTxOutputs.forEach(tx::addOutput); - - SendRequest sendRequest = SendRequest.forTx(tx); - sendRequest.shuffleOutputs = false; - sendRequest.aesKey = aesKey; - // signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet) - sendRequest.signInputs = false; - - sendRequest.fee = txFeePerVbyte.multiply(txVsizeWithUnsignedInputs + - sigSizePerInput * numLegacyInputs + - sigSizePerInput * numSegwitInputs / 4); - sendRequest.feePerKb = Coin.ZERO; - sendRequest.ensureMinRequiredFee = false; - - sendRequest.coinSelector = coinSelector; - sendRequest.changeAddress = changeAddress; - wallet.completeTx(sendRequest); - - resultTx = sendRequest.tx; - - // add OP_RETURN output - resultTx.addOutput(new TransactionOutput(params, resultTx, Coin.ZERO, ScriptBuilder.createOpReturnScript(opReturnData).getProgram())); - - numInputs = getNumInputs(resultTx); - numLegacyInputs = numInputs.first; - numSegwitInputs = numInputs.second; - txVsizeWithUnsignedInputs = resultTx.getVsize(); - final long estimatedFeeAsLong = txFeePerVbyte.multiply(txVsizeWithUnsignedInputs + - sigSizePerInput * numLegacyInputs + - sigSizePerInput * numSegwitInputs / 4).value; - // calculated fee must be inside of a tolerance range with tx fee - isFeeOutsideTolerance = Math.abs(resultTx.getFee().value - estimatedFeeAsLong) > 1000; - } - while (isFeeOutsideTolerance); - return resultTx; - } - - private void signAllBtcInputs(int indexOfBtcFirstInput, Transaction tx) throws TransactionVerificationException { - for (int i = indexOfBtcFirstInput; i < tx.getInputs().size(); i++) { - TransactionInput input = tx.getInputs().get(i); - checkArgument(input.getConnectedOutput() != null && input.getConnectedOutput().isMine(wallet), - "input.getConnectedOutput() is not in our wallet. That must not happen."); - signTransactionInput(wallet, aesKey, tx, input, i); - checkScriptSig(tx, input, i); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Vote reveal tx - /////////////////////////////////////////////////////////////////////////////////////////// - - // We add BTC fees to the prepared reveal tx - // (BsqFee)tx has following structure: - // inputs [1] BSQ input (stake) - // output [1] BSQ unlocked stake - - // preparedVoteTx has following structure: - // inputs [1] BSQ inputs (stake) - // inputs [1-n] BTC inputs for miner fee - // outputs [1] BSQ unlocked stake - // outputs [0-1] BTC change output from miner fee inputs (>= 546 Satoshi) - // outputs [1] OP_RETURN with opReturnData and amount 0 - // mining fee: BTC mining fee + burned BSQ fee - public Transaction completePreparedVoteRevealTx(Transaction preparedTx, byte[] opReturnData) - throws TransactionVerificationException, WalletException, InsufficientMoneyException { - return completePreparedBsqTxWithBtcFee(preparedTx, opReturnData); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Add fee input to prepared BSQ send tx - /////////////////////////////////////////////////////////////////////////////////////////// - - public Transaction completePreparedSendBsqTx(Transaction preparedBsqTx) throws - TransactionVerificationException, WalletException, InsufficientMoneyException { - // preparedBsqTx has following structure: - // inputs [1-n] BSQ inputs - // outputs [1] BSQ receiver's output - // outputs [0-1] BSQ change output - - // We add BTC mining fee. Result tx looks like: - // inputs [1-n] BSQ inputs - // inputs [1-n] BTC inputs - // outputs [1] BSQ receiver's output - // outputs [0-1] BSQ change output - // outputs [0-1] BTC change output - // mining fee: BTC mining fee - Coin txFeePerVbyte = getTxFeeForWithdrawalPerVbyte(); - return completePreparedBsqTx(preparedBsqTx, null, txFeePerVbyte); - } - - public Transaction completePreparedSendBsqTx(Transaction preparedBsqTx, Coin txFeePerVbyte) throws - TransactionVerificationException, WalletException, InsufficientMoneyException { - return completePreparedBsqTx(preparedBsqTx, null, txFeePerVbyte); - } - - public Transaction completePreparedBsqTx(Transaction preparedBsqTx, - @Nullable byte[] opReturnData) throws - TransactionVerificationException, WalletException, InsufficientMoneyException { - Coin txFeePerVbyte = getTxFeeForWithdrawalPerVbyte(); - return completePreparedBsqTx(preparedBsqTx, opReturnData, txFeePerVbyte); - } - - public Transaction completePreparedBsqTx(Transaction preparedBsqTx, - @Nullable byte[] opReturnData, - Coin txFeePerVbyte) throws - TransactionVerificationException, WalletException, InsufficientMoneyException { - - // preparedBsqTx has following structure: - // inputs [1-n] BSQ inputs - // outputs [1] BSQ receiver's output - // outputs [0-1] BSQ change output - // mining fee: optional burned BSQ fee (only if opReturnData != null) - - // We add BTC mining fee. Result tx looks like: - // inputs [1-n] BSQ inputs - // inputs [1-n] BTC inputs - // outputs [0-1] BSQ receiver's output - // outputs [0-1] BSQ change output - // outputs [0-1] BTC change output - // outputs [0-1] OP_RETURN with opReturnData (only if opReturnData != null) - // mining fee: BTC mining fee + optional burned BSQ fee (only if opReturnData != null) - - // In case of txs for burned BSQ fees we have no receiver output and it might be that there is no change outputs - // We need to guarantee that min. 1 valid output is added (OP_RETURN does not count). So we use a higher input - // for BTC to force an additional change output. - - // safety check counter to avoid endless loops - int counter = 0; - // estimated size of input sig - int sigSizePerInput = 106; - // typical size for a tx with 2 inputs - int txVsizeWithUnsignedInputs = 203; - // In case there are no change outputs we force a change by adding min dust to the BTC input - Coin forcedChangeValue = Coin.ZERO; - - Address changeAddress = getFreshAddressEntry().getAddress(); - checkNotNull(changeAddress, "changeAddress must not be null"); - - BtcCoinSelector coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE), - preferences.getIgnoreDustThreshold()); - List preparedBsqTxInputs = preparedBsqTx.getInputs(); - List preparedBsqTxOutputs = preparedBsqTx.getOutputs(); - // We don't know at this point what type the btc input would be (segwit/legacy). - // We use legacy to be on the safe side. - int numLegacyInputs = preparedBsqTxInputs.size() + 1; // We add 1 for the BTC fee input - int numSegwitInputs = 0; - Transaction resultTx = null; - boolean isFeeOutsideTolerance; - boolean opReturnIsOnlyOutput; - do { - counter++; - if (counter >= 10) { - checkNotNull(resultTx, "resultTx must not be null"); - log.error("Could not calculate the fee. Tx=" + resultTx); - break; - } - - Transaction tx = new Transaction(params); - preparedBsqTxInputs.stream().forEach(tx::addInput); - - if (forcedChangeValue.isZero()) { - preparedBsqTxOutputs.stream().forEach(tx::addOutput); - } else { - //TODO test that case - checkArgument(preparedBsqTxOutputs.size() == 0, "preparedBsqTxOutputs.size must be null in that code branch"); - tx.addOutput(forcedChangeValue, changeAddress); - } - - SendRequest sendRequest = SendRequest.forTx(tx); - sendRequest.shuffleOutputs = false; - sendRequest.aesKey = aesKey; - // signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet) - sendRequest.signInputs = false; - - sendRequest.fee = txFeePerVbyte.multiply(txVsizeWithUnsignedInputs + - sigSizePerInput * numLegacyInputs + - sigSizePerInput * numSegwitInputs / 4); - sendRequest.feePerKb = Coin.ZERO; - sendRequest.ensureMinRequiredFee = false; - - sendRequest.coinSelector = coinSelector; - sendRequest.changeAddress = changeAddress; - wallet.completeTx(sendRequest); - - resultTx = sendRequest.tx; - - // We might have the rare case that both inputs matched the required fees, so both did not require - // a change output. - // In such cases we need to add artificially a change output (OP_RETURN is not allowed as only output) - opReturnIsOnlyOutput = resultTx.getOutputs().size() == 0; - forcedChangeValue = opReturnIsOnlyOutput ? Restrictions.getMinNonDustOutput() : Coin.ZERO; - - // add OP_RETURN output - if (opReturnData != null) - resultTx.addOutput(new TransactionOutput(params, resultTx, Coin.ZERO, ScriptBuilder.createOpReturnScript(opReturnData).getProgram())); - - Tuple2 numInputs = getNumInputs(resultTx); - numLegacyInputs = numInputs.first; - numSegwitInputs = numInputs.second; - txVsizeWithUnsignedInputs = resultTx.getVsize(); - final long estimatedFeeAsLong = txFeePerVbyte.multiply(txVsizeWithUnsignedInputs + - sigSizePerInput * numLegacyInputs + - sigSizePerInput * numSegwitInputs / 4).value; - // calculated fee must be inside of a tolerance range with tx fee - isFeeOutsideTolerance = Math.abs(resultTx.getFee().value - estimatedFeeAsLong) > 1000; - } - while (opReturnIsOnlyOutput || - isFeeOutsideTolerance || - resultTx.getFee().value < txFeePerVbyte.multiply(resultTx.getVsize()).value); - - // Sign all BTC inputs - signAllBtcInputs(preparedBsqTxInputs.size(), resultTx); - - checkWalletConsistency(wallet); - verifyTransaction(resultTx); - - printTx("BTC wallet: Signed tx", resultTx); - return resultTx; - } - private Tuple2 getNumInputs(Transaction tx) { int numLegacyInputs = 0; int numSegwitInputs = 0; diff --git a/core/src/main/java/bisq/core/btc/wallet/NonBsqCoinSelector.java b/core/src/main/java/bisq/core/btc/wallet/NonBsqCoinSelector.java index 8de488ae8e..3f2a195046 100644 --- a/core/src/main/java/bisq/core/btc/wallet/NonBsqCoinSelector.java +++ b/core/src/main/java/bisq/core/btc/wallet/NonBsqCoinSelector.java @@ -17,8 +17,6 @@ package bisq.core.btc.wallet; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.TxOutputKey; import bisq.core.user.Preferences; import org.bitcoinj.core.Transaction; @@ -36,14 +34,12 @@ import lombok.extern.slf4j.Slf4j; */ @Slf4j public class NonBsqCoinSelector extends BisqDefaultCoinSelector { - private DaoStateService daoStateService; @Setter private Preferences preferences; @Inject - public NonBsqCoinSelector(DaoStateService daoStateService) { + public NonBsqCoinSelector() { super(false); - this.daoStateService = daoStateService; } @Override @@ -58,10 +54,7 @@ public class NonBsqCoinSelector extends BisqDefaultCoinSelector { if (parentTransaction.getConfidence().getConfidenceType() != TransactionConfidence.ConfidenceType.BUILDING) return false; - TxOutputKey key = new TxOutputKey(parentTransaction.getTxId().toString(), output.getIndex()); - // It might be that we received BTC in a non-BSQ tx so that will not be stored in out state and not found. - // So we consider any txOutput which is not in the state as BTC output. - return !daoStateService.existsTxOutput(key) || daoStateService.isRejectedIssuanceOutput(key); + return true; } // Prevent usage of dust attack utxos diff --git a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java index 2df2f90eb5..512853b610 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -151,177 +151,6 @@ public class TradeWalletService { .setRelay(broadcastTx)); } - /** - * Create a BTC trading fee transaction for the maker or taker of an offer. The first output of the tx is for the - * fee receiver. The second output is the reserve of the trade. There is an optional third output for change. - * - * @param fundingAddress the provided source of funds in case the savings wallet is not used - * @param reservedForTradeAddress the address of the trade reserve - * @param changeAddress the change address to use in case of overpayment or use of the savings wallet - * @param reservedFundsForOffer the amount to reserve for the trade - * @param useSavingsWallet {@code true} to use the savings wallet, {@code false} to use the funding address - * @param tradingFee the amount of the trading fee - * @param txFee the mining fee for this transaction - * @param feeReceiverAddress the address of the receiver of the trading fee - * @param doBroadcast {@code true} to broadcast the transaction, {@code false} otherwise - * @param callback an optional callback to use when broadcasting the transaction - * @return the optionally broadcast transaction - * @throws InsufficientMoneyException if the request could not be completed due to not enough balance - * @throws AddressFormatException if the fee receiver base58 address doesn't parse or its checksum is invalid - */ - public Transaction createBtcTradingFeeTx(Address fundingAddress, - Address reservedForTradeAddress, - Address changeAddress, - Coin reservedFundsForOffer, - boolean useSavingsWallet, - Coin tradingFee, - Coin txFee, - String feeReceiverAddress, - boolean doBroadcast, - @Nullable TxBroadcaster.Callback callback) throws InsufficientMoneyException, AddressFormatException { - Transaction tradingFeeTx = new Transaction(params); - SendRequest sendRequest = null; - try { - tradingFeeTx.addOutput(tradingFee, Address.fromString(params, feeReceiverAddress)); - // the reserved amount we need for the trade we send to our trade reservedForTradeAddress - tradingFeeTx.addOutput(reservedFundsForOffer, reservedForTradeAddress); - - // we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to - // wait for 1 confirmation) - // In case of double spend we will detect later in the trade process and use a ban score to penalize bad behaviour (not impl. yet) - sendRequest = SendRequest.forTx(tradingFeeTx); - sendRequest.shuffleOutputs = false; - sendRequest.aesKey = aesKey; - if (useSavingsWallet) { - sendRequest.coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE), - preferences.getIgnoreDustThreshold()); - } else { - sendRequest.coinSelector = new BtcCoinSelector(fundingAddress, preferences.getIgnoreDustThreshold()); - } - // We use a fixed fee - - sendRequest.fee = txFee; - sendRequest.feePerKb = Coin.ZERO; - sendRequest.ensureMinRequiredFee = false; - - // Change is optional in case of overpay or use of funds from savings wallet - sendRequest.changeAddress = changeAddress; - - checkNotNull(wallet, "Wallet must not be null"); - wallet.completeTx(sendRequest); - if (removeDust(tradingFeeTx)) { - wallet.signTransaction(sendRequest); - } - WalletService.printTx("tradingFeeTx", tradingFeeTx); - - if (doBroadcast && callback != null) { - broadcastTx(tradingFeeTx, callback); - } - - return tradingFeeTx; - } catch (Throwable t) { - if (wallet != null && sendRequest != null && sendRequest.coinSelector != null) { - log.error("Balance = {}; CoinSelector = {}", wallet.getBalance(sendRequest.coinSelector), sendRequest.coinSelector); - } - log.error("createBtcTradingFeeTx failed: tradingFeeTx={}, txOutputs={}", tradingFeeTx.toString(), - tradingFeeTx.getOutputs()); - throw t; - } - } - - public Transaction completeBsqTradingFeeTx(Transaction preparedBsqTx, - Address fundingAddress, - Address reservedForTradeAddress, - Address changeAddress, - Coin reservedFundsForOffer, - boolean useSavingsWallet, - Coin txFee) - throws TransactionVerificationException, WalletException, InsufficientMoneyException, AddressFormatException { - try { - // preparedBsqTx has following structure: - // inputs [1-n] BSQ inputs - // outputs [0-1] BSQ change output - // mining fee: burned BSQ fee - - // We add BTC mining fee. Result tx looks like: - // inputs [1-n] BSQ inputs - // inputs [1-n] BTC inputs - // outputs [0-1] BSQ change output - // outputs [1] BTC reservedForTrade output - // outputs [0-1] BTC change output - // mining fee: BTC mining fee + burned BSQ fee - - // In case all BSQ were burnt as fees we have no receiver output and it might be that there are no change outputs - // We need to guarantee that min. 1 valid output is added (OP_RETURN does not count). So we use a higher input - // for BTC to force an additional change output. - - final int preparedBsqTxInputsSize = preparedBsqTx.getInputs().size(); - final boolean hasBsqOutputs = !preparedBsqTx.getOutputs().isEmpty(); - - // If there are no BSQ change outputs an output larger than the burnt BSQ amount has to be added as the first - // output to make sure the reserved funds are in output 1, deposit tx input creation depends on the reserve - // being output 1. The amount has to be larger than the BSQ input to make sure the inputs get burnt. - // The BTC changeAddress is used, so it might get used for both output 0 and output 2. - if (!hasBsqOutputs) { - var bsqInputValue = preparedBsqTx.getInputs().stream() - .map(TransactionInput::getValue) - .reduce(Coin.valueOf(0), Coin::add); - - preparedBsqTx.addOutput(bsqInputValue.add(Coin.valueOf(1)), changeAddress); - } - // the reserved amount we need for the trade we send to our trade reservedForTradeAddress - preparedBsqTx.addOutput(reservedFundsForOffer, reservedForTradeAddress); - - // we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to - // wait for 1 confirmation) - // In case of double spend we will detect later in the trade process and use a ban score to penalize bad behaviour (not impl. yet) - - SendRequest sendRequest = SendRequest.forTx(preparedBsqTx); - sendRequest.shuffleOutputs = false; - sendRequest.aesKey = aesKey; - if (useSavingsWallet) { - sendRequest.coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE), - preferences.getIgnoreDustThreshold()); - } else { - sendRequest.coinSelector = new BtcCoinSelector(fundingAddress, preferences.getIgnoreDustThreshold()); - } - // We use a fixed fee - sendRequest.fee = txFee; - sendRequest.feePerKb = Coin.ZERO; - sendRequest.ensureMinRequiredFee = false; - - sendRequest.signInputs = false; - - // Change is optional in case of overpay or use of funds from savings wallet - sendRequest.changeAddress = changeAddress; - - checkNotNull(wallet, "Wallet must not be null"); - wallet.completeTx(sendRequest); - Transaction resultTx = sendRequest.tx; - removeDust(resultTx); - - // Sign all BTC inputs - for (int i = preparedBsqTxInputsSize; i < resultTx.getInputs().size(); i++) { - TransactionInput txIn = resultTx.getInputs().get(i); - checkArgument(txIn.getConnectedOutput() != null && - txIn.getConnectedOutput().isMine(wallet), - "txIn.getConnectedOutput() is not in our wallet. That must not happen."); - WalletService.signTransactionInput(wallet, aesKey, resultTx, txIn, i); - WalletService.checkScriptSig(resultTx, txIn, i); - } - - WalletService.checkWalletConsistency(wallet); - WalletService.verifyTransaction(resultTx); - - WalletService.printTx(Res.getBaseCurrencyCode() + " wallet: Signed tx", resultTx); - return resultTx; - } catch (Throwable t) { - log.error("completeBsqTradingFeeTx: preparedBsqTx={}", preparedBsqTx.toString()); - throw t; - } - } - - /////////////////////////////////////////////////////////////////////////////////////////// // Deposit tx /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletService.java b/core/src/main/java/bisq/core/btc/wallet/WalletService.java index a25a6a98f0..961db265ec 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletService.java @@ -111,7 +111,7 @@ import monero.wallet.MoneroWallet; import monero.wallet.model.MoneroTxWallet; /** - * Abstract base class for BTC and BSQ wallet. Provides all non-trade specific functionality. + * Abstract base class for BTC wallet. Provides all non-trade specific functionality. */ @Slf4j public abstract class WalletService { diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletsManager.java b/core/src/main/java/bisq/core/btc/wallet/WalletsManager.java index 2e6e9b6476..c1105ff4bd 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletsManager.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletsManager.java @@ -19,7 +19,6 @@ package bisq.core.btc.wallet; import bisq.core.btc.setup.WalletsSetup; import bisq.core.crypto.ScryptUtil; -import bisq.core.dao.state.model.blockchain.TxType; import bisq.core.locale.Res; import bisq.common.handlers.ExceptionHandler; @@ -47,7 +46,6 @@ public class WalletsManager { private final BtcWalletService btcWalletService; private final TradeWalletService tradeWalletService; - private final BsqWalletService bsqWalletService; private final WalletsSetup walletsSetup; /////////////////////////////////////////////////////////////////////////////////////////// @@ -57,24 +55,20 @@ public class WalletsManager { @Inject public WalletsManager(BtcWalletService btcWalletService, TradeWalletService tradeWalletService, - BsqWalletService bsqWalletService, WalletsSetup walletsSetup) { this.btcWalletService = btcWalletService; this.tradeWalletService = tradeWalletService; - this.bsqWalletService = bsqWalletService; this.walletsSetup = walletsSetup; } public void decryptWallets(KeyParameter aesKey) { btcWalletService.decryptWallet(aesKey); - bsqWalletService.decryptWallet(aesKey); tradeWalletService.setAesKey(null); } public void encryptWallets(KeyCrypterScrypt keyCrypterScrypt, KeyParameter aesKey) { try { btcWalletService.encryptWallet(keyCrypterScrypt, aesKey); - bsqWalletService.encryptWallet(keyCrypterScrypt, aesKey); // we save the key for the trade wallet as we don't require passwords here tradeWalletService.setAesKey(aesKey); @@ -87,8 +81,7 @@ public class WalletsManager { public String getWalletsAsString(boolean includePrivKeys) { final String baseCurrencyWalletDetails = Res.getBaseCurrencyCode() + " Wallet:\n" + btcWalletService.getWalletAsString(includePrivKeys); - final String bsqWalletDetails = "\n\nBSQ Wallet:\n" + bsqWalletService.getWalletAsString(includePrivKeys); - return baseCurrencyWalletDetails + bsqWalletDetails; + return baseCurrencyWalletDetails; } public void restoreSeedWords(@Nullable DeterministicSeed seed, ResultHandler resultHandler, ExceptionHandler exceptionHandler) { @@ -105,11 +98,11 @@ public class WalletsManager { public boolean areWalletsEncrypted() { return areWalletsAvailable() && - btcWalletService.isEncrypted() && bsqWalletService.isEncrypted(); + btcWalletService.isEncrypted(); } public boolean areWalletsAvailable() { - return btcWalletService.isWalletReady() && bsqWalletService.isWalletReady(); + return btcWalletService.isWalletReady(); } public KeyCrypterScrypt getKeyCrypterScrypt() { @@ -128,15 +121,12 @@ public class WalletsManager { } public boolean hasPositiveBalance() { - final Coin bsqWalletServiceBalance = bsqWalletService.getBalance(Wallet.BalanceType.AVAILABLE); return btcWalletService.getBalance(Wallet.BalanceType.AVAILABLE) - .add(bsqWalletServiceBalance) .isPositive(); } public void setAesKey(KeyParameter aesKey) { btcWalletService.setAesKey(aesKey); - bsqWalletService.setAesKey(aesKey); tradeWalletService.setAesKey(aesKey); } @@ -148,17 +138,4 @@ public class WalletsManager { return null; } } - - // A bsq tx has miner fees in btc included. Thus we need to handle it on both wallets. - public void publishAndCommitBsqTx(Transaction tx, TxType txType, TxBroadcaster.Callback callback) { - // We need to create another instance, otherwise the tx would trigger an invalid state exception - // if it gets committed 2 times - // We clone before commit to avoid unwanted side effects - Transaction clonedTx = btcWalletService.getClonedTransaction(tx); - btcWalletService.commitTx(clonedTx); - bsqWalletService.commitTx(tx, txType); - - // We use a short timeout as there are issues with BSQ txs. See comment in TxBroadcaster - bsqWalletService.broadcastTx(tx, callback, 1); - } } diff --git a/core/src/main/java/bisq/core/dao/DaoEventCoordinator.java b/core/src/main/java/bisq/core/dao/DaoEventCoordinator.java deleted file mode 100644 index 4c8cc0f409..0000000000 --- a/core/src/main/java/bisq/core/dao/DaoEventCoordinator.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao; - -import bisq.core.dao.monitoring.DaoStateMonitoringService; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.DaoStateSnapshotService; -import bisq.core.dao.state.model.blockchain.Block; - -import javax.inject.Inject; - -public class DaoEventCoordinator implements DaoSetupService, DaoStateListener { - private final DaoStateService daoStateService; - private final DaoStateSnapshotService daoStateSnapshotService; - private final DaoStateMonitoringService daoStateMonitoringService; - - @Inject - public DaoEventCoordinator(DaoStateService daoStateService, - DaoStateSnapshotService daoStateSnapshotService, - DaoStateMonitoringService daoStateMonitoringService) { - this.daoStateService = daoStateService; - this.daoStateSnapshotService = daoStateSnapshotService; - this.daoStateMonitoringService = daoStateMonitoringService; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - this.daoStateService.addDaoStateListener(this); - } - - @Override - public void start() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - // We listen onDaoStateChanged to ensure the dao state has been processed from listener clients after parsing. - // We need to listen during batch processing as well to write snapshots during that process. - @Override - public void onDaoStateChanged(Block block) { - // We need to execute first the daoStateMonitoringService - daoStateMonitoringService.createHashFromBlock(block); - daoStateSnapshotService.maybeCreateSnapshot(block); - } -} diff --git a/core/src/main/java/bisq/core/dao/DaoFacade.java b/core/src/main/java/bisq/core/dao/DaoFacade.java deleted file mode 100644 index 701849b48b..0000000000 --- a/core/src/main/java/bisq/core/dao/DaoFacade.java +++ /dev/null @@ -1,792 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao; - -import bisq.core.btc.exceptions.TransactionVerificationException; -import bisq.core.btc.exceptions.WalletException; -import bisq.core.dao.governance.ballot.BallotListPresentation; -import bisq.core.dao.governance.ballot.BallotListService; -import bisq.core.dao.governance.blindvote.BlindVoteConsensus; -import bisq.core.dao.governance.blindvote.MyBlindVoteListService; -import bisq.core.dao.governance.bond.Bond; -import bisq.core.dao.governance.bond.lockup.LockupReason; -import bisq.core.dao.governance.bond.lockup.LockupTxService; -import bisq.core.dao.governance.bond.reputation.BondedReputationRepository; -import bisq.core.dao.governance.bond.reputation.MyBondedReputation; -import bisq.core.dao.governance.bond.reputation.MyBondedReputationRepository; -import bisq.core.dao.governance.bond.role.BondedRole; -import bisq.core.dao.governance.bond.role.BondedRolesRepository; -import bisq.core.dao.governance.bond.unlock.UnlockTxService; -import bisq.core.dao.governance.myvote.MyVote; -import bisq.core.dao.governance.myvote.MyVoteListService; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.MyProposalListService; -import bisq.core.dao.governance.proposal.ProposalConsensus; -import bisq.core.dao.governance.proposal.ProposalListPresentation; -import bisq.core.dao.governance.proposal.ProposalService; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalWithTransaction; -import bisq.core.dao.governance.proposal.TxException; -import bisq.core.dao.governance.proposal.compensation.CompensationConsensus; -import bisq.core.dao.governance.proposal.compensation.CompensationProposalFactory; -import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondProposalFactory; -import bisq.core.dao.governance.proposal.generic.GenericProposalFactory; -import bisq.core.dao.governance.proposal.param.ChangeParamProposalFactory; -import bisq.core.dao.governance.proposal.reimbursement.ReimbursementConsensus; -import bisq.core.dao.governance.proposal.reimbursement.ReimbursementProposalFactory; -import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetProposalFactory; -import bisq.core.dao.governance.proposal.role.RoleProposalFactory; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.BaseTx; -import bisq.core.dao.state.model.blockchain.BaseTxOutput; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.blockchain.TxType; -import bisq.core.dao.state.model.governance.Ballot; -import bisq.core.dao.state.model.governance.BondedRoleType; -import bisq.core.dao.state.model.governance.Cycle; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.dao.state.model.governance.IssuanceType; -import bisq.core.dao.state.model.governance.Proposal; -import bisq.core.dao.state.model.governance.Role; -import bisq.core.dao.state.model.governance.RoleProposal; -import bisq.core.dao.state.model.governance.Vote; -import bisq.core.dao.state.storage.DaoStateStorageService; - -import bisq.asset.Asset; - -import bisq.common.config.Config; -import bisq.common.handlers.ErrorMessageHandler; -import bisq.common.handlers.ExceptionHandler; -import bisq.common.handlers.ResultHandler; -import bisq.common.util.Tuple2; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.Transaction; - -import javax.inject.Inject; - -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.ReadOnlyObjectProperty; -import javafx.beans.property.SimpleObjectProperty; - -import javafx.collections.ObservableList; -import javafx.collections.transformation.FilteredList; - -import java.io.File; -import java.io.IOException; - -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -import static com.google.common.base.Preconditions.checkArgument; - -/** - * Provides a facade to interact with the Dao domain. Hides complexity and domain details to clients (e.g. UI or APIs) - * by providing a reduced API and/or aggregating subroutines. - */ -@Slf4j -public class DaoFacade implements DaoSetupService { - private final ProposalListPresentation proposalListPresentation; - private final ProposalService proposalService; - private final BallotListService ballotListService; - private final BallotListPresentation ballotListPresentation; - private final MyProposalListService myProposalListService; - private final DaoStateService daoStateService; - private final PeriodService periodService; - private final MyBlindVoteListService myBlindVoteListService; - private final MyVoteListService myVoteListService; - private final CompensationProposalFactory compensationProposalFactory; - private final ReimbursementProposalFactory reimbursementProposalFactory; - private final ChangeParamProposalFactory changeParamProposalFactory; - private final ConfiscateBondProposalFactory confiscateBondProposalFactory; - private final RoleProposalFactory roleProposalFactory; - private final GenericProposalFactory genericProposalFactory; - private final RemoveAssetProposalFactory removeAssetProposalFactory; - private final BondedRolesRepository bondedRolesRepository; - private final BondedReputationRepository bondedReputationRepository; - private final MyBondedReputationRepository myBondedReputationRepository; - private final LockupTxService lockupTxService; - private final UnlockTxService unlockTxService; - private final DaoStateStorageService daoStateStorageService; - - private final ObjectProperty phaseProperty = new SimpleObjectProperty<>(DaoPhase.Phase.UNDEFINED); - - @Inject - public DaoFacade(MyProposalListService myProposalListService, - ProposalListPresentation proposalListPresentation, - ProposalService proposalService, - BallotListService ballotListService, - BallotListPresentation ballotListPresentation, - DaoStateService daoStateService, - PeriodService periodService, - MyBlindVoteListService myBlindVoteListService, - MyVoteListService myVoteListService, - CompensationProposalFactory compensationProposalFactory, - ReimbursementProposalFactory reimbursementProposalFactory, - ChangeParamProposalFactory changeParamProposalFactory, - ConfiscateBondProposalFactory confiscateBondProposalFactory, - RoleProposalFactory roleProposalFactory, - GenericProposalFactory genericProposalFactory, - RemoveAssetProposalFactory removeAssetProposalFactory, - BondedRolesRepository bondedRolesRepository, - BondedReputationRepository bondedReputationRepository, - MyBondedReputationRepository myBondedReputationRepository, - LockupTxService lockupTxService, - UnlockTxService unlockTxService, - DaoStateStorageService daoStateStorageService) { - this.proposalListPresentation = proposalListPresentation; - this.proposalService = proposalService; - this.ballotListService = ballotListService; - this.ballotListPresentation = ballotListPresentation; - this.myProposalListService = myProposalListService; - this.daoStateService = daoStateService; - this.periodService = periodService; - this.myBlindVoteListService = myBlindVoteListService; - this.myVoteListService = myVoteListService; - this.compensationProposalFactory = compensationProposalFactory; - this.reimbursementProposalFactory = reimbursementProposalFactory; - this.changeParamProposalFactory = changeParamProposalFactory; - this.confiscateBondProposalFactory = confiscateBondProposalFactory; - this.roleProposalFactory = roleProposalFactory; - this.genericProposalFactory = genericProposalFactory; - this.removeAssetProposalFactory = removeAssetProposalFactory; - this.bondedRolesRepository = bondedRolesRepository; - this.bondedReputationRepository = bondedReputationRepository; - this.myBondedReputationRepository = myBondedReputationRepository; - this.lockupTxService = lockupTxService; - this.unlockTxService = unlockTxService; - this.daoStateStorageService = daoStateStorageService; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - daoStateService.addDaoStateListener(new DaoStateListener() { - @Override - public void onNewBlockHeight(int blockHeight) { - if (blockHeight > 0 && periodService.getCurrentCycle() != null) - periodService.getCurrentCycle().getPhaseForHeight(blockHeight).ifPresent(phaseProperty::set); - } - }); - } - - @Override - public void start() { - } - - - public void addBsqStateListener(DaoStateListener listener) { - daoStateService.addDaoStateListener(listener); - } - - public void removeBsqStateListener(DaoStateListener listener) { - daoStateService.removeDaoStateListener(listener); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // - // Phase: Proposal - // - /////////////////////////////////////////////////////////////////////////////////////////// - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Use case: Present lists - /////////////////////////////////////////////////////////////////////////////////////////// - - public ObservableList getActiveOrMyUnconfirmedProposals() { - return proposalListPresentation.getActiveOrMyUnconfirmedProposals(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Use case: Create proposal - /////////////////////////////////////////////////////////////////////////////////////////// - - // Creation of Proposal and proposalTransaction - public ProposalWithTransaction getCompensationProposalWithTransaction(String name, - String link, - Coin requestedBsq) - throws ProposalValidationException, InsufficientMoneyException, TxException { - return compensationProposalFactory.createProposalWithTransaction(name, - link, - requestedBsq); - } - - public ProposalWithTransaction getReimbursementProposalWithTransaction(String name, - String link, - Coin requestedBsq) - throws ProposalValidationException, InsufficientMoneyException, TxException { - return reimbursementProposalFactory.createProposalWithTransaction(name, - link, - requestedBsq); - } - - public ProposalWithTransaction getParamProposalWithTransaction(String name, - String link, - Param param, - String paramValue) - throws ProposalValidationException, InsufficientMoneyException, TxException { - return changeParamProposalFactory.createProposalWithTransaction(name, - link, - param, - paramValue); - } - - public ProposalWithTransaction getConfiscateBondProposalWithTransaction(String name, - String link, - String lockupTxId) - throws ProposalValidationException, InsufficientMoneyException, TxException { - return confiscateBondProposalFactory.createProposalWithTransaction(name, - link, - lockupTxId); - } - - public ProposalWithTransaction getBondedRoleProposalWithTransaction(Role role) - throws ProposalValidationException, InsufficientMoneyException, TxException { - return roleProposalFactory.createProposalWithTransaction(role); - } - - public ProposalWithTransaction getGenericProposalWithTransaction(String name, - String link) - throws ProposalValidationException, InsufficientMoneyException, TxException { - return genericProposalFactory.createProposalWithTransaction(name, link); - } - - public ProposalWithTransaction getRemoveAssetProposalWithTransaction(String name, - String link, - Asset asset) - throws ProposalValidationException, InsufficientMoneyException, TxException { - return removeAssetProposalFactory.createProposalWithTransaction(name, link, asset); - } - - public ObservableList getBondedRoles() { - return bondedRolesRepository.getBonds(); - } - - public List getAcceptedBondedRoles() { - return bondedRolesRepository.getAcceptedBonds(); - } - - // Show fee - public Coin getProposalFee(int chainHeight) { - return ProposalConsensus.getFee(daoStateService, chainHeight); - } - - // Publish proposal tx, proposal payload and persist it to myProposalList - public void publishMyProposal(Proposal proposal, Transaction transaction, ResultHandler resultHandler, - ErrorMessageHandler errorMessageHandler) { - myProposalListService.publishTxAndPayload(proposal, transaction, resultHandler, errorMessageHandler); - } - - // Check if it is my proposal - public boolean isMyProposal(Proposal proposal) { - return myProposalListService.isMine(proposal); - } - - // Remove my proposal - public boolean removeMyProposal(Proposal proposal) { - return myProposalListService.remove(proposal); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // - // Phase: Blind Vote - // - /////////////////////////////////////////////////////////////////////////////////////////// - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Use case: Present lists - /////////////////////////////////////////////////////////////////////////////////////////// - - public ObservableList getAllBallots() { - return ballotListPresentation.getAllBallots(); - } - - public List getAllValidBallots() { - return ballotListPresentation.getAllValidBallots(); - } - - public FilteredList getBallotsOfCycle() { - return ballotListPresentation.getBallotsOfCycle(); - } - - public Tuple2 getMeritAndStakeForProposal(String proposalTxId) { - return myVoteListService.getMeritAndStakeForProposal(proposalTxId, myBlindVoteListService); - } - - public long getAvailableMerit() { - return myBlindVoteListService.getCurrentlyAvailableMerit(); - } - - public List getMyVoteListForCycle() { - return myVoteListService.getMyVoteListForCycle(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Use case: Vote - /////////////////////////////////////////////////////////////////////////////////////////// - - // Vote on ballot - public void setVote(Ballot ballot, @Nullable Vote vote) { - ballotListService.setVote(ballot, vote); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Use case: Create blindVote - /////////////////////////////////////////////////////////////////////////////////////////// - - // When creating blind vote we present fee - public Coin getBlindVoteFeeForCycle() { - return BlindVoteConsensus.getFee(daoStateService, daoStateService.getChainHeight()); - } - - public Tuple2 getBlindVoteMiningFeeAndTxVsize(Coin stake) - throws WalletException, InsufficientMoneyException, TransactionVerificationException { - return myBlindVoteListService.getMiningFeeAndTxVsize(stake); - } - - // Publish blindVote tx and broadcast blindVote to p2p network and store to blindVoteList. - public void publishBlindVote(Coin stake, ResultHandler resultHandler, ExceptionHandler exceptionHandler) { - myBlindVoteListService.publishBlindVote(stake, resultHandler, exceptionHandler); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // - // Generic - // - /////////////////////////////////////////////////////////////////////////////////////////// - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Use case: Presentation of phases - /////////////////////////////////////////////////////////////////////////////////////////// - - // Because last block in request and voting phases must not be used for making a tx as it will get confirmed in the - // next block which would be already the next phase we hide that last block to the user and add it to the break. - public int getFirstBlockOfPhaseForDisplay(int height, DaoPhase.Phase phase) { - int firstBlock = periodService.getFirstBlockOfPhase(height, phase); - switch (phase) { - case UNDEFINED: - break; - case PROPOSAL: - break; - case BREAK1: - firstBlock--; - break; - case BLIND_VOTE: - break; - case BREAK2: - firstBlock--; - break; - case VOTE_REVEAL: - break; - case BREAK3: - firstBlock--; - break; - case RESULT: - break; - } - return firstBlock; - } - - public Map getBlockStartDateByCycleIndex() { - AtomicInteger index = new AtomicInteger(); - Map map = new HashMap<>(); - periodService.getCycles() - .forEach(cycle -> daoStateService.getBlockAtHeight(cycle.getHeightOfFirstBlock()) - .ifPresent(block -> map.put(index.getAndIncrement(), new Date(block.getTime())))); - return map; - } - - // Because last block in request and voting phases must not be used for making a tx as it will get confirmed in the - // next block which would be already the next phase we hide that last block to the user and add it to the break. - public int getLastBlockOfPhaseForDisplay(int height, DaoPhase.Phase phase) { - int lastBlock = periodService.getLastBlockOfPhase(height, phase); - switch (phase) { - case UNDEFINED: - break; - case PROPOSAL: - lastBlock--; - break; - case BREAK1: - break; - case BLIND_VOTE: - lastBlock--; - break; - case BREAK2: - break; - case VOTE_REVEAL: - lastBlock--; - break; - case BREAK3: - break; - case RESULT: - break; - } - return lastBlock; - } - - // Because last block in request and voting phases must not be used for making a tx as it will get confirmed in the - // next block which would be already the next phase we hide that last block to the user and add it to the break. - public int getDurationForPhaseForDisplay(DaoPhase.Phase phase) { - int duration = periodService.getDurationForPhase(phase, daoStateService.getChainHeight()); - switch (phase) { - case UNDEFINED: - break; - case PROPOSAL: - duration--; - break; - case BREAK1: - duration++; - break; - case BLIND_VOTE: - duration--; - break; - case BREAK2: - duration++; - break; - case VOTE_REVEAL: - duration--; - break; - case BREAK3: - duration++; - break; - case RESULT: - break; - } - return duration; - } - - public int getCurrentCycleDuration() { - Cycle currentCycle = periodService.getCurrentCycle(); - return currentCycle != null ? currentCycle.getDuration() : 0; - } - - // listeners for phase change - public ReadOnlyObjectProperty phaseProperty() { - return phaseProperty; - } - - public int getChainHeight() { - return daoStateService.getChainHeight(); - } - - public Optional getBlockAtChainHeight() { - return getBlockAtHeight(getChainHeight()); - } - - public Optional getBlockAtHeight(int chainHeight) { - return daoStateService.getBlockAtHeight(chainHeight); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Use case: Bonding - /////////////////////////////////////////////////////////////////////////////////////////// - - public void publishLockupTx(Coin lockupAmount, int lockTime, LockupReason lockupReason, byte[] hash, - Consumer resultHandler, ExceptionHandler exceptionHandler) { - lockupTxService.publishLockupTx(lockupAmount, lockTime, lockupReason, hash, resultHandler, exceptionHandler); - } - - public Tuple2 getLockupTxMiningFeeAndTxVsize(Coin lockupAmount, - int lockTime, - LockupReason lockupReason, - byte[] hash) - throws InsufficientMoneyException, IOException, TransactionVerificationException, WalletException { - return lockupTxService.getMiningFeeAndTxVsize(lockupAmount, lockTime, lockupReason, hash); - } - - public void publishUnlockTx(String lockupTxId, Consumer resultHandler, - ExceptionHandler exceptionHandler) { - unlockTxService.publishUnlockTx(lockupTxId, resultHandler, exceptionHandler); - } - - public Tuple2 getUnlockTxMiningFeeAndTxVsize(String lockupTxId) - throws InsufficientMoneyException, TransactionVerificationException, WalletException { - return unlockTxService.getMiningFeeAndTxVsize(lockupTxId); - } - - public long getTotalLockupAmount() { - return daoStateService.getTotalLockupAmount(); - } - - public long getTotalAmountOfUnLockingTxOutputs() { - return daoStateService.getTotalAmountOfUnLockingTxOutputs(); - } - - public long getTotalAmountOfUnLockedTxOutputs() { - return daoStateService.getTotalAmountOfUnLockedTxOutputs(); - } - - public long getTotalAmountOfConfiscatedTxOutputs() { - return daoStateService.getTotalAmountOfConfiscatedTxOutputs(); - } - - public long getTotalAmountOfInvalidatedBsq() { - return daoStateService.getTotalAmountOfInvalidatedBsq(); - } - - // Contains burned fee and invalidated bsq due invalid txs - public long getTotalAmountOfBurntBsq() { - return daoStateService.getTotalAmountOfBurntBsq(); - } - - public List getInvalidTxs() { - return daoStateService.getInvalidTxs(); - } - - public List getIrregularTxs() { - return daoStateService.getIrregularTxs(); - } - - public long getTotalAmountOfUnspentTxOutputs() { - // Does not consider confiscated outputs (they stay as utxo) - return daoStateService.getUnspentTxOutputMap().values().stream().mapToLong(BaseTxOutput::getValue).sum(); - } - - public Optional getLockTime(String txId) { - return daoStateService.getLockTime(txId); - } - - - public List getAllBonds() { - List bonds = new ArrayList<>(bondedReputationRepository.getBonds()); - bonds.addAll(bondedRolesRepository.getBonds()); - return bonds; - } - - public List getAllActiveBonds() { - List bonds = new ArrayList<>(bondedReputationRepository.getActiveBonds()); - bonds.addAll(bondedRolesRepository.getActiveBonds()); - return bonds; - } - - public ObservableList getMyBondedReputations() { - return myBondedReputationRepository.getMyBondedReputations(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Use case: Present transaction related state - /////////////////////////////////////////////////////////////////////////////////////////// - - public Optional getTx(String txId) { - return daoStateService.getTx(txId); - } - - public int getGenesisBlockHeight() { - return daoStateService.getGenesisBlockHeight(); - } - - public String getGenesisTxId() { - return daoStateService.getGenesisTxId(); - } - - public Coin getGenesisTotalSupply() { - return daoStateService.getGenesisTotalSupply(); - } - - public int getNumIssuanceTransactions(IssuanceType issuanceType) { - return daoStateService.getIssuanceSetForType(issuanceType).size(); - } - - public Set getBurntFeeTxs() { - return daoStateService.getBurntFeeTxs(); - } - - public Set getUnspentTxOutputs() { - return daoStateService.getUnspentTxOutputs(); - } - - public int getNumTxs() { - return daoStateService.getNumTxs(); - } - - public Optional getLockupTxOutput(String txId) { - return daoStateService.getLockupTxOutput(txId); - } - - public long getTotalIssuedAmount(IssuanceType issuanceType) { - return daoStateService.getTotalIssuedAmount(issuanceType); - } - - public long getBlockTime(int issuanceBlockHeight) { - return daoStateService.getBlockTime(issuanceBlockHeight); - } - - public int getIssuanceBlockHeight(String txId) { - return daoStateService.getIssuanceBlockHeight(txId); - } - - public boolean isIssuanceTx(String txId, IssuanceType issuanceType) { - return daoStateService.isIssuanceTx(txId, issuanceType); - } - - public boolean hasTxBurntFee(String hashAsString) { - return daoStateService.hasTxBurntFee(hashAsString); - } - - public Optional getOptionalTxType(String txId) { - return daoStateService.getOptionalTxType(txId); - } - - public TxType getTxType(String txId) { - return daoStateService.getTx(txId).map(Tx::getTxType).orElse(TxType.UNDEFINED_TX_TYPE); - } - - public boolean isInPhaseButNotLastBlock(DaoPhase.Phase phase) { - return periodService.isInPhaseButNotLastBlock(phase); - } - - public boolean isTxInCorrectCycle(int txHeight, int chainHeight) { - return periodService.isTxInCorrectCycle(txHeight, chainHeight); - } - - public boolean isTxInCorrectCycle(String txId, int chainHeight) { - return periodService.isTxInCorrectCycle(txId, chainHeight); - } - - public Coin getMinCompensationRequestAmount() { - return CompensationConsensus.getMinCompensationRequestAmount(daoStateService, periodService.getChainHeight()); - } - - public Coin getMaxCompensationRequestAmount() { - return CompensationConsensus.getMaxCompensationRequestAmount(daoStateService, periodService.getChainHeight()); - } - - public Coin getMinReimbursementRequestAmount() { - return ReimbursementConsensus.getMinReimbursementRequestAmount(daoStateService, periodService.getChainHeight()); - } - - public Coin getMaxReimbursementRequestAmount() { - return ReimbursementConsensus.getMaxReimbursementRequestAmount(daoStateService, periodService.getChainHeight()); - } - - public String getParamValue(Param param) { - return getParamValue(param, periodService.getChainHeight()); - } - - public String getParamValue(Param param, int blockHeight) { - return daoStateService.getParamValue(param, blockHeight); - } - - public void resyncDaoStateFromGenesis(Runnable resultHandler) { - daoStateStorageService.resyncDaoStateFromGenesis(resultHandler); - } - - public void resyncDaoStateFromResources(File storageDir) throws IOException { - daoStateStorageService.resyncDaoStateFromResources(storageDir); - } - - public boolean isMyRole(Role role) { - return bondedRolesRepository.isMyRole(role); - } - - public Optional getBondByLockupTxId(String lockupTxId) { - return getAllBonds().stream().filter(e -> lockupTxId.equals(e.getLockupTxId())).findAny(); - } - - public double getRequiredThreshold(Proposal proposal) { - return proposalService.getRequiredThreshold(proposal); - } - - public Coin getRequiredQuorum(Proposal proposal) { - return proposalService.getRequiredQuorum(proposal); - } - - public long getRequiredBond(Optional roleProposal) { - Optional bondedRoleType = roleProposal.map(e -> e.getRole().getBondedRoleType()); - checkArgument(bondedRoleType.isPresent(), "bondedRoleType must be present"); - int height = roleProposal.flatMap(p -> daoStateService.getTx(p.getTxId())) - .map(BaseTx::getBlockHeight) - .orElse(daoStateService.getChainHeight()); - long requiredBondUnit = roleProposal.map(RoleProposal::getRequiredBondUnit) - .orElse(bondedRoleType.get().getRequiredBondUnit()); - long baseFactor = daoStateService.getParamValueAsCoin(Param.BONDED_ROLE_FACTOR, height).value; - return requiredBondUnit * baseFactor; - } - - public long getRequiredBond(RoleProposal roleProposal) { - return getRequiredBond(Optional.of(roleProposal)); - } - - public long getRequiredBond(BondedRoleType bondedRoleType) { - int height = daoStateService.getChainHeight(); - long requiredBondUnit = bondedRoleType.getRequiredBondUnit(); - long baseFactor = daoStateService.getParamValueAsCoin(Param.BONDED_ROLE_FACTOR, height).value; - return requiredBondUnit * baseFactor; - } - - public Set getAllPastParamValues(Param param) { - Set set = new HashSet<>(); - periodService.getCycles().forEach(cycle -> { - set.add(getParamValue(param, cycle.getHeightOfFirstBlock())); - }); - return set; - } - - public Set getAllDonationAddresses() { - // We support any of the past addresses as well as in case the peer has not enabled the DAO or is out of sync we - // do not want to break validation. - Set allPastParamValues = getAllPastParamValues(Param.RECIPIENT_BTC_ADDRESS); - - // If Dao is deactivated we need to add the default address as getAllPastParamValues will not return us any. - if (allPastParamValues.isEmpty()) { - allPastParamValues.add(Param.RECIPIENT_BTC_ADDRESS.getDefaultValue()); - } - - if (Config.baseCurrencyNetwork().isMainnet()) { - // If Dao is deactivated we need to add the past addresses used as well. - // This list need to be updated once a new address gets defined. - allPastParamValues.add("3EtUWqsGThPtjwUczw27YCo6EWvQdaPUyp"); // burning man 2019 - allPastParamValues.add("3A8Zc1XioE2HRzYfbb5P8iemCS72M6vRJV"); // burningman2 - allPastParamValues.add("34VLFgtFKAtwTdZ5rengTT2g2zC99sWQLC"); // burningman3 (https://github.com/bisq-network/roles/issues/80#issuecomment-723577776) - } - - return allPastParamValues; - } -} diff --git a/core/src/main/java/bisq/core/dao/DaoKillSwitch.java b/core/src/main/java/bisq/core/dao/DaoKillSwitch.java deleted file mode 100644 index 2dbf54c53c..0000000000 --- a/core/src/main/java/bisq/core/dao/DaoKillSwitch.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao; - -import bisq.core.dao.exceptions.DaoDisabledException; -import bisq.core.filter.Filter; -import bisq.core.filter.FilterManager; - -import bisq.common.app.Version; - -import javax.inject.Inject; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -@Slf4j -public class DaoKillSwitch implements DaoSetupService { - private final FilterManager filterManager; - - @Getter - private boolean daoDisabled; - - @Inject - public DaoKillSwitch(FilterManager filterManager) { - this.filterManager = filterManager; - } - - @Override - public void addListeners() { - filterManager.filterProperty().addListener((observable, oldValue, newValue) -> applyFilter(newValue)); - } - - @Override - public void start() { - applyFilter(filterManager.getFilter()); - } - - private void applyFilter(@Nullable Filter filter) { - if (filter == null) { - daoDisabled = false; - return; - } - - boolean requireUpdateToNewVersion = false; - String disableDaoBelowVersion = filter.getDisableDaoBelowVersion(); - if (disableDaoBelowVersion != null && !disableDaoBelowVersion.isEmpty()) { - requireUpdateToNewVersion = Version.isNewVersion(disableDaoBelowVersion); - } - - daoDisabled = requireUpdateToNewVersion || filter.isDisableDao(); - } - - public void assertDaoIsNotDisabled() { - if (isDaoDisabled()) { - throw new DaoDisabledException("The DAO features have been disabled by the Bisq developers. " + - "Please check out the Bisq Forum for further information."); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/DaoModule.java b/core/src/main/java/bisq/core/dao/DaoModule.java deleted file mode 100644 index 87d5f9e7a0..0000000000 --- a/core/src/main/java/bisq/core/dao/DaoModule.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao; - -import bisq.core.dao.governance.asset.AssetService; -import bisq.core.dao.governance.ballot.BallotListPresentation; -import bisq.core.dao.governance.ballot.BallotListService; -import bisq.core.dao.governance.blindvote.BlindVoteListService; -import bisq.core.dao.governance.blindvote.BlindVoteValidator; -import bisq.core.dao.governance.blindvote.MyBlindVoteListService; -import bisq.core.dao.governance.blindvote.network.RepublishGovernanceDataHandler; -import bisq.core.dao.governance.blindvote.storage.BlindVoteStorageService; -import bisq.core.dao.governance.blindvote.storage.BlindVoteStore; -import bisq.core.dao.governance.bond.lockup.LockupTxService; -import bisq.core.dao.governance.bond.reputation.BondedReputationRepository; -import bisq.core.dao.governance.bond.reputation.MyBondedReputationRepository; -import bisq.core.dao.governance.bond.reputation.MyReputationListService; -import bisq.core.dao.governance.bond.role.BondedRolesRepository; -import bisq.core.dao.governance.bond.unlock.UnlockTxService; -import bisq.core.dao.governance.myvote.MyVoteListService; -import bisq.core.dao.governance.period.CycleService; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proofofburn.MyProofOfBurnListService; -import bisq.core.dao.governance.proofofburn.ProofOfBurnService; -import bisq.core.dao.governance.proposal.MyProposalListService; -import bisq.core.dao.governance.proposal.ProposalListPresentation; -import bisq.core.dao.governance.proposal.ProposalService; -import bisq.core.dao.governance.proposal.ProposalValidatorProvider; -import bisq.core.dao.governance.proposal.compensation.CompensationProposalFactory; -import bisq.core.dao.governance.proposal.compensation.CompensationValidator; -import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondProposalFactory; -import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondValidator; -import bisq.core.dao.governance.proposal.generic.GenericProposalFactory; -import bisq.core.dao.governance.proposal.generic.GenericProposalValidator; -import bisq.core.dao.governance.proposal.param.ChangeParamProposalFactory; -import bisq.core.dao.governance.proposal.param.ChangeParamValidator; -import bisq.core.dao.governance.proposal.reimbursement.ReimbursementProposalFactory; -import bisq.core.dao.governance.proposal.reimbursement.ReimbursementValidator; -import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetProposalFactory; -import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetValidator; -import bisq.core.dao.governance.proposal.role.RoleProposalFactory; -import bisq.core.dao.governance.proposal.role.RoleValidator; -import bisq.core.dao.governance.proposal.storage.appendonly.ProposalStorageService; -import bisq.core.dao.governance.proposal.storage.appendonly.ProposalStore; -import bisq.core.dao.governance.proposal.storage.temp.TempProposalStorageService; -import bisq.core.dao.governance.proposal.storage.temp.TempProposalStore; -import bisq.core.dao.governance.voteresult.MissingDataRequestService; -import bisq.core.dao.governance.voteresult.VoteResultService; -import bisq.core.dao.governance.voteresult.issuance.IssuanceService; -import bisq.core.dao.governance.votereveal.VoteRevealService; -import bisq.core.dao.monitoring.BlindVoteStateMonitoringService; -import bisq.core.dao.monitoring.DaoStateMonitoringService; -import bisq.core.dao.monitoring.ProposalStateMonitoringService; -import bisq.core.dao.monitoring.network.BlindVoteStateNetworkService; -import bisq.core.dao.monitoring.network.DaoStateNetworkService; -import bisq.core.dao.monitoring.network.ProposalStateNetworkService; -import bisq.core.dao.node.BsqNodeProvider; -import bisq.core.dao.node.explorer.ExportJsonFilesService; -import bisq.core.dao.node.full.FullNode; -import bisq.core.dao.node.full.RpcService; -import bisq.core.dao.node.full.network.FullNodeNetworkService; -import bisq.core.dao.node.lite.LiteNode; -import bisq.core.dao.node.lite.network.LiteNodeNetworkService; -import bisq.core.dao.node.parser.BlockParser; -import bisq.core.dao.node.parser.TxParser; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.DaoStateSnapshotService; -import bisq.core.dao.state.GenesisTxInfo; -import bisq.core.dao.state.model.DaoState; -import bisq.core.dao.state.storage.DaoStateStorageService; -import bisq.core.dao.state.unconfirmed.UnconfirmedBsqChangeOutputListService; - -import bisq.common.app.AppModule; -import bisq.common.config.Config; - -import com.google.inject.Singleton; - -import static com.google.inject.name.Names.named; - -public class DaoModule extends AppModule { - - public DaoModule(Config config) { - super(config); - } - - @Override - protected void configure() { - bind(DaoSetup.class).in(Singleton.class); - bind(DaoFacade.class).in(Singleton.class); - bind(DaoEventCoordinator.class).in(Singleton.class); - bind(DaoKillSwitch.class).in(Singleton.class); - - // Node, parser - bind(BsqNodeProvider.class).in(Singleton.class); - bind(FullNode.class).in(Singleton.class); - bind(LiteNode.class).in(Singleton.class); - bind(RpcService.class).in(Singleton.class); - bind(BlockParser.class).in(Singleton.class); - bind(FullNodeNetworkService.class).in(Singleton.class); - bind(LiteNodeNetworkService.class).in(Singleton.class); - - // DaoState - bind(GenesisTxInfo.class).in(Singleton.class); - bind(DaoState.class).in(Singleton.class); - bind(DaoStateService.class).in(Singleton.class); - bind(DaoStateSnapshotService.class).in(Singleton.class); - bind(DaoStateStorageService.class).in(Singleton.class); - bind(DaoStateMonitoringService.class).in(Singleton.class); - bind(DaoStateNetworkService.class).in(Singleton.class); - bind(ProposalStateMonitoringService.class).in(Singleton.class); - bind(ProposalStateNetworkService.class).in(Singleton.class); - bind(BlindVoteStateMonitoringService.class).in(Singleton.class); - bind(BlindVoteStateNetworkService.class).in(Singleton.class); - bind(UnconfirmedBsqChangeOutputListService.class).in(Singleton.class); - - bind(ExportJsonFilesService.class).in(Singleton.class); - - // Period - bind(CycleService.class).in(Singleton.class); - bind(PeriodService.class).in(Singleton.class); - - // blockchain parser - bind(TxParser.class).in(Singleton.class); - - // Proposal - bind(ProposalService.class).in(Singleton.class); - - bind(MyProposalListService.class).in(Singleton.class); - bind(ProposalListPresentation.class).in(Singleton.class); - - bind(ProposalStore.class).in(Singleton.class); - bind(ProposalStorageService.class).in(Singleton.class); - bind(TempProposalStore.class).in(Singleton.class); - bind(TempProposalStorageService.class).in(Singleton.class); - - bind(ProposalValidatorProvider.class).in(Singleton.class); - - bind(CompensationValidator.class).in(Singleton.class); - bind(CompensationProposalFactory.class).in(Singleton.class); - - bind(ReimbursementValidator.class).in(Singleton.class); - bind(ReimbursementProposalFactory.class).in(Singleton.class); - - bind(ChangeParamValidator.class).in(Singleton.class); - bind(ChangeParamProposalFactory.class).in(Singleton.class); - - bind(RoleValidator.class).in(Singleton.class); - bind(RoleProposalFactory.class).in(Singleton.class); - - bind(ConfiscateBondValidator.class).in(Singleton.class); - bind(ConfiscateBondProposalFactory.class).in(Singleton.class); - - bind(GenericProposalValidator.class).in(Singleton.class); - bind(GenericProposalFactory.class).in(Singleton.class); - - bind(RemoveAssetValidator.class).in(Singleton.class); - bind(RemoveAssetProposalFactory.class).in(Singleton.class); - - // Ballot - bind(BallotListService.class).in(Singleton.class); - bind(BallotListPresentation.class).in(Singleton.class); - - // MyVote - bind(MyVoteListService.class).in(Singleton.class); - - // BlindVote - bind(BlindVoteListService.class).in(Singleton.class); - bind(BlindVoteStore.class).in(Singleton.class); - bind(BlindVoteStorageService.class).in(Singleton.class); - bind(BlindVoteValidator.class).in(Singleton.class); - bind(MyBlindVoteListService.class).in(Singleton.class); - - // VoteReveal - bind(VoteRevealService.class).in(Singleton.class); - - // VoteResult - bind(VoteResultService.class).in(Singleton.class); - bind(MissingDataRequestService.class).in(Singleton.class); - bind(IssuanceService.class).in(Singleton.class); - bind(RepublishGovernanceDataHandler.class).in(Singleton.class); - - // Genesis - bindConstant().annotatedWith(named(Config.GENESIS_TX_ID)).to(config.genesisTxId); - bindConstant().annotatedWith(named(Config.GENESIS_BLOCK_HEIGHT)).to(config.genesisBlockHeight); - bindConstant().annotatedWith(named(Config.GENESIS_TOTAL_SUPPLY)).to(config.genesisTotalSupply); - - // Bonds - bind(LockupTxService.class).in(Singleton.class); - bind(UnlockTxService.class).in(Singleton.class); - bind(BondedRolesRepository.class).in(Singleton.class); - bind(BondedReputationRepository.class).in(Singleton.class); - bind(MyReputationListService.class).in(Singleton.class); - bind(MyBondedReputationRepository.class).in(Singleton.class); - - // Asset - bind(AssetService.class).in(Singleton.class); - - // Proof of burn - bind(ProofOfBurnService.class).in(Singleton.class); - bind(MyProofOfBurnListService.class).in(Singleton.class); - - // Options - bindConstant().annotatedWith(named(Config.RPC_USER)).to(config.rpcUser); - bindConstant().annotatedWith(named(Config.RPC_PASSWORD)).to(config.rpcPassword); - bindConstant().annotatedWith(named(Config.RPC_HOST)).to(config.rpcHost); - bindConstant().annotatedWith(named(Config.RPC_PORT)).to(config.rpcPort); - bindConstant().annotatedWith(named(Config.RPC_BLOCK_NOTIFICATION_PORT)).to(config.rpcBlockNotificationPort); - bindConstant().annotatedWith(named(Config.RPC_BLOCK_NOTIFICATION_HOST)).to(config.rpcBlockNotificationHost); - bindConstant().annotatedWith(named(Config.DUMP_BLOCKCHAIN_DATA)).to(config.dumpBlockchainData); - bindConstant().annotatedWith(named(Config.FULL_DAO_NODE)).to(config.fullDaoNode); - bindConstant().annotatedWith(named(Config.DAO_ACTIVATED)).to(config.daoActivated); - } -} - diff --git a/core/src/main/java/bisq/core/dao/DaoSetup.java b/core/src/main/java/bisq/core/dao/DaoSetup.java deleted file mode 100644 index b8c13582f7..0000000000 --- a/core/src/main/java/bisq/core/dao/DaoSetup.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao; - -import bisq.core.dao.governance.asset.AssetService; -import bisq.core.dao.governance.ballot.BallotListService; -import bisq.core.dao.governance.blindvote.BlindVoteListService; -import bisq.core.dao.governance.blindvote.MyBlindVoteListService; -import bisq.core.dao.governance.bond.reputation.BondedReputationRepository; -import bisq.core.dao.governance.bond.reputation.MyBondedReputationRepository; -import bisq.core.dao.governance.bond.reputation.MyReputationListService; -import bisq.core.dao.governance.bond.role.BondedRolesRepository; -import bisq.core.dao.governance.period.CycleService; -import bisq.core.dao.governance.proofofburn.ProofOfBurnService; -import bisq.core.dao.governance.proposal.ProposalListPresentation; -import bisq.core.dao.governance.proposal.ProposalService; -import bisq.core.dao.governance.voteresult.MissingDataRequestService; -import bisq.core.dao.governance.voteresult.VoteResultService; -import bisq.core.dao.governance.votereveal.VoteRevealService; -import bisq.core.dao.monitoring.BlindVoteStateMonitoringService; -import bisq.core.dao.monitoring.DaoStateMonitoringService; -import bisq.core.dao.monitoring.ProposalStateMonitoringService; -import bisq.core.dao.node.BsqNode; -import bisq.core.dao.node.BsqNodeProvider; -import bisq.core.dao.node.explorer.ExportJsonFilesService; -import bisq.core.dao.state.DaoStateService; - -import com.google.inject.Inject; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -/** - * High level entry point for Dao domain. - * We initialize all main service classes here to be sure they are started. - */ -public class DaoSetup { - private final BsqNode bsqNode; - private final List daoSetupServices = new ArrayList<>(); - - @Inject - public DaoSetup(BsqNodeProvider bsqNodeProvider, - DaoStateService daoStateService, - CycleService cycleService, - BallotListService ballotListService, - ProposalService proposalService, - ProposalListPresentation proposalListPresentation, - BlindVoteListService blindVoteListService, - MyBlindVoteListService myBlindVoteListService, - VoteRevealService voteRevealService, - VoteResultService voteResultService, - MissingDataRequestService missingDataRequestService, - BondedReputationRepository bondedReputationRepository, - BondedRolesRepository bondedRolesRepository, - MyReputationListService myReputationListService, - MyBondedReputationRepository myBondedReputationRepository, - AssetService assetService, - ProofOfBurnService proofOfBurnService, - DaoFacade daoFacade, - ExportJsonFilesService exportJsonFilesService, - DaoKillSwitch daoKillSwitch, - DaoStateMonitoringService daoStateMonitoringService, - ProposalStateMonitoringService proposalStateMonitoringService, - BlindVoteStateMonitoringService blindVoteStateMonitoringService, - DaoEventCoordinator daoEventCoordinator) { - - bsqNode = bsqNodeProvider.getBsqNode(); - - // We need to take care of order of execution. - - // For order critical event flow we use the daoEventCoordinator to delegate the calls from anonymous listeners - // to concrete clients. - daoSetupServices.add(daoEventCoordinator); - - daoSetupServices.add(daoStateService); - daoSetupServices.add(cycleService); - daoSetupServices.add(ballotListService); - daoSetupServices.add(proposalService); - daoSetupServices.add(proposalListPresentation); - daoSetupServices.add(blindVoteListService); - daoSetupServices.add(myBlindVoteListService); - daoSetupServices.add(voteRevealService); - daoSetupServices.add(voteResultService); - daoSetupServices.add(missingDataRequestService); - daoSetupServices.add(bondedReputationRepository); - daoSetupServices.add(bondedRolesRepository); - daoSetupServices.add(myReputationListService); - daoSetupServices.add(myBondedReputationRepository); - daoSetupServices.add(assetService); - daoSetupServices.add(proofOfBurnService); - daoSetupServices.add(daoFacade); - daoSetupServices.add(exportJsonFilesService); - daoSetupServices.add(daoKillSwitch); - daoSetupServices.add(daoStateMonitoringService); - daoSetupServices.add(proposalStateMonitoringService); - daoSetupServices.add(blindVoteStateMonitoringService); - - daoSetupServices.add(bsqNodeProvider.getBsqNode()); - } - - public void onAllServicesInitialized(Consumer errorMessageHandler, - Consumer warnMessageHandler) { - bsqNode.setErrorMessageHandler(errorMessageHandler); - bsqNode.setWarnMessageHandler(warnMessageHandler); - - // We add first all listeners at all services and then call the start methods. - // Some services are listening on others so we need to make sure that the - // listeners are set before we call start as that might trigger state change - // which triggers listeners. - daoSetupServices.forEach(DaoSetupService::addListeners); - daoSetupServices.forEach(DaoSetupService::start); - } - - public void shutDown() { - bsqNode.shutDown(); - } -} diff --git a/core/src/main/java/bisq/core/dao/DaoSetupService.java b/core/src/main/java/bisq/core/dao/DaoSetupService.java deleted file mode 100644 index 86974dd386..0000000000 --- a/core/src/main/java/bisq/core/dao/DaoSetupService.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao; - -/** - * All main service classes implements that interface to guarantee a controlled - * startup sequence. - */ -public interface DaoSetupService { - void addListeners(); - - void start(); -} diff --git a/core/src/main/java/bisq/core/dao/exceptions/DaoDisabledException.java b/core/src/main/java/bisq/core/dao/exceptions/DaoDisabledException.java deleted file mode 100644 index 7845ebc98d..0000000000 --- a/core/src/main/java/bisq/core/dao/exceptions/DaoDisabledException.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.exceptions; - -public class DaoDisabledException extends RuntimeException { - public DaoDisabledException(String message) { - super(message); - } -} diff --git a/core/src/main/java/bisq/core/dao/exceptions/PublishToP2PNetworkException.java b/core/src/main/java/bisq/core/dao/exceptions/PublishToP2PNetworkException.java deleted file mode 100644 index e3be1947c4..0000000000 --- a/core/src/main/java/bisq/core/dao/exceptions/PublishToP2PNetworkException.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.exceptions; - -public class PublishToP2PNetworkException extends RuntimeException { - public PublishToP2PNetworkException(String message) { - super(message); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/ConsensusCritical.java b/core/src/main/java/bisq/core/dao/governance/ConsensusCritical.java deleted file mode 100644 index 1162a5e5b1..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/ConsensusCritical.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance; - -/** - * Marker interface for classes which are critical in the vote consensus process. Any changes in that class might cause - * consensus failures with older versions. - */ -public interface ConsensusCritical { -} diff --git a/core/src/main/java/bisq/core/dao/governance/asset/AssetConsensus.java b/core/src/main/java/bisq/core/dao/governance/asset/AssetConsensus.java deleted file mode 100644 index f144f00eb3..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/asset/AssetConsensus.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.asset; - -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.OpReturnType; - -import bisq.common.app.Version; -import bisq.common.crypto.Hash; - -import org.bitcoinj.core.Coin; - -import com.google.common.base.Charsets; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class AssetConsensus { - public static Coin getFeePerDay(DaoStateService daoStateService, int chainHeight) { - return daoStateService.getParamValueAsCoin(Param.ASSET_LISTING_FEE_PER_DAY, chainHeight); - } - - public static byte[] getHash(StatefulAsset statefulAsset) { - String stringInput = "AssetListingFee-" + statefulAsset.getTickerSymbol(); - final byte[] bytes = stringInput.getBytes(Charsets.UTF_8); - return Hash.getSha256Ripemd160hash(bytes); - } - - public static byte[] getOpReturnData(byte[] hash) { - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - outputStream.write(OpReturnType.ASSET_LISTING_FEE.getType()); - outputStream.write(Version.ASSET_LISTING_FEE); - outputStream.write(hash); - return outputStream.toByteArray(); - } catch (IOException e) { - // Not expected to happen ever - e.printStackTrace(); - log.error(e.toString()); - return new byte[0]; - } - } - - public static boolean hasOpReturnDataValidLength(byte[] opReturnData) { - return opReturnData.length == 22; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/asset/AssetService.java b/core/src/main/java/bisq/core/dao/governance/asset/AssetService.java deleted file mode 100644 index 0a6843a644..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/asset/AssetService.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.asset; - -import bisq.core.btc.exceptions.TransactionVerificationException; -import bisq.core.btc.exceptions.TxBroadcastException; -import bisq.core.btc.exceptions.WalletException; -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.TxBroadcaster; -import bisq.core.btc.wallet.WalletsManager; -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.proposal.TxException; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.BaseTx; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxType; -import bisq.core.dao.state.model.governance.EvaluatedProposal; -import bisq.core.dao.state.model.governance.RemoveAssetProposal; -import bisq.core.locale.CurrencyUtil; -import bisq.core.trade.statistics.TradeStatisticsManager; -import bisq.core.util.coin.BsqFormatter; - -import bisq.common.app.DevEnv; -import bisq.common.handlers.ErrorMessageHandler; -import bisq.common.handlers.ResultHandler; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.Transaction; - -import javax.inject.Inject; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import org.jetbrains.annotations.NotNull; - -import javax.annotation.Nullable; - -import static com.google.common.base.Preconditions.checkArgument; - -@Slf4j -public class AssetService implements DaoSetupService, DaoStateListener { - private static final long DEFAULT_LOOK_BACK_PERIOD = 120; // 120 days - - private final BsqWalletService bsqWalletService; - private final BtcWalletService btcWalletService; - private final WalletsManager walletsManager; - private final TradeStatisticsManager tradeStatisticsManager; - private final DaoStateService daoStateService; - private final BsqFormatter bsqFormatter; - - // Only accessed via getter which fills the list on demand - private final List lazyLoadedStatefulAssets = new ArrayList<>(); - private long bsqFeePerDay; - private long minVolumeInBtc; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public AssetService(BsqWalletService bsqWalletService, - BtcWalletService btcWalletService, - WalletsManager walletsManager, - TradeStatisticsManager tradeStatisticsManager, - DaoStateService daoStateService, - BsqFormatter bsqFormatter) { - this.bsqWalletService = bsqWalletService; - this.btcWalletService = btcWalletService; - this.walletsManager = walletsManager; - this.tradeStatisticsManager = tradeStatisticsManager; - this.daoStateService = daoStateService; - this.bsqFormatter = bsqFormatter; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - daoStateService.addDaoStateListener(this); - } - - @Override - @SuppressWarnings({"EmptyMethod"}) - public void start() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - int chainHeight = daoStateService.getChainHeight(); - bsqFeePerDay = daoStateService.getParamValueAsCoin(Param.ASSET_LISTING_FEE_PER_DAY, chainHeight).value; - minVolumeInBtc = daoStateService.getParamValueAsCoin(Param.ASSET_MIN_VOLUME, chainHeight).value; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public List getStatefulAssets() { - if (lazyLoadedStatefulAssets.isEmpty()) { - lazyLoadedStatefulAssets.addAll(CurrencyUtil.getSortedAssetStream() - .filter(asset -> !asset.getTickerSymbol().equals("BSQ")) - .map(StatefulAsset::new) - .collect(Collectors.toList())); - } - return lazyLoadedStatefulAssets; - } - - // Call takes bout 22 ms. Should be only called on demand (e.g. view is showing the data) - public void updateAssetStates() { - // For performance optimisation we map the trade stats to a temporary lookup map and convert it to a custom - // TradeAmountDateTuple object holding only the data we need. - Map> lookupMap = new HashMap<>(); - tradeStatisticsManager.getObservableTradeStatisticsSet().stream() - .filter(e -> CurrencyUtil.isCryptoCurrency(e.getCurrency())) - .forEach(e -> { - lookupMap.putIfAbsent(e.getCurrency(), new ArrayList<>()); - lookupMap.get(e.getCurrency()).add(new TradeAmountDateTuple(e.getAmount(), e.getDateAsLong())); - }); - - getStatefulAssets().stream() - .filter(e -> AssetState.REMOVED_BY_VOTING != e.getAssetState()) // if once set to REMOVED_BY_VOTING we ignore it for further processing - .forEach(statefulAsset -> { - AssetState assetState; - String tickerSymbol = statefulAsset.getTickerSymbol(); - if (wasAssetRemovedByVoting(tickerSymbol)) { - assetState = AssetState.REMOVED_BY_VOTING; - } else { - statefulAsset.setFeePayments(getFeePayments(statefulAsset)); - long lookBackPeriodInDays = getLookBackPeriodInDays(statefulAsset); - statefulAsset.setLookBackPeriodInDays(lookBackPeriodInDays); - long lookupDate = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(lookBackPeriodInDays); - long tradeVolume = getTradeVolume(lookupDate, lookupMap.get(tickerSymbol)); - statefulAsset.setTradeVolume(tradeVolume); - if (isInTrialPeriod(statefulAsset)) { - assetState = AssetState.IN_TRIAL_PERIOD; - } else if (tradeVolume >= minVolumeInBtc) { - assetState = AssetState.ACTIVELY_TRADED; - } else { - assetState = AssetState.DE_LISTED; - } - } - statefulAsset.setAssetState(assetState); - }); - - lookupMap.clear(); - } - - public boolean isActive(String tickerSymbol) { - return DevEnv.isDaoActivated() ? findAsset(tickerSymbol).map(StatefulAsset::isActive).orElse(false) : true; - } - - public Transaction payFee(StatefulAsset statefulAsset, - long listingFee) throws InsufficientMoneyException, TxException { - checkArgument(!statefulAsset.wasRemovedByVoting(), "Asset must not have been removed"); - checkArgument(listingFee >= getFeePerDay().value, "Fee must not be less then listing fee for 1 day."); - checkArgument(listingFee % 100 == 0, "Fee must be a multiple of 1 BSQ (100 satoshi)."); - try { - // We create a prepared Bsq Tx for the listing fee. - Transaction preparedBurnFeeTx = bsqWalletService.getPreparedBurnFeeTxForAssetListing(Coin.valueOf(listingFee)); - byte[] hash = AssetConsensus.getHash(statefulAsset); - byte[] opReturnData = AssetConsensus.getOpReturnData(hash); - // We add the BTC inputs for the miner fee. - Transaction txWithBtcFee = btcWalletService.completePreparedBurnBsqTx(preparedBurnFeeTx, opReturnData); - // We sign the BSQ inputs of the final tx. - Transaction transaction = bsqWalletService.signTx(txWithBtcFee); - log.info("Asset listing fee tx: " + transaction); - return transaction; - } catch (WalletException | TransactionVerificationException e) { - throw new TxException(e); - } - } - - public Coin getFeePerDay() { - return AssetConsensus.getFeePerDay(daoStateService, daoStateService.getChainHeight()); - } - - public void publishTransaction(Transaction transaction, ResultHandler resultHandler, - ErrorMessageHandler errorMessageHandler) { - walletsManager.publishAndCommitBsqTx(transaction, TxType.ASSET_LISTING_FEE, new TxBroadcaster.Callback() { - @Override - public void onSuccess(Transaction transaction) { - log.info("Asset listing fee tx has been published. TxId={}", transaction.getTxId().toString()); - resultHandler.handleResult(); - } - - @Override - public void onFailure(TxBroadcastException exception) { - errorMessageHandler.handleErrorMessage(exception.getMessage()); - } - }); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - // Get the trade volume from lookupDate until current date - private long getTradeVolume(long lookupDate, @Nullable List tradeAmountDateTupleList) { - if (tradeAmountDateTupleList == null) { - // Was never traded - return 0; - } - - return tradeAmountDateTupleList.stream() - .filter(e -> e.getTradeDate() > lookupDate) - .mapToLong(TradeAmountDateTuple::getTradeAmount) - .sum(); - } - - private boolean isInTrialPeriod(StatefulAsset statefulAsset) { - for (FeePayment feePayment : statefulAsset.getFeePayments()) { - Optional passedDays = feePayment.getPassedDays(daoStateService); - if (passedDays.isPresent()) { - long daysCoveredByFee = feePayment.daysCoveredByFee(bsqFeePerDay); - if (daysCoveredByFee >= passedDays.get()) { - return true; - } - } - } - return false; - } - - - @NotNull - private Long getLookBackPeriodInDays(StatefulAsset statefulAsset) { - // We need to use the block height of the fee payment tx not the current one as feePerDay might have been - // changed in the meantime. - long bsqFeePerDay = statefulAsset.getLastFeePayment() - .flatMap(feePayment -> daoStateService.getTx(feePayment.getTxId())) - .map(tx -> daoStateService.getParamValueAsCoin(Param.ASSET_LISTING_FEE_PER_DAY, tx.getBlockHeight()).value) - .orElse(bsqFormatter.parseParamValueToCoin(Param.ASSET_LISTING_FEE_PER_DAY, Param.ASSET_LISTING_FEE_PER_DAY.getDefaultValue()).value); - - return statefulAsset.getLastFeePayment() - .map(feePayment -> feePayment.daysCoveredByFee(bsqFeePerDay)) - .orElse(DEFAULT_LOOK_BACK_PERIOD); - } - - private List getFeePayments(StatefulAsset statefulAsset) { - return getFeeTxs(statefulAsset).stream() - .map(tx -> { - String txId = tx.getId(); - long burntFee = tx.getBurntFee(); - return new FeePayment(txId, burntFee); - }) - .collect(Collectors.toList()); - } - - private List getFeeTxs(StatefulAsset statefulAsset) { - return daoStateService.getAssetListingFeeOpReturnTxOutputs().stream() - .filter(txOutput -> { - byte[] hash = AssetConsensus.getHash(statefulAsset); - byte[] opReturnData = AssetConsensus.getOpReturnData(hash); - return Arrays.equals(opReturnData, txOutput.getOpReturnData()); - }) - .map(txOutput -> daoStateService.getTx(txOutput.getTxId()).orElse(null)) - .filter(Objects::nonNull) - .sorted(Comparator.comparing(BaseTx::getTime)) - .collect(Collectors.toList()); - } - - private Optional findAsset(String tickerSymbol) { - return getStatefulAssets().stream().filter(e -> e.getTickerSymbol().equals(tickerSymbol)).findAny(); - } - - private boolean wasAssetRemovedByVoting(String tickerSymbol) { - boolean isRemoved = getAcceptedRemoveAssetProposalStream() - .anyMatch(proposal -> proposal.getTickerSymbol().equals(tickerSymbol)); - if (isRemoved) - log.info("Asset '{}' was removed", CurrencyUtil.getNameAndCode(tickerSymbol)); - - return isRemoved; - } - - private Stream getAcceptedRemoveAssetProposalStream() { - return daoStateService.getEvaluatedProposalList().stream() - .filter(evaluatedProposal -> evaluatedProposal.getProposal() instanceof RemoveAssetProposal) - .filter(EvaluatedProposal::isAccepted) - .map(e -> ((RemoveAssetProposal) e.getProposal())); - } - - @Value - private static final class TradeAmountDateTuple { - private final long tradeAmount; - private final long tradeDate; - - TradeAmountDateTuple(long tradeAmount, long tradeDate) { - this.tradeAmount = tradeAmount; - this.tradeDate = tradeDate; - } - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/asset/AssetState.java b/core/src/main/java/bisq/core/dao/governance/asset/AssetState.java deleted file mode 100644 index 24fed67670..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/asset/AssetState.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.asset; - -/** - * Maintain translation stings ("dao.assetState.*") - */ -public enum AssetState { - UNDEFINED, - IN_TRIAL_PERIOD, - ACTIVELY_TRADED, - DE_LISTED, - REMOVED_BY_VOTING // Was removed by voting -} diff --git a/core/src/main/java/bisq/core/dao/governance/asset/FeePayment.java b/core/src/main/java/bisq/core/dao/governance/asset/FeePayment.java deleted file mode 100644 index 55c26c7ebd..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/asset/FeePayment.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.asset; - -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Tx; - -import java.util.Optional; - -import lombok.Value; - -@Value -public class FeePayment { - private final String txId; - private final long fee; - - FeePayment(String txId, long fee) { - this.txId = txId; - this.fee = fee; - } - - public long daysCoveredByFee(long bsqFeePerDay) { - return bsqFeePerDay > 0 ? fee / bsqFeePerDay : 0; - } - - public Optional getPassedDays(DaoStateService daoStateService) { - Optional optionalTx = daoStateService.getTx(txId); - if (optionalTx.isPresent()) { - int passedBlocks = daoStateService.getChainHeight() - optionalTx.get().getBlockHeight(); - return Optional.of(passedBlocks / 144); - } else { - return Optional.empty(); - } - } - - @Override - public String toString() { - return "FeePayment{" + - "\n txId='" + txId + '\'' + - ",\n fee=" + fee + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/asset/StatefulAsset.java b/core/src/main/java/bisq/core/dao/governance/asset/StatefulAsset.java deleted file mode 100644 index 4846e3906c..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/asset/StatefulAsset.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.asset; - -import bisq.core.locale.CurrencyUtil; - -import bisq.asset.Asset; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@Getter -public class StatefulAsset { - private final Asset asset; - @Setter - private AssetState assetState = AssetState.UNDEFINED; - private List feePayments = new ArrayList<>(); - @Setter - private long tradeVolume; - @Setter - private long lookBackPeriodInDays; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - public StatefulAsset(Asset asset) { - this.asset = asset; - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public String getNameAndCode() { - return CurrencyUtil.getNameAndCode(getTickerSymbol()); - } - - public String getTickerSymbol() { - return asset.getTickerSymbol(); - } - - public void setFeePayments(List feePayments) { - this.feePayments = feePayments; - } - - public Optional getLastFeePayment() { - return feePayments.isEmpty() ? Optional.empty() : Optional.of(feePayments.get(feePayments.size() - 1)); - } - - public long getTotalFeesPaid() { - return feePayments.stream().mapToLong(FeePayment::getFee).sum(); - } - - public long getFeeOfTrialPeriod() { - return getLastFeePayment() - .map(FeePayment::getFee) - .filter(e -> assetState == AssetState.IN_TRIAL_PERIOD) - .orElse(0L); - } - - public boolean isActive() { - return !wasRemovedByVoting() && !isDeListed(); - } - - public boolean wasRemovedByVoting() { - return assetState == AssetState.REMOVED_BY_VOTING; - } - - private boolean isDeListed() { - return assetState == AssetState.DE_LISTED; - } - - - @Override - public String toString() { - return "StatefulAsset{" + - "\n asset=" + asset + - ",\n assetState=" + assetState + - ",\n feePayments=" + feePayments + - ",\n tradeVolume=" + tradeVolume + - ",\n lookBackPeriodInDays=" + lookBackPeriodInDays + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/ballot/BallotListPresentation.java b/core/src/main/java/bisq/core/dao/governance/ballot/BallotListPresentation.java deleted file mode 100644 index e7e148061a..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/ballot/BallotListPresentation.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.ballot; - -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.ProposalValidator; -import bisq.core.dao.governance.proposal.ProposalValidatorProvider; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.governance.Ballot; -import bisq.core.dao.state.model.governance.Proposal; - -import com.google.inject.Inject; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.collections.transformation.FilteredList; - -import java.util.List; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Provides the ballots as observableList for presentation classes. - */ -@Slf4j -public class BallotListPresentation implements BallotListService.BallotListChangeListener, DaoStateListener { - private final BallotListService ballotListService; - private final PeriodService periodService; - private final ProposalValidatorProvider proposalValidatorProvider; - - @Getter - private final ObservableList allBallots = FXCollections.observableArrayList(); - @Getter - private final FilteredList ballotsOfCycle = new FilteredList<>(allBallots); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public BallotListPresentation(BallotListService ballotListService, - PeriodService periodService, - DaoStateService daoStateService, - ProposalValidatorProvider proposalValidatorProvider) { - this.ballotListService = ballotListService; - this.periodService = periodService; - this.proposalValidatorProvider = proposalValidatorProvider; - - daoStateService.addDaoStateListener(this); - ballotListService.addListener(this); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - ballotsOfCycle.setPredicate(ballot -> periodService.isTxInCorrectCycle(ballot.getTxId(), block.getHeight())); - onListChanged(ballotListService.getValidatedBallotList()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // BallotListService.BallotListChangeListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onListChanged(List list) { - allBallots.clear(); - allBallots.addAll(list); - } - - // We cannot do a phase and cycle check as we are interested in historical ballots as well - public List getAllValidBallots() { - return allBallots.stream() - .filter(ballot -> { - Proposal proposal = ballot.getProposal(); - ProposalValidator validator = proposalValidatorProvider.getValidator(proposal); - return validator.areDataFieldsValid(proposal) && validator.isTxTypeValid(proposal); - }) - .collect(Collectors.toList()); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/ballot/BallotListService.java b/core/src/main/java/bisq/core/dao/governance/ballot/BallotListService.java deleted file mode 100644 index 9662bdd9b1..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/ballot/BallotListService.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.ballot; - -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.ProposalService; -import bisq.core.dao.governance.proposal.ProposalValidatorProvider; -import bisq.core.dao.governance.proposal.storage.appendonly.ProposalPayload; -import bisq.core.dao.state.model.governance.Ballot; -import bisq.core.dao.state.model.governance.BallotList; -import bisq.core.dao.state.model.governance.Proposal; -import bisq.core.dao.state.model.governance.Vote; - -import bisq.common.app.DevEnv; -import bisq.common.persistence.PersistenceManager; -import bisq.common.proto.persistable.PersistedDataHost; - -import javax.inject.Inject; - -import javafx.collections.ListChangeListener.Change; -import javafx.collections.ObservableList; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -/** - * Takes the proposals from the append only store and makes Ballots out of it (vote is null). - * Applies voting on individual ballots and persist the list. - * The BallotList contains all ballots of all cycles. - */ -@Slf4j -public class BallotListService implements PersistedDataHost, DaoSetupService { - public interface BallotListChangeListener { - void onListChanged(List list); - } - - private final ProposalService proposalService; - private final PeriodService periodService; - private final ProposalValidatorProvider validatorProvider; - private final PersistenceManager persistenceManager; - - private final BallotList ballotList = new BallotList(); - private final List listeners = new CopyOnWriteArrayList<>(); - - @Inject - public BallotListService(ProposalService proposalService, - PeriodService periodService, - ProposalValidatorProvider validatorProvider, - PersistenceManager persistenceManager) { - this.proposalService = proposalService; - this.periodService = periodService; - this.validatorProvider = validatorProvider; - this.persistenceManager = persistenceManager; - - this.persistenceManager.initialize(ballotList, PersistenceManager.Source.NETWORK); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public final void addListeners() { - ObservableList payloads = proposalService.getProposalPayloads(); - payloads.addListener(this::onChanged); - } - - private void onChanged(Change change) { - change.next(); - if (change.wasAdded()) { - List addedPayloads = change.getAddedSubList(); - addedPayloads.stream() - .map(ProposalPayload::getProposal) - .filter(this::isNewProposal) - .forEach(this::registerProposalAsBallot); - requestPersistence(); - } - } - - private boolean isNewProposal(Proposal proposal) { - return ballotList.stream() - .map(Ballot::getProposal) - .noneMatch(proposal::equals); - } - - private void registerProposalAsBallot(Proposal proposal) { - Ballot ballot = new Ballot(proposal); // vote is null - if (log.isInfoEnabled()) { - log.debug("We create a new ballot with a proposal and add it to our list. " + - "Vote is null at that moment. proposalTxId={}", proposal.getTxId()); - } - if (ballotList.contains(ballot)) { - log.warn("Ballot {} already exists on our ballotList", ballot); - } else { - ballotList.add(ballot); - listeners.forEach(listener -> listener.onListChanged(ballotList.getList())); - } - } - - @Override - public void start() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PersistedDataHost - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void readPersisted(Runnable completeHandler) { - if (DevEnv.isDaoActivated()) { - persistenceManager.readPersisted(persisted -> { - ballotList.setAll(persisted.getList()); - listeners.forEach(l -> l.onListChanged(ballotList.getList())); - completeHandler.run(); - }, - completeHandler); - } else { - completeHandler.run(); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void setVote(Ballot ballot, @Nullable Vote vote) { - ballot.setVote(vote); - requestPersistence(); - } - - public void addListener(BallotListChangeListener listener) { - listeners.add(listener); - } - - public List getValidatedBallotList() { - return ballotList.stream() - .filter(ballot -> validatorProvider.getValidator(ballot.getProposal()).isTxTypeValid(ballot.getProposal())) - .collect(Collectors.toList()); - } - - public List getValidBallotsOfCycle() { - return ballotList.stream() - .filter(ballot -> validatorProvider.getValidator(ballot.getProposal()).isTxTypeValid(ballot.getProposal())) - .filter(ballot -> periodService.isTxInCorrectCycle(ballot.getTxId(), periodService.getChainHeight())) - .collect(Collectors.toList()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void requestPersistence() { - persistenceManager.requestPersistence(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVote.java b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVote.java deleted file mode 100644 index 5449139010..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVote.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.blindvote; - -import bisq.core.dao.governance.ConsensusCritical; - -import bisq.common.proto.network.NetworkPayload; -import bisq.common.proto.persistable.PersistablePayload; -import bisq.common.util.CollectionUtils; -import bisq.common.util.ExtraDataMapValidator; -import bisq.common.util.Utilities; - -import com.google.protobuf.ByteString; - -import java.util.Map; -import java.util.Optional; - -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import org.jetbrains.annotations.NotNull; - -import javax.annotation.concurrent.Immutable; - -/** - * Holds encryptedVotes, encryptedMeritList, txId of blindVote tx and stake. - * A encryptedVotes for 1 proposal is 304 bytes - */ -@Immutable -@Slf4j -@Value -public final class BlindVote implements PersistablePayload, NetworkPayload, ConsensusCritical { - private final byte[] encryptedVotes; // created from voteWithProposalTxIdList - private final String txId; - // Stake is revealed in the BSQ tx anyway as output value so no reason to encrypt it here. - private final long stake; - private byte[] encryptedMeritList; - // Publish date of the proposal. - // We do not use the date at the moment but we prefer to keep it here as it might be - // used as a relevant protection tool for late publishing attacks. - // We don't have a clear concept now how to do it but as it will be part of the opReturn data it will impossible - // to game the publish date. Together with the block time we can use that for some checks. But as said no clear - // concept yet... - // As adding that field later would break consensus we prefer to add it now. In the worst case it will stay - // an unused field. - private final long date; - - // This hash map allows addition of data in future versions without breaking consensus - private final Map extraDataMap; - - public BlindVote(byte[] encryptedVotes, - String txId, - long stake, - byte[] encryptedMeritList, - long date, - Map extraDataMap) { - this.encryptedVotes = encryptedVotes; - this.txId = txId; - this.stake = stake; - this.encryptedMeritList = encryptedMeritList; - this.date = date; - this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - // Used for sending over the network - @Override - public protobuf.BlindVote toProtoMessage() { - return getBuilder().build(); - } - - @NotNull - public protobuf.BlindVote.Builder getBuilder() { - protobuf.BlindVote.Builder builder = protobuf.BlindVote.newBuilder(); - builder.setEncryptedVotes(ByteString.copyFrom(encryptedVotes)) - .setTxId(txId) - .setStake(stake) - .setEncryptedMeritList(ByteString.copyFrom(encryptedMeritList)) - .setDate(date); - Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData); - return builder; - } - - public static BlindVote fromProto(protobuf.BlindVote proto) { - return new BlindVote(proto.getEncryptedVotes().toByteArray(), - proto.getTxId(), - proto.getStake(), - proto.getEncryptedMeritList().toByteArray(), - proto.getDate(), - CollectionUtils.isEmpty(proto.getExtraDataMap()) ? - null : proto.getExtraDataMap()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public String toString() { - return "BlindVotePayload{" + - "\n encryptedVotes=" + Utilities.bytesAsHexString(encryptedVotes) + - ",\n txId='" + txId + '\'' + - ",\n stake=" + stake + - ",\n encryptedMeritList=" + Utilities.bytesAsHexString(encryptedMeritList) + - ",\n date=" + date + - ",\n extraDataMap=" + extraDataMap + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteConsensus.java b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteConsensus.java deleted file mode 100644 index 1841ff2cb5..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteConsensus.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.blindvote; - -import bisq.core.dao.governance.ballot.BallotListService; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.OpReturnType; -import bisq.core.dao.state.model.governance.Ballot; -import bisq.core.dao.state.model.governance.BallotList; -import bisq.core.dao.state.model.governance.MeritList; - -import bisq.common.app.Version; -import bisq.common.crypto.CryptoException; -import bisq.common.crypto.Encryption; -import bisq.common.crypto.Hash; -import bisq.common.util.Utilities; - -import org.bitcoinj.core.Coin; - -import com.google.common.annotations.VisibleForTesting; - -import javax.crypto.SecretKey; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - -/** - * All consensus critical aspects are handled here. - */ -@Slf4j -public class BlindVoteConsensus { - public static boolean hasOpReturnDataValidLength(byte[] opReturnData) { - return opReturnData.length == 22; - } - - public static BallotList getSortedBallotList(BallotListService ballotListService) { - List ballotList = ballotListService.getValidBallotsOfCycle().stream() - .sorted(Comparator.comparing(Ballot::getTxId)) - .collect(Collectors.toList()); - log.info("Sorted ballotList: " + ballotList); - return new BallotList(ballotList); - } - - public static List getSortedBlindVoteListOfCycle(BlindVoteListService blindVoteListService) { - return getSortedBlindVoteListOfCycle(blindVoteListService.getBlindVotesInPhaseAndCycle()); - } - - @VisibleForTesting - static List getSortedBlindVoteListOfCycle(List blindVoteList) { - List list = blindVoteList.stream() - .sorted(Comparator.comparing(BlindVote::getTxId)) - .collect(Collectors.toList()); - log.debug("Sorted blindVote txId list: " + list.stream() - .map(BlindVote::getTxId) - .collect(Collectors.toList())); - return list; - } - - // 128 bit AES key is good enough for our use case - public static SecretKey createSecretKey() { - return Encryption.generateSecretKey(128); - } - - public static byte[] getEncryptedVotes(VoteWithProposalTxIdList voteWithProposalTxIdList, SecretKey secretKey) throws CryptoException { - byte[] bytes = voteWithProposalTxIdList.toProtoMessage().toByteArray(); - byte[] encrypted = Encryption.encrypt(bytes, secretKey); - log.info("EncryptedVotes: " + Utilities.bytesAsHexString(encrypted)); - return encrypted; - } - - public static byte[] getEncryptedMeritList(MeritList meritList, SecretKey secretKey) throws CryptoException { - byte[] bytes = meritList.toProtoMessage().toByteArray(); - return Encryption.encrypt(bytes, secretKey); - } - - public static byte[] getHashOfEncryptedVotes(byte[] encryptedVotes) { - return Hash.getSha256Ripemd160hash(encryptedVotes); - } - - public static byte[] getOpReturnData(byte[] hash) throws IOException { - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - outputStream.write(OpReturnType.BLIND_VOTE.getType()); - outputStream.write(Version.BLIND_VOTE); - outputStream.write(hash); - final byte[] bytes = outputStream.toByteArray(); - log.info("OpReturnData: " + Utilities.bytesAsHexString(bytes)); - return bytes; - } catch (IOException e) { - // Not expected to happen ever - e.printStackTrace(); - log.error(e.toString()); - throw e; - } - } - - public static Coin getFee(DaoStateService daoStateService, int chainHeight) { - return daoStateService.getParamValueAsCoin(Param.BLIND_VOTE_FEE, chainHeight); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteListService.java b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteListService.java deleted file mode 100644 index 4ed289d881..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteListService.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * This file is part of bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.governance.blindvote; - -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.governance.blindvote.storage.BlindVotePayload; -import bisq.core.dao.governance.blindvote.storage.BlindVoteStorageService; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.DaoPhase; - -import bisq.network.p2p.P2PService; -import bisq.network.p2p.storage.payload.PersistableNetworkPayload; -import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreListener; -import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService; - -import bisq.common.config.Config; - -import javax.inject.Inject; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; - -import java.util.List; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Listens for new BlindVotePayload and adds it to appendOnlyStoreList. - */ -@Slf4j -public class BlindVoteListService implements AppendOnlyDataStoreListener, DaoStateListener, DaoSetupService { - private final DaoStateService daoStateService; - private final P2PService p2PService; - private final PeriodService periodService; - private final BlindVoteStorageService blindVoteStorageService; - private final BlindVoteValidator blindVoteValidator; - @Getter - private final ObservableList blindVotePayloads = FXCollections.observableArrayList(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public BlindVoteListService(DaoStateService daoStateService, - P2PService p2PService, - PeriodService periodService, - BlindVoteStorageService blindVoteStorageService, - AppendOnlyDataStoreService appendOnlyDataStoreService, - BlindVoteValidator blindVoteValidator, - Config config) { - this.daoStateService = daoStateService; - this.p2PService = p2PService; - this.periodService = periodService; - this.blindVoteStorageService = blindVoteStorageService; - this.blindVoteValidator = blindVoteValidator; - - if (config.daoActivated) - appendOnlyDataStoreService.addService(blindVoteStorageService); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - daoStateService.addDaoStateListener(this); - } - - @Override - public void start() { - fillListFromAppendOnlyDataStore(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onNewBlockHeight(int blockHeight) { - // We only add blindVotes to blindVoteStorageService if we are not in the vote reveal phase. - blindVoteStorageService.setNotInVoteRevealPhase(notInVoteRevealPhase(blockHeight)); - } - - @Override - public void onParseBlockChainComplete() { - fillListFromAppendOnlyDataStore(); - - // We set the listener after parsing is complete to be sure we have a consistent state for the phase check. - p2PService.getP2PDataStorage().addAppendOnlyDataStoreListener(this); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // AppendOnlyDataStoreListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onAdded(PersistableNetworkPayload payload) { - onAppendOnlyDataAdded(payload, true); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public List getBlindVotesInPhaseAndCycle() { - return blindVotePayloads.stream() - .filter(blindVotePayload -> blindVoteValidator.isTxInPhaseAndCycle(blindVotePayload.getBlindVote())) - .map(BlindVotePayload::getBlindVote) - .collect(Collectors.toList()); - } - - public List getConfirmedBlindVotes() { - return blindVotePayloads.stream() - .filter(blindVotePayload -> blindVoteValidator.areDataFieldsValidAndTxConfirmed(blindVotePayload.getBlindVote())) - .map(BlindVotePayload::getBlindVote) - .collect(Collectors.toList()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void fillListFromAppendOnlyDataStore() { - blindVoteStorageService.getMap().values().forEach(e -> onAppendOnlyDataAdded(e, false)); - } - - private void onAppendOnlyDataAdded(PersistableNetworkPayload persistableNetworkPayload, boolean fromBroadcastMessage) { - if (persistableNetworkPayload instanceof BlindVotePayload) { - BlindVotePayload blindVotePayload = (BlindVotePayload) persistableNetworkPayload; - if (!blindVotePayloads.contains(blindVotePayload)) { - BlindVote blindVote = blindVotePayload.getBlindVote(); - String txId = blindVote.getTxId(); - - if (blindVoteValidator.areDataFieldsValid(blindVote)) { - if (fromBroadcastMessage) { - if (notInVoteRevealPhase(daoStateService.getChainHeight())) { - // We received the payload outside the vote reveal phase and add the payload. - // If we would accept it during the vote reveal phase we would be vulnerable to a late - // publishing attack where the attacker tries to pollute the data view of the voters and - // render the whole voting cycle invalid if the majority hash is not at least 80% of the - // vote stake. - blindVotePayloads.add(blindVotePayload); - } - } else { - // In case we received the data from the seed node at startup we cannot apply the phase check as - // even in the vote reveal phase we want to receive missed blind votes. - blindVotePayloads.add(blindVotePayload); - } - } else { - log.warn("We received an invalid blindVotePayload. blindVoteTxId={}", txId); - } - } - } - } - - private boolean notInVoteRevealPhase(int blockHeight) { - return !periodService.isInPhase(blockHeight, DaoPhase.Phase.VOTE_REVEAL); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteValidator.java b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteValidator.java deleted file mode 100644 index 4b1035a25e..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteValidator.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.blindvote; - -import bisq.core.btc.wallet.Restrictions; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.governance.DaoPhase; - -import bisq.common.util.ExtraDataMapValidator; - -import javax.inject.Inject; - -import java.util.Optional; - -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -@Slf4j -public class BlindVoteValidator { - - private final DaoStateService daoStateService; - private final PeriodService periodService; - - @Inject - public BlindVoteValidator(DaoStateService daoStateService, PeriodService periodService) { - this.daoStateService = daoStateService; - this.periodService = periodService; - } - - public boolean areDataFieldsValid(BlindVote blindVote) { - try { - validateDataFields(blindVote); - return true; - } catch (Throwable e) { - return false; - } - } - - private void validateDataFields(BlindVote blindVote) throws ProposalValidationException { - try { - checkNotNull(blindVote.getEncryptedVotes(), "encryptedProposalList must not be null"); - checkArgument(blindVote.getEncryptedVotes().length > 0, - "encryptedProposalList must not be empty"); - checkArgument(blindVote.getEncryptedVotes().length <= 100000, - "encryptedProposalList must not exceed 100kb"); - - checkNotNull(blindVote.getTxId(), "Tx ID must not be null"); - checkArgument(blindVote.getTxId().length() == 64, "Tx ID must be 64 chars"); - checkArgument(blindVote.getStake() >= Restrictions.getMinNonDustOutput().value, "Stake must be at least MinNonDustOutput"); - - checkNotNull(blindVote.getEncryptedMeritList(), "getEncryptedMeritList must not be null"); - checkArgument(blindVote.getEncryptedMeritList().length > 0, - "getEncryptedMeritList must not be empty"); - checkArgument(blindVote.getEncryptedMeritList().length <= 100000, - "getEncryptedMeritList must not exceed 100kb"); - - ExtraDataMapValidator.validate(blindVote.getExtraDataMap()); - } catch (Throwable e) { - log.warn(e.toString()); - throw new ProposalValidationException(e); - } - } - - public boolean areDataFieldsValidAndTxConfirmed(BlindVote blindVote) { - if (!areDataFieldsValid(blindVote)) { - log.warn("blindVote is invalid. blindVote={}", blindVote); - return false; - } - - // Check if tx is already confirmed and in DaoState - boolean isConfirmed = daoStateService.getTx(blindVote.getTxId()).isPresent(); - if (daoStateService.isParseBlockChainComplete() && !isConfirmed) - log.warn("blindVoteTx is not confirmed. blindVoteTxId={}", blindVote.getTxId()); - - return isConfirmed; - } - - public boolean isTxInPhaseAndCycle(BlindVote blindVote) { - String txId = blindVote.getTxId(); - Optional optionalTx = daoStateService.getTx(txId); - if (!optionalTx.isPresent()) { - log.debug("Tx is not in daoStateService. blindVoteTxId={}", txId); - return false; - } - - int txHeight = optionalTx.get().getBlockHeight(); - if (!periodService.isTxInCorrectCycle(txHeight, daoStateService.getChainHeight())) { - log.debug("Tx is not in current cycle. blindVote={}", blindVote); - return false; - } - if (!periodService.isTxInPhase(txId, DaoPhase.Phase.BLIND_VOTE)) { - log.debug("Tx is not in BLIND_VOTE phase. blindVote={}", blindVote); - return false; - } - return true; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteList.java b/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteList.java deleted file mode 100644 index be0890b653..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteList.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.blindvote; - -import bisq.core.dao.governance.ConsensusCritical; - -import bisq.common.proto.persistable.PersistableList; - -import com.google.protobuf.Message; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.EqualsAndHashCode; - -/** - * List of my own blind votes. Blind votes received from other voters are stored in the BlindVoteStore. - */ -@EqualsAndHashCode(callSuper = true) -public class MyBlindVoteList extends PersistableList implements ConsensusCritical { - - MyBlindVoteList() { - super(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public MyBlindVoteList(List list) { - super(list); - } - - @Override - public Message toProtoMessage() { - return protobuf.PersistableEnvelope.newBuilder() - .setMyBlindVoteList(protobuf.MyBlindVoteList.newBuilder() - .addAllBlindVote(getList().stream() - .map(BlindVote::toProtoMessage) - .collect(Collectors.toList()))) - .build(); - } - - public static MyBlindVoteList fromProto(protobuf.MyBlindVoteList proto) { - return new MyBlindVoteList(new ArrayList<>(proto.getBlindVoteList().stream() - .map(BlindVote::fromProto) - .collect(Collectors.toList()))); - } - - @Override - public String toString() { - return "MyBlindVoteList: " + getList().stream() - .map(BlindVote::getTxId) - .collect(Collectors.toList()); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteListService.java b/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteListService.java deleted file mode 100644 index 70cac97634..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteListService.java +++ /dev/null @@ -1,417 +0,0 @@ -/* - * This file is part of bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.governance.blindvote; - -import bisq.core.btc.exceptions.TransactionVerificationException; -import bisq.core.btc.exceptions.TxBroadcastException; -import bisq.core.btc.exceptions.WalletException; -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.TxBroadcaster; -import bisq.core.btc.wallet.WalletsManager; -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.exceptions.PublishToP2PNetworkException; -import bisq.core.dao.governance.ballot.BallotListService; -import bisq.core.dao.governance.blindvote.storage.BlindVotePayload; -import bisq.core.dao.governance.merit.MeritConsensus; -import bisq.core.dao.governance.myvote.MyVoteListService; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.MyProposalListService; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.TxType; -import bisq.core.dao.state.model.governance.BallotList; -import bisq.core.dao.state.model.governance.CompensationProposal; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.dao.state.model.governance.IssuanceType; -import bisq.core.dao.state.model.governance.Merit; -import bisq.core.dao.state.model.governance.MeritList; -import bisq.core.dao.state.model.governance.Proposal; - -import bisq.network.p2p.P2PService; - -import bisq.common.UserThread; -import bisq.common.app.DevEnv; -import bisq.common.config.Config; -import bisq.common.crypto.CryptoException; -import bisq.common.handlers.ErrorMessageHandler; -import bisq.common.handlers.ExceptionHandler; -import bisq.common.handlers.ResultHandler; -import bisq.common.persistence.PersistenceManager; -import bisq.common.proto.persistable.PersistedDataHost; -import bisq.common.util.Tuple2; -import bisq.common.util.Utilities; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.ECKey; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.Sha256Hash; -import org.bitcoinj.core.Transaction; -import org.bitcoinj.crypto.DeterministicKey; - -import javax.inject.Inject; - -import javafx.beans.value.ChangeListener; - -import javax.crypto.SecretKey; - -import java.io.IOException; - -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -import static com.google.common.base.Preconditions.checkArgument; - -/** - * Publishes blind vote tx and blind vote payload to p2p network. - * Maintains myBlindVoteList for own blind votes. Triggers republishing of my blind votes at startup during blind - * vote phase of current cycle. - * Publishes a BlindVote and the blind vote transaction. - */ -@Slf4j -public class MyBlindVoteListService implements PersistedDataHost, DaoStateListener, DaoSetupService { - private final P2PService p2PService; - private final DaoStateService daoStateService; - private final PeriodService periodService; - private final WalletsManager walletsManager; - private final PersistenceManager persistenceManager; - private final BsqWalletService bsqWalletService; - private final BtcWalletService btcWalletService; - private final BallotListService ballotListService; - private final MyVoteListService myVoteListService; - private final MyProposalListService myProposalListService; - private final ChangeListener numConnectedPeersListener; - @Getter - private final MyBlindVoteList myBlindVoteList = new MyBlindVoteList(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public MyBlindVoteListService(P2PService p2PService, - DaoStateService daoStateService, - PeriodService periodService, - WalletsManager walletsManager, - PersistenceManager persistenceManager, - BsqWalletService bsqWalletService, - BtcWalletService btcWalletService, - BallotListService ballotListService, - MyVoteListService myVoteListService, - MyProposalListService myProposalListService) { - this.p2PService = p2PService; - this.daoStateService = daoStateService; - this.periodService = periodService; - this.walletsManager = walletsManager; - this.persistenceManager = persistenceManager; - this.bsqWalletService = bsqWalletService; - this.btcWalletService = btcWalletService; - this.ballotListService = ballotListService; - this.myVoteListService = myVoteListService; - this.myProposalListService = myProposalListService; - - this.persistenceManager.initialize(myBlindVoteList, PersistenceManager.Source.PRIVATE); - - numConnectedPeersListener = (observable, oldValue, newValue) -> maybeRePublishMyBlindVote(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - daoStateService.addDaoStateListener(this); - p2PService.getNumConnectedPeers().addListener(numConnectedPeersListener); - } - - @Override - public void start() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PersistedDataHost - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void readPersisted(Runnable completeHandler) { - if (DevEnv.isDaoActivated()) { - persistenceManager.readPersisted(persisted -> { - myBlindVoteList.setAll(persisted.getList()); - completeHandler.run(); - }, - completeHandler); - } else { - completeHandler.run(); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockChainComplete() { - maybeRePublishMyBlindVote(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public Tuple2 getMiningFeeAndTxVsize(Coin stake) - throws InsufficientMoneyException, WalletException, TransactionVerificationException { - // We set dummy opReturn data - Coin blindVoteFee = BlindVoteConsensus.getFee(daoStateService, daoStateService.getChainHeight()); - Transaction dummyTx = getBlindVoteTx(stake, blindVoteFee, new byte[22]); - Coin miningFee = dummyTx.getFee(); - int txVsize = dummyTx.getVsize(); - return new Tuple2<>(miningFee, txVsize); - } - - public void publishBlindVote(Coin stake, ResultHandler resultHandler, ExceptionHandler exceptionHandler) { - try { - SecretKey secretKey = BlindVoteConsensus.createSecretKey(); - BallotList sortedBallotList = BlindVoteConsensus.getSortedBallotList(ballotListService); - byte[] encryptedVotes = getEncryptedVotes(sortedBallotList, secretKey); - byte[] opReturnData = getOpReturnData(encryptedVotes); - Coin blindVoteFee = BlindVoteConsensus.getFee(daoStateService, daoStateService.getChainHeight()); - Transaction blindVoteTx = getBlindVoteTx(stake, blindVoteFee, opReturnData); - String blindVoteTxId = blindVoteTx.getTxId().toString(); - - byte[] encryptedMeritList = getEncryptedMeritList(blindVoteTxId, secretKey); - - // We prefer to not wait for the tx broadcast as if the tx broadcast would fail we still prefer to have our - // blind vote stored and broadcasted to the p2p network. The tx might get re-broadcasted at a restart and - // in worst case if it does not succeed the blind vote will be ignored anyway. - // Inconsistently propagated blind votes in the p2p network could have potentially worse effects. - BlindVote blindVote = new BlindVote(encryptedVotes, - blindVoteTxId, - stake.value, - encryptedMeritList, - new Date().getTime(), - new HashMap<>()); - addBlindVoteToList(blindVote); - - addToP2PNetwork(blindVote, errorMessage -> { - log.error(errorMessage); - exceptionHandler.handleException(new PublishToP2PNetworkException(errorMessage)); - }); - - // We store our source data for the blind vote in myVoteList - myVoteListService.createAndAddMyVote(sortedBallotList, secretKey, blindVote); - - publishTx(resultHandler, exceptionHandler, blindVoteTx); - } catch (CryptoException | TransactionVerificationException | InsufficientMoneyException | - WalletException | IOException exception) { - log.error(exception.toString()); - exception.printStackTrace(); - exceptionHandler.handleException(exception); - } - } - - public long getCurrentlyAvailableMerit() { - MeritList meritList = getMerits(null); - return MeritConsensus.getCurrentlyAvailableMerit(meritList, daoStateService.getChainHeight()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - - private byte[] getEncryptedVotes(BallotList sortedBallotList, SecretKey secretKey) throws CryptoException { - // We don't want to store the proposal but only use the proposalTxId as reference in our encrypted list. - // So we convert it to the VoteWithProposalTxIdList. - // The VoteWithProposalTxIdList is used for serialisation with protobuffer, it is not actually persisted but we - // use the PersistableList base class for convenience. - final List list = sortedBallotList.stream() - .map(ballot -> new VoteWithProposalTxId(ballot.getTxId(), ballot.getVote())) - .collect(Collectors.toList()); - final VoteWithProposalTxIdList voteWithProposalTxIdList = new VoteWithProposalTxIdList(list); - log.info("voteWithProposalTxIdList used in blind vote. voteWithProposalTxIdList={}", voteWithProposalTxIdList); - return BlindVoteConsensus.getEncryptedVotes(voteWithProposalTxIdList, secretKey); - } - - private byte[] getOpReturnData(byte[] encryptedVotes) throws IOException { - // We cannot use hash of whole blindVote data because we create the merit signature with the blindVoteTxId - // So we use the encryptedVotes for the hash only. - final byte[] hash = BlindVoteConsensus.getHashOfEncryptedVotes(encryptedVotes); - log.info("Sha256Ripemd160 hash of encryptedVotes: " + Utilities.bytesAsHexString(hash)); - return BlindVoteConsensus.getOpReturnData(hash); - } - - private byte[] getEncryptedMeritList(String blindVoteTxId, SecretKey secretKey) throws CryptoException { - MeritList meritList = getMerits(blindVoteTxId); - return BlindVoteConsensus.getEncryptedMeritList(meritList, secretKey); - } - - // blindVoteTxId is null if we use the method from the getCurrentlyAvailableMerit call. - public MeritList getMerits(@Nullable String blindVoteTxId) { - // Create a lookup set for txIds of own comp. requests from past cycles (we ignore request form that cycle) - Set myCompensationProposalTxIs = myProposalListService.getList().stream() - .filter(proposal -> proposal instanceof CompensationProposal) - .map(Proposal::getTxId) - .filter(txId -> periodService.isTxInPastCycle(txId, periodService.getChainHeight())) - .collect(Collectors.toSet()); - - return new MeritList(daoStateService.getIssuanceSetForType(IssuanceType.COMPENSATION).stream() - .map(issuance -> { - checkArgument(issuance.getIssuanceType() == IssuanceType.COMPENSATION, - "IssuanceType must be COMPENSATION for MeritList"); - // We check if it is our proposal - if (!myCompensationProposalTxIs.contains(issuance.getTxId())) - return null; - - byte[] signatureAsBytes; - if (blindVoteTxId != null) { - String pubKey = issuance.getPubKey(); - if (pubKey == null) { - // Maybe add exception - log.error("We did not have a pubKey in our issuance object. " + - "txId={}, issuance={}", issuance.getTxId(), issuance); - return null; - } - - DeterministicKey key = bsqWalletService.findKeyFromPubKey(Utilities.decodeFromHex(pubKey)); - if (key == null) { - // Maybe add exception - log.error("We did not find the key for our compensation request. txId={}", - issuance.getTxId()); - return null; - } - - // We sign the txId so we be sure that the signature could not be used by anyone else - // In the verification the txId will be checked as well. - - // As we use BitcoinJ EC keys we extend our consensus dependency to BitcoinJ. - // Alternative would be to use our own Sig Key but then we need to share the key separately. - // The EC key is in the blockchain already. We prefer here to stick with EC key. If any change - // in BitcoinJ would break our consensus we would need to fall back to the old BitcoinJ EC - // implementation. - ECKey.ECDSASignature signature = bsqWalletService.isEncrypted() ? - key.sign(Sha256Hash.wrap(blindVoteTxId), bsqWalletService.getAesKey()) : - key.sign(Sha256Hash.wrap(blindVoteTxId)); - signatureAsBytes = signature.toCanonicalised().encodeToDER(); - } else { - // In case we use it for requesting the currently available merit we don't apply a signature - signatureAsBytes = new byte[0]; - } - return new Merit(issuance, signatureAsBytes); - - }) - .filter(Objects::nonNull) - .sorted(Comparator.comparing(Merit::getIssuanceTxId)) - .collect(Collectors.toList())); - } - - private void publishTx(ResultHandler resultHandler, ExceptionHandler exceptionHandler, Transaction blindVoteTx) { - log.info("blindVoteTx={}", blindVoteTx.toString()); - walletsManager.publishAndCommitBsqTx(blindVoteTx, TxType.BLIND_VOTE, new TxBroadcaster.Callback() { - @Override - public void onSuccess(Transaction transaction) { - log.info("BlindVote tx published. txId={}", transaction.getTxId().toString()); - resultHandler.handleResult(); - } - - @Override - public void onFailure(TxBroadcastException exception) { - exceptionHandler.handleException(exception); - } - }); - } - - private Transaction getBlindVoteTx(Coin stake, Coin fee, byte[] opReturnData) - throws InsufficientMoneyException, WalletException, TransactionVerificationException { - Transaction preparedTx = bsqWalletService.getPreparedBlindVoteTx(fee, stake); - Transaction txWithBtcFee = btcWalletService.completePreparedBlindVoteTx(preparedTx, opReturnData); - return bsqWalletService.signTx(txWithBtcFee); - } - - private void maybeRePublishMyBlindVote() { - // We do not republish during vote reveal phase as peer would reject blindVote data to protect against - // late publishing attacks. - // This attack is only relevant during the vote reveal phase as there it could cause damage by disturbing the - // data view of the blind votes of the voter for creating the majority hash. - // To republish after the vote reveal phase still makes sense to reduce risk that some nodes have not received - // it and would need to request the data then in the vote result phase. - if (!periodService.isInPhase(daoStateService.getChainHeight(), DaoPhase.Phase.VOTE_REVEAL)) { - // We republish at each startup at any block during the cycle. We filter anyway for valid blind votes - // of that cycle so it is 1 blind vote getting rebroadcast at each startup to my neighbors. - // Republishing only will have effect if the payload creation date is < 5 hours as other nodes would not - // accept payloads which are too old or are in future. - // Only payloads received from seed nodes would ignore that date check. - int minPeers = Config.baseCurrencyNetwork().isMainnet() ? 4 : 1; - if ((p2PService.getNumConnectedPeers().get() >= minPeers && p2PService.isBootstrapped()) || - Config.baseCurrencyNetwork().isStagenet()) { - myBlindVoteList.stream() - .filter(blindVote -> periodService.isTxInPhaseAndCycle(blindVote.getTxId(), - DaoPhase.Phase.BLIND_VOTE, - periodService.getChainHeight())) - .forEach(blindVote -> addToP2PNetwork(blindVote, null)); - - // We delay removal of listener as we call that inside listener itself. - UserThread.execute(() -> p2PService.getNumConnectedPeers().removeListener(numConnectedPeersListener)); - } - } - } - - private void addToP2PNetwork(BlindVote blindVote, @Nullable ErrorMessageHandler errorMessageHandler) { - BlindVotePayload blindVotePayload = new BlindVotePayload(blindVote); - // We use reBroadcast flag here as we only broadcast our own blindVote and want to be sure it gets distributed - // well. - boolean success = p2PService.addPersistableNetworkPayload(blindVotePayload, true); - - if (success) { - log.info("We added a blindVotePayload to the P2P network as append only data. blindVoteTxId={}", - blindVote.getTxId()); - } else { - String msg = "Adding of blindVotePayload to P2P network failed. blindVoteTxId=" + blindVote.getTxId(); - log.error(msg); - if (errorMessageHandler != null) - errorMessageHandler.handleErrorMessage(msg); - } - } - - private void addBlindVoteToList(BlindVote blindVote) { - if (!myBlindVoteList.getList().contains(blindVote)) { - myBlindVoteList.add(blindVote); - requestPersistence(); - } - } - - private void requestPersistence() { - persistenceManager.requestPersistence(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/VoteWithProposalTxId.java b/core/src/main/java/bisq/core/dao/governance/blindvote/VoteWithProposalTxId.java deleted file mode 100644 index 1c60733fcf..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/VoteWithProposalTxId.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.blindvote; - -import bisq.core.dao.state.model.governance.Vote; - -import bisq.common.proto.persistable.PersistablePayload; - -import java.util.Optional; - -import lombok.Value; - -import org.jetbrains.annotations.NotNull; - -import javax.annotation.Nullable; - - -@Value -public class VoteWithProposalTxId implements PersistablePayload { - private final String proposalTxId; - @Nullable - private final Vote vote; - - VoteWithProposalTxId(String proposalTxId, @Nullable Vote vote) { - this.proposalTxId = proposalTxId; - this.vote = vote; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - // Used for sending over the network - @Override - public protobuf.VoteWithProposalTxId toProtoMessage() { - return getBuilder().build(); - } - - @NotNull - private protobuf.VoteWithProposalTxId.Builder getBuilder() { - final protobuf.VoteWithProposalTxId.Builder builder = protobuf.VoteWithProposalTxId.newBuilder() - .setProposalTxId(proposalTxId); - Optional.ofNullable(vote).ifPresent(e -> builder.setVote((protobuf.Vote) e.toProtoMessage())); - return builder; - } - - public static VoteWithProposalTxId fromProto(protobuf.VoteWithProposalTxId proto) { - return new VoteWithProposalTxId(proto.getProposalTxId(), - proto.hasVote() ? Vote.fromProto(proto.getVote()) : null); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/VoteWithProposalTxIdList.java b/core/src/main/java/bisq/core/dao/governance/blindvote/VoteWithProposalTxIdList.java deleted file mode 100644 index 4556e88221..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/VoteWithProposalTxIdList.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.blindvote; - -import bisq.core.dao.governance.ConsensusCritical; - -import bisq.common.Proto; - -import com.google.protobuf.InvalidProtocolBufferException; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -/** - * We encode the VoteWithProposalTxId list to PB bytes in the blindVote. The bytes get encrypted and later decrypted. - * To use a ByteOutputStream and add all list elements would work for encryption but for decrypting we don't know the - * length of a list entry and it would make the process complicated (e.g. require a custom serialisation format). - */ -@Slf4j -@Value -public class VoteWithProposalTxIdList implements Proto, ConsensusCritical { - private final List list; - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public static VoteWithProposalTxIdList getVoteWithProposalTxIdListFromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return VoteWithProposalTxIdList.fromProto(protobuf.VoteWithProposalTxIdList.parseFrom(bytes)); - } - - @Override - public protobuf.VoteWithProposalTxIdList toProtoMessage() { - return getBuilder().build(); - } - - private protobuf.VoteWithProposalTxIdList.Builder getBuilder() { - return protobuf.VoteWithProposalTxIdList.newBuilder() - .addAllItem(getList().stream() - .map(VoteWithProposalTxId::toProtoMessage) - .collect(Collectors.toList())); - } - - private static VoteWithProposalTxIdList fromProto(protobuf.VoteWithProposalTxIdList proto) { - final ArrayList list = proto.getItemList().stream() - .map(VoteWithProposalTxId::fromProto).collect(Collectors.toCollection(ArrayList::new)); - return new VoteWithProposalTxIdList(list); - } - - @Override - public String toString() { - return "VoteWithProposalTxIdList: " + getList().stream() - .map(VoteWithProposalTxId::getProposalTxId) - .collect(Collectors.toList()); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/network/RepublishGovernanceDataHandler.java b/core/src/main/java/bisq/core/dao/governance/blindvote/network/RepublishGovernanceDataHandler.java deleted file mode 100644 index 9701159f72..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/network/RepublishGovernanceDataHandler.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.blindvote.network; - -import bisq.core.dao.governance.blindvote.network.messages.RepublishGovernanceDataRequest; - -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.network.Connection; -import bisq.network.p2p.network.NetworkNode; -import bisq.network.p2p.peers.PeerManager; -import bisq.network.p2p.peers.peerexchange.Peer; -import bisq.network.p2p.seed.SeedNodeRepository; - -import bisq.common.Timer; -import bisq.common.UserThread; -import bisq.common.app.Capabilities; -import bisq.common.app.Capability; - -import javax.inject.Inject; - -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.MoreExecutors; -import com.google.common.util.concurrent.SettableFuture; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - -import org.jetbrains.annotations.NotNull; - -/** - * Responsible for sending a RepublishGovernanceDataRequest to full nodes. - * Processing of RepublishBlindVotesRequests at full nodes is done in the FullNodeNetworkService. - */ -@Slf4j -public final class RepublishGovernanceDataHandler { - private static final long TIMEOUT = 120; - - private final Collection seedNodeAddresses; - private final NetworkNode networkNode; - private final PeerManager peerManager; - - private boolean stopped; - private Timer timeoutTimer; - - @Inject - public RepublishGovernanceDataHandler(NetworkNode networkNode, - PeerManager peerManager, - SeedNodeRepository seedNodesRepository) { - this.networkNode = networkNode; - this.peerManager = peerManager; - this.seedNodeAddresses = new HashSet<>(seedNodesRepository.getSeedNodeAddresses()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void sendRepublishRequest() { - // First try if we have a seed node in our connections. All seed nodes are full nodes. - if (!stopped) - connectToNextNode(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void sendRepublishRequest(NodeAddress nodeAddress) { - RepublishGovernanceDataRequest republishGovernanceDataRequest = new RepublishGovernanceDataRequest(); - if (timeoutTimer == null) { - timeoutTimer = UserThread.runAfter(() -> { - // setup before sending to avoid race conditions - if (!stopped) { - String errorMessage = "A timeout occurred at sending republishGovernanceDataRequest:" + - " to nodeAddress:" + nodeAddress; - log.warn(errorMessage); - connectToNextNode(); - } else { - log.warn("We have stopped already. We ignore that timeoutTimer.run call. " + - "Might be caused by a previous networkNode.sendMessage.onFailure."); - } - }, - TIMEOUT); - } - - log.info("We send to peer {} a republishGovernanceDataRequest.", nodeAddress); - SettableFuture future = networkNode.sendMessage(nodeAddress, republishGovernanceDataRequest); - Futures.addCallback(future, new FutureCallback<>() { - @Override - public void onSuccess(Connection connection) { - if (!stopped) { - log.info("Sending of RepublishGovernanceDataRequest message to peer {} succeeded.", nodeAddress.getFullAddress()); - stop(); - } 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 republishGovernanceDataRequest to " + nodeAddress + - " failed. That is expected if the peer is offline.\n\t" + - "\n\tException=" + throwable.getMessage(); - log.info(errorMessage); - handleFault(nodeAddress); - connectToNextNode(); - } else { - log.trace("We have stopped already. We ignore that networkNode.sendMessage.onFailure call. " + - "Might be caused by a previous timeout."); - } - } - }, MoreExecutors.directExecutor()); - } - - private void connectToNextNode() { - // First we try our connected seed nodes - Optional connectionToSeedNodeOptional = networkNode.getConfirmedConnections().stream() - .filter(peerManager::isSeedNode) - .findAny(); - if (connectionToSeedNodeOptional.isPresent() && - connectionToSeedNodeOptional.get().getPeersNodeAddressOptional().isPresent()) { - NodeAddress nodeAddress = connectionToSeedNodeOptional.get().getPeersNodeAddressOptional().get(); - sendRepublishRequest(nodeAddress); - } else { - // If connected seed nodes did not confirm receipt of message we try next seed node from seedNodeAddresses - List list = seedNodeAddresses.stream() - .filter(e -> peerManager.isSeedNode(e) && !peerManager.isSelf(e)) - .collect(Collectors.toList()); - Collections.shuffle(list); - - if (!list.isEmpty()) { - NodeAddress nodeAddress = list.get(0); - seedNodeAddresses.remove(nodeAddress); - sendRepublishRequest(nodeAddress); - } else { - log.warn("No more seed nodes available. We try any of our other peers."); - connectToAnyFullNode(); - } - } - } - - // TODO support also lite nodes - private void connectToAnyFullNode() { - Capabilities required = new Capabilities(Capability.DAO_FULL_NODE); - - List list = peerManager.getLivePeers().stream() - .filter(peer -> peer.getCapabilities().containsAll(required)) - .collect(Collectors.toList()); - - if (list.isEmpty()) - list = peerManager.getReportedPeers().stream() - .filter(peer -> peer.getCapabilities().containsAll(required)) - .collect(Collectors.toList()); - - if (list.isEmpty()) - list = peerManager.getPersistedPeers().stream() - .filter(peer -> peer.getCapabilities().containsAll(required)) - .collect(Collectors.toList()); - - if (!list.isEmpty()) { - // We avoid the complexity to maintain the results of all our peers and to iterate all until we find a good peer, - // but we prefer simplicity with the risk that we don't get the data so we request from max 4 peers in parallel - // assuming that at least one will republish and therefore we should receive all data. - list = new ArrayList<>(list.subList(0, Math.min(list.size(), 4))); - list.stream() - .map(Peer::getNodeAddress) - .forEach(this::sendRepublishRequest); - } else { - log.warn("No other nodes found. We try again in 60 seconds."); - UserThread.runAfter(this::connectToNextNode, 60); - } - } - - private void handleFault(NodeAddress nodeAddress) { - peerManager.handleConnectionFault(nodeAddress); - } - - private void stop() { - stopped = true; - stopTimeoutTimer(); - } - - private void stopTimeoutTimer() { - if (timeoutTimer != null) { - timeoutTimer.stop(); - timeoutTimer = null; - } - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/network/messages/RepublishGovernanceDataRequest.java b/core/src/main/java/bisq/core/dao/governance/blindvote/network/messages/RepublishGovernanceDataRequest.java deleted file mode 100644 index 258149a5fc..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/network/messages/RepublishGovernanceDataRequest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.blindvote.network.messages; - -import bisq.network.p2p.DirectMessage; -import bisq.network.p2p.storage.payload.CapabilityRequiringPayload; - -import bisq.common.app.Capabilities; -import bisq.common.app.Capability; -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkEnvelope; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - - -// This message is sent only to full DAO nodes -@EqualsAndHashCode(callSuper = true) -@Getter -public final class RepublishGovernanceDataRequest extends NetworkEnvelope implements DirectMessage, CapabilityRequiringPayload { - - public RepublishGovernanceDataRequest() { - this(Version.getP2PMessageVersion()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private RepublishGovernanceDataRequest(int messageVersion) { - super(messageVersion); - } - - @Override - public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { - return getNetworkEnvelopeBuilder() - .setRepublishGovernanceDataRequest(protobuf.RepublishGovernanceDataRequest.newBuilder()) - .build(); - } - - public static NetworkEnvelope fromProto(protobuf.RepublishGovernanceDataRequest proto, int messageVersion) { - return new RepublishGovernanceDataRequest(messageVersion); - } - - @Override - public Capabilities getRequiredCapabilities() { - return new Capabilities(Capability.DAO_FULL_NODE); - } - - @Override - public String toString() { - return "RepublishGovernanceDataRequest{" + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/storage/BlindVotePayload.java b/core/src/main/java/bisq/core/dao/governance/blindvote/storage/BlindVotePayload.java deleted file mode 100644 index 1a07756221..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/storage/BlindVotePayload.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.blindvote.storage; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.governance.blindvote.BlindVote; - -import bisq.network.p2p.storage.payload.PersistableNetworkPayload; - -import bisq.common.crypto.Hash; -import bisq.common.util.Utilities; - -import com.google.protobuf.ByteString; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -/** - * Wrapper for proposal to be stored in the append-only BlindVoteStore storage. - * - * Data size: 185 bytes - */ -@Immutable -@Slf4j -@Getter -@EqualsAndHashCode -public final class BlindVotePayload implements PersistableNetworkPayload, ConsensusCritical { - - private final BlindVote blindVote; - protected final byte[] hash; // 20 byte - - public BlindVotePayload(BlindVote blindVote) { - this(blindVote, Hash.getRipemd160hash(blindVote.toProtoMessage().toByteArray())); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private BlindVotePayload(BlindVote blindVote, byte[] hash) { - this.blindVote = blindVote; - this.hash = hash; - } - - private protobuf.BlindVotePayload.Builder getBlindVoteBuilder() { - return protobuf.BlindVotePayload.newBuilder() - .setBlindVote(blindVote.toProtoMessage()) - .setHash(ByteString.copyFrom(hash)); - } - - @Override - public protobuf.PersistableNetworkPayload toProtoMessage() { - return protobuf.PersistableNetworkPayload.newBuilder().setBlindVotePayload(getBlindVoteBuilder()).build(); - } - - public protobuf.BlindVotePayload toProtoBlindVotePayload() { - return getBlindVoteBuilder().build(); - } - - - public static BlindVotePayload fromProto(protobuf.BlindVotePayload proto) { - return new BlindVotePayload(BlindVote.fromProto(proto.getBlindVote()), - proto.getHash().toByteArray()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PersistableNetworkPayload - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public boolean verifyHashSize() { - return hash.length == 20; - } - - @Override - public byte[] getHash() { - return hash; - } - - @Override - public String toString() { - return "BlindVotePayload{" + - "\n blindVote=" + blindVote + - ",\n hash=" + Utilities.bytesAsHexString(hash) + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/storage/BlindVoteStorageService.java b/core/src/main/java/bisq/core/dao/governance/blindvote/storage/BlindVoteStorageService.java deleted file mode 100644 index 0271fdce7f..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/storage/BlindVoteStorageService.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.blindvote.storage; - -import bisq.network.p2p.storage.P2PDataStorage; -import bisq.network.p2p.storage.payload.PersistableNetworkPayload; -import bisq.network.p2p.storage.persistence.MapStoreService; - -import bisq.common.config.Config; -import bisq.common.persistence.PersistenceManager; - -import javax.inject.Inject; -import javax.inject.Named; - -import java.io.File; - -import java.util.Map; - -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class BlindVoteStorageService extends MapStoreService { - private static final String FILE_NAME = "BlindVoteStore"; - - // At startup it is true, so the data we receive from the seed node are not checked against the phase as we have - // not started up the DAO domain at that moment. - @Setter - private boolean notInVoteRevealPhase = true; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public BlindVoteStorageService(@Named(Config.STORAGE_DIR) File storageDir, - PersistenceManager persistenceManager) { - super(storageDir, persistenceManager); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public String getFileName() { - return FILE_NAME; - } - - @Override - protected void initializePersistenceManager() { - persistenceManager.initialize(store, PersistenceManager.Source.NETWORK); - } - - @Override - public Map getMap() { - return store.getMap(); - } - - @Override - public boolean canHandle(PersistableNetworkPayload payload) { - return payload instanceof BlindVotePayload && notInVoteRevealPhase; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Protected - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected BlindVoteStore createStore() { - return new BlindVoteStore(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/storage/BlindVoteStore.java b/core/src/main/java/bisq/core/dao/governance/blindvote/storage/BlindVoteStore.java deleted file mode 100644 index 41ed5b0368..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/storage/BlindVoteStore.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.blindvote.storage; - -import bisq.network.p2p.storage.persistence.PersistableNetworkPayloadStore; - -import com.google.protobuf.Message; - -import java.util.List; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - - -/** - * We store only the payload in the PB file to save disc space. The hash of the payload can be created anyway and - * is only used as key in the map. So we have a hybrid data structure which is represented as list in the protobuffer - * definition and provide a hashMap for the domain access. - */ -@Slf4j -public class BlindVoteStore extends PersistableNetworkPayloadStore { - BlindVoteStore() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private BlindVoteStore(List list) { - super(list); - } - - public Message toProtoMessage() { - return protobuf.PersistableEnvelope.newBuilder() - .setBlindVoteStore(getBuilder()) - .build(); - } - - private protobuf.BlindVoteStore.Builder getBuilder() { - final List protoList = map.values().stream() - .map(payload -> (BlindVotePayload) payload) - .map(BlindVotePayload::toProtoBlindVotePayload) - .collect(Collectors.toList()); - return protobuf.BlindVoteStore.newBuilder().addAllItems(protoList); - } - - public static BlindVoteStore fromProto(protobuf.BlindVoteStore proto) { - List list = proto.getItemsList().stream() - .map(BlindVotePayload::fromProto).collect(Collectors.toList()); - return new BlindVoteStore(list); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/Bond.java b/core/src/main/java/bisq/core/dao/governance/bond/Bond.java deleted file mode 100644 index d2ab83a8db..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/Bond.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond; - -import java.util.Objects; - -import lombok.Getter; -import lombok.Setter; - -import javax.annotation.Nullable; - -/** - * Base class for BondedRole and BondedReputation. Holds the state of the bonded asset. - */ -@Getter -public abstract class Bond { - @Getter - protected final T bondedAsset; - @Setter - @Nullable - protected String lockupTxId; - @Setter - @Nullable - protected String unlockTxId; - @Setter - protected BondState bondState = BondState.READY_FOR_LOCKUP; - @Setter - private long amount; - @Setter - private long lockupDate; - @Setter - private long unlockDate; - @Setter - private int lockTime; - - protected Bond(T bondedAsset) { - this.bondedAsset = bondedAsset; - } - - public boolean isActive() { - return bondState.isActive(); - } - - // 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 - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Bond)) return false; - Bond bond = (Bond) o; - return amount == bond.amount && - lockupDate == bond.lockupDate && - unlockDate == bond.unlockDate && - lockTime == bond.lockTime && - Objects.equals(bondedAsset, bond.bondedAsset) && - Objects.equals(lockupTxId, bond.lockupTxId) && - Objects.equals(unlockTxId, bond.unlockTxId) && - bondState.name().equals(bond.bondState.name()); - } - - @Override - public int hashCode() { - return Objects.hash(bondedAsset, lockupTxId, unlockTxId, bondState.name(), amount, lockupDate, unlockDate, lockTime); - } - - @Override - public String toString() { - return "Bond{" + - "\n bondedAsset=" + bondedAsset + - ",\n lockupTxId='" + lockupTxId + '\'' + - ",\n unlockTxId='" + unlockTxId + '\'' + - ",\n bondState=" + bondState + - ",\n amount=" + amount + - ",\n lockupDate=" + lockupDate + - ",\n unlockDate=" + unlockDate + - ",\n lockTime=" + lockTime + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/BondConsensus.java b/core/src/main/java/bisq/core/dao/governance/bond/BondConsensus.java deleted file mode 100644 index 25fd40deec..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/BondConsensus.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond; - -import bisq.core.dao.governance.bond.lockup.LockupReason; -import bisq.core.dao.state.model.blockchain.OpReturnType; - -import bisq.common.app.Version; -import bisq.common.util.Utilities; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import java.util.Arrays; -import java.util.Optional; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class BondConsensus { - // In the UI we don't allow 0 as that would mean that the tx gets spent - // in the same block as the unspent tx and we don't support unconfirmed txs in the DAO. Technically though 0 - // works as well. - @Getter - private static int minLockTime = 1; - - // Max value is max of a short int as we use only 2 bytes in the opReturn for the lockTime - @Getter - private static int maxLockTime = 65535; - - public static byte[] getLockupOpReturnData(int lockTime, LockupReason type, byte[] hash) throws IOException { - // PushData of <= 4 bytes is converted to int when returned from bitcoind and not handled the way we - // require by btcd-cli4j, avoid opReturns with 4 bytes or less - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - outputStream.write(OpReturnType.LOCKUP.getType()); - outputStream.write(Version.LOCKUP); - outputStream.write(type.getId()); - byte[] bytes = Utilities.integerToByteArray(lockTime, 2); - outputStream.write(bytes[0]); - outputStream.write(bytes[1]); - outputStream.write(hash); - return outputStream.toByteArray(); - } catch (IOException e) { - // Not expected to happen ever - e.printStackTrace(); - log.error(e.toString()); - throw e; - } - } - - public static boolean hasOpReturnDataValidLength(byte[] opReturnData) { - return opReturnData.length == 25; - } - - public static int getLockTime(byte[] opReturnData) { - return Utilities.byteArrayToInteger(Arrays.copyOfRange(opReturnData, 3, 5)); - } - - public static byte[] getHashFromOpReturnData(byte[] opReturnData) { - return Arrays.copyOfRange(opReturnData, 5, 25); - } - - public static boolean isLockTimeInValidRange(int lockTime) { - return lockTime >= BondConsensus.getMinLockTime() && lockTime <= BondConsensus.getMaxLockTime(); - } - - public static Optional getLockupReason(byte[] opReturnData) { - return LockupReason.getLockupReason(opReturnData[2]); - } - - public static boolean isLockTimeOver(long unlockBlockHeight, long currentBlockHeight) { - return currentBlockHeight >= unlockBlockHeight; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/BondRepository.java b/core/src/main/java/bisq/core/dao/governance/bond/BondRepository.java deleted file mode 100644 index ade5ecd783..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/BondRepository.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond; - -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.BaseTxOutput; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.blockchain.SpentInfo; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.blockchain.TxType; - -import org.bitcoinj.core.Sha256Hash; -import org.bitcoinj.core.Transaction; -import org.bitcoinj.core.TransactionInput; -import org.bitcoinj.core.TransactionOutput; -import org.bitcoinj.script.ScriptPattern; - -import javax.inject.Inject; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Collect bonds and bond asset data from other sources and provides access to the collection. - * Gets updated after a new block is parsed or at bsqWallet transaction change to detect also state changes by - * unconfirmed txs. - */ -@Slf4j -public abstract class BondRepository implements DaoSetupService, - BsqWalletService.WalletTransactionsChangeListener { - - /////////////////////////////////////////////////////////////////////////////////////////// - // Static - /////////////////////////////////////////////////////////////////////////////////////////// - - public static void applyBondState(DaoStateService daoStateService, Bond bond, Tx lockupTx, TxOutput lockupTxOutput) { - if (bond.getBondState() != BondState.LOCKUP_TX_PENDING || bond.getBondState() != BondState.UNLOCK_TX_PENDING) - bond.setBondState(BondState.LOCKUP_TX_CONFIRMED); - - bond.setLockupTxId(lockupTx.getId()); - // We use the tx time as we want to have a unique time for all users - bond.setLockupDate(lockupTx.getTime()); - bond.setAmount(lockupTx.getLockedAmount()); - bond.setLockTime(lockupTx.getLockTime()); - - if (!daoStateService.isUnspent(lockupTxOutput.getKey())) { - // Lockup is already spent (in unlock tx) - daoStateService.getSpentInfo(lockupTxOutput) - .map(SpentInfo::getTxId) - .flatMap(daoStateService::getTx) - .filter(unlockTx -> unlockTx.getTxType() == TxType.UNLOCK) - .ifPresent(unlockTx -> { - // cross check if it is in daoStateService.getUnlockTxOutputs() ? - String unlockTxId = unlockTx.getId(); - bond.setUnlockTxId(unlockTxId); - bond.setBondState(BondState.UNLOCK_TX_CONFIRMED); - bond.setUnlockDate(unlockTx.getTime()); - boolean unlocking = daoStateService.isUnlockingAndUnspent(unlockTxId); - if (unlocking) { - bond.setBondState(BondState.UNLOCKING); - } else { - bond.setBondState(BondState.UNLOCKED); - } - }); - } - - if ((bond.getLockupTxId() != null && daoStateService.isConfiscatedLockupTxOutput(bond.getLockupTxId())) || - (bond.getUnlockTxId() != null && daoStateService.isConfiscatedUnlockTxOutput(bond.getUnlockTxId()))) { - bond.setBondState(BondState.CONFISCATED); - } - } - - public static boolean isLockupTxUnconfirmed(BsqWalletService bsqWalletService, BondedAsset bondedAsset) { - return bsqWalletService.getPendingWalletTransactionsStream() - .map(transaction -> transaction.getOutputs().get(transaction.getOutputs().size() - 1)) - .filter(lastOutput -> ScriptPattern.isOpReturn(lastOutput.getScriptPubKey())) - .map(lastOutput -> lastOutput.getScriptPubKey().getChunks()) - .filter(chunks -> chunks.size() > 1) - .map(chunks -> chunks.get(1).data) - .anyMatch(data -> Arrays.equals(BondConsensus.getHashFromOpReturnData(data), bondedAsset.getHash())); - } - - public static boolean isUnlockTxUnconfirmed(BsqWalletService bsqWalletService, DaoStateService daoStateService, BondedAsset bondedAsset) { - return bsqWalletService.getPendingWalletTransactionsStream() - .filter(transaction -> transaction.getInputs().size() > 1) - .flatMap(transaction -> transaction.getInputs().stream()) // We need to iterate all inputs - .map(TransactionInput::getConnectedOutput) - .filter(Objects::nonNull) - .filter(transactionOutput -> transactionOutput.getIndex() == 0) // The output at the lockupTx must be index 0 - .map(TransactionOutput::getParentTransaction) - .filter(Objects::nonNull) - .map(Transaction::getTxId) - .map(Sha256Hash::toString) - .map(lockupTxId -> daoStateService.getLockupOpReturnTxOutput(lockupTxId).orElse(null)) - .filter(Objects::nonNull) - .map(BaseTxOutput::getOpReturnData) - .anyMatch(data -> Arrays.equals(BondConsensus.getHashFromOpReturnData(data), bondedAsset.getHash())); - } - - public static boolean isConfiscated(Bond bond, DaoStateService daoStateService) { - return (bond.getLockupTxId() != null && daoStateService.isConfiscatedLockupTxOutput(bond.getLockupTxId())) || - (bond.getUnlockTxId() != null && daoStateService.isConfiscatedUnlockTxOutput(bond.getUnlockTxId())); - } - - - protected final DaoStateService daoStateService; - protected final BsqWalletService bsqWalletService; - - // This map is just for convenience. The data which are used to fill the map are stored in the DaoState (role, txs). - protected final Map bondByUidMap = new HashMap<>(); - @Getter - protected final ObservableList bonds = FXCollections.observableArrayList(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public BondRepository(DaoStateService daoStateService, BsqWalletService bsqWalletService) { - this.daoStateService = daoStateService; - this.bsqWalletService = bsqWalletService; - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - daoStateService.addDaoStateListener(new DaoStateListener() { - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - update(); - } - }); - bsqWalletService.addWalletTransactionsChangeListener(this); - } - - @Override - public void start() { - update(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // BsqWalletService.WalletTransactionsChangeListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onWalletTransactionsChange() { - update(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public boolean isBondedAssetAlreadyInBond(R bondedAsset) { - boolean contains = bondByUidMap.containsKey(bondedAsset.getUid()); - return contains && bondByUidMap.get(bondedAsset.getUid()).getLockupTxId() != null; - } - - public List getActiveBonds() { - return bonds.stream().filter(Bond::isActive).collect(Collectors.toList()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Protected - /////////////////////////////////////////////////////////////////////////////////////////// - - protected abstract T createBond(R bondedAsset); - - protected abstract void updateBond(T bond, R bondedAsset, TxOutput lockupTxOutput); - - protected abstract Stream getBondedAssetStream(); - - protected void update() { - log.debug("update"); - getBondedAssetStream().forEach(bondedAsset -> { - String uid = bondedAsset.getUid(); - bondByUidMap.putIfAbsent(uid, createBond(bondedAsset)); - T bond = bondByUidMap.get(uid); - - daoStateService.getLockupTxOutputs().forEach(lockupTxOutput -> { - updateBond(bond, bondedAsset, lockupTxOutput); - }); - }); - - updateBondStateFromUnconfirmedLockupTxs(); - updateBondStateFromUnconfirmedUnlockTxs(); - - bonds.setAll(bondByUidMap.values()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void updateBondStateFromUnconfirmedLockupTxs() { - getBondedAssetStream().filter(bondedAsset -> isLockupTxUnconfirmed(bsqWalletService, bondedAsset)) - .map(bondedAsset -> bondByUidMap.get(bondedAsset.getUid())) - .filter(bond -> bond.getBondState() == BondState.READY_FOR_LOCKUP) - .forEach(bond -> bond.setBondState(isConfiscated(bond, daoStateService) ? BondState.CONFISCATED : BondState.LOCKUP_TX_PENDING)); - } - - private void updateBondStateFromUnconfirmedUnlockTxs() { - getBondedAssetStream().filter(bondedAsset -> isUnlockTxUnconfirmed(bsqWalletService, daoStateService, bondedAsset)) - .map(bondedAsset -> bondByUidMap.get(bondedAsset.getUid())) - .filter(bond -> bond.getBondState() == BondState.LOCKUP_TX_CONFIRMED) - .forEach(bond -> bond.setBondState(isConfiscated(bond, daoStateService) ? BondState.CONFISCATED : BondState.UNLOCK_TX_PENDING)); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/BondState.java b/core/src/main/java/bisq/core/dao/governance/bond/BondState.java deleted file mode 100644 index eb7dfe11f0..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/BondState.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond; - -/** - * Holds the different states of a bond. - * Used also in string properties ("dao.bond.bondState.*") - */ -public enum BondState { - UNDEFINED, - READY_FOR_LOCKUP, // Accepted by voting (if role) but no lockup tx made yet. - LOCKUP_TX_PENDING, // Tx broadcasted but not confirmed. Used only by tx publisher. - LOCKUP_TX_CONFIRMED, - UNLOCK_TX_PENDING, // Tx broadcasted but not confirmed. Used only by tx publisher. - UNLOCK_TX_CONFIRMED, - UNLOCKING, // Lock time still not expired - UNLOCKED, // Fully unlocked - CONFISCATED; // Bond got confiscated by DAO voting - - public boolean isActive() { - return this == BondState.LOCKUP_TX_CONFIRMED || - this == BondState.UNLOCK_TX_PENDING || - this == BondState.UNLOCK_TX_CONFIRMED || - this == BondState.UNLOCKING; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/BondedAsset.java b/core/src/main/java/bisq/core/dao/governance/bond/BondedAsset.java deleted file mode 100644 index 46f5a7c573..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/BondedAsset.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond; - -/** - * Represents the bonded asset (e.g. Role or Reputation). - */ -public interface BondedAsset { - byte[] getHash(); - - String getUid(); - - String getDisplayString(); -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/lockup/LockupReason.java b/core/src/main/java/bisq/core/dao/governance/bond/lockup/LockupReason.java deleted file mode 100644 index 75da221ce9..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/lockup/LockupReason.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond.lockup; - -import java.util.Arrays; -import java.util.Optional; - -import lombok.Getter; - -/** - * Reason for locking up a bond. - */ -public enum LockupReason { - UNDEFINED((byte) 0x00), - BONDED_ROLE((byte) 0x01), - REPUTATION((byte) 0x02); - - @Getter - private byte id; - - LockupReason(byte id) { - this.id = id; - } - - public static Optional getLockupReason(byte id) { - return Arrays.stream(LockupReason.values()) - .filter(lockupType -> lockupType.id == id) - .findAny(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/lockup/LockupTxService.java b/core/src/main/java/bisq/core/dao/governance/bond/lockup/LockupTxService.java deleted file mode 100644 index 901793c74d..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/lockup/LockupTxService.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond.lockup; - -import bisq.core.btc.exceptions.TransactionVerificationException; -import bisq.core.btc.exceptions.TxBroadcastException; -import bisq.core.btc.exceptions.WalletException; -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.TxBroadcaster; -import bisq.core.btc.wallet.WalletsManager; -import bisq.core.dao.governance.bond.BondConsensus; -import bisq.core.dao.state.model.blockchain.TxType; - -import bisq.common.handlers.ExceptionHandler; -import bisq.common.util.Tuple2; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.Transaction; - -import javax.inject.Inject; - -import java.io.IOException; - -import java.util.function.Consumer; - -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkArgument; - -/** - * Service for publishing the lockup transaction. - */ -@Slf4j -public class LockupTxService { - private final WalletsManager walletsManager; - private final BsqWalletService bsqWalletService; - private final BtcWalletService btcWalletService; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public LockupTxService(WalletsManager walletsManager, - BsqWalletService bsqWalletService, - BtcWalletService btcWalletService) { - this.walletsManager = walletsManager; - this.bsqWalletService = bsqWalletService; - this.btcWalletService = btcWalletService; - } - - public void publishLockupTx(Coin lockupAmount, int lockTime, LockupReason lockupReason, byte[] hash, - Consumer resultHandler, ExceptionHandler exceptionHandler) { - checkArgument(lockTime <= BondConsensus.getMaxLockTime() && - lockTime >= BondConsensus.getMinLockTime(), "lockTime not in range"); - try { - Transaction lockupTx = getLockupTx(lockupAmount, lockTime, lockupReason, hash); - walletsManager.publishAndCommitBsqTx(lockupTx, TxType.LOCKUP, new TxBroadcaster.Callback() { - @Override - public void onSuccess(Transaction transaction) { - resultHandler.accept(transaction.getTxId().toString()); - } - - @Override - public void onFailure(TxBroadcastException exception) { - exceptionHandler.handleException(exception); - } - }); - - } catch (TransactionVerificationException | InsufficientMoneyException | WalletException | - IOException exception) { - exceptionHandler.handleException(exception); - } - } - - public Tuple2 getMiningFeeAndTxVsize(Coin lockupAmount, int lockTime, LockupReason lockupReason, byte[] hash) - throws InsufficientMoneyException, WalletException, TransactionVerificationException, IOException { - Transaction tx = getLockupTx(lockupAmount, lockTime, lockupReason, hash); - Coin miningFee = tx.getFee(); - int txVsize = tx.getVsize(); - return new Tuple2<>(miningFee, txVsize); - } - - private Transaction getLockupTx(Coin lockupAmount, int lockTime, LockupReason lockupReason, byte[] hash) - throws InsufficientMoneyException, WalletException, TransactionVerificationException, IOException { - byte[] opReturnData = BondConsensus.getLockupOpReturnData(lockTime, lockupReason, hash); - Transaction preparedTx = bsqWalletService.getPreparedLockupTx(lockupAmount); - Transaction txWithBtcFee = btcWalletService.completePreparedBsqTx(preparedTx, opReturnData); - Transaction transaction = bsqWalletService.signTx(txWithBtcFee); - log.info("Lockup tx: " + transaction); - return transaction; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/reputation/BondedReputation.java b/core/src/main/java/bisq/core/dao/governance/bond/reputation/BondedReputation.java deleted file mode 100644 index a630672771..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/reputation/BondedReputation.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond.reputation; - -import bisq.core.dao.governance.bond.Bond; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -/** - * Wrapper for reputation which contains the mutable state of a bonded reputation. Only kept in memory. - */ -@Getter -@EqualsAndHashCode(callSuper = true) -public final class BondedReputation extends Bond { - - BondedReputation(Reputation reputation) { - super(reputation); - } - - @Override - public String toString() { - return "BondedReputation{" + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/reputation/BondedReputationRepository.java b/core/src/main/java/bisq/core/dao/governance/bond/reputation/BondedReputationRepository.java deleted file mode 100644 index 8ec84f5677..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/reputation/BondedReputationRepository.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond.reputation; - -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.dao.governance.bond.Bond; -import bisq.core.dao.governance.bond.BondConsensus; -import bisq.core.dao.governance.bond.BondRepository; -import bisq.core.dao.governance.bond.role.BondedRole; -import bisq.core.dao.governance.bond.role.BondedRolesRepository; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.TxOutput; - -import javax.inject.Inject; - -import javafx.collections.ListChangeListener; - -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import lombok.extern.slf4j.Slf4j; - -/** - * Collect bonded reputations from the daoState blockchain data excluding bonded roles - * and provides access to the collection. - * Gets updated after a new block is parsed or at bsqWallet transaction change to detect also state changes by - * unconfirmed txs. - */ -@Slf4j -public class BondedReputationRepository extends BondRepository { - private final BondedRolesRepository bondedRolesRepository; - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public BondedReputationRepository(DaoStateService daoStateService, BsqWalletService bsqWalletService, - BondedRolesRepository bondedRolesRepository) { - super(daoStateService, bsqWalletService); - - this.bondedRolesRepository = bondedRolesRepository; - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - super.addListeners(); - - // As event listeners do not have a deterministic ordering of callback we need to ensure - // that we get updated our data after the bondedRolesRepository was updated. - // The update gets triggered by daoState or wallet changes. It could be that we get triggered first the - // listeners and update our data with stale data from bondedRolesRepository. After that the bondedRolesRepository - // gets triggered the listeners and we would miss the current state if we would not listen here as well on the - // bond list. - bondedRolesRepository.getBonds().addListener((ListChangeListener) c -> update()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Protected - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected BondedReputation createBond(Reputation reputation) { - return new BondedReputation(reputation); - } - - @Override - protected Stream getBondedAssetStream() { - return getBondedReputationStream().map(Bond::getBondedAsset); - } - - @Override - protected void update() { - bondByUidMap.clear(); - getBondedReputationStream().forEach(bondedReputation -> bondByUidMap.put(bondedReputation.getBondedAsset().getUid(), bondedReputation)); - bonds.setAll(bondByUidMap.values()); - } - - private Stream getBondedReputationStream() { - return getLockupTxOutputsForBondedReputation() - .map(lockupTxOutput -> { - String lockupTxId = lockupTxOutput.getTxId(); - Optional optionalOpReturnTxOutput = daoStateService.getLockupOpReturnTxOutput(lockupTxId); - if (optionalOpReturnTxOutput.isPresent()) { - TxOutput opReturnTxOutput = optionalOpReturnTxOutput.get(); - byte[] hash = BondConsensus.getHashFromOpReturnData(opReturnTxOutput.getOpReturnData()); - Reputation reputation = new Reputation(hash); - BondedReputation bondedReputation = new BondedReputation(reputation); - updateBond(bondedReputation, reputation, lockupTxOutput); - return bondedReputation; - } else { - return null; - } - - }) - .filter(Objects::nonNull); - } - - private Stream getLockupTxOutputsForBondedReputation() { - // We exclude bonded roles, so we store those in a lookup set. - Set bondedRolesLockupTxIdSet = bondedRolesRepository.getBonds().stream().map(Bond::getLockupTxId).collect(Collectors.toSet()); - return daoStateService.getLockupTxOutputs().stream() - .filter(e -> !bondedRolesLockupTxIdSet.contains(e.getTxId())); - } - - @Override - protected void updateBond(BondedReputation bond, Reputation bondedAsset, TxOutput lockupTxOutput) { - // Lets see if we have a lock up tx. - String lockupTxId = lockupTxOutput.getTxId(); - daoStateService.getTx(lockupTxId).ifPresent(lockupTx -> { - BondRepository.applyBondState(daoStateService, bond, lockupTx, lockupTxOutput); - }); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/reputation/MyBondedReputation.java b/core/src/main/java/bisq/core/dao/governance/bond/reputation/MyBondedReputation.java deleted file mode 100644 index f9af093c4a..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/reputation/MyBondedReputation.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond.reputation; - -import bisq.core.dao.governance.bond.Bond; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -/** - * Wrapper for reputation which contains the mutable state of my bonded reputation. Only kept in memory. - * As it carries MyReputation it has access to the private salt data. - */ -@Getter -@EqualsAndHashCode(callSuper = true) -public final class MyBondedReputation extends Bond { - - public MyBondedReputation(MyReputation myReputation) { - super(myReputation); - } - - @Override - public String toString() { - return "MyBondedReputation{" + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/reputation/MyBondedReputationRepository.java b/core/src/main/java/bisq/core/dao/governance/bond/reputation/MyBondedReputationRepository.java deleted file mode 100644 index b3935408cb..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/reputation/MyBondedReputationRepository.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond.reputation; - -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.governance.bond.BondConsensus; -import bisq.core.dao.governance.bond.BondRepository; -import bisq.core.dao.governance.bond.BondState; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Block; - -import javax.inject.Inject; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Collect MyBondedReputations from the myReputationListService and provides access to the collection. - * Gets updated after a new block is parsed or at bsqWallet transaction change to detect also state changes by - * unconfirmed txs. - */ -@Slf4j -public class MyBondedReputationRepository implements DaoSetupService, BsqWalletService.WalletTransactionsChangeListener { - private final DaoStateService daoStateService; - private final BsqWalletService bsqWalletService; - private final MyReputationListService myReputationListService; - @Getter - private final ObservableList myBondedReputations = FXCollections.observableArrayList(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public MyBondedReputationRepository(DaoStateService daoStateService, - BsqWalletService bsqWalletService, - MyReputationListService myReputationListService) { - this.daoStateService = daoStateService; - this.bsqWalletService = bsqWalletService; - this.myReputationListService = myReputationListService; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - daoStateService.addDaoStateListener(new DaoStateListener() { - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - update(); - } - }); - bsqWalletService.addWalletTransactionsChangeListener(this); - } - - @Override - public void start() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // BsqWalletService.WalletTransactionsChangeListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onWalletTransactionsChange() { - update(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void update() { - log.debug("update"); - // It can be that the same salt/hash is in several lockupTxs, so we use the bondByLockupTxIdMap to eliminate - // duplicates by the collection algorithm. - Map bondByLockupTxIdMap = new HashMap<>(); - myReputationListService.getMyReputationList().stream() - .flatMap(this::getMyBondedReputation) - .forEach(e -> bondByLockupTxIdMap.putIfAbsent(e.getLockupTxId(), e)); - - myBondedReputations.setAll(bondByLockupTxIdMap.values().stream() - .peek(myBondedReputation -> { - if (BondRepository.isConfiscated(myBondedReputation, daoStateService)) { - myBondedReputation.setBondState(BondState.CONFISCATED); - } else { - // We don't have a UI use case for showing LOCKUP_TX_PENDING yet, but let's keep the code so if needed - // it's there. - if (BondRepository.isLockupTxUnconfirmed(bsqWalletService, myBondedReputation.getBondedAsset()) && - myBondedReputation.getBondState() == BondState.READY_FOR_LOCKUP) { - myBondedReputation.setBondState(BondState.LOCKUP_TX_PENDING); - } else if (BondRepository.isUnlockTxUnconfirmed(bsqWalletService, daoStateService, myBondedReputation.getBondedAsset()) && - myBondedReputation.getBondState() == BondState.LOCKUP_TX_CONFIRMED) { - myBondedReputation.setBondState(BondState.UNLOCK_TX_PENDING); - } - } - }) - .collect(Collectors.toList())); - } - - private Stream getMyBondedReputation(MyReputation myReputation) { - return daoStateService.getLockupTxOutputs().stream() - .flatMap(lockupTxOutput -> { - String lockupTxId = lockupTxOutput.getTxId(); - return daoStateService.getTx(lockupTxId) - .map(lockupTx -> { - byte[] opReturnData = lockupTx.getLastTxOutput().getOpReturnData(); - byte[] hash = BondConsensus.getHashFromOpReturnData(opReturnData); - // There could be multiple txs with the same hash, so we collect a stream and not use an optional. - if (Arrays.equals(hash, myReputation.getHash())) { - MyBondedReputation myBondedReputation = new MyBondedReputation(myReputation); - BondRepository.applyBondState(daoStateService, myBondedReputation, lockupTx, lockupTxOutput); - return myBondedReputation; - } else { - return null; - } - }) - .stream(); - }) - .filter(Objects::nonNull); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/reputation/MyReputation.java b/core/src/main/java/bisq/core/dao/governance/bond/reputation/MyReputation.java deleted file mode 100644 index f4bef01ac4..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/reputation/MyReputation.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond.reputation; - -import bisq.core.dao.governance.bond.BondedAsset; - -import bisq.common.crypto.Hash; -import bisq.common.proto.network.NetworkPayload; -import bisq.common.proto.persistable.PersistablePayload; -import bisq.common.util.Utilities; - -import com.google.protobuf.ByteString; - -import java.util.Arrays; -import java.util.Objects; -import java.util.UUID; - -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -import static com.google.common.base.Preconditions.checkArgument; - -/** - * MyReputation is persisted locally and carries the private salt data. In contrast to Reputation which is the public - * data everyone can derive from the blockchain data (hash in opReturn). - */ -@Immutable -@Value -@Slf4j -public final class MyReputation implements PersistablePayload, NetworkPayload, BondedAsset { - // Uid is needed to be sure that 2 objects with the same salt are kept separate. - private final String uid; - private final byte[] salt; - private final transient byte[] hash; // Not persisted as it is derived from salt. Stored for caching purpose only. - - public MyReputation(byte[] salt) { - this(UUID.randomUUID().toString(), salt); - checkArgument(salt.length <= 20, "salt must not be longer then 20 bytes"); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private MyReputation(String uid, byte[] salt) { - this.uid = uid; - this.salt = salt; - this.hash = Hash.getSha256Ripemd160hash(salt); - } - - @Override - public protobuf.MyReputation toProtoMessage() { - return protobuf.MyReputation.newBuilder() - .setUid(uid) - .setSalt(ByteString.copyFrom(salt)) - .build(); - } - - public static MyReputation fromProto(protobuf.MyReputation proto) { - return new MyReputation(proto.getUid(), proto.getSalt().toByteArray()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // BondedAsset implementation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public byte[] getHash() { - return hash; - } - - @Override - public String getDisplayString() { - return Utilities.bytesAsHexString(hash); - } - - @Override - public String getUid() { - return Utilities.bytesAsHexString(hash); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof MyReputation)) return false; - if (!super.equals(o)) return false; - MyReputation that = (MyReputation) o; - return Objects.equals(uid, that.uid) && - Arrays.equals(salt, that.salt); - } - - @Override - public int hashCode() { - - int result = Objects.hash(super.hashCode(), uid); - result = 31 * result + Arrays.hashCode(salt); - return result; - } -/////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public String toString() { - return "MyReputation{" + - "\n uid=" + uid + - "\n salt=" + Utilities.bytesAsHexString(salt) + - "\n hash=" + Utilities.bytesAsHexString(hash) + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/reputation/MyReputationList.java b/core/src/main/java/bisq/core/dao/governance/bond/reputation/MyReputationList.java deleted file mode 100644 index 2a39f78317..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/reputation/MyReputationList.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond.reputation; - -import bisq.common.proto.persistable.PersistableList; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.EqualsAndHashCode; - -/** - * PersistableEnvelope wrapper for list of MyReputations. - */ -@EqualsAndHashCode(callSuper = true) -public class MyReputationList extends PersistableList { - - private MyReputationList(List list) { - super(list); - } - - MyReputationList() { - super(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.PersistableEnvelope toProtoMessage() { - return protobuf.PersistableEnvelope.newBuilder().setMyReputationList(getBuilder()).build(); - } - - private protobuf.MyReputationList.Builder getBuilder() { - return protobuf.MyReputationList.newBuilder() - .addAllMyReputation(getList().stream() - .map(MyReputation::toProtoMessage) - .collect(Collectors.toList())); - } - - public static MyReputationList fromProto(protobuf.MyReputationList proto) { - return new MyReputationList(new ArrayList<>(proto.getMyReputationList().stream() - .map(MyReputation::fromProto) - .collect(Collectors.toList()))); - } - - @Override - public String toString() { - return "List of salts in MyReputationList: " + getList().stream() - .map(MyReputation::getSalt) - .collect(Collectors.toList()); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/reputation/MyReputationListService.java b/core/src/main/java/bisq/core/dao/governance/bond/reputation/MyReputationListService.java deleted file mode 100644 index 5995a3be35..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/reputation/MyReputationListService.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond.reputation; - -import bisq.core.dao.DaoSetupService; - -import bisq.common.app.DevEnv; -import bisq.common.persistence.PersistenceManager; -import bisq.common.proto.persistable.PersistedDataHost; - -import javax.inject.Inject; - -import java.util.List; - -import lombok.extern.slf4j.Slf4j; - -/** - * Manages the persistence of myReputation objects. - */ -@Slf4j -public class MyReputationListService implements PersistedDataHost, DaoSetupService { - - private final PersistenceManager persistenceManager; - private final MyReputationList myReputationList = new MyReputationList(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public MyReputationListService(PersistenceManager persistenceManager) { - this.persistenceManager = persistenceManager; - persistenceManager.initialize(myReputationList, PersistenceManager.Source.PRIVATE); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PersistedDataHost - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void readPersisted(Runnable completeHandler) { - if (DevEnv.isDaoActivated()) { - persistenceManager.readPersisted(persisted -> { - myReputationList.setAll(persisted.getList()); - completeHandler.run(); - }, - completeHandler); - } else { - completeHandler.run(); - } - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - } - - @Override - public void start() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void addReputation(MyReputation reputation) { - if (!myReputationList.contains(reputation)) { - myReputationList.add(reputation); - requestPersistence(); - } - } - - public List getMyReputationList() { - return myReputationList.getList(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void requestPersistence() { - persistenceManager.requestPersistence(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/reputation/Reputation.java b/core/src/main/java/bisq/core/dao/governance/bond/reputation/Reputation.java deleted file mode 100644 index e7394604be..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/reputation/Reputation.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond.reputation; - -import bisq.core.dao.governance.bond.BondedAsset; - -import bisq.common.util.Utilities; - -import java.util.Arrays; - -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -/** - * Reputation objects we found on the blockchain. We only know the hash of it. - * In contrast to MyReputation which represents the object we created and contains the - * private salt data. - */ -@Immutable -@Value -@Slf4j -public final class Reputation implements BondedAsset { - private final byte[] hash; - - public Reputation(byte[] hash) { - this.hash = hash; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // BondedAsset implementation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public byte[] getHash() { - return hash; - } - - @Override - public String getDisplayString() { - return Utilities.bytesAsHexString(hash); - } - - @Override - public String getUid() { - return Utilities.bytesAsHexString(hash); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Reputation)) return false; - if (!super.equals(o)) return false; - Reputation that = (Reputation) o; - return Arrays.equals(hash, that.hash); - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + Arrays.hashCode(hash); - return result; - } - - @Override - public String toString() { - return "Reputation{" + - "\n hash=" + Utilities.bytesAsHexString(hash) + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/role/BondedRole.java b/core/src/main/java/bisq/core/dao/governance/bond/role/BondedRole.java deleted file mode 100644 index f5a7026241..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/role/BondedRole.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond.role; - -import bisq.core.dao.governance.bond.Bond; -import bisq.core.dao.state.model.governance.Role; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -/** - * Wrapper for role which contains the mutable state of a bonded role. Only kept in memory. - */ -@Getter -@EqualsAndHashCode(callSuper = true) -public class BondedRole extends Bond { - - BondedRole(Role role) { - super(role); - } - - @Override - public String toString() { - return "BondedRole{" + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/role/BondedRolesRepository.java b/core/src/main/java/bisq/core/dao/governance/bond/role/BondedRolesRepository.java deleted file mode 100644 index d7a10ae1e0..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/role/BondedRolesRepository.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond.role; - -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.dao.governance.bond.BondConsensus; -import bisq.core.dao.governance.bond.BondRepository; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.governance.EvaluatedProposal; -import bisq.core.dao.state.model.governance.Proposal; -import bisq.core.dao.state.model.governance.Role; -import bisq.core.dao.state.model.governance.RoleProposal; - -import org.bitcoinj.core.Sha256Hash; -import org.bitcoinj.core.Transaction; - -import javax.inject.Inject; - -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import lombok.extern.slf4j.Slf4j; - -/** - * Collect bonded roles from the evaluatedProposals from the daoState and provides access to the collection. - */ -@Slf4j -public class BondedRolesRepository extends BondRepository { - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public BondedRolesRepository(DaoStateService daoStateService, BsqWalletService bsqWalletService) { - super(daoStateService, bsqWalletService); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public boolean isMyRole(Role role) { - Set myWalletTransactionIds = bsqWalletService.getClonedWalletTransactions().stream() - .map(Transaction::getTxId) - .map(Sha256Hash::toString) - .collect(Collectors.toSet()); - return getAcceptedBondedRoleProposalStream() - .filter(roleProposal -> roleProposal.getRole().equals(role)) - .map(Proposal::getTxId) - .anyMatch(myWalletTransactionIds::contains); - } - - public Optional getAcceptedBondedRoleProposal(Role role) { - return getAcceptedBondedRoleProposalStream().filter(e -> e.getRole().getUid().equals(role.getUid())).findAny(); - } - - - public List getAcceptedBonds() { - return bonds.stream() - .filter(bondedRole -> getAcceptedBondedRoleProposal(bondedRole.getBondedAsset()).isPresent()) - .collect(Collectors.toList()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Protected - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected BondedRole createBond(Role role) { - return new BondedRole(role); - } - - @Override - protected Stream getBondedAssetStream() { - return getBondedRoleProposalStream().map(RoleProposal::getRole); - } - - @Override - protected void updateBond(BondedRole bond, Role bondedAsset, TxOutput lockupTxOutput) { - // Lets see if we have a lock up tx. - String lockupTxId = lockupTxOutput.getTxId(); - daoStateService.getTx(lockupTxId).ifPresent(lockupTx -> { - byte[] opReturnData = lockupTx.getLastTxOutput().getOpReturnData(); - // We used the hash of the bonded bondedAsset object as our hash in OpReturn of the lock up tx to have a - // unique binding of the tx to the data object. - byte[] hash = BondConsensus.getHashFromOpReturnData(opReturnData); - Optional candidate = findBondedAssetByHash(hash); - if (candidate.isPresent() && bondedAsset.equals(candidate.get())) - applyBondState(daoStateService, bond, lockupTx, lockupTxOutput); - }); - } - - private Optional findBondedAssetByHash(byte[] hash) { - return getBondedAssetStream() - .filter(bondedAsset -> Arrays.equals(bondedAsset.getHash(), hash)) - .findAny(); - } - - private Stream getBondedRoleProposalStream() { - return daoStateService.getEvaluatedProposalList().stream() - .filter(evaluatedProposal -> evaluatedProposal.getProposal() instanceof RoleProposal) - .map(e -> ((RoleProposal) e.getProposal())); - } - - private Stream getAcceptedBondedRoleProposalStream() { - return daoStateService.getEvaluatedProposalList().stream() - .filter(evaluatedProposal -> evaluatedProposal.getProposal() instanceof RoleProposal) - .filter(EvaluatedProposal::isAccepted) - .map(e -> ((RoleProposal) e.getProposal())); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/bond/unlock/UnlockTxService.java b/core/src/main/java/bisq/core/dao/governance/bond/unlock/UnlockTxService.java deleted file mode 100644 index a4c6691cf6..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/bond/unlock/UnlockTxService.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.bond.unlock; - -import bisq.core.btc.exceptions.TransactionVerificationException; -import bisq.core.btc.exceptions.TxBroadcastException; -import bisq.core.btc.exceptions.WalletException; -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.TxBroadcaster; -import bisq.core.btc.wallet.WalletsManager; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.blockchain.TxType; - -import bisq.common.handlers.ExceptionHandler; -import bisq.common.util.Tuple2; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.Transaction; - -import javax.inject.Inject; - -import java.util.Optional; -import java.util.function.Consumer; - -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkArgument; - -/** - * Service for publishing the unlock transaction. - */ -@Slf4j -public class UnlockTxService { - private final WalletsManager walletsManager; - private final BsqWalletService bsqWalletService; - private final BtcWalletService btcWalletService; - private final DaoStateService daoStateService; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public UnlockTxService(WalletsManager walletsManager, - BsqWalletService bsqWalletService, - BtcWalletService btcWalletService, - DaoStateService daoStateService) { - this.walletsManager = walletsManager; - this.bsqWalletService = bsqWalletService; - this.btcWalletService = btcWalletService; - this.daoStateService = daoStateService; - } - - public void publishUnlockTx(String lockupTxId, Consumer resultHandler, ExceptionHandler exceptionHandler) { - try { - Transaction unlockTx = getUnlockTx(lockupTxId); - walletsManager.publishAndCommitBsqTx(unlockTx, TxType.UNLOCK, new TxBroadcaster.Callback() { - @Override - public void onSuccess(Transaction transaction) { - resultHandler.accept(transaction.getTxId().toString()); - } - - @Override - public void onFailure(TxBroadcastException exception) { - exceptionHandler.handleException(exception); - } - }); - } catch (TransactionVerificationException | InsufficientMoneyException | WalletException exception) { - exceptionHandler.handleException(exception); - } - } - - public Tuple2 getMiningFeeAndTxVsize(String lockupTxId) - throws InsufficientMoneyException, WalletException, TransactionVerificationException { - Transaction tx = getUnlockTx(lockupTxId); - Coin miningFee = tx.getFee(); - int txVsize = tx.getVsize(); - return new Tuple2<>(miningFee, txVsize); - } - - private Transaction getUnlockTx(String lockupTxId) - throws InsufficientMoneyException, WalletException, TransactionVerificationException { - Optional optionalLockupTxOutput = daoStateService.getLockupTxOutput(lockupTxId); - checkArgument(optionalLockupTxOutput.isPresent(), "lockupTxOutput must be present"); - TxOutput lockupTxOutput = optionalLockupTxOutput.get(); - Transaction preparedTx = bsqWalletService.getPreparedUnlockTx(lockupTxOutput); - Transaction txWithBtcFee = btcWalletService.completePreparedBsqTx(preparedTx, null); - Transaction transaction = bsqWalletService.signTx(txWithBtcFee); - log.info("Unlock tx: " + transaction); - return transaction; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/merit/MeritConsensus.java b/core/src/main/java/bisq/core/dao/governance/merit/MeritConsensus.java deleted file mode 100644 index 6ea2c7e075..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/merit/MeritConsensus.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.merit; - -import bisq.core.dao.governance.voteresult.VoteResultException; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.governance.Issuance; -import bisq.core.dao.state.model.governance.IssuanceType; -import bisq.core.dao.state.model.governance.MeritList; - -import bisq.common.crypto.Encryption; -import bisq.common.util.Utilities; - -import org.bitcoinj.core.ECKey; -import org.bitcoinj.core.Sha256Hash; - -import com.google.common.annotations.VisibleForTesting; - -import javax.crypto.SecretKey; - -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkArgument; - -@Slf4j -public class MeritConsensus { - // Value with 144 blocks a day and 365 days would be 52560. We take a close round number instead. - private static final int BLOCKS_PER_YEAR = 50_000; - - public static MeritList decryptMeritList(byte[] encryptedMeritList, SecretKey secretKey) - throws VoteResultException.DecryptionException { - try { - byte[] decrypted = Encryption.decrypt(encryptedMeritList, secretKey); - return MeritList.getMeritListFromBytes(decrypted); - } catch (Throwable t) { - throw new VoteResultException.DecryptionException(t); - } - } - - public static long getMeritStake(String blindVoteTxId, MeritList meritList, DaoStateService daoStateService) { - // We need to take the chain height when the blindVoteTx got published so we get the same merit for the vote even at - // later blocks (merit decreases with each block). - int blindVoteTxHeight = daoStateService.getTx(blindVoteTxId).map(Tx::getBlockHeight).orElse(0); - if (blindVoteTxHeight == 0) { - log.error("Error at getMeritStake: blindVoteTx not found in daoStateService. blindVoteTxId=" + blindVoteTxId); - return 0; - } - - // We only use past issuance. In case we would calculate the merit after the vote result phase we have the - // issuance from the same cycle but we must not add that to the merit. - return meritList.getList().stream() - .filter(merit -> isSignatureValid(merit.getSignature(), merit.getIssuance().getPubKey(), blindVoteTxId)) - .filter(merit -> merit.getIssuance().getChainHeight() <= blindVoteTxHeight) - .mapToLong(merit -> { - try { - Issuance issuance = merit.getIssuance(); - checkArgument(issuance.getIssuanceType() == IssuanceType.COMPENSATION, - "issuance must be of type COMPENSATION"); - return getWeightedMeritAmount(issuance.getAmount(), - issuance.getChainHeight(), - blindVoteTxHeight, - BLOCKS_PER_YEAR); - } catch (Throwable t) { - log.error("Error at getMeritStake: error={}, merit={}", t.toString(), merit); - return 0; - } - }) - .sum(); - } - - @VisibleForTesting - private static boolean isSignatureValid(byte[] signatureFromMerit, String pubKeyAsHex, String blindVoteTxId) { - // We verify if signature of hash of blindVoteTxId is correct. EC key from first input for blind vote tx is - // used for signature. - if (pubKeyAsHex == null) { - log.error("Error at isSignatureValid: pubKeyAsHex is null"); - return false; - } - - boolean result = false; - try { - ECKey pubKey = ECKey.fromPublicOnly(Utilities.decodeFromHex(pubKeyAsHex)); - ECKey.ECDSASignature signature = ECKey.ECDSASignature.decodeFromDER(signatureFromMerit).toCanonicalised(); - Sha256Hash msg = Sha256Hash.wrap(blindVoteTxId); - result = pubKey.verify(msg, signature); - } catch (Throwable t) { - log.error("Signature verification of issuance failed: " + t.toString()); - } - if (!result) { - log.error("Signature verification of issuance failed: blindVoteTxId={}, pubKeyAsHex={}", - blindVoteTxId, pubKeyAsHex); - } - return result; - } - - public static long getWeightedMeritAmount(long amount, int issuanceHeight, int blockHeight, int blocksPerYear) { - if (issuanceHeight > blockHeight) - throw new IllegalArgumentException("issuanceHeight must not be larger than blockHeight. issuanceHeight=" + issuanceHeight + "; blockHeight=" + blockHeight); - if (blockHeight < 0) - throw new IllegalArgumentException("blockHeight must not be negative. blockHeight=" + blockHeight); - if (amount < 0) - throw new IllegalArgumentException("amount must not be negative. amount" + amount); - if (blocksPerYear < 0) - throw new IllegalArgumentException("blocksPerYear must not be negative. blocksPerYear=" + blocksPerYear); - if (issuanceHeight < 0) - throw new IllegalArgumentException("issuanceHeight must not be negative. issuanceHeight=" + issuanceHeight); - - // We use a linear function to apply a factor for the issuance amount of 1 if the issuance was recent and 0 - // if the issuance was 2 years old or older. - // To avoid rounding issues with double values we multiply initially with a large number and divide at the end - // by that number again. As we multiply the amount in satoshis we get a reasonable good precision even the long - // division is not using rounding. Sticking with long values makes that operation safer against consensus - // failures causes by rounding differences with double. - - long maxAge = 2 * blocksPerYear; // maxAge=100 000 (MeritConsensus.BLOCKS_PER_YEAR is 50_000) - long age = Math.min(maxAge, blockHeight - issuanceHeight); - long inverseAge = maxAge - age; - - // We want a resolution of 1 block so we use the inverseAge and divide by maxAge afterwards to get the - // weighted amount - // We need to multiply first before we divide! - long weightedAmount = (amount * inverseAge) / maxAge; - - log.debug("getWeightedMeritAmount: age={}, inverseAge={}, weightedAmount={}, amount={}", age, inverseAge, weightedAmount, amount); - return weightedAmount; - } - - public static long getCurrentlyAvailableMerit(MeritList meritList, int currentChainHeight) { - // We need to take the chain height when the blindVoteTx got published so we get the same merit for the vote even at - // later blocks (merit decreases with each block). - // We add 1 block to currentChainHeight so that the displayed merit would match the merit in case we get the - // blind vote tx into the next block. - int height = currentChainHeight + 1; - return meritList.getList().stream() - .mapToLong(merit -> { - try { - Issuance issuance = merit.getIssuance(); - checkArgument(issuance.getIssuanceType() == IssuanceType.COMPENSATION, "issuance must be of type COMPENSATION"); - int issuanceHeight = issuance.getChainHeight(); - checkArgument(issuanceHeight <= height, - "issuanceHeight must not be larger as currentChainHeight"); - return getWeightedMeritAmount(issuance.getAmount(), - issuanceHeight, - height, - BLOCKS_PER_YEAR); - } catch (Throwable t) { - log.error("Error at getCurrentlyAvailableMerit: " + t.toString()); - return 0; - } - }) - .sum(); - } - -} diff --git a/core/src/main/java/bisq/core/dao/governance/myvote/MyVote.java b/core/src/main/java/bisq/core/dao/governance/myvote/MyVote.java deleted file mode 100644 index 6036bc6260..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/myvote/MyVote.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.myvote; - -import bisq.core.dao.governance.blindvote.BlindVote; -import bisq.core.dao.governance.blindvote.MyBlindVoteListService; -import bisq.core.dao.governance.merit.MeritConsensus; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.BallotList; -import bisq.core.dao.state.model.governance.MeritList; - -import bisq.common.crypto.Encryption; -import bisq.common.proto.persistable.PersistablePayload; -import bisq.common.util.JsonExclude; -import bisq.common.util.Utilities; - -import com.google.protobuf.ByteString; - -import javax.crypto.SecretKey; - -import java.util.Date; -import java.util.Optional; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -/** - * Holds all my vote related data. Is not immutable as revealTxId is set later. Only used for local persistence and not - * in consensus critical operations. - */ -@EqualsAndHashCode -@Slf4j -@Getter -public class MyVote implements PersistablePayload { - private int height; - private final BallotList ballotList; - private final byte[] secretKeyEncoded; - private final BlindVote blindVote; - private final long date; - @Setter - @Nullable - private String revealTxId; - - // Used just for caching - @JsonExclude - private final transient SecretKey secretKey; - - MyVote(int height, - BallotList ballotList, - byte[] secretKeyEncoded, - BlindVote blindVote) { - this(height, - ballotList, - secretKeyEncoded, - blindVote, - new Date().getTime(), - null); - - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private MyVote(int height, - BallotList ballotList, - byte[] secretKeyEncoded, - BlindVote blindVote, - long date, - @Nullable String revealTxId) { - this.height = height; - this.ballotList = ballotList; - this.secretKeyEncoded = secretKeyEncoded; - this.blindVote = blindVote; - this.date = date; - this.revealTxId = revealTxId; - - secretKey = Encryption.getSecretKeyFromBytes(secretKeyEncoded); - } - - @Override - public protobuf.MyVote toProtoMessage() { - final protobuf.MyVote.Builder builder = protobuf.MyVote.newBuilder() - .setHeight(height) - .setBlindVote(blindVote.getBuilder()) - .setBallotList(ballotList.getBuilder()) - .setSecretKeyEncoded(ByteString.copyFrom(secretKeyEncoded)) - .setDate(date); - Optional.ofNullable(revealTxId).ifPresent(builder::setRevealTxId); - return builder.build(); - } - - public static MyVote fromProto(protobuf.MyVote proto) { - return new MyVote(proto.getHeight(), - BallotList.fromProto(proto.getBallotList()), - proto.getSecretKeyEncoded().toByteArray(), - BlindVote.fromProto(proto.getBlindVote()), - proto.getDate(), - proto.getRevealTxId().isEmpty() ? null : proto.getRevealTxId()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public String getBlindVoteTxId() { - return blindVote.getTxId(); - } - - public long getMerit(MyBlindVoteListService myBlindVoteListService, DaoStateService daoStateService) { - MeritList meritList = myBlindVoteListService.getMerits(blindVote.getTxId()); - if (daoStateService.getTx(blindVote.getTxId()).isPresent()) - return MeritConsensus.getMeritStake(blindVote.getTxId(), meritList, daoStateService); - else - return MeritConsensus.getCurrentlyAvailableMerit(meritList, daoStateService.getChainHeight()); - } - - @Override - public String toString() { - return "MyVote{" + - "\n ballotList=" + ballotList + - ",\n secretKeyEncoded=" + Utilities.bytesAsHexString(secretKeyEncoded) + - ",\n blindVotePayload=" + blindVote + - ",\n date=" + new Date(date) + - ",\n revealTxId='" + revealTxId + '\'' + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/myvote/MyVoteList.java b/core/src/main/java/bisq/core/dao/governance/myvote/MyVoteList.java deleted file mode 100644 index 05610f8dcb..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/myvote/MyVoteList.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.myvote; - -import bisq.common.proto.persistable.PersistableList; - -import com.google.protobuf.Message; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.EqualsAndHashCode; - -@EqualsAndHashCode(callSuper = true) -public class MyVoteList extends PersistableList { - - MyVoteList() { - super(); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private MyVoteList(List list) { - super(list); - } - - @Override - public Message toProtoMessage() { - return protobuf.PersistableEnvelope.newBuilder() - .setMyVoteList(protobuf.MyVoteList.newBuilder() - .addAllMyVote(getList().stream() - .map(MyVote::toProtoMessage) - .collect(Collectors.toList()))) - .build(); - } - - public static MyVoteList fromProto(protobuf.MyVoteList proto) { - return new MyVoteList(new ArrayList<>(proto.getMyVoteList().stream() - .map(MyVote::fromProto) - .collect(Collectors.toList()))); - } - - @Override - public String toString() { - return "List of TxId's in MyVoteList: " + getList().stream() - .map(MyVote::getBlindVoteTxId) - .collect(Collectors.toList()); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/myvote/MyVoteListService.java b/core/src/main/java/bisq/core/dao/governance/myvote/MyVoteListService.java deleted file mode 100644 index dacf524344..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/myvote/MyVoteListService.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * This file is part of bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.governance.myvote; - -import bisq.core.dao.governance.blindvote.BlindVote; -import bisq.core.dao.governance.blindvote.MyBlindVoteListService; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.Ballot; -import bisq.core.dao.state.model.governance.BallotList; - -import bisq.common.app.DevEnv; -import bisq.common.crypto.Encryption; -import bisq.common.persistence.PersistenceManager; -import bisq.common.proto.persistable.PersistedDataHost; -import bisq.common.util.Tuple2; - -import javax.inject.Inject; - -import javax.crypto.SecretKey; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - -/** - * Creates and stores myVote items. Persist in MyVoteList. - */ -@Slf4j -public class MyVoteListService implements PersistedDataHost { - private final DaoStateService daoStateService; - private final PersistenceManager persistenceManager; - private final MyVoteList myVoteList = new MyVoteList(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public MyVoteListService(DaoStateService daoStateService, - PersistenceManager persistenceManager) { - this.daoStateService = daoStateService; - this.persistenceManager = persistenceManager; - - this.persistenceManager.initialize(myVoteList, PersistenceManager.Source.PRIVATE); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PersistedDataHost - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void readPersisted(Runnable completeHandler) { - if (DevEnv.isDaoActivated()) { - persistenceManager.readPersisted(persisted -> { - myVoteList.setAll(persisted.getList()); - completeHandler.run(); - }, - completeHandler); - } else { - completeHandler.run(); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void createAndAddMyVote(BallotList sortedBallotListForCycle, SecretKey secretKey, BlindVote blindVote) { - final byte[] secretKeyBytes = Encryption.getSecretKeyBytes(secretKey); - MyVote myVote = new MyVote(daoStateService.getChainHeight(), sortedBallotListForCycle, secretKeyBytes, blindVote); - log.info("Add new MyVote to myVotesList list.\nMyVote=" + myVote); - myVoteList.add(myVote); - requestPersistence(); - } - - public void applyRevealTxId(MyVote myVote, String voteRevealTxId) { - myVote.setRevealTxId(voteRevealTxId); - log.debug("Applied revealTxId to myVote.\nmyVote={}\nvoteRevealTxId={}", myVote, voteRevealTxId); - requestPersistence(); - } - - public Tuple2 getMeritAndStakeForProposal(String proposalTxId, - MyBlindVoteListService myBlindVoteListService) { - long merit = 0; - long stake = 0; - List list = new ArrayList<>(myVoteList.getList()); - list.sort(Comparator.comparing(MyVote::getDate)); - for (MyVote myVote : list) { - for (Ballot ballot : myVote.getBallotList().getList()) { - if (ballot.getTxId().equals(proposalTxId)) { - merit = myVote.getMerit(myBlindVoteListService, daoStateService); - stake = myVote.getBlindVote().getStake(); - break; - } - } - } - return new Tuple2<>(merit, stake); - } - - public MyVoteList getMyVoteList() { - return myVoteList; - } - - public List getMyVoteListForCycle() { - return myVoteList.getList().stream() - .filter(e -> daoStateService.getCurrentCycle() != null) - .filter(e -> daoStateService.getCurrentCycle().isInCycle(e.getHeight())) - .collect(Collectors.toList()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void requestPersistence() { - persistenceManager.requestPersistence(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/param/Param.java b/core/src/main/java/bisq/core/dao/governance/param/Param.java deleted file mode 100644 index b20e7784b7..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/param/Param.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.param; - -import bisq.core.locale.Res; - -import bisq.common.config.Config; -import bisq.common.proto.ProtoUtil; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * All parameters in the Bisq DAO which can be changed by voting. - * We will add more on demand. - * We need to support updates with new types in future. We use in the persisted data only the enum name, thus the names - * must not change once the dao has started. Default values must not be changed as well. - * For parameters which are used by Bisq users (trade fee,...) we have more strict requirements for backward compatibility. - * Parameters which are only used in proposals and voting are less strict limited as we can require that those users are - * using the latest software version. - * The UNDEFINED entry is used as fallback for error cases and will get ignored. - * - * Name of the params must not change as that is used for serialisation in Protobuffer. The data fields are not part of - * the PB serialisation so changes for those would not change the hash for the dao state hash chain. - * Though changing the values might break consensus as the validations might return a different result (e.g. a param - * change proposal was accepted with older min/max values but then after a change it is not valid anymore and - * might break the consequences of that change. So in fact we MUST not change anything here, only way is to add new - * entries and don't use the deprecated enum in future releases anymore. - */ -@Slf4j -public enum Param { - UNDEFINED("N/A", ParamType.UNDEFINED), - - // Fee in BTC for a 1 BTC trade. 0.001 is 0.1%. @5000 USD/BTC price 0.1% fee is 5 USD. - DEFAULT_MAKER_FEE_BTC("0.001", ParamType.BTC, 5, 5), - DEFAULT_TAKER_FEE_BTC("0.003", ParamType.BTC, 5, 5), // 0.2% of trade amount - MIN_MAKER_FEE_BTC("0.00005", ParamType.BTC, 5, 5), // 0.005% of trade amount - MIN_TAKER_FEE_BTC("0.00005", ParamType.BTC, 5, 5), - - // Fee in BSQ satoshis for a 1 BTC trade. 100 satoshis = 1 BSQ - // If 1 BTS is 1 USD the fee @5000 USD/BTC is 0.5 USD which is 10% of the BTC fee of 5 USD. - // Might need adjustment if BSQ/BTC rate changes. - DEFAULT_MAKER_FEE_BSQ("0.50", ParamType.BSQ, 5, 5), // ~ 0.01% of trade amount - DEFAULT_TAKER_FEE_BSQ("1.5", ParamType.BSQ, 5, 5), - // Min fee is the smallest fee allowed for a trade. If the default fee would be less than min fee the - // min fee is used instead. - // 0.03 BSQ (3 satoshis) for a 1 BTC trade. 0.05 USD if 1 BSQ = 1 USD, 10 % of the BTC fee - MIN_MAKER_FEE_BSQ("0.03", ParamType.BSQ, 5, 5), // 0.0003%. - MIN_TAKER_FEE_BSQ("0.03", ParamType.BSQ, 5, 5), - - // Fees proposal/voting. Atm we don't use diff. fees for diff. proposal types - // See: https://github.com/bisq-network/proposals/issues/46 - PROPOSAL_FEE("2", ParamType.BSQ, 5, 5), // 2 BSQ - BLIND_VOTE_FEE("2", ParamType.BSQ, 5, 5), // 2 BSQ - - // As BSQ based validation values can change over time if BSQ value rise we need to support that in the Params as well - COMPENSATION_REQUEST_MIN_AMOUNT("10", ParamType.BSQ, 4, 2), - COMPENSATION_REQUEST_MAX_AMOUNT("100000", ParamType.BSQ, 4, 2), - REIMBURSEMENT_MIN_AMOUNT("10", ParamType.BSQ, 4, 2), - REIMBURSEMENT_MAX_AMOUNT("10000", ParamType.BSQ, 4, 2), - - // Quorum required for voting result to be valid. - // Quorum is the min. amount of total BSQ (earned+stake) which was used for voting on a request. - // E.g. If only 2000 BSQ was used on a vote but 10 000 is required the result is invalid even if the voters voted - // 100% for acceptance. This should prevent that changes can be done with low stakeholder participation. - QUORUM_COMP_REQUEST("20000", ParamType.BSQ, 2, 2), - QUORUM_REIMBURSEMENT("20000", ParamType.BSQ, 2, 2), - QUORUM_CHANGE_PARAM("100000", ParamType.BSQ, 2, 2), - QUORUM_ROLE("50000", ParamType.BSQ, 2, 2), - QUORUM_CONFISCATION("200000", ParamType.BSQ, 2, 2), - QUORUM_GENERIC("5000", ParamType.BSQ, 2, 2), - QUORUM_REMOVE_ASSET("10000", ParamType.BSQ, 2, 2), - - // Threshold for voting in % with precision of 2 (e.g. 5000 -> 50.00%) - // This is the required amount of weighted vote result needed for acceptance of the result. - // E.g. If the result ends up in 65% weighted vote for acceptance and threshold was 50% it is accepted. - // The result must be larger than the threshold. A 50% vote result for a threshold with 50% is not sufficient, - // it requires min. 50.01%. - // The maxDecrease value is only relevant if the decreased value will not result in a value below 50.01%. - THRESHOLD_COMP_REQUEST("50.01", ParamType.PERCENT, 1.2, 1.2), - THRESHOLD_REIMBURSEMENT("50.01", ParamType.PERCENT, 1.2, 1.2), - THRESHOLD_CHANGE_PARAM("75.01", ParamType.PERCENT, 1.2, 1.2), // That might change the THRESHOLD_CHANGE_PARAM and QUORUM_CHANGE_PARAM as well. So we have to be careful here! - THRESHOLD_ROLE("50.01", ParamType.PERCENT, 1.2, 1.2), - THRESHOLD_CONFISCATION("85.01", ParamType.PERCENT, 1.2, 1.2), // Confiscation is considered an exceptional case and need very high consensus among the stakeholders. - THRESHOLD_GENERIC("50.01", ParamType.PERCENT, 1.2, 1.2), - THRESHOLD_REMOVE_ASSET("50.01", ParamType.PERCENT, 1.2, 1.2), - - // BTC address as recipient for BTC trade fee once the arbitration system is replaced as well as destination for - // the time locked payout tx in case the traders do not cooperate. Will be likely a donation address (Bisq, Tor,...) - // but can be also a burner address if we prefer to burn the BTC - @SuppressWarnings("SpellCheckingInspection") - RECIPIENT_BTC_ADDRESS(Config.baseCurrencyNetwork().isMainnet() ? "1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7" : // mainnet - Config.baseCurrencyNetwork().isDaoBetaNet() ? "1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7" : // daoBetaNet - Config.baseCurrencyNetwork().isTestnet() ? "2N4mVTpUZAnhm9phnxB7VrHB4aBhnWrcUrV" : // testnet - "2MzBNTJDjjXgViKBGnatDU3yWkJ8pJkEg9w", // regtest or DAO testnet (regtest) - ParamType.ADDRESS), - - // Fee for activating an asset or re-listing after deactivation due lack of trade activity. Fee per day of trial period without activity checks. - ASSET_LISTING_FEE_PER_DAY("1", ParamType.BSQ, 10, 10), - // Min required trade volume to not get de-listed. Check starts after trial period and use trial period afterwards to look back for trade activity. - ASSET_MIN_VOLUME("0.01", ParamType.BTC, 10, 10), - - LOCK_TIME_TRADE_PAYOUT("4320", ParamType.BLOCK), // 30 days, can be disabled by setting to 0 - ARBITRATOR_FEE("0", ParamType.PERCENT), // % of trade. For new trade protocol. Arbitration will become optional and we can apply a fee to it. Initially we start with no fee. - MAX_TRADE_LIMIT("2", ParamType.BTC, 2, 2), // max trade limit for lowest risk payment method. Others will get derived from that. - - // The base factor to multiply the bonded role amount. E.g. If Twitter admin has 20 as amount and BONDED_ROLE_FACTOR is 1000 we get 20000 BSQ as required bond. - // Using BSQ as type is not really the best option but we don't want to introduce a new ParamType just for that one Param. - // As the end rules is in fact BSQ it is not completely incorrect as well. - BONDED_ROLE_FACTOR("1000", ParamType.BSQ, 2, 2), - ISSUANCE_LIMIT("200000", ParamType.BSQ, 2, 2), // Max. issuance+reimbursement per cycle. - - // The last block in the proposal and vote phases are not shown to the user as he cannot make a tx there as it would be - // confirmed in the next block which would be the following break phase. To hide that complexity we show only the - // blocks where the user can be active. To have still round numbers for the durations we add 1 block to those - // phases and subtract 1 block from the following breaks. - // So in the UI the user will see 3600 blocks and the last - // block of the technical 3601 blocks is displayed as part of the break1 phase. - // For testnet we want to have a short cycle of about a week (1012 blocks) - // For regtest we use very short periods - PHASE_UNDEFINED("0", ParamType.BLOCK), - PHASE_PROPOSAL(Config.baseCurrencyNetwork().isMainnet() ? - "3601" : // mainnet; 24 days - Config.baseCurrencyNetwork().isStagenet() ? - "4" : // regtest - Config.baseCurrencyNetwork().isDaoBetaNet() ? - "144" : // daoBetaNet; 1 day - Config.baseCurrencyNetwork().isDaoRegTest() ? - "134" : // dao regtest; 0.93 days - "380", // testnet or dao testnet (server side regtest); 2.6 days - ParamType.BLOCK, 2, 2), - PHASE_BREAK1(Config.baseCurrencyNetwork().isMainnet() ? - "149" : // mainnet; 1 day - Config.baseCurrencyNetwork().isStagenet() ? - "1" : // regtest - Config.baseCurrencyNetwork().isDaoBetaNet() ? - "10" : // daoBetaNet - Config.baseCurrencyNetwork().isDaoRegTest() ? - "10" : // dao regtest - "10", // testnet or dao testnet (server side regtest) - ParamType.BLOCK, 2, 2), - PHASE_BLIND_VOTE(Config.baseCurrencyNetwork().isMainnet() ? - "451" : // mainnet; 3 days - Config.baseCurrencyNetwork().isStagenet() ? - "2" : // regtest - Config.baseCurrencyNetwork().isDaoBetaNet() ? - "144" : // daoBetaNet; 1 day - Config.baseCurrencyNetwork().isDaoRegTest() ? - "134" : // dao regtest; 0.93 days - "300", // testnet or dao testnet (server side regtest); 2 days - ParamType.BLOCK, 2, 2), - PHASE_BREAK2(Config.baseCurrencyNetwork().isMainnet() ? - "9" : // mainnet - Config.baseCurrencyNetwork().isStagenet() ? - "1" : // regtest - Config.baseCurrencyNetwork().isDaoBetaNet() ? - "10" : // daoBetaNet - Config.baseCurrencyNetwork().isDaoRegTest() ? - "10" : // dao regtest - "10", // testnet or dao testnet (server side regtest) - ParamType.BLOCK, 2, 2), - PHASE_VOTE_REVEAL(Config.baseCurrencyNetwork().isMainnet() ? - "451" : // mainnet; 3 days - Config.baseCurrencyNetwork().isStagenet() ? - "2" : // regtest - Config.baseCurrencyNetwork().isDaoBetaNet() ? - "144" : // daoBetaNet; 1 day - Config.baseCurrencyNetwork().isDaoRegTest() ? - "132" : // dao regtest; 0.93 days - "300", // testnet or dao testnet (server side regtest); 2 days - ParamType.BLOCK, 2, 2), - PHASE_BREAK3(Config.baseCurrencyNetwork().isMainnet() ? - "9" : // mainnet - Config.baseCurrencyNetwork().isStagenet() ? - "1" : // regtest - Config.baseCurrencyNetwork().isDaoBetaNet() ? - "10" : // daoBetaNet - Config.baseCurrencyNetwork().isDaoRegTest() ? - "10" : // dao regtest - "10", // testnet or dao testnet (server side regtest) - ParamType.BLOCK, 2, 2), - PHASE_RESULT(Config.baseCurrencyNetwork().isMainnet() ? - "10" : // mainnet - Config.baseCurrencyNetwork().isStagenet() ? - "2" : // regtest - Config.baseCurrencyNetwork().isDaoBetaNet() ? - "10" : // daoBetaNet - Config.baseCurrencyNetwork().isDaoRegTest() ? - "2" : // dao regtest - "2", // testnet or dao testnet (server side regtest) - ParamType.BLOCK, 2, 2); - - @Getter - private final String defaultValue; - @Getter - private final ParamType paramType; - // If 0 we ignore check for max decrease - @Getter - private final double maxDecrease; - // If 0 we ignore check for max increase - @Getter - private final double maxIncrease; - - Param(String defaultValue, ParamType paramType) { - this(defaultValue, paramType, 0, 0); - } - - /** - * @param defaultValue Value at the start of the DAO - * @param paramType Type of parameter - * @param maxDecrease Decrease of param value limited to current value / maxDecrease. If 0 we don't apply the check and any change is possible - * @param maxIncrease Increase of param value limited to current value * maxIncrease. If 0 we don't apply the check and any change is possible - */ - Param(String defaultValue, ParamType paramType, double maxDecrease, double maxIncrease) { - this.defaultValue = defaultValue; - this.paramType = paramType; - this.maxDecrease = maxDecrease; - this.maxIncrease = maxIncrease; - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public static Param fromProto(protobuf.ChangeParamProposal proposalProto) { - Param param; - try { - param = ProtoUtil.enumFromProto(Param.class, proposalProto.getParam()); - checkNotNull(param, "param must not be null"); - } catch (Throwable t) { - log.error("fromProto: " + t.toString()); - param = Param.UNDEFINED; - } - return param; - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public String getDisplayString() { - return name().startsWith("PHASE_") ? - Res.get("dao.phase." + name()) : - Res.get("dao.param." + name()); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/param/ParamType.java b/core/src/main/java/bisq/core/dao/governance/param/ParamType.java deleted file mode 100644 index 5e89b7a427..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/param/ParamType.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.param; - -public enum ParamType { - UNDEFINED, - BSQ, - BTC, - PERCENT, - BLOCK, - ADDRESS -} diff --git a/core/src/main/java/bisq/core/dao/governance/period/CycleService.java b/core/src/main/java/bisq/core/dao/governance/period/CycleService.java deleted file mode 100644 index acc4cfa031..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/period/CycleService.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.period; - -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.GenesisTxInfo; -import bisq.core.dao.state.model.governance.Cycle; -import bisq.core.dao.state.model.governance.DaoPhase; - -import com.google.inject.Inject; - -import com.google.common.collect.ImmutableList; - -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class CycleService implements DaoStateListener, DaoSetupService { - private final DaoStateService daoStateService; - private final int genesisBlockHeight; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public CycleService(DaoStateService daoStateService, - GenesisTxInfo genesisTxInfo) { - this.daoStateService = daoStateService; - this.genesisBlockHeight = genesisTxInfo.getGenesisBlockHeight(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - daoStateService.addDaoStateListener(this); - } - - @Override - public void start() { - addFirstCycle(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onNewBlockHeight(int blockHeight) { - maybeCreateNewCycle(blockHeight, daoStateService.getCycles()).ifPresent(daoStateService::addCycle); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void addFirstCycle() { - daoStateService.addCycle(getFirstCycle()); - } - - public int getCycleIndex(Cycle cycle) { - Optional previousCycle = getCycle(cycle.getHeightOfFirstBlock() - 1, daoStateService.getCycles()); - return previousCycle.map(cycle1 -> getCycleIndex(cycle1) + 1).orElse(0); - } - - public boolean isTxInCycle(Cycle cycle, String txId) { - return daoStateService.getTx(txId).filter(tx -> isBlockHeightInCycle(tx.getBlockHeight(), cycle)).isPresent(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private boolean isBlockHeightInCycle(int blockHeight, Cycle cycle) { - return blockHeight >= cycle.getHeightOfFirstBlock() && - blockHeight <= cycle.getHeightOfLastBlock(); - } - - private Optional maybeCreateNewCycle(int blockHeight, LinkedList cycles) { - // We want to set the correct phase and cycle before we start parsing a new block. - // For Genesis block we did it already in the start method. - // We copy over the phases from the current block as we get the phase only set in - // applyParamToPhasesInCycle if there was a changeEvent. - // The isFirstBlockInCycle methods returns from the previous cycle the first block as we have not - // applied the new cycle yet. But the first block of the old cycle will always be the same as the - // first block of the new cycle. - Cycle cycle = null; - if (blockHeight > genesisBlockHeight && !cycles.isEmpty() && isFirstBlockAfterPreviousCycle(blockHeight, cycles)) { - // We have the not update daoStateService.getCurrentCycle() so we grab here the previousCycle - Cycle previousCycle = cycles.getLast(); - // We create the new cycle as clone of the previous cycle and only if there have been change events we use - // the new values from the change event. - cycle = createNewCycle(blockHeight, previousCycle); - } - return Optional.ofNullable(cycle); - } - - - private Cycle getFirstCycle() { - // We want to have the initial data set up before the genesis tx gets parsed so we do it here in the constructor - // as onAllServicesInitialized might get called after the parser has started. - // We add the default values from the Param enum to our StateChangeEvent list. - List daoPhasesWithDefaultDuration = Arrays.stream(DaoPhase.Phase.values()) - .map(this::getPhaseWithDefaultDuration) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toList()); - return new Cycle(genesisBlockHeight, ImmutableList.copyOf(daoPhasesWithDefaultDuration)); - } - - private Cycle createNewCycle(int blockHeight, Cycle previousCycle) { - List daoPhaseList = previousCycle.getDaoPhaseList().stream() - .map(daoPhase -> { - DaoPhase.Phase phase = daoPhase.getPhase(); - try { - Param param = Param.valueOf("PHASE_" + phase.name()); - int value = daoStateService.getParamValueAsBlock(param, blockHeight); - return new DaoPhase(phase, value); - } catch (Throwable ignore) { - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - - return new Cycle(blockHeight, ImmutableList.copyOf(daoPhaseList)); - } - - private boolean isFirstBlockAfterPreviousCycle(int height, LinkedList cycles) { - int previousBlockHeight = height - 1; - Optional previousCycle = getCycle(previousBlockHeight, cycles); - return previousCycle - .filter(cycle -> cycle.getHeightOfLastBlock() + 1 == height) - .isPresent(); - } - - private Optional getPhaseWithDefaultDuration(DaoPhase.Phase phase) { - return Arrays.stream(Param.values()) - .filter(param -> isParamMatchingPhase(param, phase)) - .map(param -> new DaoPhase(phase, Integer.parseInt(param.getDefaultValue()))) - .findAny(); // We will always have a default value defined - } - - private boolean isParamMatchingPhase(Param param, DaoPhase.Phase phase) { - return param.name().contains("PHASE_") && param.name().replace("PHASE_", "").equals(phase.name()); - } - - private Optional getCycle(int height, LinkedList cycles) { - return cycles.stream() - .filter(cycle -> cycle.getHeightOfFirstBlock() <= height) - .filter(cycle -> cycle.getHeightOfLastBlock() >= height) - .findAny(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/period/PeriodService.java b/core/src/main/java/bisq/core/dao/governance/period/PeriodService.java deleted file mode 100644 index dfe18cb07a..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/period/PeriodService.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.period; - -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.governance.Cycle; -import bisq.core.dao.state.model.governance.DaoPhase; - -import com.google.inject.Inject; - -import java.util.List; -import java.util.Optional; - -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -@Slf4j -public class PeriodService { - private final DaoStateService daoStateService; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public PeriodService(DaoStateService daoStateService) { - this.daoStateService = daoStateService; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public List getCycles() { - return daoStateService.getCycles(); - } - - @Nullable - public Cycle getCurrentCycle() { - return daoStateService.getCurrentCycle(); - } - - public int getChainHeight() { - return daoStateService.getChainHeight(); - } - - private Optional getOptionalTx(String txId) { - return daoStateService.getTx(txId); - } - - public DaoPhase.Phase getCurrentPhase() { - return getCurrentCycle() != null ? - getCurrentCycle().getPhaseForHeight(this.getChainHeight()).orElse(DaoPhase.Phase.UNDEFINED) : - DaoPhase.Phase.UNDEFINED; - } - - public boolean isFirstBlockInCycle(int height) { - return getCycle(height) - .filter(cycle -> cycle.getHeightOfFirstBlock() == height) - .isPresent(); - } - - public boolean isLastBlockInCycle(int height) { - return getCycle(height) - .filter(cycle -> cycle.getHeightOfLastBlock() == height) - .isPresent(); - } - - public Optional getCycle(int height) { - return daoStateService.getCycle(height); - } - - public boolean isInPhase(int height, DaoPhase.Phase phase) { - return getCycle(height) - .filter(cycle -> cycle.isInPhase(height, phase)) - .isPresent(); - } - - public boolean isTxInPhase(String txId, DaoPhase.Phase phase) { - return getOptionalTx(txId) - .filter(tx -> isInPhase(tx.getBlockHeight(), phase)) - .isPresent(); - } - - public boolean isTxInPhaseAndCycle(String txId, DaoPhase.Phase phase, int currentChainHeadHeight) { - return isTxInPhase(txId, phase) && isTxInCorrectCycle(txId, currentChainHeadHeight); - } - - public DaoPhase.Phase getPhaseForHeight(int height) { - return getCycle(height) - .flatMap(cycle -> cycle.getPhaseForHeight(height)) - .orElse(DaoPhase.Phase.UNDEFINED); - } - - public boolean isTxInCorrectCycle(int txHeight, int currentChainHeadHeight) { - return getCycle(txHeight) - .filter(cycle -> currentChainHeadHeight >= cycle.getHeightOfFirstBlock()) - .filter(cycle -> currentChainHeadHeight <= cycle.getHeightOfLastBlock()) - .isPresent(); - } - - public boolean isTxInCorrectCycle(String txId, int currentChainHeadHeight) { - return getOptionalTx(txId) - .filter(tx -> isTxInCorrectCycle(tx.getBlockHeight(), currentChainHeadHeight)) - .isPresent(); - } - - private boolean isTxInPastCycle(int txHeight, int currentChainHeadHeight) { - return getCycle(txHeight) - .filter(cycle -> currentChainHeadHeight > cycle.getHeightOfLastBlock()) - .isPresent(); - } - - public int getDurationForPhase(DaoPhase.Phase phase, int height) { - return getCycle(height) - .map(cycle -> cycle.getDurationOfPhase(phase)) - .orElse(0); - } - - public boolean isTxInPastCycle(String txId, int chainHeight) { - return getOptionalTx(txId) - .filter(tx -> isTxInPastCycle(tx.getBlockHeight(), chainHeight)) - .isPresent(); - } - - public int getFirstBlockOfPhase(int height, DaoPhase.Phase phase) { - return getCycle(height) - .map(cycle -> cycle.getFirstBlockOfPhase(phase)) - .orElse(0); - } - - public boolean isFirstBlockInCycle() { - final int chainHeight = getChainHeight(); - return getFirstBlockOfPhase(chainHeight, DaoPhase.Phase.PROPOSAL) == chainHeight; - } - - public int getLastBlockOfPhase(int height, DaoPhase.Phase phase) { - return getCycle(height) - .map(cycle -> cycle.getLastBlockOfPhase(phase)) - .orElse(0); - } - - public boolean isInPhaseButNotLastBlock(DaoPhase.Phase phase) { - final int chainHeight = getChainHeight(); - return isInPhase(chainHeight, phase) && - chainHeight != getLastBlockOfPhase(chainHeight, phase); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proofofburn/MyProofOfBurn.java b/core/src/main/java/bisq/core/dao/governance/proofofburn/MyProofOfBurn.java deleted file mode 100644 index 7902741a9c..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proofofburn/MyProofOfBurn.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proofofburn; - -import bisq.common.crypto.Hash; -import bisq.common.proto.network.NetworkPayload; -import bisq.common.proto.persistable.PersistablePayload; -import bisq.common.util.Utilities; - -import com.google.common.base.Charsets; - -import java.util.Objects; - -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -/** - * MyProofOfBurn is persisted locally and holds the preImage and txId. - */ -@Immutable -@Value -@Slf4j -public final class MyProofOfBurn implements PersistablePayload, NetworkPayload { - private final String txId; - private final String preImage; - private final transient byte[] hash; // Not persisted as it is derived from preImage. Stored for caching purpose only. - - public MyProofOfBurn(String txId, String preImage) { - this.txId = txId; - this.preImage = preImage; - this.hash = Hash.getSha256Ripemd160hash(preImage.getBytes(Charsets.UTF_8)); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.MyProofOfBurn toProtoMessage() { - return protobuf.MyProofOfBurn.newBuilder() - .setTxId(txId) - .setPreImage(preImage) - .build(); - } - - public static MyProofOfBurn fromProto(protobuf.MyProofOfBurn proto) { - return new MyProofOfBurn(proto.getTxId(), proto.getPreImage()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof MyProofOfBurn)) return false; - if (!super.equals(o)) return false; - MyProofOfBurn that = (MyProofOfBurn) o; - return Objects.equals(txId, that.txId) && - Objects.equals(preImage, that.preImage); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), txId, preImage); - } - - @Override - public String toString() { - return "MyProofOfBurn{" + - "\n txId='" + txId + '\'' + - ",\n preImage=" + preImage + - ",\n hash=" + Utilities.bytesAsHexString(hash) + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proofofburn/MyProofOfBurnList.java b/core/src/main/java/bisq/core/dao/governance/proofofburn/MyProofOfBurnList.java deleted file mode 100644 index a1f55c2499..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proofofburn/MyProofOfBurnList.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proofofburn; - -import bisq.common.proto.persistable.PersistableList; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.EqualsAndHashCode; - -/** - * PersistableEnvelope wrapper for list of MyProofOfBurn objects. - */ -@EqualsAndHashCode(callSuper = true) -public class MyProofOfBurnList extends PersistableList { - - private MyProofOfBurnList(List list) { - super(list); - } - - MyProofOfBurnList() { - super(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.PersistableEnvelope toProtoMessage() { - return protobuf.PersistableEnvelope.newBuilder().setMyProofOfBurnList(getBuilder()).build(); - } - - private protobuf.MyProofOfBurnList.Builder getBuilder() { - return protobuf.MyProofOfBurnList.newBuilder() - .addAllMyProofOfBurn(getList().stream() - .map(MyProofOfBurn::toProtoMessage) - .collect(Collectors.toList())); - } - - public static MyProofOfBurnList fromProto(protobuf.MyProofOfBurnList proto) { - return new MyProofOfBurnList(new ArrayList<>(proto.getMyProofOfBurnList().stream() - .map(MyProofOfBurn::fromProto) - .collect(Collectors.toList()))); - } - - @Override - public String toString() { - return "List of txIds in MyProofOfBurnList: " + getList().stream() - .map(MyProofOfBurn::getTxId) - .collect(Collectors.toList()); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proofofburn/MyProofOfBurnListService.java b/core/src/main/java/bisq/core/dao/governance/proofofburn/MyProofOfBurnListService.java deleted file mode 100644 index d61c6a93be..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proofofburn/MyProofOfBurnListService.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proofofburn; - -import bisq.core.dao.DaoSetupService; - -import bisq.common.app.DevEnv; -import bisq.common.persistence.PersistenceManager; -import bisq.common.proto.persistable.PersistedDataHost; - -import javax.inject.Inject; - -import java.util.List; - -import lombok.extern.slf4j.Slf4j; - -/** - * Manages the persistence of MyProofOfBurn objects. - */ -@Slf4j -public class MyProofOfBurnListService implements PersistedDataHost, DaoSetupService { - - private final PersistenceManager persistenceManager; - private final MyProofOfBurnList myProofOfBurnList = new MyProofOfBurnList(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public MyProofOfBurnListService(PersistenceManager persistenceManager) { - this.persistenceManager = persistenceManager; - persistenceManager.initialize(myProofOfBurnList, PersistenceManager.Source.PRIVATE); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PersistedDataHost - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void readPersisted(Runnable completeHandler) { - if (DevEnv.isDaoActivated()) { - persistenceManager.readPersisted(persisted -> { - myProofOfBurnList.setAll(persisted.getList()); - completeHandler.run(); - }, - completeHandler); - } else { - completeHandler.run(); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - } - - @Override - public void start() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void addMyProofOfBurn(MyProofOfBurn myProofOfBurn) { - if (!myProofOfBurnList.contains(myProofOfBurn)) { - myProofOfBurnList.add(myProofOfBurn); - requestPersistence(); - } - } - - public List getMyProofOfBurnList() { - return myProofOfBurnList.getList(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void requestPersistence() { - persistenceManager.requestPersistence(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proofofburn/ProofOfBurnConsensus.java b/core/src/main/java/bisq/core/dao/governance/proofofburn/ProofOfBurnConsensus.java deleted file mode 100644 index 490a2cefc8..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proofofburn/ProofOfBurnConsensus.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proofofburn; - -import bisq.core.dao.state.model.blockchain.OpReturnType; - -import bisq.common.app.Version; -import bisq.common.crypto.Hash; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import java.util.Arrays; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class ProofOfBurnConsensus { - public static byte[] getHash(byte[] bytes) { - return Hash.getSha256Ripemd160hash(bytes); - } - - public static byte[] getOpReturnData(byte[] hash) { - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - outputStream.write(OpReturnType.PROOF_OF_BURN.getType()); - outputStream.write(Version.PROOF_OF_BURN); - outputStream.write(hash); - return outputStream.toByteArray(); - } catch (IOException e) { - // Not expected to happen ever - e.printStackTrace(); - log.error(e.toString()); - return new byte[0]; - } - } - - public static boolean hasOpReturnDataValidLength(byte[] opReturnData) { - return opReturnData.length == 22; - } - - public static byte[] getHashFromOpReturnData(byte[] opReturnData) { - return Arrays.copyOfRange(opReturnData, 2, 22); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proofofburn/ProofOfBurnService.java b/core/src/main/java/bisq/core/dao/governance/proofofburn/ProofOfBurnService.java deleted file mode 100644 index 47125ad155..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proofofburn/ProofOfBurnService.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proofofburn; - -import bisq.core.btc.exceptions.TransactionVerificationException; -import bisq.core.btc.exceptions.TxBroadcastException; -import bisq.core.btc.exceptions.WalletException; -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.TxBroadcaster; -import bisq.core.btc.wallet.WalletsManager; -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.governance.proposal.TxException; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.BaseTx; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxType; - -import bisq.common.handlers.ErrorMessageHandler; -import bisq.common.handlers.ResultHandler; -import bisq.common.util.Utilities; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.ECKey; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.Transaction; - -import javax.inject.Inject; - -import com.google.common.base.Charsets; - -import javafx.beans.property.IntegerProperty; -import javafx.beans.property.SimpleIntegerProperty; - -import java.security.SignatureException; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.bitcoinj.core.Utils.HEX; - -@Slf4j -public class ProofOfBurnService implements DaoSetupService, DaoStateListener { - private final BsqWalletService bsqWalletService; - private final BtcWalletService btcWalletService; - private final WalletsManager walletsManager; - private final MyProofOfBurnListService myProofOfBurnListService; - private final DaoStateService daoStateService; - - @Getter - private IntegerProperty updateFlag = new SimpleIntegerProperty(0); - @Getter - private final List proofOfBurnTxList = new ArrayList<>(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public ProofOfBurnService(BsqWalletService bsqWalletService, - BtcWalletService btcWalletService, - WalletsManager walletsManager, - MyProofOfBurnListService myProofOfBurnListService, - DaoStateService daoStateService) { - this.bsqWalletService = bsqWalletService; - this.btcWalletService = btcWalletService; - this.walletsManager = walletsManager; - this.myProofOfBurnListService = myProofOfBurnListService; - this.daoStateService = daoStateService; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - daoStateService.addDaoStateListener(this); - } - - @Override - public void start() { - } - - private void updateList() { - proofOfBurnTxList.clear(); - proofOfBurnTxList.addAll(getAllProofOfBurnTxs()); - - updateFlag.set(updateFlag.get() + 1); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - updateList(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public Transaction burn(String preImageAsString, long amount) throws InsufficientMoneyException, TxException { - try { - // We create a prepared Bsq Tx for the burn amount - Transaction preparedBurnFeeTx = bsqWalletService.getPreparedProofOfBurnTx(Coin.valueOf(amount)); - byte[] hash = getHashFromPreImage(preImageAsString); - byte[] opReturnData = ProofOfBurnConsensus.getOpReturnData(hash); - // We add the BTC inputs for the miner fee. - Transaction txWithBtcFee = btcWalletService.completePreparedBurnBsqTx(preparedBurnFeeTx, opReturnData); - // We sign the BSQ inputs of the final tx. - Transaction transaction = bsqWalletService.signTx(txWithBtcFee); - log.info("Proof of burn tx: " + transaction); - return transaction; - } catch (WalletException | TransactionVerificationException e) { - throw new TxException(e); - } - } - - public void publishTransaction(Transaction transaction, String preImageAsString, ResultHandler resultHandler, - ErrorMessageHandler errorMessageHandler) { - walletsManager.publishAndCommitBsqTx(transaction, TxType.PROOF_OF_BURN, new TxBroadcaster.Callback() { - @Override - public void onSuccess(Transaction transaction) { - log.info("Proof of burn tx has been published. TxId={}", transaction.getTxId().toString()); - resultHandler.handleResult(); - } - - @Override - public void onFailure(TxBroadcastException exception) { - errorMessageHandler.handleErrorMessage(exception.getMessage()); - } - }); - - MyProofOfBurn myProofOfBurn = new MyProofOfBurn(transaction.getTxId().toString(), preImageAsString); - myProofOfBurnListService.addMyProofOfBurn(myProofOfBurn); - } - - public byte[] getHashFromOpReturnData(Tx tx) { - return ProofOfBurnConsensus.getHashFromOpReturnData(tx.getLastTxOutput().getOpReturnData()); - } - - public String getHashAsString(String preImageAsString) { - return Utilities.bytesAsHexString(getHashFromPreImage(preImageAsString)); - } - - public Optional getTx(String txId) { - return daoStateService.getTx(txId); - } - - // Of connected output of first input. Used for signing and verification. - // Proofs ownership of the proof of burn tx. - public byte[] getPubKey(String txId) { - return daoStateService.getTx(txId) - .map(tx -> tx.getTxInputs().get(0)) - .map(e -> Utilities.decodeFromHex(e.getPubKey())) - .orElse(new byte[0]); - } - - public String getPubKeyAsHex(String proofOfBurnTxId) { - return Utilities.bytesAsHexString(getPubKey(proofOfBurnTxId)); - } - - public Optional sign(String proofOfBurnTxId, String message) { - byte[] pubKey = getPubKey(proofOfBurnTxId); - ECKey key = bsqWalletService.findKeyFromPubKey(pubKey); - if (key == null) - return Optional.empty(); - - try { - String signatureBase64 = bsqWalletService.isEncrypted() - ? key.signMessage(message, bsqWalletService.getAesKey()) - : key.signMessage(message); - return Optional.of(signatureBase64); - } catch (Throwable t) { - log.error(t.toString()); - t.printStackTrace(); - return Optional.empty(); - } - } - - public void verify(String message, String pubKey, String signatureBase64) throws SignatureException { - ECKey key = ECKey.fromPublicOnly(HEX.decode(pubKey)); - checkNotNull(key, "ECKey must not be null"); - key.verifyMessage(message, signatureBase64); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private List getAllProofOfBurnTxs() { - return daoStateService.getProofOfBurnOpReturnTxOutputs().stream() - .map(txOutput -> daoStateService.getTx(txOutput.getTxId()).orElse(null)) - .filter(Objects::nonNull) - .sorted(Comparator.comparing(BaseTx::getTime).reversed()) - .collect(Collectors.toList()); - } - - private byte[] getHashFromPreImage(String preImageAsString) { - byte[] preImage = preImageAsString.getBytes(Charsets.UTF_8); - return ProofOfBurnConsensus.getHash(preImage); - } - - public long getAmount(Tx tx) { - return tx.getBurntFee(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/BaseProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/BaseProposalFactory.java deleted file mode 100644 index 53c2967feb..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/BaseProposalFactory.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal; - -import bisq.core.btc.exceptions.TransactionVerificationException; -import bisq.core.btc.exceptions.WalletException; -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.OpReturnType; -import bisq.core.dao.state.model.governance.Proposal; - -import bisq.common.app.Version; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.Transaction; - -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -/** - * Base class for proposalFactory classes. Provides creation of a transaction. Proposal creation is delegated to - * concrete classes. - */ -@Slf4j -public abstract class BaseProposalFactory { - protected final BsqWalletService bsqWalletService; - protected final BtcWalletService btcWalletService; - private final DaoStateService daoStateService; - private final ProposalValidator proposalValidator; - @Nullable - protected String name; - @Nullable - protected String link; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - protected BaseProposalFactory(BsqWalletService bsqWalletService, - BtcWalletService btcWalletService, - DaoStateService daoStateService, - ProposalValidator proposalValidator) { - this.bsqWalletService = bsqWalletService; - this.btcWalletService = btcWalletService; - this.daoStateService = daoStateService; - this.proposalValidator = proposalValidator; - } - - protected ProposalWithTransaction createProposalWithTransaction(String name, - String link) - throws ProposalValidationException, InsufficientMoneyException, TxException { - this.name = name; - this.link = link; - // As we don't know the txId yet we create a temp proposal with txId set to an empty string. - R proposal = createProposalWithoutTxId(); - proposalValidator.validateDataFields(proposal); - Transaction transaction = createTransaction(proposal); - Proposal proposalWithTxId = proposal.cloneProposalAndAddTxId(transaction.getTxId().toString()); - return new ProposalWithTransaction(proposalWithTxId, transaction); - } - - protected abstract R createProposalWithoutTxId(); - - // We have txId set to null in proposal as we cannot know it before the tx is created. - // Once the tx is known we will create a new object including the txId. - // The hashOfPayload used in the opReturnData is created with the txId set to null. - private Transaction createTransaction(R proposal) throws InsufficientMoneyException, TxException { - try { - Coin fee = ProposalConsensus.getFee(daoStateService, daoStateService.getChainHeight()); - // We create a prepared Bsq Tx for the proposal fee. - Transaction preparedBurnFeeTx = proposal instanceof IssuanceProposal ? - bsqWalletService.getPreparedIssuanceTx(fee) : - bsqWalletService.getPreparedProposalTx(fee); - - // payload does not have txId at that moment - byte[] hashOfPayload = ProposalConsensus.getHashOfPayload(proposal); - byte[] opReturnData = getOpReturnData(hashOfPayload); - - // We add the BTC inputs for the miner fee. - Transaction txWithBtcFee = completeTx(preparedBurnFeeTx, opReturnData, proposal); - - // We sign the BSQ inputs of the final tx. - Transaction transaction = bsqWalletService.signTx(txWithBtcFee); - log.info("Proposal tx: " + transaction); - return transaction; - } catch (WalletException | TransactionVerificationException e) { - throw new TxException(e); - } - } - - protected byte[] getOpReturnData(byte[] hashOfPayload) { - return ProposalConsensus.getOpReturnData(hashOfPayload, OpReturnType.PROPOSAL.getType(), Version.PROPOSAL); - } - - protected Transaction completeTx(Transaction preparedBurnFeeTx, byte[] opReturnData, Proposal proposal) - throws WalletException, InsufficientMoneyException, TransactionVerificationException { - return btcWalletService.completePreparedBurnBsqTx(preparedBurnFeeTx, opReturnData); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/IssuanceProposal.java b/core/src/main/java/bisq/core/dao/governance/proposal/IssuanceProposal.java deleted file mode 100644 index a466b99965..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/IssuanceProposal.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal; - -import org.bitcoinj.core.Coin; - -/** - * Marker interface for proposals which can lead to new BSQ issuance - */ -public interface IssuanceProposal { - Coin getRequestedBsq(); - - String getBsqAddress(); - - String getTxId(); -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalList.java b/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalList.java deleted file mode 100644 index 7ec120d719..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalList.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.state.model.governance.Proposal; - -import bisq.common.proto.persistable.PersistableList; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.EqualsAndHashCode; - -/** - * PersistableEnvelope wrapper for list of proposals. Used in vote consensus, so changes can break consensus! - */ -@EqualsAndHashCode(callSuper = true) -public class MyProposalList extends PersistableList implements ConsensusCritical { - - public MyProposalList(List list) { - super(list); - } - - MyProposalList() { - super(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.PersistableEnvelope toProtoMessage() { - return protobuf.PersistableEnvelope.newBuilder().setMyProposalList(getBuilder()).build(); - } - - private protobuf.MyProposalList.Builder getBuilder() { - return protobuf.MyProposalList.newBuilder() - .addAllProposal(getList().stream() - .map(Proposal::toProtoMessage) - .collect(Collectors.toList())); - } - - public static MyProposalList fromProto(protobuf.MyProposalList proto) { - return new MyProposalList(new ArrayList<>(proto.getProposalList().stream() - .map(Proposal::fromProto) - .collect(Collectors.toList()))); - } - - @Override - public String toString() { - return "List of TxId's in MyProposalList: " + getList().stream() - .map(Proposal::getTxId) - .collect(Collectors.toList()); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalListService.java b/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalListService.java deleted file mode 100644 index 8bb83c54a4..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalListService.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal; - -import bisq.core.btc.exceptions.TxBroadcastException; -import bisq.core.btc.wallet.TxBroadcaster; -import bisq.core.btc.wallet.WalletsManager; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.storage.temp.TempProposalPayload; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.dao.state.model.governance.Proposal; - -import bisq.network.p2p.P2PService; - -import bisq.common.UserThread; -import bisq.common.app.DevEnv; -import bisq.common.config.Config; -import bisq.common.crypto.PubKeyRing; -import bisq.common.handlers.ErrorMessageHandler; -import bisq.common.handlers.ResultHandler; -import bisq.common.persistence.PersistenceManager; -import bisq.common.proto.persistable.PersistedDataHost; - -import org.bitcoinj.core.Transaction; - -import com.google.inject.Inject; - -import javafx.beans.value.ChangeListener; - -import java.security.PublicKey; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -import lombok.extern.slf4j.Slf4j; - -/** - * Publishes proposal tx and proposalPayload to p2p network. - * Allow removal of proposal if in proposal phase. - * Maintains myProposalList for own proposals. - * Triggers republishing of my proposals at startup. - */ -@Slf4j -public class MyProposalListService implements PersistedDataHost, DaoStateListener { - public interface Listener { - void onListChanged(List list); - } - - private final P2PService p2PService; - private final DaoStateService daoStateService; - private final PeriodService periodService; - private final WalletsManager walletsManager; - private final PersistenceManager persistenceManager; - private final PublicKey signaturePubKey; - - private final MyProposalList myProposalList = new MyProposalList(); - private final ChangeListener numConnectedPeersListener; - private final List listeners = new CopyOnWriteArrayList<>(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public MyProposalListService(P2PService p2PService, - DaoStateService daoStateService, - PeriodService periodService, - WalletsManager walletsManager, - PersistenceManager persistenceManager, - PubKeyRing pubKeyRing) { - this.p2PService = p2PService; - this.daoStateService = daoStateService; - this.periodService = periodService; - this.walletsManager = walletsManager; - this.persistenceManager = persistenceManager; - - this.persistenceManager.initialize(myProposalList, PersistenceManager.Source.PRIVATE); - - signaturePubKey = pubKeyRing.getSignaturePubKey(); - - numConnectedPeersListener = (observable, oldValue, newValue) -> rePublishMyProposalsOnceWellConnected(); - daoStateService.addDaoStateListener(this); - p2PService.getNumConnectedPeers().addListener(numConnectedPeersListener); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PersistedDataHost - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void readPersisted(Runnable completeHandler) { - if (DevEnv.isDaoActivated()) { - persistenceManager.readPersisted(persisted -> { - myProposalList.setAll(persisted.getList()); - listeners.forEach(l -> l.onListChanged(getList())); - completeHandler.run(); - }, - completeHandler); - } else { - completeHandler.run(); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockChainComplete() { - rePublishMyProposalsOnceWellConnected(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - // Broadcast tx and publish proposal to P2P network - public void publishTxAndPayload(Proposal proposal, Transaction transaction, ResultHandler resultHandler, - ErrorMessageHandler errorMessageHandler) { - walletsManager.publishAndCommitBsqTx(transaction, proposal.getTxType(), new TxBroadcaster.Callback() { - @Override - public void onSuccess(Transaction transaction) { - log.info("Proposal tx has been published. TxId={}", transaction.getTxId().toString()); - resultHandler.handleResult(); - } - - @Override - public void onFailure(TxBroadcastException exception) { - errorMessageHandler.handleErrorMessage(exception.getMessage()); - } - }); - - // We prefer to not wait for the tx broadcast as if the tx broadcast would fail we still prefer to have our - // proposal stored and broadcasted to the p2p network. The tx might get re-broadcasted at a restart and - // in worst case if it does not succeed the proposal will be ignored anyway. - // Inconsistently propagated payloads in the p2p network could have potentially worse effects. - addToP2PNetworkAsProtectedData(proposal, errorMessageHandler); - - // Add to list - if (!getList().contains(proposal)) { - myProposalList.add(proposal); - listeners.forEach(l -> l.onListChanged(getList())); - requestPersistence(); - } - } - - public boolean remove(Proposal proposal) { - if (canRemoveProposal(proposal, daoStateService, periodService)) { - boolean success = p2PService.removeData(new TempProposalPayload(proposal, signaturePubKey)); - if (!success) - log.warn("Removal of proposal from p2p network failed. proposal={}", proposal); - - if (myProposalList.remove(proposal)) { - listeners.forEach(l -> l.onListChanged(getList())); - requestPersistence(); - } else { - log.warn("We called remove at a proposal which was not in our list"); - } - return success; - } else { - final String msg = "remove called with a proposal which is outside of the proposal phase."; - DevEnv.logErrorAndThrowIfDevMode(msg); - return false; - } - } - - public boolean isMine(Proposal proposal) { - return getList().contains(proposal); - } - - public List getList() { - return myProposalList.getList(); - } - - public void addListener(Listener listener) { - listeners.add(listener); - } - - public void removeListener(Listener listener) { - listeners.remove(listener); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void addToP2PNetworkAsProtectedData(Proposal proposal, ErrorMessageHandler errorMessageHandler) { - final boolean success = addToP2PNetworkAsProtectedData(proposal); - if (success) { - log.info("TempProposalPayload has been added to P2P network. ProposalTxId={}", proposal.getTxId()); - } else { - final String msg = "Adding of proposal to P2P network failed. proposal=" + proposal; - log.error(msg); - errorMessageHandler.handleErrorMessage(msg); - } - } - - private boolean addToP2PNetworkAsProtectedData(Proposal proposal) { - return p2PService.addProtectedStorageEntry(new TempProposalPayload(proposal, signaturePubKey)); - } - - private void rePublishMyProposalsOnceWellConnected() { - // We republish at each startup at any block during the cycle. We filter anyway for valid blind votes - // of that cycle so it is 1 blind vote getting rebroadcast at each startup to my neighbors. - int minPeers = Config.baseCurrencyNetwork().isMainnet() ? 4 : 1; - if ((p2PService.getNumConnectedPeers().get() >= minPeers && p2PService.isBootstrapped()) || - Config.baseCurrencyNetwork().isStagenet()) { - myProposalList.stream() - .filter(proposal -> periodService.isTxInPhaseAndCycle(proposal.getTxId(), - DaoPhase.Phase.PROPOSAL, - periodService.getChainHeight())) - .forEach(this::addToP2PNetworkAsProtectedData); - - // We delay removal of listener as we call that inside listener itself. - UserThread.execute(() -> p2PService.getNumConnectedPeers().removeListener(numConnectedPeersListener)); - } - } - - private void requestPersistence() { - persistenceManager.requestPersistence(); - } - - private boolean canRemoveProposal(Proposal proposal, DaoStateService daoStateService, PeriodService periodService) { - boolean inProposalPhase = periodService.isInPhase(daoStateService.getChainHeight(), DaoPhase.Phase.PROPOSAL); - return isMine(proposal) && inProposalPhase; - - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalConsensus.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalConsensus.java deleted file mode 100644 index 067086faf8..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalConsensus.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal; - -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.Proposal; - -import bisq.common.crypto.Hash; - -import org.bitcoinj.core.Coin; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import lombok.extern.slf4j.Slf4j; - -/** - * Encapsulates consensus critical aspects. - */ -@Slf4j -public class ProposalConsensus { - public static Coin getFee(DaoStateService daoStateService, int chainHeight) { - return daoStateService.getParamValueAsCoin(Param.PROPOSAL_FEE, chainHeight); - } - - public static byte[] getHashOfPayload(Proposal payload) { - final byte[] bytes = payload.toProtoMessage().toByteArray(); - return Hash.getSha256Ripemd160hash(bytes); - } - - public static byte[] getOpReturnData(byte[] hashOfPayload, byte opReturnType, byte version) { - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - outputStream.write(opReturnType); - outputStream.write(version); - outputStream.write(hashOfPayload); - return outputStream.toByteArray(); - } catch (IOException e) { - // Not expected to happen ever - e.printStackTrace(); - log.error(e.toString()); - return new byte[0]; - } - } - - public static boolean hasOpReturnDataValidLength(byte[] opReturnData) { - return opReturnData.length == 22; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalListPresentation.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalListPresentation.java deleted file mode 100644 index 6e8209c7c2..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalListPresentation.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal; - -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.governance.proposal.storage.appendonly.ProposalPayload; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.governance.Proposal; - -import bisq.common.UserThread; - -import org.bitcoinj.core.TransactionConfidence; - -import com.google.inject.Inject; - -import javafx.collections.FXCollections; -import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; -import javafx.collections.transformation.FilteredList; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Provides filtered observableLists of the Proposals from proposalService and myProposalListService. - * We want to show the own proposals in unconfirmed state (validation of phase and cycle cannot be done but as it is - * our own proposal that is not critical). Foreign proposals are only shown if confirmed and fully validated. - */ -@Slf4j -public class ProposalListPresentation implements DaoStateListener, MyProposalListService.Listener, DaoSetupService { - private final ProposalService proposalService; - private final DaoStateService daoStateService; - private final MyProposalListService myProposalListService; - private final BsqWalletService bsqWalletService; - private final ProposalValidatorProvider validatorProvider; - private final ObservableList allProposals = FXCollections.observableArrayList(); - @Getter - private final FilteredList activeOrMyUnconfirmedProposals = new FilteredList<>(allProposals); - private final ListChangeListener proposalListChangeListener; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public ProposalListPresentation(ProposalService proposalService, - DaoStateService daoStateService, - MyProposalListService myProposalListService, - BsqWalletService bsqWalletService, - ProposalValidatorProvider validatorProvider) { - this.proposalService = proposalService; - this.daoStateService = daoStateService; - this.myProposalListService = myProposalListService; - this.bsqWalletService = bsqWalletService; - this.validatorProvider = validatorProvider; - - daoStateService.addDaoStateListener(this); - myProposalListService.addListener(this); - - proposalListChangeListener = c -> updateLists(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - } - - @Override - public void start() { - // We must set the listeners initially and not on onParseBlockChainComplete as activeOrMyUnconfirmedProposals - // is used in voteResults which can be called earlier during sync. - // To avoid unneeded upDateLists calls we delay one render frame so that once the proposalService is complete we - // register out listeners. - UserThread.execute(() -> { - proposalService.getTempProposals().addListener(proposalListChangeListener); - proposalService.getProposalPayloads().addListener((ListChangeListener) c -> updateLists()); - }); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - updateLists(); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // MyProposalListService.Listener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onListChanged(List list) { - updateLists(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void updateLists() { - List tempProposals = proposalService.getTempProposals(); - Set verifiedProposals = proposalService.getProposalPayloads().stream() - .map(ProposalPayload::getProposal) - .filter(proposal -> !daoStateService.isParseBlockChainComplete() || - validatorProvider.getValidator(proposal).isValidAndConfirmed(proposal)) - .collect(Collectors.toSet()); - Set set = new HashSet<>(tempProposals); - set.addAll(verifiedProposals); - - // We want to show our own unconfirmed proposals. Unconfirmed proposals from other users are not included - // in the list. - // If a tx is not found in the daoStateService it can be that it is either unconfirmed or invalid. - // To avoid inclusion of invalid txs we add a check for the confidence type PENDING from the bsqWalletService. - // So we only add proposals if they are unconfirmed and therefore not yet parsed. Once confirmed they have to be - // found in the daoStateService. - List myUnconfirmedProposals = myProposalListService.getList().stream() - .filter(p -> !daoStateService.getTx(p.getTxId()).isPresent()) // Tx is still not in our bsq blocks - .filter(p -> { - TransactionConfidence confidenceForTxId = bsqWalletService.getConfidenceForTxId(p.getTxId()); - return confidenceForTxId != null && - confidenceForTxId.getConfidenceType() == TransactionConfidence.ConfidenceType.PENDING; - }) - .collect(Collectors.toList()); - set.addAll(myUnconfirmedProposals); - - allProposals.clear(); - allProposals.addAll(set); - - activeOrMyUnconfirmedProposals.setPredicate(proposal -> validatorProvider.getValidator(proposal).isValidAndConfirmed(proposal) || - myUnconfirmedProposals.contains(proposal)); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java deleted file mode 100644 index a50b7e2f35..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java +++ /dev/null @@ -1,338 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal; - -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.storage.appendonly.ProposalPayload; -import bisq.core.dao.governance.proposal.storage.appendonly.ProposalStorageService; -import bisq.core.dao.governance.proposal.storage.temp.TempProposalPayload; -import bisq.core.dao.governance.proposal.storage.temp.TempProposalStorageService; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.BaseTx; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.dao.state.model.governance.Proposal; - -import bisq.network.p2p.P2PService; -import bisq.network.p2p.storage.HashMapChangedListener; -import bisq.network.p2p.storage.payload.PersistableNetworkPayload; -import bisq.network.p2p.storage.payload.ProtectedStorageEntry; -import bisq.network.p2p.storage.payload.ProtectedStoragePayload; -import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreListener; -import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService; -import bisq.network.p2p.storage.persistence.ProtectedDataStoreService; - -import bisq.common.config.Config; - -import org.bitcoinj.core.Coin; - -import com.google.inject.Inject; - -import javax.inject.Named; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Maintains protectedStoreList and appendOnlyStoreList for received proposals. - * Republishes protectedStoreList to append-only data store when entering the break before the blind vote phase. - */ -@Slf4j -public class ProposalService implements HashMapChangedListener, AppendOnlyDataStoreListener, - DaoStateListener, DaoSetupService { - private final P2PService p2PService; - private final PeriodService periodService; - private final ProposalStorageService proposalStorageService; - private final DaoStateService daoStateService; - private final ProposalValidatorProvider validatorProvider; - - // Proposals we receive in the proposal phase. They can be removed in that phase. That list must not be used for - // consensus critical code. - @Getter - private final ObservableList tempProposals = FXCollections.observableArrayList(); - - // Proposals which got added to the append-only data store in the break before the blind vote phase. - // They cannot be removed anymore. This list is used for consensus critical code. Different nodes might have - // different data collections due the eventually consistency of the P2P network. - @Getter - private final ObservableList proposalPayloads = FXCollections.observableArrayList(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public ProposalService(P2PService p2PService, - PeriodService periodService, - ProposalStorageService proposalStorageService, - TempProposalStorageService tempProposalStorageService, - AppendOnlyDataStoreService appendOnlyDataStoreService, - ProtectedDataStoreService protectedDataStoreService, - DaoStateService daoStateService, - ProposalValidatorProvider validatorProvider, - @Named(Config.DAO_ACTIVATED) boolean daoActivated) { - this.p2PService = p2PService; - this.periodService = periodService; - this.proposalStorageService = proposalStorageService; - this.daoStateService = daoStateService; - this.validatorProvider = validatorProvider; - - if (daoActivated) { - // We add our stores to the global stores - appendOnlyDataStoreService.addService(proposalStorageService); - protectedDataStoreService.addService(tempProposalStorageService); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - daoStateService.addDaoStateListener(this); - // Listen for tempProposals - p2PService.addHashSetChangedListener(this); - // Listen for proposalPayloads - p2PService.getP2PDataStorage().addAppendOnlyDataStoreListener(this); - } - - @Override - public void start() { - fillListFromProtectedStore(); - fillListFromAppendOnlyDataStore(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // HashMapChangedListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onAdded(Collection protectedStorageEntries) { - protectedStorageEntries.forEach(protectedStorageEntry -> { - onProtectedDataAdded(protectedStorageEntry, true); - }); - } - - @Override - public void onRemoved(Collection protectedStorageEntries) { - onProtectedDataRemoved(protectedStorageEntries); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // AppendOnlyDataStoreListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onAdded(PersistableNetworkPayload payload) { - onAppendOnlyDataAdded(payload, true); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - // We try to broadcast at any block in the break1 phase. If we have received the data already we do not - // broadcast so we do not flood the network. - if (periodService.isInPhase(block.getHeight(), DaoPhase.Phase.BREAK1)) { - // We only republish if we are completed with parsing old blocks, otherwise we would republish old - // proposals all the time - maybePublishToAppendOnlyDataStore(); - fillListFromAppendOnlyDataStore(); - } - } - - @Override - public void onParseBlockChainComplete() { - // Fill the lists with the data we have collected in our stores. - fillListFromProtectedStore(); - fillListFromAppendOnlyDataStore(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public List getValidatedProposals() { - return proposalPayloads.stream() - .map(ProposalPayload::getProposal) - .filter(proposal -> validatorProvider.getValidator(proposal).isTxTypeValid(proposal)) - .collect(Collectors.toList()); - } - - public Coin getRequiredQuorum(Proposal proposal) { - int chainHeight = daoStateService.getTx(proposal.getTxId()) - .map(BaseTx::getBlockHeight). - orElse(daoStateService.getChainHeight()); - return daoStateService.getParamValueAsCoin(proposal.getQuorumParam(), chainHeight); - } - - public double getRequiredThreshold(Proposal proposal) { - int chainHeight = daoStateService.getTx(proposal.getTxId()) - .map(BaseTx::getBlockHeight). - orElse(daoStateService.getChainHeight()); - return daoStateService.getParamValueAsPercentDouble(proposal.getThresholdParam(), chainHeight); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void fillListFromProtectedStore() { - p2PService.getDataMap().values().forEach(e -> onProtectedDataAdded(e, false)); - } - - private void fillListFromAppendOnlyDataStore() { - proposalStorageService.getMap().values().forEach(e -> onAppendOnlyDataAdded(e, false)); - } - - private void maybePublishToAppendOnlyDataStore() { - // We set reBroadcast to false to avoid to flood the network. - // If we have 20 proposals and 200 nodes with 10 neighbor peers we would send 40 000 messages if we would set - // reBroadcast to ! - tempProposals.stream() - .filter(proposal -> validatorProvider.getValidator(proposal).isValidAndConfirmed(proposal)) - .map(ProposalPayload::new) - .forEach(proposalPayload -> { - boolean success = p2PService.addPersistableNetworkPayload(proposalPayload, false); - if (success) { - log.info("We published a ProposalPayload to the P2P network as append-only data. proposalTxId={}", - proposalPayload.getProposal().getTxId()); - } - // If we had data already we did not broadcast and success is false - }); - } - - private void onProtectedDataAdded(ProtectedStorageEntry entry, boolean fromBroadcastMessage) { - ProtectedStoragePayload protectedStoragePayload = entry.getProtectedStoragePayload(); - if (protectedStoragePayload instanceof TempProposalPayload) { - Proposal proposal = ((TempProposalPayload) protectedStoragePayload).getProposal(); - // We do not validate if we are in current cycle and if tx is confirmed yet as the tx might be not - // available/confirmed. - // We check if we are in the proposal or break1 phase. We are tolerant to accept tempProposals in the break1 - // phase to avoid risks that a proposal published very closely to the end of the proposal phase will not be - // sufficiently broadcast. - // When we receive tempProposals from the seed node at startup we only keep those which are in the current - // proposal/break1 phase if we are in that phase. We ignore tempProposals in case we are not in the - // proposal/break1 phase as they are not used anyway but the proposalPayloads will be relevant once we - // left the proposal/break1 phase. - if (periodService.isInPhase(daoStateService.getChainHeight(), DaoPhase.Phase.PROPOSAL) || - periodService.isInPhase(daoStateService.getChainHeight(), DaoPhase.Phase.BREAK1)) { - if (!tempProposals.contains(proposal)) { - // We only validate in case the blocks are parsed as otherwise some validators like param validator - // might fail as Dao state is not complete. - if (!daoStateService.isParseBlockChainComplete() || - validatorProvider.getValidator(proposal).areDataFieldsValid(proposal)) { - if (fromBroadcastMessage) { - log.info("We received a TempProposalPayload and store it to our protectedStoreList. proposalTxId={}", - proposal.getTxId()); - } - tempProposals.add(proposal); - } else { - log.debug("We received an invalid proposal from the P2P network. Proposal={}, blockHeight={}", - proposal, daoStateService.getChainHeight()); - } - } - } - } - } - - private void onProtectedDataRemoved(Collection protectedStorageEntries) { - - // The listeners of tmpProposals can do large amounts of work that cause performance issues. Apply all of the - // updates at once using retainAll which will cause all listeners to be updated only once. - ArrayList tempProposalsWithUpdates = new ArrayList<>(tempProposals); - - protectedStorageEntries.forEach(protectedStorageEntry -> { - ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload(); - if (protectedStoragePayload instanceof TempProposalPayload) { - Proposal proposal = ((TempProposalPayload) protectedStoragePayload).getProposal(); - // We allow removal only if we are in the proposal phase. - boolean inPhase = periodService.isInPhase(daoStateService.getChainHeight(), DaoPhase.Phase.PROPOSAL); - boolean txInPastCycle = periodService.isTxInPastCycle(proposal.getTxId(), daoStateService.getChainHeight()); - Optional tx = daoStateService.getTx(proposal.getTxId()); - boolean unconfirmedOrNonBsqTx = !tx.isPresent(); - // if the tx is unconfirmed we need to be in the PROPOSAL phase, otherwise the tx must be confirmed. - if (inPhase || txInPastCycle || unconfirmedOrNonBsqTx) { - if (tempProposalsWithUpdates.contains(proposal)) { - tempProposalsWithUpdates.remove(proposal); - log.debug("We received a remove request for a TempProposalPayload and have removed the proposal " + - "from our list. proposal creation date={}, proposalTxId={}, inPhase={}, " + - "txInPastCycle={}, unconfirmedOrNonBsqTx={}", - proposal.getCreationDateAsDate(), proposal.getTxId(), inPhase, txInPastCycle, unconfirmedOrNonBsqTx); - } - } else { - log.warn("We received a remove request outside the PROPOSAL phase. " + - "Proposal creation date={}, proposal.txId={}, current blockHeight={}", - proposal.getCreationDateAsDate(), proposal.getTxId(), daoStateService.getChainHeight()); - } - } - }); - - tempProposals.retainAll(tempProposalsWithUpdates); - } - - private void onAppendOnlyDataAdded(PersistableNetworkPayload persistableNetworkPayload, boolean fromBroadcastMessage) { - if (persistableNetworkPayload instanceof ProposalPayload) { - ProposalPayload proposalPayload = (ProposalPayload) persistableNetworkPayload; - if (!proposalPayloads.contains(proposalPayload)) { - Proposal proposal = proposalPayload.getProposal(); - - // We don't validate phase and cycle as we might receive proposals from other cycles or phases at startup. - // Beside that we might receive payloads we requested at the vote result phase in case we missed some - // payloads. We prefer here resilience over protection against late publishing attacks. - - // We only validate in case the blocks are parsed as otherwise some validators like param validator - // might fail as Dao state is not complete. - if (!daoStateService.isParseBlockChainComplete() || - validatorProvider.getValidator(proposal).areDataFieldsValid(proposal)) { - if (fromBroadcastMessage) { - log.info("We received a ProposalPayload and store it to our appendOnlyStoreList. proposalTxId={}", - proposal.getTxId()); - } - proposalPayloads.add(proposalPayload); - } else { - log.warn("We received a invalid append-only proposal from the P2P network. " + - "Proposal={}, blockHeight={}", - proposal, daoStateService.getChainHeight()); - } - } - } - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalType.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalType.java deleted file mode 100644 index 079f37b722..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalType.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal; - -import bisq.core.locale.Res; - -public enum ProposalType { - UNDEFINED, - COMPENSATION_REQUEST, - REIMBURSEMENT_REQUEST, - CHANGE_PARAM, - BONDED_ROLE, - CONFISCATE_BOND, - GENERIC, - REMOVE_ASSET; - - public String getDisplayName() { - return Res.get("dao.proposal.type." + name()); - } - - public String getShortDisplayName() { - return Res.get("dao.proposal.type.short." + name()); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidationException.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidationException.java deleted file mode 100644 index 4e0e2065f5..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidationException.java +++ /dev/null @@ -1,57 +0,0 @@ -package bisq.core.dao.governance.proposal; - -import bisq.core.dao.state.model.blockchain.Tx; - -import org.bitcoinj.core.Coin; - -import lombok.Getter; - -import javax.annotation.Nullable; - -@Getter -public class ProposalValidationException extends Exception { - @Nullable - private Coin requestedBsq; - @Nullable - private Coin minRequestAmount; - @Nullable - private Tx tx; - - public ProposalValidationException(String message, Coin requestedBsq, Coin minRequestAmount) { - super(message); - this.requestedBsq = requestedBsq; - this.minRequestAmount = minRequestAmount; - } - - public ProposalValidationException(Throwable cause) { - super(cause); - } - - public ProposalValidationException(String message) { - super(message); - } - - public ProposalValidationException(String message, Throwable throwable) { - super(message, throwable); - - } - - public ProposalValidationException(String message, Tx tx) { - super(message); - this.tx = tx; - } - - public ProposalValidationException(Throwable cause, Tx tx) { - super(cause); - this.tx = tx; - } - - @Override - public String toString() { - return "ProposalValidationException{" + - "\n requestedBsq=" + requestedBsq + - ",\n minRequestAmount=" + minRequestAmount + - ",\n tx=" + tx + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java deleted file mode 100644 index 2f7d97a7f5..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.BaseTx; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxType; -import bisq.core.dao.state.model.governance.CompensationProposal; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.dao.state.model.governance.Proposal; -import bisq.core.dao.state.model.governance.ReimbursementProposal; - -import bisq.common.util.ExtraDataMapValidator; - -import java.util.Optional; - -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkArgument; -import static org.apache.commons.lang3.Validate.notEmpty; - -/** - * Changes here can potentially break consensus! - */ -@Slf4j -public abstract class ProposalValidator implements ConsensusCritical { - protected final DaoStateService daoStateService; - protected final PeriodService periodService; - - protected ProposalValidator(DaoStateService daoStateService, PeriodService periodService) { - this.daoStateService = daoStateService; - this.periodService = periodService; - } - - public boolean areDataFieldsValid(Proposal proposal) { - try { - validateDataFields(proposal); - return true; - } catch (ProposalValidationException e) { - log.warn("proposal data fields are invalid. proposal={}, error={}", proposal, e.toString()); - return false; - } - } - - public void validateDataFields(Proposal proposal) throws ProposalValidationException { - try { - notEmpty(proposal.getName(), "name must not be empty"); - notEmpty(proposal.getLink(), "link must not be empty"); - checkArgument(proposal.getName().length() <= 200, "Name must not exceed 200 chars"); - checkArgument(proposal.getLink().length() <= 200, "Link must not exceed 200 chars"); - if (proposal.getTxId() != null) - checkArgument(proposal.getTxId().length() == 64, "Tx ID must be 64 chars"); - - ExtraDataMapValidator.validate(proposal.getExtraDataMap()); - } catch (Throwable throwable) { - throw new ProposalValidationException(throwable); - } - } - - public boolean isValidOrUnconfirmed(Proposal proposal) { - return isValid(proposal, true); - } - - public boolean isValidAndConfirmed(Proposal proposal) { - return isValid(proposal, false); - } - - public boolean isTxTypeValid(Proposal proposal) { - String txId = proposal.getTxId(); - if (txId == null || txId.equals("")) { - log.warn("txId must be set. proposal.getTxId()={}", proposal.getTxId()); - return false; - } - Optional optionalTxType = daoStateService.getOptionalTxType(txId); - boolean present = optionalTxType.filter(txType -> txType == proposal.getTxType()).isPresent(); - if (!present) - log.debug("optionalTxType not present for proposal {}" + proposal); - return present; - } - - private boolean isValid(Proposal proposal, boolean allowUnconfirmed) { - if (!areDataFieldsValid(proposal)) { - return false; - } - - String txId = proposal.getTxId(); - if (txId == null || txId.equals("")) { - log.warn("txId must be set. proposal.getTxId()={}", proposal.getTxId()); - return false; - } - - Optional optionalTx = daoStateService.getTx(txId); - boolean isTxConfirmed = optionalTx.isPresent(); - int chainHeight = daoStateService.getChainHeight(); - - if (isTxConfirmed) { - int txHeight = optionalTx.get().getBlockHeight(); - if (!periodService.isTxInCorrectCycle(txHeight, chainHeight)) { - log.trace("Tx is not in current cycle. proposal.getTxId()={}", proposal.getTxId()); - return false; - } - if (!periodService.isInPhase(txHeight, DaoPhase.Phase.PROPOSAL)) { - log.debug("Tx is not in PROPOSAL phase. proposal.getTxId()={}", proposal.getTxId()); - return false; - } - if (proposal instanceof CompensationProposal) { - if (optionalTx.get().getTxType() != TxType.COMPENSATION_REQUEST) { - log.error("TxType is not a COMPENSATION_REQUEST. proposal.getTxId()={}", proposal.getTxId()); - return false; - } - } else if (proposal instanceof ReimbursementProposal) { - if (optionalTx.get().getTxType() != TxType.REIMBURSEMENT_REQUEST) { - log.error("TxType is not a REIMBURSEMENT_REQUEST. proposal.getTxId()={}", proposal.getTxId()); - return false; - } - } else { - if (optionalTx.get().getTxType() != TxType.PROPOSAL) { - log.error("TxType is not PROPOSAL. proposal.getTxId()={}", proposal.getTxId()); - return false; - } - } - - return true; - } else if (allowUnconfirmed) { - // We want to show own unconfirmed proposals in the active proposals list. - boolean inPhase = periodService.isInPhase(chainHeight, DaoPhase.Phase.PROPOSAL); - if (inPhase) - log.debug("proposal is unconfirmed and we are in proposal phase: txId={}", txId); - return inPhase; - } else { - return false; - } - } - - protected int getBlockHeight(Proposal proposal) { - // When we receive a temp proposal the tx is usually not confirmed so we cannot lookup the block height of - // the tx. We take the current block height in that case as it would be in the same cycle anyway. - return daoStateService.getTx(proposal.getTxId()) - .map(BaseTx::getBlockHeight) - .orElseGet(daoStateService::getChainHeight); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidatorProvider.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidatorProvider.java deleted file mode 100644 index cd2ca7b3e4..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidatorProvider.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal; - -import bisq.core.dao.governance.proposal.compensation.CompensationValidator; -import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondValidator; -import bisq.core.dao.governance.proposal.generic.GenericProposalValidator; -import bisq.core.dao.governance.proposal.param.ChangeParamValidator; -import bisq.core.dao.governance.proposal.reimbursement.ReimbursementValidator; -import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetValidator; -import bisq.core.dao.governance.proposal.role.RoleValidator; -import bisq.core.dao.state.model.governance.Proposal; - -import javax.inject.Inject; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class ProposalValidatorProvider { - private final CompensationValidator compensationValidator; - private final ConfiscateBondValidator confiscateBondValidator; - private final GenericProposalValidator genericProposalValidator; - private final ChangeParamValidator changeParamValidator; - private final ReimbursementValidator reimbursementValidator; - private final RemoveAssetValidator removeAssetValidator; - private final RoleValidator roleValidator; - - @Inject - public ProposalValidatorProvider(CompensationValidator compensationValidator, - ConfiscateBondValidator confiscateBondValidator, - GenericProposalValidator genericProposalValidator, - ChangeParamValidator changeParamValidator, - ReimbursementValidator reimbursementValidator, - RemoveAssetValidator removeAssetValidator, - RoleValidator roleValidator) { - this.compensationValidator = compensationValidator; - this.confiscateBondValidator = confiscateBondValidator; - this.genericProposalValidator = genericProposalValidator; - this.changeParamValidator = changeParamValidator; - this.reimbursementValidator = reimbursementValidator; - this.removeAssetValidator = removeAssetValidator; - this.roleValidator = roleValidator; - } - - public ProposalValidator getValidator(Proposal proposal) { - return getValidator(proposal.getType()); - } - - private ProposalValidator getValidator(ProposalType proposalType) { - switch (proposalType) { - case COMPENSATION_REQUEST: - return compensationValidator; - case REIMBURSEMENT_REQUEST: - return reimbursementValidator; - case CHANGE_PARAM: - return changeParamValidator; - case BONDED_ROLE: - return roleValidator; - case CONFISCATE_BOND: - return confiscateBondValidator; - case GENERIC: - return genericProposalValidator; - case REMOVE_ASSET: - return removeAssetValidator; - } - throw new RuntimeException("Proposal type " + proposalType.name() + " was not covered by switch case."); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalWithTransaction.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalWithTransaction.java deleted file mode 100644 index 99d53336ad..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalWithTransaction.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal; - -import bisq.core.dao.state.model.governance.Proposal; - -import org.bitcoinj.core.Transaction; - -import lombok.Value; - -@Value -public class ProposalWithTransaction { - private final Proposal proposal; - private final Transaction transaction; - - ProposalWithTransaction(Proposal proposal, Transaction transaction) { - this.proposal = proposal; - this.transaction = transaction; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/TxException.java b/core/src/main/java/bisq/core/dao/governance/proposal/TxException.java deleted file mode 100644 index 3d1b2d3253..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/TxException.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal; - -public class TxException extends Exception { - public TxException(Throwable cause) { - super(cause); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationConsensus.java b/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationConsensus.java deleted file mode 100644 index ced36309e2..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationConsensus.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This file is part of bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.compensation; - -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.state.DaoStateService; - -import org.bitcoinj.core.Coin; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class CompensationConsensus { - public static Coin getMinCompensationRequestAmount(DaoStateService daoStateService, int chainHeight) { - return daoStateService.getParamValueAsCoin(Param.COMPENSATION_REQUEST_MIN_AMOUNT, chainHeight); - } - - public static Coin getMaxCompensationRequestAmount(DaoStateService daoStateService, int chainHeight) { - return daoStateService.getParamValueAsCoin(Param.COMPENSATION_REQUEST_MAX_AMOUNT, chainHeight); - } - -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationProposalFactory.java deleted file mode 100644 index bd307f55a0..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationProposalFactory.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.compensation; - -import bisq.core.btc.exceptions.TransactionVerificationException; -import bisq.core.btc.exceptions.WalletException; -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.governance.proposal.BaseProposalFactory; -import bisq.core.dao.governance.proposal.ProposalConsensus; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalWithTransaction; -import bisq.core.dao.governance.proposal.TxException; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.OpReturnType; -import bisq.core.dao.state.model.governance.CompensationProposal; -import bisq.core.dao.state.model.governance.Proposal; - -import bisq.common.app.Version; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.Transaction; - -import javax.inject.Inject; - -import java.util.HashMap; - -import lombok.extern.slf4j.Slf4j; - -/** - * Creates the CompensationProposal and the transaction. - */ -@Slf4j -public class CompensationProposalFactory extends BaseProposalFactory { - - private Coin requestedBsq; - private String bsqAddress; - - @Inject - public CompensationProposalFactory(BsqWalletService bsqWalletService, - BtcWalletService btcWalletService, - DaoStateService daoStateService, - CompensationValidator proposalValidator) { - super(bsqWalletService, - btcWalletService, - daoStateService, - proposalValidator); - } - - public ProposalWithTransaction createProposalWithTransaction(String name, - String link, - Coin requestedBsq) - throws ProposalValidationException, InsufficientMoneyException, TxException { - this.requestedBsq = requestedBsq; - this.bsqAddress = bsqWalletService.getUnusedBsqAddressAsString(); - - return super.createProposalWithTransaction(name, link); - } - - @Override - protected CompensationProposal createProposalWithoutTxId() { - return new CompensationProposal( - name, - link, - requestedBsq, - bsqAddress, - new HashMap<>()); - } - - @Override - protected byte[] getOpReturnData(byte[] hashOfPayload) { - return ProposalConsensus.getOpReturnData(hashOfPayload, - OpReturnType.COMPENSATION_REQUEST.getType(), - Version.COMPENSATION_REQUEST); - } - - @Override - protected Transaction completeTx(Transaction preparedBurnFeeTx, byte[] opReturnData, Proposal proposal) - throws WalletException, InsufficientMoneyException, TransactionVerificationException { - CompensationProposal compensationProposal = (CompensationProposal) proposal; - return btcWalletService.completePreparedCompensationRequestTx( - compensationProposal.getRequestedBsq(), - compensationProposal.getAddress(), - preparedBurnFeeTx, - opReturnData); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationValidator.java deleted file mode 100644 index e76f77fa02..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/compensation/CompensationValidator.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.compensation; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalValidator; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.CompensationProposal; -import bisq.core.dao.state.model.governance.Proposal; - -import org.bitcoinj.core.Coin; - -import javax.inject.Inject; - -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkArgument; -import static org.apache.commons.lang3.Validate.notEmpty; - -/** - * Changes here can potentially break consensus! - */ -@Slf4j -public class CompensationValidator extends ProposalValidator implements ConsensusCritical { - - @Inject - public CompensationValidator(DaoStateService daoStateService, PeriodService periodService) { - super(daoStateService, periodService); - } - - @Override - public void validateDataFields(Proposal proposal) throws ProposalValidationException { - try { - super.validateDataFields(proposal); - - CompensationProposal compensationProposal = (CompensationProposal) proposal; - String bsqAddress = compensationProposal.getBsqAddress(); - notEmpty(bsqAddress, "bsqAddress must not be empty"); - checkArgument(bsqAddress.substring(0, 1).equals("B"), "bsqAddress must start with B"); - compensationProposal.getAddress(); // throws AddressFormatException if wrong address - - Coin requestedBsq = compensationProposal.getRequestedBsq(); - int chainHeight = getBlockHeight(proposal); - Coin maxCompensationRequestAmount = CompensationConsensus.getMaxCompensationRequestAmount(daoStateService, chainHeight); - checkArgument(requestedBsq.compareTo(maxCompensationRequestAmount) <= 0, - "Requested BSQ must not exceed " + (maxCompensationRequestAmount.value / 100L) + " BSQ"); - Coin minCompensationRequestAmount = CompensationConsensus.getMinCompensationRequestAmount(daoStateService, chainHeight); - checkArgument(requestedBsq.compareTo(minCompensationRequestAmount) >= 0, - "Requested BSQ must not be less than " + (minCompensationRequestAmount.value / 100L) + " BSQ"); - } catch (ProposalValidationException e) { - throw e; - } catch (Throwable throwable) { - throw new ProposalValidationException(throwable); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondProposalFactory.java deleted file mode 100644 index 8b4c2a2710..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondProposalFactory.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.confiscatebond; - -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.governance.proposal.BaseProposalFactory; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalWithTransaction; -import bisq.core.dao.governance.proposal.TxException; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.ConfiscateBondProposal; - -import org.bitcoinj.core.InsufficientMoneyException; - -import javax.inject.Inject; - -import java.util.HashMap; - -import lombok.extern.slf4j.Slf4j; - -/** - * Creates ConfiscateBondProposal and transaction. - */ -@Slf4j -public class ConfiscateBondProposalFactory extends BaseProposalFactory { - private String lockupTxId; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public ConfiscateBondProposalFactory(BsqWalletService bsqWalletService, - BtcWalletService btcWalletService, - DaoStateService daoStateService, - ConfiscateBondValidator proposalValidator) { - super(bsqWalletService, - btcWalletService, - daoStateService, - proposalValidator); - } - - public ProposalWithTransaction createProposalWithTransaction(String name, - String link, - String lockupTxId) - throws ProposalValidationException, InsufficientMoneyException, TxException { - this.lockupTxId = lockupTxId; - - return super.createProposalWithTransaction(name, link); - } - - @Override - protected ConfiscateBondProposal createProposalWithoutTxId() { - return new ConfiscateBondProposal( - name, - link, - lockupTxId, - new HashMap<>()); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondValidator.java deleted file mode 100644 index 02124b7949..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondValidator.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.confiscatebond; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalValidator; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.ConfiscateBondProposal; -import bisq.core.dao.state.model.governance.Proposal; - -import javax.inject.Inject; - -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkArgument; -import static org.apache.commons.lang3.Validate.notEmpty; - -/** - * Changes here can potentially break consensus! - */ -@Slf4j -public class ConfiscateBondValidator extends ProposalValidator implements ConsensusCritical { - - @Inject - public ConfiscateBondValidator(DaoStateService daoStateService, PeriodService periodService) { - super(daoStateService, periodService); - } - - @Override - public void validateDataFields(Proposal proposal) throws ProposalValidationException { - try { - super.validateDataFields(proposal); - ConfiscateBondProposal confiscateBondProposal = (ConfiscateBondProposal) proposal; - notEmpty(confiscateBondProposal.getLockupTxId(), "LockupTxId must not be empty"); - checkArgument(confiscateBondProposal.getLockupTxId().length() == 64, "LockupTxId must be 64 chars"); - } catch (ProposalValidationException e) { - throw e; - } catch (Throwable throwable) { - throw new ProposalValidationException(throwable); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalFactory.java deleted file mode 100644 index f824bb323c..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalFactory.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.generic; - -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.governance.proposal.BaseProposalFactory; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalWithTransaction; -import bisq.core.dao.governance.proposal.TxException; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.GenericProposal; - -import org.bitcoinj.core.InsufficientMoneyException; - -import javax.inject.Inject; - -import java.util.HashMap; - -import lombok.extern.slf4j.Slf4j; - -/** - * Creates GenericProposal and transaction. - */ -@Slf4j -public class GenericProposalFactory extends BaseProposalFactory { - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public GenericProposalFactory(BsqWalletService bsqWalletService, - BtcWalletService btcWalletService, - DaoStateService daoStateService, - GenericProposalValidator proposalValidator) { - super(bsqWalletService, - btcWalletService, - daoStateService, - proposalValidator); - } - - public ProposalWithTransaction createProposalWithTransaction(String name, String link) - throws ProposalValidationException, InsufficientMoneyException, TxException { - - return super.createProposalWithTransaction(name, link); - } - - @Override - protected GenericProposal createProposalWithoutTxId() { - return new GenericProposal(name, link, new HashMap<>()); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalValidator.java deleted file mode 100644 index f9d38a6d63..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/generic/GenericProposalValidator.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.generic; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalValidator; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.Proposal; - -import javax.inject.Inject; - -import lombok.extern.slf4j.Slf4j; - -/** - * Changes here can potentially break consensus! - */ -@Slf4j -public class GenericProposalValidator extends ProposalValidator implements ConsensusCritical { - - @Inject - public GenericProposalValidator(DaoStateService daoStateService, PeriodService periodService) { - super(daoStateService, periodService); - } - - @Override - public void validateDataFields(Proposal proposal) throws ProposalValidationException { - try { - super.validateDataFields(proposal); - } catch (ProposalValidationException e) { - throw e; - } catch (Throwable throwable) { - throw new ProposalValidationException(throwable); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamInputValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamInputValidator.java deleted file mode 100644 index 72ad23671a..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamInputValidator.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.param; - -import bisq.core.dao.governance.param.Param; -import bisq.core.util.validation.InputValidator; - -public class ChangeParamInputValidator extends InputValidator { - private final Param param; - private final ChangeParamValidator changeParamValidator; - - public ChangeParamInputValidator(Param param, ChangeParamValidator changeParamValidator) { - this.changeParamValidator = changeParamValidator; - this.param = param; - } - - @Override - public ValidationResult validate(String input) { - ValidationResult validationResult = super.validate(input); - if (!validationResult.isValid) - return validationResult; - - return validateParam(input); - } - - private ValidationResult validateParam(String input) { - try { - changeParamValidator.validateParamValue(param, input); - return new ValidationResult(true); - } catch (Throwable e) { - return new ValidationResult(false, e.getMessage()); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamProposalFactory.java deleted file mode 100644 index 1935a53b66..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamProposalFactory.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.param; - -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.proposal.BaseProposalFactory; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalWithTransaction; -import bisq.core.dao.governance.proposal.TxException; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.ChangeParamProposal; - -import org.bitcoinj.core.InsufficientMoneyException; - -import javax.inject.Inject; - -import java.util.HashMap; - -import lombok.extern.slf4j.Slf4j; - -/** - * Creates ChangeParamProposal and transaction. - */ -@Slf4j -public class ChangeParamProposalFactory extends BaseProposalFactory { - private Param param; - private String paramValue; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public ChangeParamProposalFactory(BsqWalletService bsqWalletService, - BtcWalletService btcWalletService, - DaoStateService daoStateService, - ChangeParamValidator proposalValidator) { - super(bsqWalletService, - btcWalletService, - daoStateService, - proposalValidator); - } - - public ProposalWithTransaction createProposalWithTransaction(String name, - String link, - Param param, - String paramValue) - throws ProposalValidationException, InsufficientMoneyException, TxException { - this.param = param; - this.paramValue = paramValue; - - return super.createProposalWithTransaction(name, link); - } - - @Override - protected ChangeParamProposal createProposalWithoutTxId() { - return new ChangeParamProposal( - name, - link, - param, - paramValue, - new HashMap<>()); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamValidator.java deleted file mode 100644 index 6aa2187eed..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamValidator.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.param; - -import bisq.core.btc.wallet.Restrictions; -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalValidator; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.ChangeParamProposal; -import bisq.core.dao.state.model.governance.Proposal; -import bisq.core.locale.Res; -import bisq.core.util.coin.BsqFormatter; -import bisq.core.util.validation.BtcAddressValidator; -import bisq.core.util.validation.InputValidator; - -import bisq.common.config.Config; - -import org.bitcoinj.core.Coin; - -import javax.inject.Inject; - -import com.google.common.annotations.VisibleForTesting; - -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkArgument; - -/** - * Changes here can potentially break consensus! - * - * We do not store the values as the actual data type (Coin, int, String) but as Strings. So we need to convert it to the - * expected data type even if we get the data not from user input. - */ -@Slf4j -public class ChangeParamValidator extends ProposalValidator implements ConsensusCritical { - - private final BsqFormatter bsqFormatter; - - @Inject - public ChangeParamValidator(DaoStateService daoStateService, PeriodService periodService, BsqFormatter bsqFormatter) { - super(daoStateService, periodService); - this.bsqFormatter = bsqFormatter; - } - - @Override - public void validateDataFields(Proposal proposal) throws ProposalValidationException { - try { - super.validateDataFields(proposal); - - // Only once parsing is complete we can check for param changes - if (daoStateService.isParseBlockChainComplete()) { - ChangeParamProposal changeParamProposal = (ChangeParamProposal) proposal; - validateParamValue(changeParamProposal.getParam(), changeParamProposal.getParamValue(), getBlockHeight(proposal)); - checkArgument(changeParamProposal.getParamValue().length() <= 200, "ParamValue must not exceed 200 chars"); - } - } catch (ProposalValidationException e) { - throw e; - } catch (Throwable throwable) { - throw new ProposalValidationException(throwable); - } - } - - public void validateParamValue(Param param, String inputValue) throws ParamValidationException { - int blockHeight = periodService.getChainHeight(); - validateParamValue(param, inputValue, blockHeight); - } - - private void validateParamValue(Param param, String inputValue, int blockHeight) throws ParamValidationException { - String currentParamValue = daoStateService.getParamValue(param, blockHeight); - validateParamValue(param, currentParamValue, inputValue); - } - - private void validateParamValue(Param param, String currentParamValue, String inputValue) throws ParamValidationException { - try { - Coin currentParamValueAsCoin, inputValueAsCoin; - switch (param.getParamType()) { - case UNDEFINED: - break; - case BSQ: - currentParamValueAsCoin = daoStateService.getParamValueAsCoin(param, currentParamValue); - inputValueAsCoin = daoStateService.getParamValueAsCoin(param, inputValue); - validateBsqValue(currentParamValueAsCoin, inputValueAsCoin, param); - break; - case BTC: - currentParamValueAsCoin = daoStateService.getParamValueAsCoin(param, currentParamValue); - inputValueAsCoin = daoStateService.getParamValueAsCoin(param, inputValue); - validateBtcValue(currentParamValueAsCoin, inputValueAsCoin, param); - break; - case PERCENT: - double currentParamValueAsPercentDouble = daoStateService.getParamValueAsPercentDouble(currentParamValue); - double inputValueAsPercentDouble = daoStateService.getParamValueAsPercentDouble(inputValue); - validatePercentValue(currentParamValueAsPercentDouble, inputValueAsPercentDouble, param); - break; - case BLOCK: - int currentParamValueAsBlock = daoStateService.getParamValueAsBlock(currentParamValue); - int inputValueAsBlock = daoStateService.getParamValueAsBlock(inputValue); - validateBlockValue(currentParamValueAsBlock, inputValueAsBlock, param); - break; - case ADDRESS: - validateAddressValue(currentParamValue, inputValue); - break; - default: - log.warn("Param type {} not handled in switch case at validateParamValue", param.getParamType()); - } - } catch (ParamValidationException e) { - throw e; - } catch (NumberFormatException t) { - throw new ParamValidationException(Res.get("validation.numberFormatException", t.getMessage().toLowerCase())); - } catch (Throwable t) { - throw new ParamValidationException(t); - } - } - - private void validateBsqValue(Coin currentParamValueAsCoin, Coin inputValueAsCoin, Param param) throws ParamValidationException { - switch (param) { - case DEFAULT_MAKER_FEE_BSQ: - case DEFAULT_TAKER_FEE_BSQ: - case MIN_MAKER_FEE_BSQ: - case MIN_TAKER_FEE_BSQ: - break; - case PROPOSAL_FEE: - case BLIND_VOTE_FEE: - break; - case COMPENSATION_REQUEST_MIN_AMOUNT: - case REIMBURSEMENT_MIN_AMOUNT: - case COMPENSATION_REQUEST_MAX_AMOUNT: - case REIMBURSEMENT_MAX_AMOUNT: - checkArgument(inputValueAsCoin.value >= Restrictions.getMinNonDustOutput().value, - Res.get("validation.amountBelowDust", Restrictions.getMinNonDustOutput().value)); - checkArgument(inputValueAsCoin.value <= 200000000, - Res.get("validation.inputTooLarge", "200 000 BSQ")); - break; - case QUORUM_COMP_REQUEST: - case QUORUM_REIMBURSEMENT: - case QUORUM_CHANGE_PARAM: - case QUORUM_ROLE: - case QUORUM_CONFISCATION: - case QUORUM_GENERIC: - case QUORUM_REMOVE_ASSET: - checkArgument(inputValueAsCoin.value > 100000, - Res.get("validation.inputTooSmall", "1000 BSQ")); - break; - case ASSET_LISTING_FEE_PER_DAY: - break; - case BONDED_ROLE_FACTOR: - checkArgument(inputValueAsCoin.value > 100, - Res.get("validation.inputTooSmall", "1 BSQ")); - break; - } - checkArgument(inputValueAsCoin.isPositive(), Res.get("validation.inputTooSmall", "0 BSQ")); - validationChange((double) currentParamValueAsCoin.value, (double) inputValueAsCoin.value, param); - } - - private void validateBtcValue(Coin currentParamValueAsCoin, Coin inputValueAsCoin, Param param) throws ParamValidationException { - switch (param) { - case DEFAULT_MAKER_FEE_BTC: - case DEFAULT_TAKER_FEE_BTC: - case MIN_MAKER_FEE_BTC: - case MIN_TAKER_FEE_BTC: - checkArgument(inputValueAsCoin.value >= Restrictions.getMinNonDustOutput().value, - Res.get("validation.amountBelowDust", Restrictions.getMinNonDustOutput().value)); - break; - case ASSET_MIN_VOLUME: - case MAX_TRADE_LIMIT: - checkArgument(inputValueAsCoin.isPositive(), Res.get("validation.inputTooSmall", "0")); - break; - } - validationChange((double) currentParamValueAsCoin.value, (double) inputValueAsCoin.value, param); - } - - private void validatePercentValue(double currentParamValueAsPercentDouble, double inputValueAsPercentDouble, Param param) throws ParamValidationException { - switch (param) { - case THRESHOLD_COMP_REQUEST: - case THRESHOLD_REIMBURSEMENT: - case THRESHOLD_CHANGE_PARAM: - case THRESHOLD_ROLE: - case THRESHOLD_CONFISCATION: - case THRESHOLD_GENERIC: - case THRESHOLD_REMOVE_ASSET: - // We show only 2 decimals in the UI for % value - checkArgument(inputValueAsPercentDouble >= 0.5001, - Res.get("validation.inputTooSmall", "50%")); - checkArgument(inputValueAsPercentDouble <= 1, - Res.get("validation.inputTooLarge", "100%")); - break; - case ARBITRATOR_FEE: - checkArgument(inputValueAsPercentDouble >= 0, Res.get("validation.mustNotBeNegative")); - break; - } - validationChange(currentParamValueAsPercentDouble, inputValueAsPercentDouble, param); - } - - private void validateBlockValue(int currentParamValueAsBlock, int inputValueAsBlock, Param param) throws ParamValidationException { - boolean isMainnet = Config.baseCurrencyNetwork().isMainnet(); - switch (param) { - case LOCK_TIME_TRADE_PAYOUT: - break; - case PHASE_UNDEFINED: - break; - case PHASE_PROPOSAL: - case PHASE_BREAK1: - case PHASE_BLIND_VOTE: - case PHASE_BREAK2: - case PHASE_VOTE_REVEAL: - case PHASE_BREAK3: - if (isMainnet) - checkArgument(inputValueAsBlock >= 6, Res.get("validation.inputToBeAtLeast", "6 blocks")); - break; - case PHASE_RESULT: - if (isMainnet) - checkArgument(inputValueAsBlock >= 1, Res.get("validation.inputToBeAtLeast", "1 block")); - break; - } - - validationChange((double) currentParamValueAsBlock, (double) inputValueAsBlock, param); - // We allow 0 values (e.g. time lock for trade) - checkArgument(inputValueAsBlock >= 0, Res.get("validation.mustNotBeNegative")); - - } - - private void validateAddressValue(String currentParamValue, String inputValue) throws ParamValidationException { - checkArgument(!inputValue.equals(currentParamValue), Res.get("validation.mustBeDifferent")); - InputValidator.ValidationResult validationResult = new BtcAddressValidator().validate(inputValue); - if (!validationResult.isValid) - throw new ParamValidationException(validationResult.errorMessage); - } - - private void validationChange(double currentParamValue, double inputValue, Param param) throws ParamValidationException { - validationChange(currentParamValue, - inputValue, - param.getMaxDecrease(), - param.getMaxIncrease(), - param); - } - - /** - * @param currentValue Current value - * @param newValue New value - * @param min Decrease of param value limited to current value / maxDecrease. If 0 we don't apply the check and any change is possible - * @param max Increase of param value limited to current value * maxIncrease. If 0 we don't apply the check and any change is possible - * @param param - */ - @VisibleForTesting - void validationChange(double currentValue, double newValue, double min, double max, Param param) throws ParamValidationException { - // No need for translation as it would be a developer error to use such min/max values - checkArgument(min >= 0, "Min must be >= 0"); - checkArgument(max >= 0, "Max must be >= 0"); - if (currentValue == newValue) { - throw new ParamValidationException(ParamValidationException.ERROR.SAME, Res.get("validation.mustBeDifferent")); - } - - if (max == 0) - return; - - //TODO some cases with min = 0 and max not 0 or the other way round are not correctly implemented yet. - // Not intended to be used that way anyway but should be fixed... - double change = currentValue != 0 ? newValue / currentValue : 0; - if (change > max) { - double val = currentValue * max; - String value = getFormattedValue(param, val); - throw new ParamValidationException(ParamValidationException.ERROR.TOO_HIGH, Res.get("validation.inputTooLarge", value)); - } - - if (min == 0) - return; - - // If min/max are > 0 and currentValue is 0 it cannot be changed. min/max must be 0 in such cases. - if (currentValue == 0) { - throw new ParamValidationException(ParamValidationException.ERROR.NO_CHANGE_POSSIBLE, Res.get("validation.cannotBeChanged")); - } - - if (change < (1 / min)) { - double val = currentValue / min; - String value = getFormattedValue(param, val); - throw new ParamValidationException(ParamValidationException.ERROR.TOO_LOW, Res.get("validation.inputToBeAtLeast", value)); - } - } - - private String getFormattedValue(Param param, double val) { - String value = String.valueOf(val); - switch (param.getParamType()) { - case UNDEFINED: - // Not used - break; - case BSQ: - value = bsqFormatter.formatBSQSatoshis((long) val); - break; - case BTC: - value = bsqFormatter.formatBTCSatoshis((long) val); - break; - case PERCENT: - value = String.valueOf(val * 100); - break; - case BLOCK: - value = String.valueOf(Math.round(val)); - break; - case ADDRESS: - // Not used here - break; - } - return bsqFormatter.formatParamValue(param, value); - - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/param/ParamValidationException.java b/core/src/main/java/bisq/core/dao/governance/proposal/param/ParamValidationException.java deleted file mode 100644 index 31cb3ac079..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/param/ParamValidationException.java +++ /dev/null @@ -1,41 +0,0 @@ -package bisq.core.dao.governance.proposal.param; - -import lombok.Getter; - -import javax.annotation.Nullable; - -@Getter -public class -ParamValidationException extends Exception { - enum ERROR { - SAME, - NO_CHANGE_POSSIBLE, - TOO_LOW, - TOO_HIGH - } - - @Nullable - private ParamValidationException.ERROR error; - - - ParamValidationException(ParamValidationException.ERROR error, String errorMessage) { - super(errorMessage); - this.error = error; - } - - ParamValidationException(Throwable throwable) { - super(throwable.getMessage()); - initCause(throwable); - } - - ParamValidationException(String errorMessage) { - super(errorMessage); - } - - @Override - public String toString() { - return "ParamValidationException{" + - "\n error=" + error + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementConsensus.java b/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementConsensus.java deleted file mode 100644 index 2c7bce08e0..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementConsensus.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This file is part of bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.reimbursement; - -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.state.DaoStateService; - -import org.bitcoinj.core.Coin; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class ReimbursementConsensus { - public static Coin getMinReimbursementRequestAmount(DaoStateService daoStateService, int chainHeight) { - return daoStateService.getParamValueAsCoin(Param.REIMBURSEMENT_MIN_AMOUNT, chainHeight); - } - - public static Coin getMaxReimbursementRequestAmount(DaoStateService daoStateService, int chainHeight) { - return daoStateService.getParamValueAsCoin(Param.REIMBURSEMENT_MAX_AMOUNT, chainHeight); - } - -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementProposalFactory.java deleted file mode 100644 index 994b9ef8c5..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementProposalFactory.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.reimbursement; - -import bisq.core.btc.exceptions.TransactionVerificationException; -import bisq.core.btc.exceptions.WalletException; -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.governance.proposal.BaseProposalFactory; -import bisq.core.dao.governance.proposal.ProposalConsensus; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalWithTransaction; -import bisq.core.dao.governance.proposal.TxException; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.OpReturnType; -import bisq.core.dao.state.model.governance.Proposal; -import bisq.core.dao.state.model.governance.ReimbursementProposal; - -import bisq.common.app.Version; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.Transaction; - -import javax.inject.Inject; - -import java.util.HashMap; - -import lombok.extern.slf4j.Slf4j; - -/** - * Creates the ReimbursementProposal and the transaction. - */ -@Slf4j -public class ReimbursementProposalFactory extends BaseProposalFactory { - - private Coin requestedBsq; - private String bsqAddress; - - @Inject - public ReimbursementProposalFactory(BsqWalletService bsqWalletService, - BtcWalletService btcWalletService, - DaoStateService daoStateService, - ReimbursementValidator proposalValidator) { - super(bsqWalletService, - btcWalletService, - daoStateService, - proposalValidator); - } - - public ProposalWithTransaction createProposalWithTransaction(String name, - String link, - Coin requestedBsq) - throws ProposalValidationException, InsufficientMoneyException, TxException { - this.requestedBsq = requestedBsq; - this.bsqAddress = bsqWalletService.getUnusedBsqAddressAsString(); - - return super.createProposalWithTransaction(name, link); - } - - @Override - protected ReimbursementProposal createProposalWithoutTxId() { - return new ReimbursementProposal( - name, - link, - requestedBsq, - bsqAddress, - new HashMap<>()); - } - - @Override - protected byte[] getOpReturnData(byte[] hashOfPayload) { - return ProposalConsensus.getOpReturnData(hashOfPayload, - OpReturnType.REIMBURSEMENT_REQUEST.getType(), - Version.REIMBURSEMENT_REQUEST); - } - - @Override - protected Transaction completeTx(Transaction preparedBurnFeeTx, byte[] opReturnData, Proposal proposal) - throws WalletException, InsufficientMoneyException, TransactionVerificationException { - ReimbursementProposal reimbursementProposal = (ReimbursementProposal) proposal; - return btcWalletService.completePreparedReimbursementRequestTx( - reimbursementProposal.getRequestedBsq(), - reimbursementProposal.getAddress(), - preparedBurnFeeTx, - opReturnData); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementValidator.java deleted file mode 100644 index 4a80c99450..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/reimbursement/ReimbursementValidator.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.reimbursement; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalValidator; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.Proposal; -import bisq.core.dao.state.model.governance.ReimbursementProposal; - -import org.bitcoinj.core.Coin; - -import javax.inject.Inject; - -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkArgument; -import static org.apache.commons.lang3.Validate.notEmpty; - -/** - * Changes here can potentially break consensus! - */ -@Slf4j -public class ReimbursementValidator extends ProposalValidator implements ConsensusCritical { - - @Inject - public ReimbursementValidator(DaoStateService daoStateService, PeriodService periodService) { - super(daoStateService, periodService); - } - - @Override - public void validateDataFields(Proposal proposal) throws ProposalValidationException { - try { - super.validateDataFields(proposal); - - ReimbursementProposal reimbursementProposal = (ReimbursementProposal) proposal; - String bsqAddress = reimbursementProposal.getBsqAddress(); - notEmpty(bsqAddress, "bsqAddress must not be empty"); - checkArgument(bsqAddress.substring(0, 1).equals("B"), "bsqAddress must start with B"); - reimbursementProposal.getAddress(); // throws AddressFormatException if wrong address - - Coin requestedBsq = reimbursementProposal.getRequestedBsq(); - int chainHeight = getBlockHeight(proposal); - Coin maxReimbursementRequestAmount = ReimbursementConsensus.getMaxReimbursementRequestAmount(daoStateService, chainHeight); - checkArgument(requestedBsq.compareTo(maxReimbursementRequestAmount) <= 0, - "Requested BSQ must not exceed " + (maxReimbursementRequestAmount.value / 100L) + " BSQ"); - Coin minReimbursementRequestAmount = ReimbursementConsensus.getMinReimbursementRequestAmount(daoStateService, chainHeight); - checkArgument(requestedBsq.compareTo(minReimbursementRequestAmount) >= 0, - "Requested BSQ must not be less than " + (minReimbursementRequestAmount.value / 100L) + " BSQ"); - } catch (ProposalValidationException e) { - throw e; - } catch (Throwable throwable) { - throw new ProposalValidationException(throwable); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetProposalFactory.java deleted file mode 100644 index 865700a9e7..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetProposalFactory.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.removeAsset; - -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.governance.proposal.BaseProposalFactory; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalWithTransaction; -import bisq.core.dao.governance.proposal.TxException; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.RemoveAssetProposal; - -import bisq.asset.Asset; - -import org.bitcoinj.core.InsufficientMoneyException; - -import javax.inject.Inject; - -import java.util.HashMap; - -import lombok.extern.slf4j.Slf4j; - -/** - * Creates RemoveAssetProposal and transaction. - */ -@Slf4j -public class RemoveAssetProposalFactory extends BaseProposalFactory { - private Asset asset; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public RemoveAssetProposalFactory(BsqWalletService bsqWalletService, - BtcWalletService btcWalletService, - DaoStateService daoStateService, - RemoveAssetValidator proposalValidator) { - super(bsqWalletService, - btcWalletService, - daoStateService, - proposalValidator); - } - - public ProposalWithTransaction createProposalWithTransaction(String name, - String link, - Asset asset) - throws ProposalValidationException, InsufficientMoneyException, TxException { - this.asset = asset; - - return super.createProposalWithTransaction(name, link); - } - - @Override - protected RemoveAssetProposal createProposalWithoutTxId() { - return new RemoveAssetProposal( - name, - link, - asset.getTickerSymbol(), - new HashMap<>()); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.java deleted file mode 100644 index 156e1753c8..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.removeAsset; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalValidator; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.Proposal; -import bisq.core.dao.state.model.governance.RemoveAssetProposal; - -import javax.inject.Inject; - -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkArgument; -import static org.apache.commons.lang3.Validate.notEmpty; - -/** - * Changes here can potentially break consensus! - */ -@Slf4j -public class RemoveAssetValidator extends ProposalValidator implements ConsensusCritical { - - @Inject - public RemoveAssetValidator(DaoStateService daoStateService, PeriodService periodService) { - super(daoStateService, periodService); - } - - @Override - public void validateDataFields(Proposal proposal) throws ProposalValidationException { - try { - super.validateDataFields(proposal); - - RemoveAssetProposal removeAssetProposal = (RemoveAssetProposal) proposal; - notEmpty(removeAssetProposal.getTickerSymbol(), "TickerSymbol must not be empty"); - - // We want to avoid that someone causes damage by inserting a super long string. Real ticker symbols - // are usually very short but we don't want to add additional restrictions here. - checkArgument(removeAssetProposal.getTickerSymbol().length() <= 100, "TickerSymbol must not exceed 100 chars"); - } catch (ProposalValidationException e) { - throw e; - } catch (Throwable throwable) { - throw new ProposalValidationException(throwable); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/role/RoleProposalFactory.java b/core/src/main/java/bisq/core/dao/governance/proposal/role/RoleProposalFactory.java deleted file mode 100644 index 4211b3d661..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/role/RoleProposalFactory.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.role; - -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.governance.proposal.BaseProposalFactory; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalWithTransaction; -import bisq.core.dao.governance.proposal.TxException; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.Role; -import bisq.core.dao.state.model.governance.RoleProposal; - -import org.bitcoinj.core.InsufficientMoneyException; - -import javax.inject.Inject; - -import java.util.HashMap; - -import lombok.extern.slf4j.Slf4j; - -/** - * Creates RoleProposal and transaction. - */ -@Slf4j -public class RoleProposalFactory extends BaseProposalFactory { - private Role role; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public RoleProposalFactory(BsqWalletService bsqWalletService, - BtcWalletService btcWalletService, - DaoStateService daoStateService, - RoleValidator proposalValidator) { - super(bsqWalletService, - btcWalletService, - daoStateService, - proposalValidator); - } - - public ProposalWithTransaction createProposalWithTransaction(Role role) - throws ProposalValidationException, InsufficientMoneyException, TxException { - this.role = role; - - return super.createProposalWithTransaction(role.getName(), role.getLink()); - } - - @Override - protected RoleProposal createProposalWithoutTxId() { - return new RoleProposal(role, new HashMap<>()); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/role/RoleValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/role/RoleValidator.java deleted file mode 100644 index 9f2ff334eb..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/role/RoleValidator.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.role; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalValidator; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.Proposal; -import bisq.core.dao.state.model.governance.RoleProposal; - -import javax.inject.Inject; - -import lombok.extern.slf4j.Slf4j; - -import static org.apache.commons.lang3.Validate.notNull; - -/** - * Changes here can potentially break consensus! - */ -@Slf4j -public class RoleValidator extends ProposalValidator implements ConsensusCritical { - - @Inject - public RoleValidator(DaoStateService daoStateService, PeriodService periodService) { - super(daoStateService, periodService); - } - - @Override - public void validateDataFields(Proposal proposal) throws ProposalValidationException { - try { - super.validateDataFields(proposal); - - RoleProposal roleProposal = (RoleProposal) proposal; - notNull(roleProposal.getRole(), "Bonded role must not be null"); - } catch (Throwable throwable) { - throw new ProposalValidationException(throwable); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/storage/appendonly/ProposalPayload.java b/core/src/main/java/bisq/core/dao/governance/proposal/storage/appendonly/ProposalPayload.java deleted file mode 100644 index 33386f82a9..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/storage/appendonly/ProposalPayload.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.storage.appendonly; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.state.model.governance.Proposal; - -import bisq.network.p2p.storage.payload.PersistableNetworkPayload; - -import bisq.common.crypto.Hash; -import bisq.common.util.Utilities; - -import com.google.protobuf.ByteString; - -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -/** - * Wrapper for proposal to be stored in the append-only ProposalStore storage. - * Data size: with typical proposal about 272 bytes - */ -@Immutable -@Slf4j -@Value -public class ProposalPayload implements PersistableNetworkPayload, ConsensusCritical { - private final Proposal proposal; - protected final byte[] hash; // 20 byte - - public ProposalPayload(Proposal proposal) { - this(proposal, Hash.getRipemd160hash(proposal.toProtoMessage().toByteArray())); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private ProposalPayload(Proposal proposal, byte[] hash) { - this.proposal = proposal; - this.hash = hash; - } - - private protobuf.ProposalPayload.Builder getProposalBuilder() { - return protobuf.ProposalPayload.newBuilder() - .setProposal(proposal.toProtoMessage()) - .setHash(ByteString.copyFrom(hash)); - } - - @Override - public protobuf.PersistableNetworkPayload toProtoMessage() { - return protobuf.PersistableNetworkPayload.newBuilder() - .setProposalPayload(getProposalBuilder()) - .build(); - } - - public protobuf.ProposalPayload toProtoProposalPayload() { - return getProposalBuilder().build(); - } - - - public static ProposalPayload fromProto(protobuf.ProposalPayload proto) { - return new ProposalPayload(Proposal.fromProto(proto.getProposal()), - proto.getHash().toByteArray()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PersistableNetworkPayload - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public boolean verifyHashSize() { - return hash.length == 20; - } - - @Override - public byte[] getHash() { - return hash; - } - - @Override - public String toString() { - return "ProposalPayload{" + - "\n proposal=" + proposal + - ",\n hash=" + Utilities.bytesAsHexString(hash) + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/storage/appendonly/ProposalStorageService.java b/core/src/main/java/bisq/core/dao/governance/proposal/storage/appendonly/ProposalStorageService.java deleted file mode 100644 index b6f2022201..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/storage/appendonly/ProposalStorageService.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.storage.appendonly; - -import bisq.network.p2p.storage.P2PDataStorage; -import bisq.network.p2p.storage.payload.PersistableNetworkPayload; -import bisq.network.p2p.storage.persistence.MapStoreService; - -import bisq.common.config.Config; -import bisq.common.persistence.PersistenceManager; - -import javax.inject.Inject; -import javax.inject.Named; - -import java.io.File; - -import java.util.Map; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class ProposalStorageService extends MapStoreService { - private static final String FILE_NAME = "ProposalStore"; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public ProposalStorageService(@Named(Config.STORAGE_DIR) File storageDir, - PersistenceManager persistenceManager) { - super(storageDir, persistenceManager); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public String getFileName() { - return FILE_NAME; - } - - @Override - protected void initializePersistenceManager() { - persistenceManager.initialize(store, PersistenceManager.Source.NETWORK); - } - - @Override - public Map getMap() { - return store.getMap(); - } - - @Override - public boolean canHandle(PersistableNetworkPayload payload) { - return payload instanceof ProposalPayload; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Protected - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected ProposalStore createStore() { - return new ProposalStore(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/storage/appendonly/ProposalStore.java b/core/src/main/java/bisq/core/dao/governance/proposal/storage/appendonly/ProposalStore.java deleted file mode 100644 index 23c9b3732e..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/storage/appendonly/ProposalStore.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.storage.appendonly; - -import bisq.network.p2p.storage.persistence.PersistableNetworkPayloadStore; - -import com.google.protobuf.Message; - -import java.util.List; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - - -/** - * We store only the payload in the PB file to save disc space. The hash of the payload can be created anyway and - * is only used as key in the map. So we have a hybrid data structure which is represented as list in the protobuffer - * definition and provide a hashMap for the domain access. - */ -@Slf4j -public class ProposalStore extends PersistableNetworkPayloadStore { - - ProposalStore() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private ProposalStore(List list) { - super(list); - } - - public Message toProtoMessage() { - return protobuf.PersistableEnvelope.newBuilder() - .setProposalStore(getBuilder()) - .build(); - } - - private protobuf.ProposalStore.Builder getBuilder() { - final List protoList = map.values().stream() - .map(payload -> (ProposalPayload) payload) - .map(ProposalPayload::toProtoProposalPayload) - .collect(Collectors.toList()); - return protobuf.ProposalStore.newBuilder().addAllItems(protoList); - } - - public static ProposalStore fromProto(protobuf.ProposalStore proto) { - List list = proto.getItemsList().stream() - .map(ProposalPayload::fromProto).collect(Collectors.toList()); - return new ProposalStore(list); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalPayload.java b/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalPayload.java deleted file mode 100644 index 71b2c3d798..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalPayload.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.storage.temp; - -import bisq.core.dao.state.model.governance.Proposal; - -import bisq.network.p2p.storage.payload.ExpirablePayload; -import bisq.network.p2p.storage.payload.ProcessOncePersistableNetworkPayload; -import bisq.network.p2p.storage.payload.ProtectedStoragePayload; - -import bisq.common.crypto.Sig; -import bisq.common.proto.persistable.PersistablePayload; -import bisq.common.util.CollectionUtils; -import bisq.common.util.ExtraDataMapValidator; - -import com.google.protobuf.ByteString; - -import java.security.PublicKey; - -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import lombok.AccessLevel; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.experimental.FieldDefaults; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** - * TempProposalPayload is wrapper for proposal sent over wire as well as it gets persisted. - * Data size: about 1.245 bytes (pubKey makes it big) - */ -@Immutable -@Slf4j -@Getter -@EqualsAndHashCode -@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) -public class TempProposalPayload implements ProcessOncePersistableNetworkPayload, ProtectedStoragePayload, - ExpirablePayload, PersistablePayload { - // We keep data 2 months to be safe if we increase durations of cycle. Also give a bit more resilience in case - // of any issues with the append-only data store - public static final long TTL = TimeUnit.DAYS.toMillis(60); - - protected final Proposal proposal; - protected final byte[] ownerPubKeyEncoded; - - // Should be only used in emergency case if we need to add data but do not want to break backward compatibility - // at the P2P network storage checks. The hash of the object will be used to verify if the data is valid. Any new - // field in a class would break that hash and therefore break the storage mechanism. - @Nullable - protected final Map extraDataMap; - - // Used just for caching. Don't persist. - private final transient PublicKey ownerPubKey; - - public TempProposalPayload(Proposal proposal, - PublicKey ownerPublicKey) { - this(proposal, Sig.getPublicKeyBytes(ownerPublicKey), null); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private TempProposalPayload(Proposal proposal, - byte[] ownerPubPubKeyEncoded, - @Nullable Map extraDataMap) { - this.proposal = proposal; - this.ownerPubKeyEncoded = ownerPubPubKeyEncoded; - this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); - - ownerPubKey = Sig.getPublicKeyFromBytes(ownerPubKeyEncoded); - } - - private protobuf.TempProposalPayload.Builder getTempProposalPayloadBuilder() { - final protobuf.TempProposalPayload.Builder builder = protobuf.TempProposalPayload.newBuilder() - .setProposal(proposal.getProposalBuilder()) - .setOwnerPubKeyEncoded(ByteString.copyFrom(ownerPubKeyEncoded)); - Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData); - return builder; - } - - @Override - public protobuf.StoragePayload toProtoMessage() { - return protobuf.StoragePayload.newBuilder().setTempProposalPayload(getTempProposalPayloadBuilder()).build(); - } - - public static TempProposalPayload fromProto(protobuf.TempProposalPayload proto) { - return new TempProposalPayload(Proposal.fromProto(proto.getProposal()), - proto.getOwnerPubKeyEncoded().toByteArray(), - CollectionUtils.isEmpty(proto.getExtraDataMap()) ? null : proto.getExtraDataMap()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // TempStoragePayload - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public PublicKey getOwnerPubKey() { - return ownerPubKey; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // ExpirablePayload - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public long getTTL() { - return TTL; - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalStorageService.java b/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalStorageService.java deleted file mode 100644 index b8af27c672..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalStorageService.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.storage.temp; - -import bisq.network.p2p.storage.P2PDataStorage; -import bisq.network.p2p.storage.payload.ProtectedStorageEntry; -import bisq.network.p2p.storage.persistence.MapStoreService; - -import bisq.common.config.Config; -import bisq.common.persistence.PersistenceManager; - -import javax.inject.Inject; -import javax.inject.Named; - -import java.io.File; - -import java.util.Map; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class TempProposalStorageService extends MapStoreService { - private static final String FILE_NAME = "TempProposalStore"; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public TempProposalStorageService(@Named(Config.STORAGE_DIR) File storageDir, - PersistenceManager persistenceManager) { - super(storageDir, persistenceManager); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public String getFileName() { - return FILE_NAME; - } - - @Override - protected void initializePersistenceManager() { - persistenceManager.initialize(store, PersistenceManager.Source.NETWORK); - } - - @Override - public Map getMap() { - return store.getMap(); - } - - @Override - public boolean canHandle(ProtectedStorageEntry entry) { - return entry.getProtectedStoragePayload() instanceof TempProposalPayload; - } - - @Override - protected void readFromResources(String postFix, Runnable completeHandler) { - // We do not have a resource file for that store, so we just call the readStore method instead. - readStore(persisted -> completeHandler.run()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Protected - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected TempProposalStore createStore() { - return new TempProposalStore(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalStore.java b/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalStore.java deleted file mode 100644 index 6ff483eae2..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalStore.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.storage.temp; - -import bisq.network.p2p.storage.P2PDataStorage; -import bisq.network.p2p.storage.payload.ProtectedStorageEntry; - -import bisq.common.proto.network.NetworkProtoResolver; -import bisq.common.proto.persistable.PersistableEnvelope; - -import com.google.protobuf.Message; - -import javax.inject.Inject; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - - -/** - * We store only the payload in the PB file to save disc space. The hash of the payload can be created anyway and - * is only used as key in the map. So we have a hybrid data structure which is represented as list in the protobuffer - * definition and provide a hashMap for the domain access. - */ -@Slf4j -public class TempProposalStore implements PersistableEnvelope { - @Getter - private final Map map = new ConcurrentHashMap<>(); - - @Inject - TempProposalStore() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private TempProposalStore(List list) { - list.forEach(entry -> map.put(P2PDataStorage.get32ByteHashAsByteArray(entry.getProtectedStoragePayload()), entry)); - } - - public Message toProtoMessage() { - return protobuf.PersistableEnvelope.newBuilder() - .setTempProposalStore(getBuilder()) - .build(); - } - - private protobuf.TempProposalStore.Builder getBuilder() { - final List protoList = map.values().stream() - .map(ProtectedStorageEntry::toProtectedStorageEntry) - .collect(Collectors.toList()); - return protobuf.TempProposalStore.newBuilder().addAllItems(protoList); - } - - public static TempProposalStore fromProto(protobuf.TempProposalStore proto, NetworkProtoResolver networkProtoResolver) { - List list = proto.getItemsList().stream() - .map(entry -> ProtectedStorageEntry.fromProto(entry, networkProtoResolver)) - .collect(Collectors.toList()); - return new TempProposalStore(list); - } - - public boolean containsKey(P2PDataStorage.ByteArray hash) { - return map.containsKey(hash); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/MissingDataRequestService.java b/core/src/main/java/bisq/core/dao/governance/voteresult/MissingDataRequestService.java deleted file mode 100644 index 6d5a5da517..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/MissingDataRequestService.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.voteresult; - -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.governance.blindvote.BlindVoteListService; -import bisq.core.dao.governance.blindvote.network.RepublishGovernanceDataHandler; -import bisq.core.dao.governance.blindvote.storage.BlindVotePayload; -import bisq.core.dao.governance.proposal.ProposalService; -import bisq.core.dao.governance.proposal.storage.appendonly.ProposalPayload; - -import bisq.network.p2p.P2PService; - -import bisq.common.UserThread; - -import javax.inject.Inject; - -import javafx.collections.ObservableList; - -import java.util.Random; -import java.util.concurrent.TimeUnit; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class MissingDataRequestService implements DaoSetupService { - private final RepublishGovernanceDataHandler republishGovernanceDataHandler; - private final BlindVoteListService blindVoteListService; - private final ProposalService proposalService; - private final P2PService p2PService; - private boolean reRepublishAllGovernanceDataDone; - - @Inject - public MissingDataRequestService(RepublishGovernanceDataHandler republishGovernanceDataHandler, - BlindVoteListService blindVoteListService, - ProposalService proposalService, - P2PService p2PService) { - this.republishGovernanceDataHandler = republishGovernanceDataHandler; - this.blindVoteListService = blindVoteListService; - this.proposalService = proposalService; - this.p2PService = p2PService; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - } - - @Override - public void start() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void sendRepublishRequest() { - republishGovernanceDataHandler.sendRepublishRequest(); - } - - // Can be triggered with shortcut ctrl+h, cmd+h or alt+h - public void reRepublishAllGovernanceData() { - // We only want to do it once in case we would get flooded with requests. - if (!reRepublishAllGovernanceDataDone) { - reRepublishAllGovernanceDataDone = true; - ObservableList proposalPayloads = proposalService.getProposalPayloads(); - proposalPayloads.forEach(proposalPayload -> { - // We want a random delay between 0.1 and 300 sec. depending on the number of items. - // We send all proposals including those from old cycles. - int delay = Math.max(100, Math.min(300_000, new Random().nextInt(proposalPayloads.size() * 1000))); - UserThread.runAfter(() -> { - boolean success = p2PService.addPersistableNetworkPayload(proposalPayload, true); - String txId = proposalPayload.getProposal().getTxId(); - if (success) { - log.debug("We received a RepublishGovernanceDataRequest and re-published a proposalPayload to " + - "the P2P network as append only data. proposalTxId={}", txId); - } else { - log.error("Adding of proposalPayload to P2P network failed. proposalTxId={}", txId); - } - }, delay, TimeUnit.MILLISECONDS); - }); - - ObservableList blindVotePayloads = blindVoteListService.getBlindVotePayloads(); - blindVotePayloads.forEach(blindVotePayload -> { - // We want a random delay between 0.1 and 300 sec. depending on the number of items. - // We send all blindVotes including those from old cycles. - int delay = Math.max(100, Math.min(300_000, new Random().nextInt(blindVotePayloads.size() * 1000))); - UserThread.runAfter(() -> { - boolean success = p2PService.addPersistableNetworkPayload(blindVotePayload, true); - String txId = blindVotePayload.getBlindVote().getTxId(); - if (success) { - log.debug("We received a RepublishGovernanceDataRequest and re-published a blindVotePayload to " + - "the P2P network as append only data. blindVoteTxId={}", txId); - } else { - log.error("Adding of blindVotePayload to P2P network failed. blindVoteTxId={}", txId); - } - }, delay, TimeUnit.MILLISECONDS); - }); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultConsensus.java b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultConsensus.java deleted file mode 100644 index 672e4bf75f..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultConsensus.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.voteresult; - -import bisq.core.dao.governance.blindvote.VoteWithProposalTxIdList; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxInput; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.blockchain.TxOutputType; -import bisq.core.dao.state.model.blockchain.TxType; -import bisq.core.dao.state.model.governance.DaoPhase; - -import bisq.common.crypto.Encryption; -import bisq.common.util.Utilities; - -import javax.crypto.SecretKey; - -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.Optional; - -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -import static com.google.common.base.Preconditions.checkArgument; - -@Slf4j -public class VoteResultConsensus { - public static boolean hasOpReturnDataValidLength(byte[] opReturnData) { - return opReturnData.length == 38; - } - - // Hash of the list of Blind votes is 20 bytes after version and type bytes - public static byte[] getHashOfBlindVoteList(byte[] opReturnData) { - return Arrays.copyOfRange(opReturnData, 2, 22); - } - - public static VoteWithProposalTxIdList decryptVotes(byte[] encryptedVotes, SecretKey secretKey) - throws VoteResultException.DecryptionException { - try { - byte[] decrypted = Encryption.decrypt(encryptedVotes, secretKey); - return VoteWithProposalTxIdList.getVoteWithProposalTxIdListFromBytes(decrypted); - } catch (Throwable t) { - throw new VoteResultException.DecryptionException(t); - } - } - - // We compare first by stake and in case we have multiple entries with same stake we use the - // hex encoded hashOfProposalList for comparison - @Nullable - public static byte[] getMajorityHash(List hashWithStakeList) - throws VoteResultException.ValidationException, VoteResultException.ConsensusException { - try { - checkArgument(!hashWithStakeList.isEmpty(), "hashWithStakeList must not be empty"); - } catch (Throwable t) { - throw new VoteResultException.ValidationException(t); - } - - hashWithStakeList.sort(Comparator.comparingLong(VoteResultService.HashWithStake::getStake).reversed() - .thenComparing(hashWithStake -> Utilities.encodeToHex(hashWithStake.getHash()))); - - // If there are conflicting data views (multiple hashes) we only consider the voting round as valid if - // the majority is a super majority with > 80%. - if (hashWithStakeList.size() > 1) { - long stakeOfAll = hashWithStakeList.stream().mapToLong(VoteResultService.HashWithStake::getStake).sum(); - long stakeOfFirst = hashWithStakeList.get(0).getStake(); - if ((double) stakeOfFirst / (double) stakeOfAll < 0.8) { - log.warn("The winning data view has less then 80% of the " + - "total stake of all data views. We consider the voting cycle as invalid if the " + - "winning data view does not reach a super majority. hashWithStakeList={}", hashWithStakeList); - throw new VoteResultException.ConsensusException("The winning data view has less then 80% of the " + - "total stake of all data views. We consider the voting cycle as invalid if the " + - "winning data view does not reach a super majority."); - } - } - return hashWithStakeList.get(0).getHash(); - } - - // Key is stored after version and type bytes and list of Blind votes. It has 16 bytes - public static SecretKey getSecretKey(byte[] opReturnData) { - byte[] secretKeyAsBytes = Arrays.copyOfRange(opReturnData, 22, 38); - return Encryption.getSecretKeyFromBytes(secretKeyAsBytes); - } - - public static TxOutput getConnectedBlindVoteStakeOutput(Tx voteRevealTx, DaoStateService daoStateService) - throws VoteResultException.ValidationException { - try { - // We use the stake output of the blind vote tx as first input - TxInput stakeTxInput = voteRevealTx.getTxInputs().get(0); - Optional optionalBlindVoteStakeOutput = daoStateService.getConnectedTxOutput(stakeTxInput); - checkArgument(optionalBlindVoteStakeOutput.isPresent(), "blindVoteStakeOutput must be present"); - TxOutput blindVoteStakeOutput = optionalBlindVoteStakeOutput.get(); - if (blindVoteStakeOutput.getTxOutputType() != TxOutputType.BLIND_VOTE_LOCK_STAKE_OUTPUT) { - String message = "blindVoteStakeOutput must be of type BLIND_VOTE_LOCK_STAKE_OUTPUT but is " + - blindVoteStakeOutput.getTxOutputType(); - log.warn(message + ". VoteRevealTx=" + voteRevealTx); - throw new VoteResultException.ValidationException(message + ". VoteRevealTxId=" + voteRevealTx.getId()); - } - return blindVoteStakeOutput; - } catch (VoteResultException.ValidationException t) { - throw t; - } catch (Throwable t) { - throw new VoteResultException.ValidationException(t); - } - } - - public static void validateBlindVoteTx(String blindVoteTxId, DaoStateService daoStateService, - PeriodService periodService, int chainHeight) - throws VoteResultException.ValidationException { - try { - Optional optionalBlindVoteTx = daoStateService.getTx(blindVoteTxId); - - checkArgument(optionalBlindVoteTx.isPresent(), "blindVoteTx with txId " + - blindVoteTxId + " not found."); - - Tx blindVoteTx = optionalBlindVoteTx.get(); - Optional optionalTxType = daoStateService.getOptionalTxType(blindVoteTx.getId()); - - checkArgument(optionalTxType.isPresent(), "optionalTxType must be present" + - ". blindVoteTxId=" + blindVoteTx.getId()); - - checkArgument(optionalTxType.get() == TxType.BLIND_VOTE, - "blindVoteTx must have type BLIND_VOTE but is " + optionalTxType.get() + - ". blindVoteTxId=" + blindVoteTx.getId()); - - checkArgument(periodService.isTxInCorrectCycle(blindVoteTx.getBlockHeight(), chainHeight), - "blindVoteTx is not in correct cycle. blindVoteTx.getBlockHeight()=" - + blindVoteTx.getBlockHeight() + ". chainHeight=" + chainHeight + - ". blindVoteTxId=" + blindVoteTx.getId()); - - checkArgument(periodService.isInPhase(blindVoteTx.getBlockHeight(), DaoPhase.Phase.BLIND_VOTE), - "blindVoteTx is not in BLIND_VOTE phase. blindVoteTx.getBlockHeight()=" - + blindVoteTx.getBlockHeight() + ". blindVoteTxId=" + blindVoteTx.getId()); - } catch (Throwable t) { - throw new VoteResultException.ValidationException(t); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultException.java b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultException.java deleted file mode 100644 index 505c4e7655..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultException.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.voteresult; - -import bisq.core.dao.state.model.governance.Ballot; -import bisq.core.dao.state.model.governance.Cycle; - -import java.util.List; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Value; - -@EqualsAndHashCode(callSuper = true) -public class VoteResultException extends Exception { - @Getter - private final int heightOfFirstBlockInCycle; - - VoteResultException(Cycle cycle, Throwable cause) { - super(cause); - this.heightOfFirstBlockInCycle = cycle.getHeightOfFirstBlock(); - } - - @Override - public String toString() { - return "VoteResultException{" + - "\n heightOfFirstBlockInCycle=" + heightOfFirstBlockInCycle + - "\n} " + super.toString(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Static sub classes - /////////////////////////////////////////////////////////////////////////////////////////// - - @EqualsAndHashCode(callSuper = true) - public static class ConsensusException extends Exception { - - ConsensusException(String message) { - super(message); - } - - @Override - public String toString() { - return "ConsensusException{" + - "\n} " + super.toString(); - } - } - - @EqualsAndHashCode(callSuper = true) - public static class ValidationException extends Exception { - - ValidationException(Throwable cause) { - super("Validation of vote result failed.", cause); - } - - public ValidationException(String message) { - super(message); - } - - @Override - public String toString() { - return "VoteResultException{" + - "\n cause=" + getCause() + - "\n} " + super.toString(); - } - } - - @EqualsAndHashCode(callSuper = true) - static abstract class MissingDataException extends Exception { - private MissingDataException(String message) { - super(message); - } - } - - @EqualsAndHashCode(callSuper = true) - @Value - public static class MissingBallotException extends MissingDataException { - private List existingBallots; - private List proposalTxIdsOfMissingBallots; - - MissingBallotException(List existingBallots, List proposalTxIdsOfMissingBallots) { - super("Missing ballots. proposalTxIdsOfMissingBallots=" + proposalTxIdsOfMissingBallots); - this.existingBallots = existingBallots; - this.proposalTxIdsOfMissingBallots = proposalTxIdsOfMissingBallots; - } - } - - - @EqualsAndHashCode(callSuper = true) - @Value - public static class DecryptionException extends Exception { - public DecryptionException(Throwable cause) { - super(cause); - } - - @Override - public String toString() { - return "DecryptionException{" + - "\n} " + super.toString(); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java deleted file mode 100644 index ddf402d323..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java +++ /dev/null @@ -1,821 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.voteresult; - -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.governance.ballot.BallotListService; -import bisq.core.dao.governance.blindvote.BlindVote; -import bisq.core.dao.governance.blindvote.BlindVoteConsensus; -import bisq.core.dao.governance.blindvote.BlindVoteListService; -import bisq.core.dao.governance.blindvote.VoteWithProposalTxId; -import bisq.core.dao.governance.blindvote.VoteWithProposalTxIdList; -import bisq.core.dao.governance.merit.MeritConsensus; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.IssuanceProposal; -import bisq.core.dao.governance.proposal.ProposalListPresentation; -import bisq.core.dao.governance.voteresult.issuance.IssuanceService; -import bisq.core.dao.governance.votereveal.VoteRevealConsensus; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.governance.Ballot; -import bisq.core.dao.state.model.governance.BallotList; -import bisq.core.dao.state.model.governance.ChangeParamProposal; -import bisq.core.dao.state.model.governance.ConfiscateBondProposal; -import bisq.core.dao.state.model.governance.Cycle; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.dao.state.model.governance.DecryptedBallotsWithMerits; -import bisq.core.dao.state.model.governance.EvaluatedProposal; -import bisq.core.dao.state.model.governance.MeritList; -import bisq.core.dao.state.model.governance.ParamChange; -import bisq.core.dao.state.model.governance.Proposal; -import bisq.core.dao.state.model.governance.ProposalVoteResult; -import bisq.core.dao.state.model.governance.RemoveAssetProposal; -import bisq.core.dao.state.model.governance.Role; -import bisq.core.dao.state.model.governance.RoleProposal; -import bisq.core.dao.state.model.governance.Vote; -import bisq.core.locale.CurrencyUtil; - -import bisq.network.p2p.storage.P2PDataStorage; - -import bisq.common.util.MathUtils; -import bisq.common.util.PermutationUtil; -import bisq.common.util.Utilities; - -import javax.inject.Inject; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; - -import javax.crypto.SecretKey; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import org.jetbrains.annotations.NotNull; - -import javax.annotation.Nullable; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Calculates the result of the voting at the VoteResult period. - * We take all data from the bitcoin domain and additionally the blindVote list which we received from the p2p network. - * Due to eventual consistency we use the hash of the data view of the voters (majority by merit+stake). If our local - * blindVote list contains the blindVotes used by the voters we can calculate the result, otherwise we need to request - * the missing blindVotes from the network. - */ -@Slf4j -public class VoteResultService implements DaoStateListener, DaoSetupService { - private final ProposalListPresentation proposalListPresentation; - private final DaoStateService daoStateService; - private final PeriodService periodService; - private final BallotListService ballotListService; - private final BlindVoteListService blindVoteListService; - private final IssuanceService issuanceService; - private final MissingDataRequestService missingDataRequestService; - @Getter - private final ObservableList voteResultExceptions = FXCollections.observableArrayList(); - @Getter - private Set invalidDecryptedBallotsWithMeritItems = new HashSet<>(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public VoteResultService(ProposalListPresentation proposalListPresentation, - DaoStateService daoStateService, - PeriodService periodService, - BallotListService ballotListService, - BlindVoteListService blindVoteListService, - IssuanceService issuanceService, - MissingDataRequestService missingDataRequestService) { - this.proposalListPresentation = proposalListPresentation; - this.daoStateService = daoStateService; - this.periodService = periodService; - this.ballotListService = ballotListService; - this.blindVoteListService = blindVoteListService; - this.issuanceService = issuanceService; - this.missingDataRequestService = missingDataRequestService; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - daoStateService.addDaoStateListener(this); - } - - @Override - public void start() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockComplete(Block block) { - maybeCalculateVoteResult(block.getHeight()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void maybeCalculateVoteResult(int chainHeight) { - if (isInVoteResultPhase(chainHeight)) { - log.info("CalculateVoteResult at chainHeight={}", chainHeight); - Cycle currentCycle = periodService.getCurrentCycle(); - checkNotNull(currentCycle, "currentCycle must not be null"); - long startTs = System.currentTimeMillis(); - - Set decryptedBallotsWithMeritsSet = getDecryptedBallotsWithMeritsSet(chainHeight); - if (!decryptedBallotsWithMeritsSet.isEmpty()) { - // From the decryptedBallotsWithMeritsSet we create a map with the hash of the blind vote list as key and the - // aggregated stake as value (no merit as that is part of the P2P network data and might lead to inconsistency). - // That map is used for calculating the majority of the blind vote lists. - // There might be conflicting versions due the eventually consistency of the P2P network (if some blind - // vote payloads do not arrive at all voters) which would lead to consensus failure in the result calculation. - // To solve that problem we will only consider the blind votes valid which are matching the majority hash. - // If multiple data views would have the same stake we sort additionally by the hex value of the - // blind vote hash and use the first one in the sorted list as winner. - // A node which has a local blindVote list which does not match the winner data view will try - // permutations of his local list and if that does not succeed he need to recover it's - // local blindVote list by requesting the correct list from other peers. - Map stakeByHashOfBlindVoteListMap = getStakeByHashOfBlindVoteListMap(decryptedBallotsWithMeritsSet); - - try { - // Get majority hash - byte[] majorityBlindVoteListHash = calculateMajorityBlindVoteListHash(stakeByHashOfBlindVoteListMap); - - // Is our local list matching the majority data view? - Optional> optionalBlindVoteListMatchingMajorityHash = findBlindVoteListMatchingMajorityHash(majorityBlindVoteListHash); - if (optionalBlindVoteListMatchingMajorityHash.isPresent()) { - List blindVoteList = optionalBlindVoteListMatchingMajorityHash.get(); - log.debug("blindVoteListMatchingMajorityHash: {}", - blindVoteList.stream() - .map(e -> "blindVoteTxId=" + e.getTxId() + ", Stake=" + e.getStake()) - .collect(Collectors.toList())); - - Set blindVoteTxIdSet = blindVoteList.stream().map(BlindVote::getTxId).collect(Collectors.toSet()); - // We need to filter out result list according to the majority hash list - Set filteredDecryptedBallotsWithMeritsSet = decryptedBallotsWithMeritsSet.stream() - .filter(decryptedBallotsWithMerits -> { - boolean contains = blindVoteTxIdSet.contains(decryptedBallotsWithMerits.getBlindVoteTxId()); - if (!contains) { - invalidDecryptedBallotsWithMeritItems.add(decryptedBallotsWithMerits); - } - return contains; - }) - .collect(Collectors.toSet()); - - // Only if we have all blind vote payloads and know the right list matching the majority we add - // it to our state. Otherwise we are not in consensus with the network. - daoStateService.addDecryptedBallotsWithMeritsSet(filteredDecryptedBallotsWithMeritsSet); - - Set evaluatedProposals = getEvaluatedProposals(filteredDecryptedBallotsWithMeritsSet, chainHeight); - daoStateService.addEvaluatedProposalSet(evaluatedProposals); - Set acceptedEvaluatedProposals = getAcceptedEvaluatedProposals(evaluatedProposals); - applyAcceptedProposals(acceptedEvaluatedProposals, chainHeight); - log.info("processAllVoteResults completed"); - } else { - String msg = "We could not find a list which matches the majority so we cannot calculate the vote result. Please restart and resync the DAO state."; - log.warn(msg); - voteResultExceptions.add(new VoteResultException(currentCycle, new Exception(msg))); - } - } catch (Throwable e) { - log.warn(e.toString()); - log.warn("decryptedBallotsWithMeritsSet " + decryptedBallotsWithMeritsSet); - e.printStackTrace(); - voteResultExceptions.add(new VoteResultException(currentCycle, e)); - } - } else { - log.info("There have not been any votes in that cycle. chainHeight={}", chainHeight); - } - log.info("Evaluating vote result took {} ms", System.currentTimeMillis() - startTs); - } - } - - private Set getDecryptedBallotsWithMeritsSet(int chainHeight) { - // We want all voteRevealTxOutputs which are in current cycle we are processing. - return daoStateService.getVoteRevealOpReturnTxOutputs().stream() - .filter(txOutput -> periodService.isTxInCorrectCycle(txOutput.getTxId(), chainHeight)) - .filter(this::isInVoteRevealPhase) - .map(txOutputToDecryptedBallotsWithMerits(chainHeight)) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); - } - - private boolean isInVoteRevealPhase(TxOutput txOutput) { - String voteRevealTx = txOutput.getTxId(); - boolean txInPhase = periodService.isTxInPhase(voteRevealTx, DaoPhase.Phase.VOTE_REVEAL); - if (!txInPhase) - log.warn("We got a vote reveal tx with was not in the correct phase of that cycle. voteRevealTxId={}", voteRevealTx); - - return txInPhase; - } - - @NotNull - private Function txOutputToDecryptedBallotsWithMerits(int chainHeight) { - return voteRevealTxOutput -> { - String voteRevealTxId = voteRevealTxOutput.getTxId(); - Cycle currentCycle = periodService.getCurrentCycle(); - checkNotNull(currentCycle, "currentCycle must not be null"); - try { - byte[] voteRevealOpReturnData = voteRevealTxOutput.getOpReturnData(); - Optional optionalVoteRevealTx = daoStateService.getTx(voteRevealTxId); - checkArgument(optionalVoteRevealTx.isPresent(), "optionalVoteRevealTx must be present. voteRevealTxId=" + voteRevealTxId); - Tx voteRevealTx = optionalVoteRevealTx.get(); - - // Here we use only blockchain tx data so far so we don't have risks with missing P2P network data. - // We work back from the voteRealTx to the blindVoteTx to calculate the majority hash. From that we - // will derive the blind vote list we will use for result calculation and as it was based on - // blockchain data it will be consistent for all peers independent on their P2P network data state. - TxOutput blindVoteStakeOutput = VoteResultConsensus.getConnectedBlindVoteStakeOutput(voteRevealTx, daoStateService); - String blindVoteTxId = blindVoteStakeOutput.getTxId(); - - // If we get a blind vote tx which was published too late we ignore it. - if (!periodService.isTxInPhaseAndCycle(blindVoteTxId, DaoPhase.Phase.BLIND_VOTE, chainHeight)) { - log.warn("We got a blind vote tx with was not in the correct phase and/or cycle. " + - "We ignore that vote reveal and blind vote tx. voteRevealTx={}, blindVoteTxId={}", - voteRevealTx, blindVoteTxId); - return null; - } - - VoteResultConsensus.validateBlindVoteTx(blindVoteTxId, daoStateService, periodService, chainHeight); - - byte[] hashOfBlindVoteList = VoteResultConsensus.getHashOfBlindVoteList(voteRevealOpReturnData); - long blindVoteStake = blindVoteStakeOutput.getValue(); - - List blindVoteList = BlindVoteConsensus.getSortedBlindVoteListOfCycle(blindVoteListService); - Optional optionalBlindVote = blindVoteList.stream() - .filter(blindVote -> blindVote.getTxId().equals(blindVoteTxId)) - .findAny(); - if (optionalBlindVote.isPresent()) { - return getDecryptedBallotsWithMerits(voteRevealTxId, currentCycle, voteRevealOpReturnData, - blindVoteTxId, hashOfBlindVoteList, blindVoteStake, optionalBlindVote.get()); - } - - // We are missing P2P network data - return getEmptyDecryptedBallotsWithMerits(voteRevealTxId, blindVoteTxId, hashOfBlindVoteList, - blindVoteStake); - } catch (Throwable e) { - log.error("Could not create DecryptedBallotsWithMerits from voteRevealTxId {} because of " + - "exception: {}", voteRevealTxId, e.toString()); - voteResultExceptions.add(new VoteResultException(currentCycle, e)); - return null; - } - }; - } - - @NotNull - private DecryptedBallotsWithMerits getEmptyDecryptedBallotsWithMerits( - String voteRevealTxId, String blindVoteTxId, byte[] hashOfBlindVoteList, long blindVoteStake) { - log.warn("We have a blindVoteTx but we do not have the corresponding blindVote payload.\n" + - "That can happen if the blindVote item was not properly broadcast. " + - "We still add it to our result collection because it might be relevant for the majority " + - "hash by stake calculation. blindVoteTxId={}", blindVoteTxId); - - missingDataRequestService.sendRepublishRequest(); - - // We prefer to use an empty list here instead a null or optional value to avoid that - // client code need to handle nullable or optional values. - BallotList emptyBallotList = new BallotList(new ArrayList<>()); - MeritList emptyMeritList = new MeritList(new ArrayList<>()); - log.debug("Add entry to decryptedBallotsWithMeritsSet: blindVoteTxId={}, voteRevealTxId={}, " + - "blindVoteStake={}, ballotList={}", - blindVoteTxId, voteRevealTxId, blindVoteStake, emptyBallotList); - return new DecryptedBallotsWithMerits(hashOfBlindVoteList, blindVoteTxId, voteRevealTxId, - blindVoteStake, emptyBallotList, emptyMeritList); - } - - @Nullable - private DecryptedBallotsWithMerits getDecryptedBallotsWithMerits( - String voteRevealTxId, Cycle currentCycle, byte[] voteRevealOpReturnData, String blindVoteTxId, - byte[] hashOfBlindVoteList, long blindVoteStake, BlindVote blindVote) - throws VoteResultException.MissingBallotException { - SecretKey secretKey = VoteResultConsensus.getSecretKey(voteRevealOpReturnData); - try { - VoteWithProposalTxIdList voteWithProposalTxIdList = VoteResultConsensus.decryptVotes(blindVote.getEncryptedVotes(), secretKey); - MeritList meritList = MeritConsensus.decryptMeritList(blindVote.getEncryptedMeritList(), secretKey); - // We lookup for the proposals we have in our local list which match the txId from the - // voteWithProposalTxIdList and create a ballot list with the proposal and the vote from - // the voteWithProposalTxIdList - BallotList ballotList = createBallotList(voteWithProposalTxIdList); - log.debug("Add entry to decryptedBallotsWithMeritsSet: blindVoteTxId={}, voteRevealTxId={}, blindVoteStake={}, ballotList={}", - blindVoteTxId, voteRevealTxId, blindVoteStake, ballotList); - return new DecryptedBallotsWithMerits(hashOfBlindVoteList, blindVoteTxId, voteRevealTxId, blindVoteStake, ballotList, meritList); - } catch (VoteResultException.DecryptionException decryptionException) { - // We don't consider such vote reveal txs valid for the majority hash - // calculation and don't add it to our result collection - log.error("Could not decrypt blind vote. This vote reveal and blind vote will be ignored. " + - "VoteRevealTxId={}. DecryptionException={}", voteRevealTxId, decryptionException.toString()); - voteResultExceptions.add(new VoteResultException(currentCycle, decryptionException)); - return null; - } - } - - private BallotList createBallotList(VoteWithProposalTxIdList voteWithProposalTxIdList) - throws VoteResultException.MissingBallotException { - // voteWithProposalTxIdList is the list of ProposalTxId + vote from the blind vote (decrypted vote data) - - // We convert the list to a map with proposalTxId as key and the vote as value. As the vote can be null we - // wrap it into an optional. - Map> voteByTxIdMap = voteWithProposalTxIdList.getList().stream() - .collect(Collectors.toMap(VoteWithProposalTxId::getProposalTxId, e -> Optional.ofNullable(e.getVote()))); - - // We make a map with proposalTxId as key and the ballot as value out of our stored ballot list. - // This can contain ballots which have been added later and have a null value for the vote. - Map ballotByTxIdMap = ballotListService.getValidBallotsOfCycle().stream() - .collect(Collectors.toMap(Ballot::getTxId, ballot -> ballot)); - - // It could be that we missed some proposalPayloads. - // If we have votes with proposals which are not found in our ballots we add it to missingBallots. - List missingBallots = new ArrayList<>(); - List ballots = voteByTxIdMap.entrySet().stream() - .map(entry -> { - String txId = entry.getKey(); - if (ballotByTxIdMap.containsKey(txId)) { - Ballot ballot = ballotByTxIdMap.get(txId); - // We create a new Ballot with the proposal from the ballot list and the vote from our decrypted votes - // We clone the ballot instead applying the vote to the existing ballot from ballotListService - // The items from ballotListService.getBallotList() contains my votes. - - if (ballot.getVote() != null) { - // If we had set a vote it was an own active vote - if (!entry.getValue().isPresent()) { - log.warn("We found a local vote but don't have that vote in the data from the " + - "blind vote. ballot={}", ballot); - } else if (!ballot.getVote().equals(entry.getValue().get())) { - log.warn("We found a local vote but the vote from the " + - "blind vote does not match. ballot={}, vote from blindVote data={}", - ballot, entry.getValue().get()); - } - } - - // We only return accepted or rejected votes - return entry.getValue().map(vote -> new Ballot(ballot.getProposal(), vote)).orElse(null); - } else { - // We got a vote but we don't have the ballot (which includes the proposal) - // We add it to the missing list to handle it as exception later. We want all missing data so we - // do not throw here. - log.warn("missingBallot for proposal with txId={}. Optional tx={}", txId, daoStateService.getTx(txId)); - missingBallots.add(txId); - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - - if (!missingBallots.isEmpty()) - throw new VoteResultException.MissingBallotException(ballots, missingBallots); - - // If we received a proposal after we had already voted we consider it as a proposal withhold attack and - // treat the proposal as it was voted with a rejected vote. - ballotByTxIdMap.entrySet().stream() - .filter(e -> !voteByTxIdMap.containsKey(e.getKey())) - .map(Map.Entry::getValue) - .forEach(ballot -> { - log.warn("We have a proposal which was not part of our blind vote and reject it. " + - "Proposal={}", ballot.getProposal()); - ballots.add(new Ballot(ballot.getProposal(), new Vote(false))); - }); - - // Let's keep the data more deterministic by sorting it by txId. Though we are not using the sorting. - ballots.sort(Comparator.comparing(Ballot::getTxId)); - return new BallotList(ballots); - } - - private Map getStakeByHashOfBlindVoteListMap(Set decryptedBallotsWithMeritsSet) { - // Don't use byte[] as key as byte[] uses object identity for equals and hashCode - Map map = new HashMap<>(); - decryptedBallotsWithMeritsSet.forEach(decryptedBallotsWithMerits -> { - P2PDataStorage.ByteArray hash = new P2PDataStorage.ByteArray(decryptedBallotsWithMerits.getHashOfBlindVoteList()); - map.putIfAbsent(hash, 0L); - // We must not use the merit(stake) as that is from the P2P network data and it is not guaranteed that we - // have received it. We must rely only on blockchain data. The stake is from the vote reveal tx input. - long aggregatedStake = map.get(hash); - long stake = decryptedBallotsWithMerits.getStake(); - aggregatedStake += stake; - map.put(hash, aggregatedStake); - - log.debug("blindVoteTxId={}, stake={}", - decryptedBallotsWithMerits.getBlindVoteTxId(), stake); - }); - return map; - } - - private byte[] calculateMajorityBlindVoteListHash(Map stakes) - throws VoteResultException.ValidationException, VoteResultException.ConsensusException { - List stakeList = stakes.entrySet().stream() - .map(entry -> new HashWithStake(entry.getKey().bytes, entry.getValue())) - .collect(Collectors.toList()); - return VoteResultConsensus.getMajorityHash(stakeList); - } - - // Deal with eventually consistency of P2P network - private Optional> findBlindVoteListMatchingMajorityHash(byte[] majorityVoteListHash) { - // We reuse the method at voteReveal domain used when creating the hash - List blindVotes = BlindVoteConsensus.getSortedBlindVoteListOfCycle(blindVoteListService); - if (isListMatchingMajority(majorityVoteListHash, blindVotes, true)) { - // Out local list is matching the majority hash - return Optional.of(blindVotes); - } else { - log.warn("Our local list of blind vote payloads does not match the majorityVoteListHash. " + - "We try permuting our list to find a matching variant"); - // Each voter has re-published his blind vote list when broadcasting the reveal tx so there should have a very - // high chance that we have received all blind votes which have been used by the majority of the - // voters (majority by stake). - // It still could be that we have additional blind votes so our hash does not match. We can try to permute - // our list with excluding items to see if we get a matching list. If not last resort is to request the - // missing items from the network. - Optional> permutatedList = findPermutatedListMatchingMajority(majorityVoteListHash); - if (permutatedList.isPresent()) { - return permutatedList; - } else { - log.warn("We did not find a permutation of our blindVote list which matches the majority view. " + - "We will request the blindVote data from the peers."); - // This is async operation. We will restart the whole verification process once we received the data. - missingDataRequestService.sendRepublishRequest(); - return Optional.empty(); - } - } - } - - private Optional> findPermutatedListMatchingMajority(byte[] majorityVoteListHash) { - List list = BlindVoteConsensus.getSortedBlindVoteListOfCycle(blindVoteListService); - long ts = System.currentTimeMillis(); - - BiFunction, Boolean> predicate = (hash, variation) -> - isListMatchingMajority(hash, variation, false); - - List result = PermutationUtil.findMatchingPermutation(majorityVoteListHash, list, predicate, 1000000); - log.info("findPermutatedListMatchingMajority for {} items took {} ms.", - list.size(), (System.currentTimeMillis() - ts)); - if (result.isEmpty()) { - log.info("We did not find a variation of the blind vote list which matches the majority hash."); - return Optional.empty(); - } else { - log.info("We found a variation of the blind vote list which matches the majority hash. variation={}", result); - return Optional.of(result); - } - } - - private boolean isListMatchingMajority(byte[] majorityVoteListHash, List list, boolean doLog) { - byte[] hashOfBlindVoteList = VoteRevealConsensus.getHashOfBlindVoteList(list); - if (doLog) { - log.debug("majorityVoteListHash {}", Utilities.bytesAsHexString(majorityVoteListHash)); - log.debug("hashOfBlindVoteList {}", Utilities.bytesAsHexString(hashOfBlindVoteList)); - log.debug("List of blindVoteTxIds {}", list.stream().map(BlindVote::getTxId) - .collect(Collectors.joining(", "))); - } - return Arrays.equals(majorityVoteListHash, hashOfBlindVoteList); - } - - private Set getEvaluatedProposals(Set decryptedBallotsWithMeritsSet, - int chainHeight) { - // We reorganize the data structure to have a map of proposals with a list of VoteWithStake objects - Map> resultListByProposalMap = getVoteWithStakeListByProposalMap(decryptedBallotsWithMeritsSet); - - Set evaluatedProposals = new HashSet<>(); - resultListByProposalMap.forEach((proposal, voteWithStakeList) -> { - long requiredQuorum = daoStateService.getParamValueAsCoin(proposal.getQuorumParam(), chainHeight).value; - long requiredVoteThreshold = getRequiredVoteThreshold(chainHeight, proposal); - checkArgument(requiredVoteThreshold >= 5000, - "requiredVoteThreshold must be not be less then 50% otherwise we could have conflicting results."); - - // move to consensus class - ProposalVoteResult proposalVoteResult = getResultPerProposal(voteWithStakeList, proposal); - // Quorum is min. required BSQ stake to be considered valid - long reachedQuorum = proposalVoteResult.getQuorum(); - log.debug("proposalTxId: {}, required requiredQuorum: {}, requiredVoteThreshold: {}", - proposal.getTxId(), requiredVoteThreshold / 100D, requiredQuorum); - if (reachedQuorum >= requiredQuorum) { - // We multiply by 10000 as we use a long for reachedThreshold and we want precision of 2 with - // a % value. E.g. 50% is 5000. - // Threshold is percentage of accepted to total stake - long reachedThreshold = proposalVoteResult.getThreshold(); - - log.debug("reached threshold: {} %, required threshold: {} %", - reachedThreshold / 100D, - requiredVoteThreshold / 100D); - // We need to exceed requiredVoteThreshold e.g. 50% is not enough but 50.01%. - // Otherwise we could have 50% vs 50% - if (reachedThreshold > requiredVoteThreshold) { - evaluatedProposals.add(new EvaluatedProposal(true, proposalVoteResult)); - } else { - evaluatedProposals.add(new EvaluatedProposal(false, proposalVoteResult)); - log.debug("Proposal did not reach the requiredVoteThreshold. reachedThreshold={} %, " + - "requiredVoteThreshold={} %", reachedThreshold / 100D, requiredVoteThreshold / 100D); - } - } else { - evaluatedProposals.add(new EvaluatedProposal(false, proposalVoteResult)); - log.debug("Proposal did not reach the requiredQuorum. reachedQuorum={}, requiredQuorum={}", - reachedQuorum, requiredQuorum); - } - }); - - Map evaluatedProposalsByTxIdMap = new HashMap<>(); - evaluatedProposals.forEach(evaluatedProposal -> evaluatedProposalsByTxIdMap.put(evaluatedProposal.getProposalTxId(), evaluatedProposal)); - - // Proposals which did not get any vote need to be set as failed. - // TODO We should not use proposalListPresentation here - proposalListPresentation.getActiveOrMyUnconfirmedProposals().stream() - .filter(proposal -> periodService.isTxInCorrectCycle(proposal.getTxId(), chainHeight)) - .filter(proposal -> !evaluatedProposalsByTxIdMap.containsKey(proposal.getTxId())) - .forEach(proposal -> { - ProposalVoteResult proposalVoteResult = new ProposalVoteResult(proposal, 0, - 0, 0, 0, decryptedBallotsWithMeritsSet.size()); - EvaluatedProposal evaluatedProposal = new EvaluatedProposal(false, proposalVoteResult); - evaluatedProposals.add(evaluatedProposal); - log.info("Proposal ignored by all voters: {}", evaluatedProposal); - }); - - // Check if our issuance sum is not exceeding the limit - long sumIssuance = evaluatedProposals.stream() - .filter(EvaluatedProposal::isAccepted) - .map(EvaluatedProposal::getProposal) - .filter(proposal -> proposal instanceof IssuanceProposal) - .map(proposal -> (IssuanceProposal) proposal) - .mapToLong(proposal -> proposal.getRequestedBsq().value) - .sum(); - long limit = daoStateService.getParamValueAsCoin(Param.ISSUANCE_LIMIT, chainHeight).value; - if (sumIssuance > limit) { - Set evaluatedProposals2 = new HashSet<>(); - evaluatedProposals.stream().filter(EvaluatedProposal::isAccepted) - .forEach(e -> evaluatedProposals2.add(new EvaluatedProposal(false, e.getProposalVoteResult()))); - String msg = "We have a total issuance amount of " + sumIssuance / 100 + " BSQ but our limit for a cycle is " + limit / 100 + " BSQ. " + - "We consider that cycle as invalid and have set all proposals as rejected."; - log.warn(msg); - - checkNotNull(daoStateService.getCurrentCycle(), "daoStateService.getCurrentCycle() must not be null"); - voteResultExceptions.add(new VoteResultException(daoStateService.getCurrentCycle(), new VoteResultException.ConsensusException(msg))); - return evaluatedProposals2; - } - - return evaluatedProposals; - } - - // We use long for calculation to avoid issues with rounding. So we multiply the % value as double (e.g. 0.5 = 50%) - // by 100 to get the percentage value and again by 100 to get 2 decimal -> 5000 = 50.00% - private long getRequiredVoteThreshold(int chainHeight, Proposal proposal) { - double paramValueAsPercentDouble = daoStateService.getParamValueAsPercentDouble(proposal.getThresholdParam(), chainHeight); - return MathUtils.roundDoubleToLong(paramValueAsPercentDouble * 10000); - } - - private Map> getVoteWithStakeListByProposalMap(Set decryptedBallotsWithMeritsSet) { - Map> voteWithStakeByProposalMap = new HashMap<>(); - decryptedBallotsWithMeritsSet.forEach(decryptedBallotsWithMerits -> decryptedBallotsWithMerits.getBallotList() - .forEach(ballot -> { - Proposal proposal = ballot.getProposal(); - voteWithStakeByProposalMap.putIfAbsent(proposal, new ArrayList<>()); - List voteWithStakeList = voteWithStakeByProposalMap.get(proposal); - long sumOfAllMerits = MeritConsensus.getMeritStake(decryptedBallotsWithMerits.getBlindVoteTxId(), - decryptedBallotsWithMerits.getMeritList(), daoStateService); - VoteWithStake voteWithStake = new VoteWithStake(ballot.getVote(), decryptedBallotsWithMerits.getStake(), sumOfAllMerits); - voteWithStakeList.add(voteWithStake); - log.debug("Add entry to voteWithStakeListByProposalMap: proposalTxId={}, voteWithStake={} ", proposal.getTxId(), voteWithStake); - })); - return voteWithStakeByProposalMap; - } - - - private ProposalVoteResult getResultPerProposal(List voteWithStakeList, Proposal proposal) { - int numAcceptedVotes = 0; - int numRejectedVotes = 0; - int numIgnoredVotes = 0; - long stakeOfAcceptedVotes = 0; - long stakeOfRejectedVotes = 0; - - for (VoteWithStake voteWithStake : voteWithStakeList) { - long sumOfAllMerits = voteWithStake.getSumOfAllMerits(); - long stake = voteWithStake.getStake(); - long combinedStake = stake + sumOfAllMerits; - log.debug("proposalTxId={}, stake={}, sumOfAllMerits={}, combinedStake={}", - proposal.getTxId(), stake, sumOfAllMerits, combinedStake); - Vote vote = voteWithStake.getVote(); - if (vote != null) { - if (vote.isAccepted()) { - stakeOfAcceptedVotes += combinedStake; - numAcceptedVotes++; - } else { - stakeOfRejectedVotes += combinedStake; - numRejectedVotes++; - } - } else { - numIgnoredVotes++; - log.debug("Voter ignored proposal"); - } - } - return new ProposalVoteResult(proposal, stakeOfAcceptedVotes, stakeOfRejectedVotes, numAcceptedVotes, numRejectedVotes, numIgnoredVotes); - } - - private void applyAcceptedProposals(Set acceptedEvaluatedProposals, int chainHeight) { - applyIssuance(acceptedEvaluatedProposals, chainHeight); - applyParamChange(acceptedEvaluatedProposals, chainHeight); - applyBondedRole(acceptedEvaluatedProposals); - applyConfiscateBond(acceptedEvaluatedProposals); - applyRemoveAsset(acceptedEvaluatedProposals); - } - - private void applyIssuance(Set acceptedEvaluatedProposals, int chainHeight) { - acceptedEvaluatedProposals.stream() - .map(EvaluatedProposal::getProposal) - .filter(proposal -> proposal instanceof IssuanceProposal) - .forEach(proposal -> issuanceService.issueBsq((IssuanceProposal) proposal, chainHeight)); - } - - private void applyParamChange(Set acceptedEvaluatedProposals, int chainHeight) { - Map> evaluatedProposalsByParam = new HashMap<>(); - acceptedEvaluatedProposals.forEach(evaluatedProposal -> { - if (evaluatedProposal.getProposal() instanceof ChangeParamProposal) { - ChangeParamProposal changeParamProposal = (ChangeParamProposal) evaluatedProposal.getProposal(); - ParamChange paramChange = getParamChange(changeParamProposal, chainHeight); - if (paramChange != null) { - String paramName = paramChange.getParamName(); - evaluatedProposalsByParam.putIfAbsent(paramName, new ArrayList<>()); - evaluatedProposalsByParam.get(paramName).add(evaluatedProposal); - } - } - }); - - evaluatedProposalsByParam.forEach((key, list) -> { - if (list.size() == 1) { - applyAcceptedChangeParamProposal((ChangeParamProposal) list.get(0).getProposal(), chainHeight); - } else if (list.size() > 1) { - log.warn("There have been multiple winning param change proposals with the same item. " + - "This is a sign of a social consensus failure. " + - "We treat all requests as failed in such a case."); - } - }); - } - - private void applyAcceptedChangeParamProposal(ChangeParamProposal changeParamProposal, int chainHeight) { - @SuppressWarnings("StringBufferReplaceableByString") - StringBuilder sb = new StringBuilder(); - sb.append("\n################################################################################\n"); - sb.append("We changed a parameter. ProposalTxId=").append(changeParamProposal.getTxId()) - .append("\nParam: ").append(changeParamProposal.getParam().name()) - .append(" new value: ").append(changeParamProposal.getParamValue()) - .append("\n################################################################################\n"); - log.info(sb.toString()); - - daoStateService.setNewParam(chainHeight, changeParamProposal.getParam(), changeParamProposal.getParamValue()); - } - - private ParamChange getParamChange(ChangeParamProposal changeParamProposal, int chainHeight) { - return daoStateService.getStartHeightOfNextCycle(chainHeight) - .map(heightOfNewCycle -> new ParamChange(changeParamProposal.getParam().name(), - changeParamProposal.getParamValue(), - heightOfNewCycle)) - .orElse(null); - } - - private void applyBondedRole(Set acceptedEvaluatedProposals) { - acceptedEvaluatedProposals.forEach(evaluatedProposal -> { - if (evaluatedProposal.getProposal() instanceof RoleProposal) { - RoleProposal roleProposal = (RoleProposal) evaluatedProposal.getProposal(); - Role role = roleProposal.getRole(); - @SuppressWarnings("StringBufferReplaceableByString") - StringBuilder sb = new StringBuilder(); - sb.append("\n################################################################################\n"); - sb.append("We added a bonded role. ProposalTxId=").append(roleProposal.getTxId()) - .append("\nRole: ").append(role.getDisplayString()) - .append("\n################################################################################\n"); - log.info(sb.toString()); - } - }); - } - - private void applyConfiscateBond(Set acceptedEvaluatedProposals) { - acceptedEvaluatedProposals.forEach(evaluatedProposal -> { - if (evaluatedProposal.getProposal() instanceof ConfiscateBondProposal) { - ConfiscateBondProposal confiscateBondProposal = (ConfiscateBondProposal) evaluatedProposal.getProposal(); - daoStateService.confiscateBond(confiscateBondProposal.getLockupTxId()); - - @SuppressWarnings("StringBufferReplaceableByString") - StringBuilder sb = new StringBuilder(); - sb.append("\n################################################################################\n"); - sb.append("We confiscated a bond. ProposalTxId=").append(confiscateBondProposal.getTxId()) - .append("\nLockupTxId: ").append(confiscateBondProposal.getLockupTxId()) - .append("\n################################################################################\n"); - log.info(sb.toString()); - } - }); - } - - private void applyRemoveAsset(Set acceptedEvaluatedProposals) { - acceptedEvaluatedProposals.forEach(evaluatedProposal -> { - if (evaluatedProposal.getProposal() instanceof RemoveAssetProposal) { - RemoveAssetProposal removeAssetProposal = (RemoveAssetProposal) evaluatedProposal.getProposal(); - String tickerSymbol = removeAssetProposal.getTickerSymbol(); - @SuppressWarnings("StringBufferReplaceableByString") - StringBuilder sb = new StringBuilder(); - sb.append("\n################################################################################\n"); - sb.append("We removed an asset. ProposalTxId=").append(removeAssetProposal.getTxId()) - .append("\nAsset: ").append(CurrencyUtil.getNameByCode(tickerSymbol)) - .append("\n################################################################################\n"); - log.info(sb.toString()); - } - }); - } - - private Set getAcceptedEvaluatedProposals(Set evaluatedProposals) { - return evaluatedProposals.stream() - .filter(EvaluatedProposal::isAccepted) - .collect(Collectors.toSet()); - } - - private boolean isInVoteResultPhase(int chainHeight) { - return periodService.getFirstBlockOfPhase(chainHeight, DaoPhase.Phase.RESULT) == chainHeight; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Inner classes - /////////////////////////////////////////////////////////////////////////////////////////// - - @Value - public static class HashWithStake { - private final byte[] hash; - private final long stake; - - HashWithStake(byte[] hash, long stake) { - this.hash = hash; - this.stake = stake; - } - - @Override - public String toString() { - return "HashWithStake{" + - "\n hash=" + Utilities.bytesAsHexString(hash) + - ",\n stake=" + stake + - "\n}"; - } - } - - @Value - private static class VoteWithStake { - @Nullable - private final Vote vote; - private final long stake; - private final long sumOfAllMerits; - - VoteWithStake(@Nullable Vote vote, long stake, long sumOfAllMerits) { - this.vote = vote; - this.stake = stake; - this.sumOfAllMerits = sumOfAllMerits; - } - - @Override - public String toString() { - return "VoteWithStake{" + - "\n vote=" + vote + - ",\n stake=" + stake + - ",\n sumOfAllMerits=" + sumOfAllMerits + - "\n}"; - } - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/issuance/IssuanceService.java b/core/src/main/java/bisq/core/dao/governance/voteresult/issuance/IssuanceService.java deleted file mode 100644 index be93a447a5..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/issuance/IssuanceService.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.voteresult.issuance; - -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.IssuanceProposal; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxInput; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.governance.CompensationProposal; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.dao.state.model.governance.Issuance; -import bisq.core.dao.state.model.governance.IssuanceType; -import bisq.core.dao.state.model.governance.ReimbursementProposal; - -import bisq.common.util.MathUtils; - -import javax.inject.Inject; - -import java.util.Optional; - -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkArgument; - - -@Slf4j -public class IssuanceService { - private final DaoStateService daoStateService; - private final PeriodService periodService; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public IssuanceService(DaoStateService daoStateService, PeriodService periodService) { - this.daoStateService = daoStateService; - this.periodService = periodService; - } - - public void issueBsq(IssuanceProposal issuanceProposal, int chainHeight) { - daoStateService.getIssuanceCandidateTxOutputs().stream() - .filter(txOutput -> isValid(txOutput, issuanceProposal, periodService, chainHeight)) - .forEach(txOutput -> { - IssuanceType issuanceType = IssuanceType.UNDEFINED; - if (issuanceProposal instanceof CompensationProposal) { - issuanceType = IssuanceType.COMPENSATION; - } else if (issuanceProposal instanceof ReimbursementProposal) { - issuanceType = IssuanceType.REIMBURSEMENT; - } - checkArgument(issuanceType != IssuanceType.UNDEFINED, "issuanceType must not be undefined"); - - // We don't check atm if the output is unspent. We cannot use the bsqWallet as that would not - // reflect our current block state (could have been spent at later block which is valid and - // bsqWallet would show that spent state). We would need to support a spent status for the outputs - // which are interpreted as BTC (as a not yet accepted comp. request). - Optional optionalTx = daoStateService.getTx(issuanceProposal.getTxId()); - checkArgument(optionalTx.isPresent(), "optionalTx must be present"); - long amount = issuanceProposal.getRequestedBsq().value; - Tx tx = optionalTx.get(); - // We use key from first input - TxInput txInput = tx.getTxInputs().get(0); - String pubKey = txInput.getPubKey(); - Issuance issuance = new Issuance(tx.getId(), chainHeight, amount, pubKey, issuanceType); - daoStateService.addIssuance(issuance); - daoStateService.addUnspentTxOutput(txOutput); - - @SuppressWarnings("StringBufferReplaceableByString") - StringBuilder sb = new StringBuilder(); - sb.append("\n################################################################################\n"); - sb.append("We issued new BSQ to tx with ID ").append(txOutput.getTxId()) - .append("\nIssued BSQ: ").append(MathUtils.scaleDownByPowerOf10(amount, 2)) - .append("\nIssuance type: ").append(issuanceType.name()) - .append("\n################################################################################\n"); - log.info(sb.toString()); - }); - } - - private boolean isValid(TxOutput txOutput, IssuanceProposal issuanceProposal, PeriodService periodService, int chainHeight) { - return txOutput.getTxId().equals(issuanceProposal.getTxId()) - && issuanceProposal.getRequestedBsq().value == txOutput.getValue() - && issuanceProposal.getBsqAddress().substring(1).equals(txOutput.getAddress()) - && periodService.isTxInPhaseAndCycle(txOutput.getTxId(), DaoPhase.Phase.PROPOSAL, chainHeight); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealConsensus.java b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealConsensus.java deleted file mode 100644 index 19993205fe..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealConsensus.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.votereveal; - -import bisq.core.dao.governance.blindvote.BlindVote; -import bisq.core.dao.state.model.blockchain.OpReturnType; - -import bisq.common.app.Version; -import bisq.common.crypto.Hash; - -import javax.crypto.SecretKey; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import java.util.List; - -import lombok.extern.slf4j.Slf4j; - -/** - * All consensus critical aspects are handled here. - */ -@Slf4j -public class VoteRevealConsensus { - - public static byte[] getHashOfBlindVoteList(List blindVotes) { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - blindVotes.forEach(blindVote -> { - byte[] data = blindVote.toProtoMessage().toByteArray(); - try { - outputStream.write(data); - } catch (IOException e) { - e.printStackTrace(); - } - }); - return Hash.getSha256Ripemd160hash(outputStream.toByteArray()); - } - - public static byte[] getOpReturnData(byte[] hashOfBlindVoteList, SecretKey secretKey) throws IOException { - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - outputStream.write(OpReturnType.VOTE_REVEAL.getType()); - outputStream.write(Version.VOTE_REVEAL); - outputStream.write(hashOfBlindVoteList); // hash is 20 bytes - outputStream.write(secretKey.getEncoded()); // encoded secretKey has 16 bytes - return outputStream.toByteArray(); - } catch (IOException e) { - // Not expected to happen ever - e.printStackTrace(); - log.error(e.toString()); - throw e; - } - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealException.java b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealException.java deleted file mode 100644 index 673df1f28e..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealException.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.votereveal; - -import bisq.core.dao.governance.myvote.MyVote; - -import org.bitcoinj.core.Transaction; - -import lombok.Getter; - -import javax.annotation.Nullable; - -@SuppressWarnings("SameParameterValue") -public class VoteRevealException extends Exception { - @Getter - @Nullable - private Transaction voteRevealTx; - @Getter - @Nullable - private String blindVoteTxId; - @Getter - @Nullable - private MyVote myVote; - - VoteRevealException(String message, Throwable cause, @SuppressWarnings("NullableProblems") String blindVoteTxId) { - super(message, cause); - this.blindVoteTxId = blindVoteTxId; - } - - VoteRevealException(String message, @SuppressWarnings("NullableProblems") MyVote myVote) { - super(message); - this.myVote = myVote; - } - - VoteRevealException(String message, Throwable cause, @SuppressWarnings("NullableProblems") Transaction voteRevealTx) { - super(message, cause); - this.voteRevealTx = voteRevealTx; - } - - - @Override - public String toString() { - return "VoteRevealException{" + - "\n voteRevealTx=" + voteRevealTx + - ",\n blindVoteTxId='" + blindVoteTxId + '\'' + - ",\n myVote=" + myVote + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java deleted file mode 100644 index 94b4f0b9a6..0000000000 --- a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.votereveal; - -import bisq.core.btc.exceptions.TransactionVerificationException; -import bisq.core.btc.exceptions.TxBroadcastException; -import bisq.core.btc.exceptions.WalletException; -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.TxBroadcaster; -import bisq.core.btc.wallet.WalletsManager; -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.governance.blindvote.BlindVote; -import bisq.core.dao.governance.blindvote.BlindVoteConsensus; -import bisq.core.dao.governance.blindvote.BlindVoteListService; -import bisq.core.dao.governance.myvote.MyVote; -import bisq.core.dao.governance.myvote.MyVoteListService; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.blockchain.TxType; -import bisq.core.dao.state.model.governance.DaoPhase; - -import bisq.common.util.Utilities; - -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.Transaction; - -import javax.inject.Inject; - -import javafx.collections.FXCollections; -import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; - -import java.io.IOException; - -import java.util.ArrayList; -import java.util.List; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -// TODO Broadcast the winning list at the moment the reveal period is over and have the break -// interval as time buffer for all nodes to receive that winning list. All nodes which are in sync with the -// majority data view can broadcast. That way it will become a very unlikely case that a node is missing -// data. - -/** - * Publishes voteRevealTx with the secret key used for encryption at blind vote and the hash of the list of - * the blind vote payloads. Republishes also all blindVotes of that cycle to add more resilience. - */ -@Slf4j -public class VoteRevealService implements DaoStateListener, DaoSetupService { - - public interface VoteRevealTxPublishedListener { - void onVoteRevealTxPublished(String txId); - } - - private final DaoStateService daoStateService; - private final BlindVoteListService blindVoteListService; - private final PeriodService periodService; - private final MyVoteListService myVoteListService; - private final BsqWalletService bsqWalletService; - private final BtcWalletService btcWalletService; - private final WalletsManager walletsManager; - - @Getter - private final ObservableList voteRevealExceptions = FXCollections.observableArrayList(); - private final List voteRevealTxPublishedListeners = new ArrayList<>(); - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public VoteRevealService(DaoStateService daoStateService, - BlindVoteListService blindVoteListService, - PeriodService periodService, - MyVoteListService myVoteListService, - BsqWalletService bsqWalletService, - BtcWalletService btcWalletService, - WalletsManager walletsManager) { - this.daoStateService = daoStateService; - this.blindVoteListService = blindVoteListService; - this.periodService = periodService; - this.myVoteListService = myVoteListService; - this.bsqWalletService = bsqWalletService; - this.btcWalletService = btcWalletService; - this.walletsManager = walletsManager; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - voteRevealExceptions.addListener((ListChangeListener) c -> { - c.next(); - if (c.wasAdded()) - c.getAddedSubList().forEach(exception -> log.error(exception.toString())); - }); - daoStateService.addDaoStateListener(this); - } - - @Override - public void start() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - private byte[] getHashOfBlindVoteList() { - List blindVotes = BlindVoteConsensus.getSortedBlindVoteListOfCycle(blindVoteListService); - byte[] hashOfBlindVoteList = VoteRevealConsensus.getHashOfBlindVoteList(blindVotes); - log.debug("blindVoteList for creating hash: {}", blindVotes); - log.info("Sha256Ripemd160 hash of hashOfBlindVoteList {}", Utilities.bytesAsHexString(hashOfBlindVoteList)); - return hashOfBlindVoteList; - } - - public void addVoteRevealTxPublishedListener(VoteRevealTxPublishedListener voteRevealTxPublishedListener) { - voteRevealTxPublishedListeners.add(voteRevealTxPublishedListener); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - maybeRevealVotes(block.getHeight()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - // Creation of vote reveal tx is done without user activity! - // We create automatically the vote reveal tx when we are in the reveal phase of the current cycle when - // the blind vote was created in case we have not done it already. - // The voter needs to be at least once online in the reveal phase when he has a blind vote created, - // otherwise his vote becomes invalid. - // In case the user misses the vote reveal phase an (invalid) vote reveal tx will be created the next time the user is - // online. That tx only serves the purpose to unlock the stake from the blind vote but it will be ignored for voting. - // A blind vote which did not get revealed might still be part of the majority hash calculation as we cannot know - // which blind votes might be revealed until the phase is over at the moment when we publish the vote reveal tx. - private void maybeRevealVotes(int chainHeight) { - myVoteListService.getMyVoteList().stream() - .filter(myVote -> myVote.getRevealTxId() == null) // we have not already revealed - .forEach(myVote -> { - boolean isInVoteRevealPhase = periodService.getPhaseForHeight(chainHeight) == DaoPhase.Phase.VOTE_REVEAL; - // If we would create the tx in the last block it would be confirmed in the best case in th next - // block which would be already the break and would invalidate the vote reveal. - boolean isLastBlockInPhase = chainHeight == periodService.getLastBlockOfPhase(chainHeight, DaoPhase.Phase.VOTE_REVEAL); - String blindVoteTxId = myVote.getBlindVoteTxId(); - boolean isBlindVoteTxInCorrectPhaseAndCycle = periodService.isTxInPhaseAndCycle(blindVoteTxId, DaoPhase.Phase.BLIND_VOTE, chainHeight); - if (isInVoteRevealPhase && !isLastBlockInPhase && isBlindVoteTxInCorrectPhaseAndCycle) { - log.info("We call revealVote at blockHeight {} for blindVoteTxId {}", chainHeight, blindVoteTxId); - // Standard case that we are in the correct phase and cycle and create the reveal tx. - revealVote(myVote, true); - } else { - // We missed the vote reveal phase but publish a vote reveal tx to unlock the blind vote stake. - boolean isAfterVoteRevealPhase = periodService.getPhaseForHeight(chainHeight).ordinal() > DaoPhase.Phase.VOTE_REVEAL.ordinal(); - - // We missed the reveal phase but we are in the correct cycle - boolean missedPhaseSameCycle = isAfterVoteRevealPhase && isBlindVoteTxInCorrectPhaseAndCycle; - - // If we missed the cycle we don't care about the phase anymore. - boolean isBlindVoteTxInPastCycle = periodService.isTxInPastCycle(blindVoteTxId, chainHeight); - - if (missedPhaseSameCycle || isBlindVoteTxInPastCycle) { - // Exceptional case that the user missed the vote reveal phase. We still publish the vote - // reveal tx to unlock the vote stake. - - // We cannot handle that case in the parser directly to avoid that reveal tx and unlock the - // BSQ because the blind vote tx is already in the snapshot and does not get parsed - // again. It would require a reset of the snapshot and parse all blocks again. - // As this is an exceptional case we prefer to have a simple solution instead and just - // publish the vote reveal tx but are aware that it is invalid. - log.warn("We missed the vote reveal phase but publish now the tx to unlock our locked " + - "BSQ from the blind vote tx. BlindVoteTxId={}, blockHeight={}", - blindVoteTxId, chainHeight); - - // We handle the exception here inside the stream iteration as we have not get triggered from an - // outside user intent anyway. We keep errors in a observable list so clients can observe that to - // get notified if anything went wrong. - revealVote(myVote, false); - } - } - }); - } - - private void revealVote(MyVote myVote, boolean isInVoteRevealPhase) { - try { - // We collect all valid blind vote items we received via the p2p network. - // It might be that different nodes have a different collection of those items. - // To ensure we get a consensus of the data for later calculating the result we will put a hash of each - // voter's blind vote collection into the opReturn data and check for a majority in the vote result phase. - // The voters "vote" with their stake at the reveal tx for their version of the blind vote collection. - - // If we are not in the right phase we just add an empty hash (still need to have the hash as otherwise we - // would not recognize the tx as vote reveal tx) - byte[] hashOfBlindVoteList = isInVoteRevealPhase ? getHashOfBlindVoteList() : new byte[20]; - byte[] opReturnData = VoteRevealConsensus.getOpReturnData(hashOfBlindVoteList, myVote.getSecretKey()); - - // We search for my unspent stake output. - // myVote is already tested if it is in current cycle at maybeRevealVotes - // We expect that the blind vote tx and stake output is available. If not we throw an exception. - TxOutput stakeTxOutput = daoStateService.getUnspentBlindVoteStakeTxOutputs().stream() - .filter(txOutput -> txOutput.getTxId().equals(myVote.getBlindVoteTxId())) - .findFirst() - .orElseThrow(() -> new VoteRevealException("stakeTxOutput is not found for myVote.", myVote)); - - Transaction voteRevealTx = getVoteRevealTx(stakeTxOutput, opReturnData); - log.info("voteRevealTx={}", voteRevealTx); - publishTx(voteRevealTx); - - // We don't want to wait for a successful broadcast to avoid issues if the broadcast succeeds delayed or at - // next startup but the tx was actually broadcast. - myVoteListService.applyRevealTxId(myVote, voteRevealTx.getTxId().toString()); - } catch (IOException | WalletException | TransactionVerificationException - | InsufficientMoneyException e) { - voteRevealExceptions.add(new VoteRevealException("Exception at calling revealVote.", - e, myVote.getBlindVoteTxId())); - } catch (VoteRevealException e) { - voteRevealExceptions.add(e); - } - } - - private void publishTx(Transaction voteRevealTx) { - walletsManager.publishAndCommitBsqTx(voteRevealTx, TxType.VOTE_REVEAL, new TxBroadcaster.Callback() { - @Override - public void onSuccess(Transaction transaction) { - log.info("voteRevealTx successfully broadcast."); - voteRevealTxPublishedListeners.forEach(l -> l.onVoteRevealTxPublished(transaction.getTxId().toString())); - } - - @Override - public void onFailure(TxBroadcastException exception) { - log.error(exception.toString()); - voteRevealExceptions.add(new VoteRevealException("Publishing of voteRevealTx failed.", - exception, voteRevealTx)); - } - }); - } - - private Transaction getVoteRevealTx(TxOutput stakeTxOutput, byte[] opReturnData) - throws InsufficientMoneyException, WalletException, TransactionVerificationException { - Transaction preparedTx = bsqWalletService.getPreparedVoteRevealTx(stakeTxOutput); - Transaction txWithBtcFee = btcWalletService.completePreparedVoteRevealTx(preparedTx, opReturnData); - return bsqWalletService.signTx(txWithBtcFee); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/BlindVoteStateMonitoringService.java b/core/src/main/java/bisq/core/dao/monitoring/BlindVoteStateMonitoringService.java deleted file mode 100644 index 530b197d72..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/BlindVoteStateMonitoringService.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.monitoring; - -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.governance.blindvote.BlindVote; -import bisq.core.dao.governance.blindvote.BlindVoteListService; -import bisq.core.dao.governance.blindvote.MyBlindVoteList; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.monitoring.model.BlindVoteStateBlock; -import bisq.core.dao.monitoring.model.BlindVoteStateHash; -import bisq.core.dao.monitoring.network.BlindVoteStateNetworkService; -import bisq.core.dao.monitoring.network.messages.GetBlindVoteStateHashesRequest; -import bisq.core.dao.monitoring.network.messages.NewBlindVoteStateHashMessage; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.GenesisTxInfo; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.governance.Cycle; -import bisq.core.dao.state.model.governance.DaoPhase; - -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.network.Connection; -import bisq.network.p2p.seed.SeedNodeRepository; - -import bisq.common.UserThread; -import bisq.common.crypto.Hash; - -import javax.inject.Inject; - -import org.apache.commons.lang3.ArrayUtils; - -import java.util.Comparator; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Monitors the BlindVote P2P network payloads with using a hash of a sorted list of BlindVotes from one cycle and - * make it accessible to the network so we can detect quickly if any consensus issue arises. - * We create that hash at the first block of the VoteReveal phase. There is one hash created per cycle. - * The hash contains the hash of the previous block so we can ensure the validity of the whole history by - * comparing the last block. - * - * We request the state from the connected seed nodes after batch processing of BSQ is complete as well as we start - * to listen for broadcast messages from our peers about dao state of new blocks. - * - * We do NOT persist that chain of hashes as there is only one per cycle and the performance costs are very low. - */ -@Slf4j -public class BlindVoteStateMonitoringService implements DaoSetupService, DaoStateListener, BlindVoteStateNetworkService.Listener { - public interface Listener { - void onBlindVoteStateBlockChainChanged(); - } - - private final DaoStateService daoStateService; - private final BlindVoteStateNetworkService blindVoteStateNetworkService; - private final GenesisTxInfo genesisTxInfo; - private final PeriodService periodService; - private final BlindVoteListService blindVoteListService; - private final Set seedNodeAddresses; - - @Getter - private final LinkedList blindVoteStateBlockChain = new LinkedList<>(); - @Getter - private final LinkedList blindVoteStateHashChain = new LinkedList<>(); - private final List listeners = new CopyOnWriteArrayList<>(); - @Getter - private boolean isInConflictWithNonSeedNode; - @Getter - private boolean isInConflictWithSeedNode; - private boolean parseBlockChainComplete; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public BlindVoteStateMonitoringService(DaoStateService daoStateService, - BlindVoteStateNetworkService blindVoteStateNetworkService, - GenesisTxInfo genesisTxInfo, - PeriodService periodService, - BlindVoteListService blindVoteListService, - SeedNodeRepository seedNodeRepository) { - this.daoStateService = daoStateService; - this.blindVoteStateNetworkService = blindVoteStateNetworkService; - this.genesisTxInfo = genesisTxInfo; - this.periodService = periodService; - this.blindVoteListService = blindVoteListService; - seedNodeAddresses = seedNodeRepository.getSeedNodeAddresses().stream() - .map(NodeAddress::getFullAddress) - .collect(Collectors.toSet()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - daoStateService.addDaoStateListener(this); - blindVoteStateNetworkService.addListener(this); - } - - @Override - public void start() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @SuppressWarnings("Duplicates") - @Override - public void onDaoStateChanged(Block block) { - int blockHeight = block.getHeight(); - - int genesisBlockHeight = genesisTxInfo.getGenesisBlockHeight(); - - if (blindVoteStateBlockChain.isEmpty() && blockHeight > genesisBlockHeight) { - // Takes about 150 ms for dao testnet data - long ts = System.currentTimeMillis(); - for (int i = genesisBlockHeight; i < blockHeight; i++) { - maybeUpdateHashChain(i); - } - if (!blindVoteStateBlockChain.isEmpty()) { - log.info("updateHashChain for {} blocks took {} ms", - blockHeight - genesisBlockHeight, - System.currentTimeMillis() - ts); - } - } - - long ts = System.currentTimeMillis(); - boolean updated = maybeUpdateHashChain(blockHeight); - if (updated) { - log.info("updateHashChain for block {} took {} ms", - blockHeight, - System.currentTimeMillis() - ts); - } - } - - @SuppressWarnings("Duplicates") - @Override - public void onParseBlockChainComplete() { - parseBlockChainComplete = true; - blindVoteStateNetworkService.addListeners(); - - // We wait for processing messages until we have completed batch processing - - // We request data from last 5 cycles. We ignore possible duration changes done by voting. - // period is arbitrary anyway... - Cycle currentCycle = periodService.getCurrentCycle(); - checkNotNull(currentCycle, "currentCycle must not be null"); - int fromHeight = Math.max(genesisTxInfo.getGenesisBlockHeight(), daoStateService.getChainHeight() - currentCycle.getDuration() * 5); - - blindVoteStateNetworkService.requestHashesFromAllConnectedSeedNodes(fromHeight); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // StateNetworkService.Listener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onNewStateHashMessage(NewBlindVoteStateHashMessage newStateHashMessage, Connection connection) { - if (newStateHashMessage.getStateHash().getHeight() <= daoStateService.getChainHeight()) { - processPeersBlindVoteStateHash(newStateHashMessage.getStateHash(), connection.getPeersNodeAddressOptional(), true); - } - } - - @Override - public void onGetStateHashRequest(Connection connection, GetBlindVoteStateHashesRequest getStateHashRequest) { - int fromHeight = getStateHashRequest.getHeight(); - List blindVoteStateHashes = blindVoteStateBlockChain.stream() - .filter(e -> e.getHeight() >= fromHeight) - .map(BlindVoteStateBlock::getMyStateHash) - .collect(Collectors.toList()); - blindVoteStateNetworkService.sendGetStateHashesResponse(connection, getStateHashRequest.getNonce(), blindVoteStateHashes); - } - - @Override - public void onPeersStateHashes(List stateHashes, Optional peersNodeAddress) { - AtomicBoolean hasChanged = new AtomicBoolean(false); - stateHashes.forEach(daoStateHash -> { - boolean changed = processPeersBlindVoteStateHash(daoStateHash, peersNodeAddress, false); - if (changed) { - hasChanged.set(true); - } - }); - - if (hasChanged.get()) { - listeners.forEach(Listener::onBlindVoteStateBlockChainChanged); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void requestHashesFromGenesisBlockHeight(String peersAddress) { - blindVoteStateNetworkService.requestHashes(genesisTxInfo.getGenesisBlockHeight(), peersAddress); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Listeners - /////////////////////////////////////////////////////////////////////////////////////////// - - public void addListener(Listener listener) { - listeners.add(listener); - } - - public void removeListener(Listener listener) { - listeners.remove(listener); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private boolean maybeUpdateHashChain(int blockHeight) { - // We use first block in blind vote phase to create the hash of our blindVotes. We prefer to wait as long as - // possible to increase the chance that we have received all blindVotes. - if (!isFirstBlockOfBlindVotePhase(blockHeight)) { - return false; - } - - periodService.getCycle(blockHeight).ifPresent(cycle -> { - List blindVotes = blindVoteListService.getConfirmedBlindVotes().stream() - .filter(e -> periodService.isTxInCorrectCycle(e.getTxId(), blockHeight)) - .sorted(Comparator.comparing(BlindVote::getTxId)).collect(Collectors.toList()); - - // We use MyBlindVoteList to get the serialized bytes from the blindVotes list - byte[] serializedBlindVotes = new MyBlindVoteList(blindVotes).toProtoMessage().toByteArray(); - - byte[] prevHash; - if (blindVoteStateBlockChain.isEmpty()) { - prevHash = new byte[0]; - } else { - prevHash = blindVoteStateBlockChain.getLast().getHash(); - } - byte[] combined = ArrayUtils.addAll(prevHash, serializedBlindVotes); - byte[] hash = Hash.getSha256Ripemd160hash(combined); - - BlindVoteStateHash myBlindVoteStateHash = new BlindVoteStateHash(blockHeight, hash, prevHash, blindVotes.size()); - BlindVoteStateBlock blindVoteStateBlock = new BlindVoteStateBlock(myBlindVoteStateHash); - blindVoteStateBlockChain.add(blindVoteStateBlock); - blindVoteStateHashChain.add(myBlindVoteStateHash); - - // We only broadcast after parsing of blockchain is complete - if (parseBlockChainComplete) { - // We notify listeners only after batch processing to avoid performance issues at UI code - listeners.forEach(Listener::onBlindVoteStateBlockChainChanged); - - // We delay broadcast to give peers enough time to have received the block. - // Otherwise they would ignore our data if received block is in future to their local blockchain. - int delayInSec = 5 + new Random().nextInt(10); - UserThread.runAfter(() -> blindVoteStateNetworkService.broadcastMyStateHash(myBlindVoteStateHash), delayInSec); - } - }); - return true; - } - - private boolean processPeersBlindVoteStateHash(BlindVoteStateHash blindVoteStateHash, - Optional peersNodeAddress, - boolean notifyListeners) { - AtomicBoolean changed = new AtomicBoolean(false); - AtomicBoolean inConflictWithNonSeedNode = new AtomicBoolean(this.isInConflictWithNonSeedNode); - AtomicBoolean inConflictWithSeedNode = new AtomicBoolean(this.isInConflictWithSeedNode); - StringBuilder sb = new StringBuilder(); - blindVoteStateBlockChain.stream() - .filter(e -> e.getHeight() == blindVoteStateHash.getHeight()).findAny() - .ifPresent(daoStateBlock -> { - String peersNodeAddressAsString = peersNodeAddress.map(NodeAddress::getFullAddress) - .orElseGet(() -> "Unknown peer " + new Random().nextInt(10000)); - daoStateBlock.putInPeersMap(peersNodeAddressAsString, blindVoteStateHash); - if (!daoStateBlock.getMyStateHash().hasEqualHash(blindVoteStateHash)) { - daoStateBlock.putInConflictMap(peersNodeAddressAsString, blindVoteStateHash); - if (seedNodeAddresses.contains(peersNodeAddressAsString)) { - inConflictWithSeedNode.set(true); - } else { - inConflictWithNonSeedNode.set(true); - } - - sb.append("We received a block hash from peer ") - .append(peersNodeAddressAsString) - .append(" which conflicts with our block hash.\n") - .append("my blindVoteStateHash=") - .append(daoStateBlock.getMyStateHash()) - .append("\npeers blindVoteStateHash=") - .append(blindVoteStateHash); - } - changed.set(true); - }); - - this.isInConflictWithNonSeedNode = inConflictWithNonSeedNode.get(); - this.isInConflictWithSeedNode = inConflictWithSeedNode.get(); - - String conflictMsg = sb.toString(); - if (!conflictMsg.isEmpty()) { - if (this.isInConflictWithSeedNode) - log.warn("Conflict with seed nodes: {}", conflictMsg); - else if (this.isInConflictWithNonSeedNode) - log.info("Conflict with non-seed nodes: {}", conflictMsg); - } - - if (notifyListeners && changed.get()) { - listeners.forEach(Listener::onBlindVoteStateBlockChainChanged); - } - - return changed.get(); - } - - private boolean isFirstBlockOfBlindVotePhase(int blockHeight) { - return blockHeight == periodService.getFirstBlockOfPhase(blockHeight, DaoPhase.Phase.VOTE_REVEAL); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java deleted file mode 100644 index 18eb1f9683..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java +++ /dev/null @@ -1,429 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.monitoring; - -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.monitoring.model.DaoStateBlock; -import bisq.core.dao.monitoring.model.DaoStateHash; -import bisq.core.dao.monitoring.model.UtxoMismatch; -import bisq.core.dao.monitoring.network.Checkpoint; -import bisq.core.dao.monitoring.network.DaoStateNetworkService; -import bisq.core.dao.monitoring.network.messages.GetDaoStateHashesRequest; -import bisq.core.dao.monitoring.network.messages.NewDaoStateHashMessage; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.GenesisTxInfo; -import bisq.core.dao.state.model.blockchain.BaseTxOutput; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.governance.IssuanceType; - -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.network.Connection; -import bisq.network.p2p.seed.SeedNodeRepository; - -import bisq.common.UserThread; -import bisq.common.config.Config; -import bisq.common.crypto.Hash; -import bisq.common.file.FileUtil; -import bisq.common.util.Utilities; - -import javax.inject.Inject; -import javax.inject.Named; - -import org.apache.commons.lang3.ArrayUtils; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; - -import java.io.File; - -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkArgument; - -/** - * Monitors the DaoState by using a hash for the complete daoState and make it accessible to the network - * so we can detect quickly if any consensus issue arise. - * We create that hash after parsing and processing of a block is completed. There is one hash created per block. - * The hash contains the hash of the previous block so we can ensure the validity of the whole history by - * comparing the last block. - * - * We request the state from the connected seed nodes after batch processing of BSQ is complete as well as we start - * to listen for broadcast messages from our peers about dao state of new blocks. It could be that the received dao - * state from the peers is already covering the next block we have not received yet. So we only take data in account - * which are inside the block height we have already. To avoid such race conditions we delay the broadcasting of our - * state to the peers to not get ignored it in case they have not received the block yet. - * - * We do persist that chain of hashes with the snapshot. - */ -@Slf4j -public class DaoStateMonitoringService implements DaoSetupService, DaoStateListener, - DaoStateNetworkService.Listener { - - public interface Listener { - void onChangeAfterBatchProcessing(); - - void onCheckpointFail(); - } - - private final DaoStateService daoStateService; - private final DaoStateNetworkService daoStateNetworkService; - private final GenesisTxInfo genesisTxInfo; - private final Set seedNodeAddresses; - - - @Getter - private final LinkedList daoStateBlockChain = new LinkedList<>(); - @Getter - private final LinkedList daoStateHashChain = new LinkedList<>(); - private final List listeners = new CopyOnWriteArrayList<>(); - private boolean parseBlockChainComplete; - @Getter - private boolean isInConflictWithNonSeedNode; - @Getter - private boolean isInConflictWithSeedNode; - @Getter - private final ObservableList utxoMismatches = FXCollections.observableArrayList(); - - private final List checkpoints = Arrays.asList( - new Checkpoint(586920, Utilities.decodeFromHex("523aaad4e760f6ac6196fec1b3ec9a2f42e5b272")) - ); - private boolean checkpointFailed; - private final boolean ignoreDevMsg; - private int numCalls; - private long accumulatedDuration; - - private final File storageDir; - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public DaoStateMonitoringService(DaoStateService daoStateService, - DaoStateNetworkService daoStateNetworkService, - GenesisTxInfo genesisTxInfo, - SeedNodeRepository seedNodeRepository, - @Named(Config.STORAGE_DIR) File storageDir, - @Named(Config.IGNORE_DEV_MSG) boolean ignoreDevMsg) { - this.daoStateService = daoStateService; - this.daoStateNetworkService = daoStateNetworkService; - this.genesisTxInfo = genesisTxInfo; - this.storageDir = storageDir; - this.ignoreDevMsg = ignoreDevMsg; - seedNodeAddresses = seedNodeRepository.getSeedNodeAddresses().stream() - .map(NodeAddress::getFullAddress) - .collect(Collectors.toSet()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - daoStateService.addDaoStateListener(this); - daoStateNetworkService.addListener(this); - } - - @Override - public void start() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - // We do not use onDaoStateChanged but let the DaoEventCoordinator call createHashFromBlock to ensure the - // correct order of execution. - - @Override - public void onParseBlockChainComplete() { - parseBlockChainComplete = true; - daoStateNetworkService.addListeners(); - - // We wait for processing messages until we have completed batch processing - int fromHeight = daoStateService.getChainHeight() - 10; - daoStateNetworkService.requestHashesFromAllConnectedSeedNodes(fromHeight); - - if (!ignoreDevMsg) { - verifyCheckpoints(); - } - - log.info("ParseBlockChainComplete: Accumulated updateHashChain() calls for {} block took {} ms " + - "({} ms in average / block)", - numCalls, - accumulatedDuration, - (int) ((double) accumulatedDuration / (double) numCalls)); - } - - @Override - public void onDaoStateChanged(Block block) { - long genesisTotalSupply = daoStateService.getGenesisTotalSupply().value; - long compensationIssuance = daoStateService.getTotalIssuedAmount(IssuanceType.COMPENSATION); - long reimbursementIssuance = daoStateService.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT); - long totalAmountOfBurntBsq = daoStateService.getTotalAmountOfBurntBsq(); - // confiscated funds are still in the utxo set - long sumUtxo = daoStateService.getUnspentTxOutputMap().values().stream().mapToLong(BaseTxOutput::getValue).sum(); - long sumBsq = genesisTotalSupply + compensationIssuance + reimbursementIssuance - totalAmountOfBurntBsq; - - if (sumBsq != sumUtxo) { - utxoMismatches.add(new UtxoMismatch(block.getHeight(), sumUtxo, sumBsq)); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // StateNetworkService.Listener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onNewStateHashMessage(NewDaoStateHashMessage newStateHashMessage, Connection connection) { - if (newStateHashMessage.getStateHash().getHeight() <= daoStateService.getChainHeight()) { - processPeersDaoStateHash(newStateHashMessage.getStateHash(), connection.getPeersNodeAddressOptional(), true); - } - } - - @Override - public void onGetStateHashRequest(Connection connection, GetDaoStateHashesRequest getStateHashRequest) { - int fromHeight = getStateHashRequest.getHeight(); - List daoStateHashes = daoStateBlockChain.stream() - .filter(e -> e.getHeight() >= fromHeight) - .map(DaoStateBlock::getMyStateHash) - .collect(Collectors.toList()); - daoStateNetworkService.sendGetStateHashesResponse(connection, getStateHashRequest.getNonce(), daoStateHashes); - } - - @Override - public void onPeersStateHashes(List stateHashes, Optional peersNodeAddress) { - AtomicBoolean hasChanged = new AtomicBoolean(false); - - stateHashes.forEach(daoStateHash -> { - boolean changed = processPeersDaoStateHash(daoStateHash, peersNodeAddress, false); - if (changed) { - hasChanged.set(true); - } - }); - - if (hasChanged.get()) { - listeners.forEach(Listener::onChangeAfterBatchProcessing); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void createHashFromBlock(Block block) { - updateHashChain(block); - } - - public void requestHashesFromGenesisBlockHeight(String peersAddress) { - daoStateNetworkService.requestHashes(genesisTxInfo.getGenesisBlockHeight(), peersAddress); - } - - public void applySnapshot(LinkedList persistedDaoStateHashChain) { - // We could get a reset from a reorg, so we clear all and start over from the genesis block. - daoStateHashChain.clear(); - daoStateBlockChain.clear(); - daoStateNetworkService.reset(); - - if (!persistedDaoStateHashChain.isEmpty()) { - log.info("Apply snapshot with {} daoStateHashes. Last daoStateHash={}", - persistedDaoStateHashChain.size(), persistedDaoStateHashChain.getLast()); - } - daoStateHashChain.addAll(persistedDaoStateHashChain); - daoStateHashChain.forEach(e -> daoStateBlockChain.add(new DaoStateBlock(e))); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Listeners - /////////////////////////////////////////////////////////////////////////////////////////// - - public void addListener(Listener listener) { - listeners.add(listener); - } - - public void removeListener(Listener listener) { - listeners.remove(listener); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void updateHashChain(Block block) { - long ts = System.currentTimeMillis(); - byte[] prevHash; - int height = block.getHeight(); - if (daoStateBlockChain.isEmpty()) { - // Only at genesis we allow an empty prevHash - if (height == genesisTxInfo.getGenesisBlockHeight()) { - prevHash = new byte[0]; - } else { - log.warn("DaoStateBlockchain is empty but we received the block which was not the genesis block. " + - "We stop execution here."); - return; - } - } else { - checkArgument(height == daoStateBlockChain.getLast().getHeight() + 1, - "New block must be 1 block above previous block. height={}, " + - "daoStateBlockChain.getLast().getHeight()={}", - height, daoStateBlockChain.getLast().getHeight()); - prevHash = daoStateBlockChain.getLast().getHash(); - } - byte[] stateHash = daoStateService.getSerializedStateForHashChain(); - // We include the prev. hash in our new hash so we can be sure that if one hash is matching all the past would - // match as well. - byte[] combined = ArrayUtils.addAll(prevHash, stateHash); - byte[] hash = Hash.getSha256Ripemd160hash(combined); - - DaoStateHash myDaoStateHash = new DaoStateHash(height, hash, prevHash); - DaoStateBlock daoStateBlock = new DaoStateBlock(myDaoStateHash); - daoStateBlockChain.add(daoStateBlock); - daoStateHashChain.add(myDaoStateHash); - - // We only broadcast after parsing of blockchain is complete - if (parseBlockChainComplete) { - // We notify listeners only after batch processing to avoid performance issues at UI code - listeners.forEach(Listener::onChangeAfterBatchProcessing); - - // We delay broadcast to give peers enough time to have received the block. - // Otherwise they would ignore our data if received block is in future to their local blockchain. - int delayInSec = 5 + new Random().nextInt(10); - UserThread.runAfter(() -> daoStateNetworkService.broadcastMyStateHash(myDaoStateHash), delayInSec); - } - long duration = System.currentTimeMillis() - ts; - // We don't want to spam the output. We log accumulated time after parsing is completed. - log.trace("updateHashChain for block {} took {} ms", - block.getHeight(), - duration); - accumulatedDuration += duration; - numCalls++; - } - - private boolean processPeersDaoStateHash(DaoStateHash daoStateHash, Optional peersNodeAddress, - boolean notifyListeners) { - AtomicBoolean changed = new AtomicBoolean(false); - AtomicBoolean inConflictWithNonSeedNode = new AtomicBoolean(this.isInConflictWithNonSeedNode); - AtomicBoolean inConflictWithSeedNode = new AtomicBoolean(this.isInConflictWithSeedNode); - StringBuilder sb = new StringBuilder(); - daoStateBlockChain.stream() - .filter(e -> e.getHeight() == daoStateHash.getHeight()).findAny() - .ifPresent(daoStateBlock -> { - String peersNodeAddressAsString = peersNodeAddress.map(NodeAddress::getFullAddress) - .orElseGet(() -> "Unknown peer " + new Random().nextInt(10000)); - daoStateBlock.putInPeersMap(peersNodeAddressAsString, daoStateHash); - if (!daoStateBlock.getMyStateHash().hasEqualHash(daoStateHash)) { - daoStateBlock.putInConflictMap(peersNodeAddressAsString, daoStateHash); - if (seedNodeAddresses.contains(peersNodeAddressAsString)) { - inConflictWithSeedNode.set(true); - } else { - inConflictWithNonSeedNode.set(true); - } - sb.append("We received a block hash from peer ") - .append(peersNodeAddressAsString) - .append(" which conflicts with our block hash.\n") - .append("my daoStateHash=") - .append(daoStateBlock.getMyStateHash()) - .append("\npeers daoStateHash=") - .append(daoStateHash); - } - changed.set(true); - }); - - this.isInConflictWithNonSeedNode = inConflictWithNonSeedNode.get(); - this.isInConflictWithSeedNode = inConflictWithSeedNode.get(); - - String conflictMsg = sb.toString(); - if (!conflictMsg.isEmpty()) { - if (this.isInConflictWithSeedNode) - log.warn("Conflict with seed nodes: {}", conflictMsg); - else if (this.isInConflictWithNonSeedNode) - log.debug("Conflict with non-seed nodes: {}", conflictMsg); - } - - - if (notifyListeners && changed.get()) { - listeners.forEach(Listener::onChangeAfterBatchProcessing); - } - - return changed.get(); - } - - private void verifyCheckpoints() { - // Checkpoint - checkpoints.forEach(checkpoint -> daoStateHashChain.stream() - .filter(daoStateHash -> daoStateHash.getHeight() == checkpoint.getHeight()) - .findAny() - .ifPresent(daoStateHash -> { - if (Arrays.equals(daoStateHash.getHash(), checkpoint.getHash())) { - log.info("Passed checkpoint {}", checkpoint.toString()); - } else { - if (checkpointFailed) { - return; - } - checkpointFailed = true; - try { - // Delete state and stop - removeFile("DaoStateStore"); - removeFile("BlindVoteStore"); - removeFile("ProposalStore"); - removeFile("TempProposalStore"); - - listeners.forEach(Listener::onCheckpointFail); - log.error("Failed checkpoint {}", checkpoint.toString()); - } catch (Throwable t) { - t.printStackTrace(); - log.error(t.toString()); - } - } - })); - } - - private void removeFile(String storeName) { - long currentTime = System.currentTimeMillis(); - String newFileName = storeName + "_" + currentTime; - String backupDirName = "out_of_sync_dao_data"; - File corrupted = new File(storageDir, storeName); - try { - if (corrupted.exists()) { - FileUtil.removeAndBackupFile(storageDir, corrupted, newFileName, backupDirName); - } - } catch (Throwable t) { - t.printStackTrace(); - log.error(t.toString()); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/ProposalStateMonitoringService.java b/core/src/main/java/bisq/core/dao/monitoring/ProposalStateMonitoringService.java deleted file mode 100644 index 56581168c0..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/ProposalStateMonitoringService.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.monitoring; - -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.MyProposalList; -import bisq.core.dao.governance.proposal.ProposalService; -import bisq.core.dao.monitoring.model.ProposalStateBlock; -import bisq.core.dao.monitoring.model.ProposalStateHash; -import bisq.core.dao.monitoring.network.ProposalStateNetworkService; -import bisq.core.dao.monitoring.network.messages.GetProposalStateHashesRequest; -import bisq.core.dao.monitoring.network.messages.NewProposalStateHashMessage; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.GenesisTxInfo; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.governance.Cycle; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.dao.state.model.governance.Proposal; - -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.network.Connection; -import bisq.network.p2p.seed.SeedNodeRepository; - -import bisq.common.UserThread; -import bisq.common.crypto.Hash; - -import javax.inject.Inject; - -import org.apache.commons.lang3.ArrayUtils; - -import java.util.Comparator; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Monitors the Proposal P2P network payloads with using a hash of a sorted list of Proposals from one cycle and - * make it accessible to the network so we can detect quickly if any consensus issue arises. - * We create that hash at the first block of the BlindVote phase. There is one hash created per cycle. - * The hash contains the hash of the previous block so we can ensure the validity of the whole history by - * comparing the last block. - * - * We request the state from the connected seed nodes after batch processing of BSQ is complete as well as we start - * to listen for broadcast messages from our peers about dao state of new blocks. - * - * We do NOT persist that chain of hashes as there is only one per cycle and the performance costs are very low. - */ -@Slf4j -public class ProposalStateMonitoringService implements DaoSetupService, DaoStateListener, ProposalStateNetworkService.Listener { - public interface Listener { - void onProposalStateBlockChainChanged(); - } - - private final DaoStateService daoStateService; - private final ProposalStateNetworkService proposalStateNetworkService; - private final GenesisTxInfo genesisTxInfo; - private final PeriodService periodService; - private final ProposalService proposalService; - private final Set seedNodeAddresses; - - - @Getter - private final LinkedList proposalStateBlockChain = new LinkedList<>(); - @Getter - private final LinkedList proposalStateHashChain = new LinkedList<>(); - private final List listeners = new CopyOnWriteArrayList<>(); - @Getter - private boolean isInConflictWithNonSeedNode; - @Getter - private boolean isInConflictWithSeedNode; - private boolean parseBlockChainComplete; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public ProposalStateMonitoringService(DaoStateService daoStateService, - ProposalStateNetworkService proposalStateNetworkService, - GenesisTxInfo genesisTxInfo, - PeriodService periodService, - ProposalService proposalService, - SeedNodeRepository seedNodeRepository) { - this.daoStateService = daoStateService; - this.proposalStateNetworkService = proposalStateNetworkService; - this.genesisTxInfo = genesisTxInfo; - this.periodService = periodService; - this.proposalService = proposalService; - seedNodeAddresses = seedNodeRepository.getSeedNodeAddresses().stream() - .map(NodeAddress::getFullAddress) - .collect(Collectors.toSet()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - daoStateService.addDaoStateListener(this); - proposalStateNetworkService.addListener(this); - } - - @Override - public void start() { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @SuppressWarnings("Duplicates") - public void onDaoStateChanged(Block block) { - int blockHeight = block.getHeight(); - int genesisBlockHeight = genesisTxInfo.getGenesisBlockHeight(); - - boolean hashChainUpdated = false; - if (proposalStateBlockChain.isEmpty() && blockHeight > genesisBlockHeight) { - // Takes about 150 ms for dao testnet data - long ts = System.currentTimeMillis(); - for (int i = genesisBlockHeight; i < blockHeight; i++) { - boolean isHashChainUpdated = maybeUpdateHashChain(i); - if (isHashChainUpdated) { - hashChainUpdated = true; - } - } - if (hashChainUpdated) { - log.info("updateHashChain for {} blocks took {} ms", - blockHeight - genesisBlockHeight, - System.currentTimeMillis() - ts); - } - } - long ts = System.currentTimeMillis(); - boolean updated = maybeUpdateHashChain(blockHeight); - if (updated) { - log.info("updateHashChain for block {} took {} ms", - blockHeight, - System.currentTimeMillis() - ts); - } - } - - @SuppressWarnings("Duplicates") - @Override - public void onParseBlockChainComplete() { - parseBlockChainComplete = true; - proposalStateNetworkService.addListeners(); - - // We wait for processing messages until we have completed batch processing - - // We request data from last 5 cycles. We ignore possible duration changes done by voting. - // period is arbitrary anyway... - Cycle currentCycle = periodService.getCurrentCycle(); - checkNotNull(currentCycle, "currentCycle must not be null"); - int fromHeight = Math.max(genesisTxInfo.getGenesisBlockHeight(), daoStateService.getChainHeight() - currentCycle.getDuration() * 5); - - proposalStateNetworkService.requestHashesFromAllConnectedSeedNodes(fromHeight); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // StateNetworkService.Listener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onNewStateHashMessage(NewProposalStateHashMessage newStateHashMessage, Connection connection) { - if (newStateHashMessage.getStateHash().getHeight() <= daoStateService.getChainHeight()) { - processPeersProposalStateHash(newStateHashMessage.getStateHash(), connection.getPeersNodeAddressOptional(), true); - } - } - - @Override - public void onGetStateHashRequest(Connection connection, GetProposalStateHashesRequest getStateHashRequest) { - int fromHeight = getStateHashRequest.getHeight(); - List proposalStateHashes = proposalStateBlockChain.stream() - .filter(e -> e.getHeight() >= fromHeight) - .map(ProposalStateBlock::getMyStateHash) - .collect(Collectors.toList()); - proposalStateNetworkService.sendGetStateHashesResponse(connection, getStateHashRequest.getNonce(), proposalStateHashes); - } - - @Override - public void onPeersStateHashes(List stateHashes, Optional peersNodeAddress) { - AtomicBoolean hasChanged = new AtomicBoolean(false); - stateHashes.forEach(daoStateHash -> { - boolean changed = processPeersProposalStateHash(daoStateHash, peersNodeAddress, false); - if (changed) { - hasChanged.set(true); - } - }); - - if (hasChanged.get()) { - listeners.forEach(Listener::onProposalStateBlockChainChanged); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void requestHashesFromGenesisBlockHeight(String peersAddress) { - proposalStateNetworkService.requestHashes(genesisTxInfo.getGenesisBlockHeight(), peersAddress); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Listeners - /////////////////////////////////////////////////////////////////////////////////////////// - - public void addListener(Listener listener) { - listeners.add(listener); - } - - public void removeListener(Listener listener) { - listeners.remove(listener); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private boolean maybeUpdateHashChain(int blockHeight) { - // We use first block in blind vote phase to create the hash of our proposals. We prefer to wait as long as - // possible to increase the chance that we have received all proposals. - if (!isFirstBlockOfBlindVotePhase(blockHeight)) { - return false; - } - - periodService.getCycle(blockHeight).ifPresent(cycle -> { - List proposals = proposalService.getValidatedProposals().stream() - .filter(e -> periodService.isTxInPhaseAndCycle(e.getTxId(), DaoPhase.Phase.PROPOSAL, blockHeight)) - .filter(e -> e.getTxId() != null) - .sorted(Comparator.comparing(Proposal::getTxId)) - .collect(Collectors.toList()); - - // We use MyProposalList to get the serialized bytes from the proposals list - byte[] serializedProposals = new MyProposalList(proposals).toProtoMessage().toByteArray(); - - byte[] prevHash; - if (proposalStateBlockChain.isEmpty()) { - prevHash = new byte[0]; - } else { - prevHash = proposalStateBlockChain.getLast().getHash(); - } - byte[] combined = ArrayUtils.addAll(prevHash, serializedProposals); - byte[] hash = Hash.getSha256Ripemd160hash(combined); - ProposalStateHash myProposalStateHash = new ProposalStateHash(blockHeight, hash, prevHash, proposals.size()); - ProposalStateBlock proposalStateBlock = new ProposalStateBlock(myProposalStateHash); - proposalStateBlockChain.add(proposalStateBlock); - proposalStateHashChain.add(myProposalStateHash); - - // We only broadcast after parsing of blockchain is complete - if (parseBlockChainComplete) { - // We notify listeners only after batch processing to avoid performance issues at UI code - listeners.forEach(Listener::onProposalStateBlockChainChanged); - - // We delay broadcast to give peers enough time to have received the block. - // Otherwise they would ignore our data if received block is in future to their local blockchain. - int delayInSec = 5 + new Random().nextInt(10); - UserThread.runAfter(() -> proposalStateNetworkService.broadcastMyStateHash(myProposalStateHash), delayInSec); - } - }); - return true; - } - - private boolean processPeersProposalStateHash(ProposalStateHash proposalStateHash, Optional peersNodeAddress, boolean notifyListeners) { - AtomicBoolean changed = new AtomicBoolean(false); - AtomicBoolean inConflictWithNonSeedNode = new AtomicBoolean(this.isInConflictWithNonSeedNode); - AtomicBoolean inConflictWithSeedNode = new AtomicBoolean(this.isInConflictWithSeedNode); - StringBuilder sb = new StringBuilder(); - proposalStateBlockChain.stream() - .filter(e -> e.getHeight() == proposalStateHash.getHeight()).findAny() - .ifPresent(daoStateBlock -> { - String peersNodeAddressAsString = peersNodeAddress.map(NodeAddress::getFullAddress) - .orElseGet(() -> "Unknown peer " + new Random().nextInt(10000)); - daoStateBlock.putInPeersMap(peersNodeAddressAsString, proposalStateHash); - if (!daoStateBlock.getMyStateHash().hasEqualHash(proposalStateHash)) { - daoStateBlock.putInConflictMap(peersNodeAddressAsString, proposalStateHash); - if (seedNodeAddresses.contains(peersNodeAddressAsString)) { - inConflictWithSeedNode.set(true); - } else { - inConflictWithNonSeedNode.set(true); - } - sb.append("We received a block hash from peer ") - .append(peersNodeAddressAsString) - .append(" which conflicts with our block hash.\n") - .append("my proposalStateHash=") - .append(daoStateBlock.getMyStateHash()) - .append("\npeers proposalStateHash=") - .append(proposalStateHash); - } - changed.set(true); - }); - - this.isInConflictWithNonSeedNode = inConflictWithNonSeedNode.get(); - this.isInConflictWithSeedNode = inConflictWithSeedNode.get(); - - String conflictMsg = sb.toString(); - if (!conflictMsg.isEmpty()) { - if (this.isInConflictWithSeedNode) - log.warn("Conflict with seed nodes: {}", conflictMsg); - else if (this.isInConflictWithNonSeedNode) - log.info("Conflict with non-seed nodes: {}", conflictMsg); - } - - if (notifyListeners && changed.get()) { - listeners.forEach(Listener::onProposalStateBlockChainChanged); - } - - return changed.get(); - } - - private boolean isFirstBlockOfBlindVotePhase(int blockHeight) { - return blockHeight == periodService.getFirstBlockOfPhase(blockHeight, DaoPhase.Phase.BLIND_VOTE); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/BlindVoteStateBlock.java b/core/src/main/java/bisq/core/dao/monitoring/model/BlindVoteStateBlock.java deleted file mode 100644 index bc527eeb6e..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/model/BlindVoteStateBlock.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.model; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@Getter -@EqualsAndHashCode(callSuper = true) -public class BlindVoteStateBlock extends StateBlock { - public BlindVoteStateBlock(BlindVoteStateHash myBlindVoteStateHash) { - super(myBlindVoteStateHash); - } - - public int getNumBlindVotes() { - return myStateHash.getNumBlindVotes(); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/BlindVoteStateHash.java b/core/src/main/java/bisq/core/dao/monitoring/model/BlindVoteStateHash.java deleted file mode 100644 index 79a59032c7..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/model/BlindVoteStateHash.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.model; - - -import com.google.protobuf.ByteString; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@EqualsAndHashCode(callSuper = true) - -public final class BlindVoteStateHash extends StateHash { - @Getter - private final int numBlindVotes; - - public BlindVoteStateHash(int cycleStartBlockHeight, byte[] hash, byte[] prevHash, int numBlindVotes) { - super(cycleStartBlockHeight, hash, prevHash); - this.numBlindVotes = numBlindVotes; - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.BlindVoteStateHash toProtoMessage() { - return protobuf.BlindVoteStateHash.newBuilder() - .setHeight(height) - .setHash(ByteString.copyFrom(hash)) - .setPrevHash(ByteString.copyFrom(prevHash)) - .setNumBlindVotes(numBlindVotes).build(); - } - - public static BlindVoteStateHash fromProto(protobuf.BlindVoteStateHash proto) { - return new BlindVoteStateHash(proto.getHeight(), - proto.getHash().toByteArray(), - proto.getPrevHash().toByteArray(), - proto.getNumBlindVotes()); - } - - @Override - public String toString() { - return "BlindVoteStateHash{" + - "\n numBlindVotes=" + numBlindVotes + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/DaoStateBlock.java b/core/src/main/java/bisq/core/dao/monitoring/model/DaoStateBlock.java deleted file mode 100644 index 8bd91e67fa..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/model/DaoStateBlock.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.model; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@Getter -@EqualsAndHashCode(callSuper = true) -public class DaoStateBlock extends StateBlock { - public DaoStateBlock(DaoStateHash myDaoStateHash) { - super(myDaoStateHash); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/DaoStateHash.java b/core/src/main/java/bisq/core/dao/monitoring/model/DaoStateHash.java deleted file mode 100644 index e0e7f5e403..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/model/DaoStateHash.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.model; - - -import com.google.protobuf.ByteString; - -import lombok.EqualsAndHashCode; - -@EqualsAndHashCode(callSuper = true) -public final class DaoStateHash extends StateHash { - public DaoStateHash(int height, byte[] hash, byte[] prevHash) { - super(height, hash, prevHash); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.DaoStateHash toProtoMessage() { - return protobuf.DaoStateHash.newBuilder() - .setHeight(height) - .setHash(ByteString.copyFrom(hash)) - .setPrevHash(ByteString.copyFrom(prevHash)).build(); - } - - public static DaoStateHash fromProto(protobuf.DaoStateHash proto) { - return new DaoStateHash(proto.getHeight(), - proto.getHash().toByteArray(), - proto.getPrevHash().toByteArray()); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/ProposalStateBlock.java b/core/src/main/java/bisq/core/dao/monitoring/model/ProposalStateBlock.java deleted file mode 100644 index ddfc83dbb6..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/model/ProposalStateBlock.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.model; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@Getter -@EqualsAndHashCode(callSuper = true) -public class ProposalStateBlock extends StateBlock { - public ProposalStateBlock(ProposalStateHash myProposalStateHash) { - super(myProposalStateHash); - } - - public int getNumProposals() { - return myStateHash.getNumProposals(); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/ProposalStateHash.java b/core/src/main/java/bisq/core/dao/monitoring/model/ProposalStateHash.java deleted file mode 100644 index dc2ccdeabf..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/model/ProposalStateHash.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.model; - - -import com.google.protobuf.ByteString; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@EqualsAndHashCode(callSuper = true) - -public final class ProposalStateHash extends StateHash { - @Getter - private final int numProposals; - - public ProposalStateHash(int cycleStartBlockHeight, byte[] hash, byte[] prevHash, int numProposals) { - super(cycleStartBlockHeight, hash, prevHash); - this.numProposals = numProposals; - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.ProposalStateHash toProtoMessage() { - return protobuf.ProposalStateHash.newBuilder() - .setHeight(height) - .setHash(ByteString.copyFrom(hash)) - .setPrevHash(ByteString.copyFrom(prevHash)) - .setNumProposals(numProposals).build(); - } - - public static ProposalStateHash fromProto(protobuf.ProposalStateHash proto) { - return new ProposalStateHash(proto.getHeight(), - proto.getHash().toByteArray(), - proto.getPrevHash().toByteArray(), - proto.getNumProposals()); - } - - - @Override - public String toString() { - return "ProposalStateHash{" + - "\n numProposals=" + numProposals + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/StateBlock.java b/core/src/main/java/bisq/core/dao/monitoring/model/StateBlock.java deleted file mode 100644 index b41ddc2296..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/model/StateBlock.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.model; - -import java.util.HashMap; -import java.util.Map; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -/** - * Contains my StateHash at a particular block height and the received stateHash from our peers. - * The maps get updated over time, this is not an immutable class. - */ -@Getter -@EqualsAndHashCode -public abstract class StateBlock { - protected final T myStateHash; - - private final Map peersMap = new HashMap<>(); - private final Map inConflictMap = new HashMap<>(); - - StateBlock(T myStateHash) { - this.myStateHash = myStateHash; - } - - public void putInPeersMap(String peersNodeAddress, T stateHash) { - peersMap.putIfAbsent(peersNodeAddress, stateHash); - } - - public void putInConflictMap(String peersNodeAddress, T stateHash) { - inConflictMap.putIfAbsent(peersNodeAddress, stateHash); - } - - // Delegates - public int getHeight() { - return myStateHash.getHeight(); - } - - public byte[] getHash() { - return myStateHash.getHash(); - } - - public byte[] getPrevHash() { - return myStateHash.getPrevHash(); - } - - @Override - public String toString() { - return "StateBlock{" + - "\n myStateHash=" + myStateHash + - ",\n peersMap=" + peersMap + - ",\n inConflictMap=" + inConflictMap + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/StateHash.java b/core/src/main/java/bisq/core/dao/monitoring/model/StateHash.java deleted file mode 100644 index 12cb299a11..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/model/StateHash.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.model; - - -import bisq.common.proto.network.NetworkPayload; -import bisq.common.proto.persistable.PersistablePayload; -import bisq.common.util.Utilities; - -import java.util.Arrays; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Contains the blockHeight, the hash and the previous hash of the state. - * As the hash is created from the state at the particular height including the previous hash we get the history of - * the full chain included and we know if the hash matches at a particular height that all the past blocks need to match - * as well. - */ -@EqualsAndHashCode -@Getter -@Slf4j -public abstract class StateHash implements PersistablePayload, NetworkPayload { - protected final int height; - protected final byte[] hash; - // For first block the prevHash is an empty byte array - protected final byte[] prevHash; - - StateHash(int height, byte[] hash, byte[] prevHash) { - this.height = height; - this.hash = hash; - this.prevHash = prevHash; - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - public boolean hasEqualHash(StateHash other) { - return Arrays.equals(hash, other.getHash()); - } - - public byte[] getHash() { - return hash; - } - - @Override - public String toString() { - return "StateHash{" + - "\n height=" + height + - ",\n hash=" + Utilities.bytesAsHexString(hash) + - ",\n prevHash=" + Utilities.bytesAsHexString(prevHash) + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/UtxoMismatch.java b/core/src/main/java/bisq/core/dao/monitoring/model/UtxoMismatch.java deleted file mode 100644 index 1509023c92..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/model/UtxoMismatch.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.model; - -import lombok.Value; - -@Value -public class UtxoMismatch { - private final int height; - private final long sumUtxo; - private final long sumBsq; - - public UtxoMismatch(int height, long sumUtxo, long sumBsq) { - this.height = height; - this.sumUtxo = sumUtxo; - this.sumBsq = sumBsq; - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/BlindVoteStateNetworkService.java b/core/src/main/java/bisq/core/dao/monitoring/network/BlindVoteStateNetworkService.java deleted file mode 100644 index 81bd46f8ee..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/BlindVoteStateNetworkService.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network; - -import bisq.core.dao.monitoring.model.BlindVoteStateHash; -import bisq.core.dao.monitoring.network.messages.GetBlindVoteStateHashesRequest; -import bisq.core.dao.monitoring.network.messages.GetBlindVoteStateHashesResponse; -import bisq.core.dao.monitoring.network.messages.NewBlindVoteStateHashMessage; - -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.network.NetworkNode; -import bisq.network.p2p.peers.Broadcaster; -import bisq.network.p2p.peers.PeerManager; - -import bisq.common.proto.network.NetworkEnvelope; - -import javax.inject.Inject; - -import java.util.List; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class BlindVoteStateNetworkService extends StateNetworkService { - @Inject - public BlindVoteStateNetworkService(NetworkNode networkNode, - PeerManager peerManager, - Broadcaster broadcaster) { - super(networkNode, peerManager, broadcaster); - } - - @Override - protected GetBlindVoteStateHashesRequest castToGetStateHashRequest(NetworkEnvelope networkEnvelope) { - return (GetBlindVoteStateHashesRequest) networkEnvelope; - } - - @Override - protected boolean isGetStateHashesRequest(NetworkEnvelope networkEnvelope) { - return networkEnvelope instanceof GetBlindVoteStateHashesRequest; - } - - @Override - protected NewBlindVoteStateHashMessage castToNewStateHashMessage(NetworkEnvelope networkEnvelope) { - return (NewBlindVoteStateHashMessage) networkEnvelope; - } - - @Override - protected boolean isNewStateHashMessage(NetworkEnvelope networkEnvelope) { - return networkEnvelope instanceof NewBlindVoteStateHashMessage; - } - - @Override - protected GetBlindVoteStateHashesResponse getGetStateHashesResponse(int nonce, List stateHashes) { - return new GetBlindVoteStateHashesResponse(stateHashes, nonce); - } - - @Override - protected NewBlindVoteStateHashMessage getNewStateHashMessage(BlindVoteStateHash myStateHash) { - return new NewBlindVoteStateHashMessage(myStateHash); - } - - @Override - protected RequestBlindVoteStateHashesHandler getRequestStateHashesHandler(NodeAddress nodeAddress, RequestStateHashesHandler.Listener listener) { - return new RequestBlindVoteStateHashesHandler(networkNode, peerManager, nodeAddress, listener); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/Checkpoint.java b/core/src/main/java/bisq/core/dao/monitoring/network/Checkpoint.java deleted file mode 100644 index 911db2e15a..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/Checkpoint.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network; - -import bisq.common.util.Utilities; - -import lombok.Getter; -import lombok.Setter; - -@Getter -public class Checkpoint { - final int height; - final byte[] hash; - @Setter - boolean passed; - - public Checkpoint(int height, byte[] hash) { - this.height = height; - this.hash = hash; - } - - @Override - public String toString() { - return "Checkpoint {" + - "\n height=" + height + - ",\n hash=" + Utilities.bytesAsHexString(hash) + - "\n}"; - } - -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/DaoStateNetworkService.java b/core/src/main/java/bisq/core/dao/monitoring/network/DaoStateNetworkService.java deleted file mode 100644 index e394977b31..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/DaoStateNetworkService.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network; - -import bisq.core.dao.monitoring.model.DaoStateHash; -import bisq.core.dao.monitoring.network.messages.GetDaoStateHashesRequest; -import bisq.core.dao.monitoring.network.messages.GetDaoStateHashesResponse; -import bisq.core.dao.monitoring.network.messages.NewDaoStateHashMessage; - -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.network.NetworkNode; -import bisq.network.p2p.peers.Broadcaster; -import bisq.network.p2p.peers.PeerManager; - -import bisq.common.proto.network.NetworkEnvelope; - -import javax.inject.Inject; - -import java.util.List; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class DaoStateNetworkService extends StateNetworkService { - @Inject - public DaoStateNetworkService(NetworkNode networkNode, - PeerManager peerManager, - Broadcaster broadcaster) { - super(networkNode, peerManager, broadcaster); - } - - @Override - protected GetDaoStateHashesRequest castToGetStateHashRequest(NetworkEnvelope networkEnvelope) { - return (GetDaoStateHashesRequest) networkEnvelope; - } - - @Override - protected boolean isGetStateHashesRequest(NetworkEnvelope networkEnvelope) { - return networkEnvelope instanceof GetDaoStateHashesRequest; - } - - @Override - protected NewDaoStateHashMessage castToNewStateHashMessage(NetworkEnvelope networkEnvelope) { - return (NewDaoStateHashMessage) networkEnvelope; - } - - @Override - protected boolean isNewStateHashMessage(NetworkEnvelope networkEnvelope) { - return networkEnvelope instanceof NewDaoStateHashMessage; - } - - @Override - protected GetDaoStateHashesResponse getGetStateHashesResponse(int nonce, List stateHashes) { - return new GetDaoStateHashesResponse(stateHashes, nonce); - } - - @Override - protected NewDaoStateHashMessage getNewStateHashMessage(DaoStateHash myStateHash) { - return new NewDaoStateHashMessage(myStateHash); - } - - @Override - protected RequestDaoStateHashesHandler getRequestStateHashesHandler(NodeAddress nodeAddress, RequestStateHashesHandler.Listener listener) { - return new RequestDaoStateHashesHandler(networkNode, peerManager, nodeAddress, listener); - } - -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/ProposalStateNetworkService.java b/core/src/main/java/bisq/core/dao/monitoring/network/ProposalStateNetworkService.java deleted file mode 100644 index 76e6f33bd1..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/ProposalStateNetworkService.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network; - -import bisq.core.dao.monitoring.model.ProposalStateHash; -import bisq.core.dao.monitoring.network.messages.GetProposalStateHashesRequest; -import bisq.core.dao.monitoring.network.messages.GetProposalStateHashesResponse; -import bisq.core.dao.monitoring.network.messages.NewProposalStateHashMessage; - -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.network.NetworkNode; -import bisq.network.p2p.peers.Broadcaster; -import bisq.network.p2p.peers.PeerManager; - -import bisq.common.proto.network.NetworkEnvelope; - -import javax.inject.Inject; - -import java.util.List; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class ProposalStateNetworkService extends StateNetworkService { - @Inject - public ProposalStateNetworkService(NetworkNode networkNode, - PeerManager peerManager, - Broadcaster broadcaster) { - super(networkNode, peerManager, broadcaster); - } - - @Override - protected GetProposalStateHashesRequest castToGetStateHashRequest(NetworkEnvelope networkEnvelope) { - return (GetProposalStateHashesRequest) networkEnvelope; - } - - @Override - protected boolean isGetStateHashesRequest(NetworkEnvelope networkEnvelope) { - return networkEnvelope instanceof GetProposalStateHashesRequest; - } - - @Override - protected NewProposalStateHashMessage castToNewStateHashMessage(NetworkEnvelope networkEnvelope) { - return (NewProposalStateHashMessage) networkEnvelope; - } - - @Override - protected boolean isNewStateHashMessage(NetworkEnvelope networkEnvelope) { - return networkEnvelope instanceof NewProposalStateHashMessage; - } - - @Override - protected GetProposalStateHashesResponse getGetStateHashesResponse(int nonce, List stateHashes) { - return new GetProposalStateHashesResponse(stateHashes, nonce); - } - - @Override - protected NewProposalStateHashMessage getNewStateHashMessage(ProposalStateHash myStateHash) { - return new NewProposalStateHashMessage(myStateHash); - } - - @Override - protected RequestProposalStateHashesHandler getRequestStateHashesHandler(NodeAddress nodeAddress, RequestStateHashesHandler.Listener listener) { - return new RequestProposalStateHashesHandler(networkNode, peerManager, nodeAddress, listener); - } - -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/RequestBlindVoteStateHashesHandler.java b/core/src/main/java/bisq/core/dao/monitoring/network/RequestBlindVoteStateHashesHandler.java deleted file mode 100644 index 5f33b05a48..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/RequestBlindVoteStateHashesHandler.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network; - -import bisq.core.dao.monitoring.network.messages.GetBlindVoteStateHashesRequest; -import bisq.core.dao.monitoring.network.messages.GetBlindVoteStateHashesResponse; - -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.network.NetworkNode; -import bisq.network.p2p.peers.PeerManager; - -import bisq.common.proto.network.NetworkEnvelope; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class RequestBlindVoteStateHashesHandler extends RequestStateHashesHandler { - RequestBlindVoteStateHashesHandler(NetworkNode networkNode, - PeerManager peerManager, - NodeAddress nodeAddress, - Listener listener) { - super(networkNode, peerManager, nodeAddress, listener); - } - - @Override - protected GetBlindVoteStateHashesRequest getGetStateHashesRequest(int fromHeight) { - return new GetBlindVoteStateHashesRequest(fromHeight, nonce); - } - - @Override - protected GetBlindVoteStateHashesResponse castToGetStateHashesResponse(NetworkEnvelope networkEnvelope) { - return (GetBlindVoteStateHashesResponse) networkEnvelope; - } - - @Override - protected boolean isGetStateHashesResponse(NetworkEnvelope networkEnvelope) { - return networkEnvelope instanceof GetBlindVoteStateHashesResponse; - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/RequestDaoStateHashesHandler.java b/core/src/main/java/bisq/core/dao/monitoring/network/RequestDaoStateHashesHandler.java deleted file mode 100644 index 6a7b7bdcc7..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/RequestDaoStateHashesHandler.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network; - -import bisq.core.dao.monitoring.network.messages.GetDaoStateHashesRequest; -import bisq.core.dao.monitoring.network.messages.GetDaoStateHashesResponse; - -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.network.NetworkNode; -import bisq.network.p2p.peers.PeerManager; - -import bisq.common.proto.network.NetworkEnvelope; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class RequestDaoStateHashesHandler extends RequestStateHashesHandler { - RequestDaoStateHashesHandler(NetworkNode networkNode, - PeerManager peerManager, - NodeAddress nodeAddress, - Listener listener) { - super(networkNode, peerManager, nodeAddress, listener); - } - - @Override - protected GetDaoStateHashesRequest getGetStateHashesRequest(int fromHeight) { - return new GetDaoStateHashesRequest(fromHeight, nonce); - } - - @Override - protected GetDaoStateHashesResponse castToGetStateHashesResponse(NetworkEnvelope networkEnvelope) { - return (GetDaoStateHashesResponse) networkEnvelope; - } - - @Override - protected boolean isGetStateHashesResponse(NetworkEnvelope networkEnvelope) { - return networkEnvelope instanceof GetDaoStateHashesResponse; - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/RequestProposalStateHashesHandler.java b/core/src/main/java/bisq/core/dao/monitoring/network/RequestProposalStateHashesHandler.java deleted file mode 100644 index 0e4a20502e..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/RequestProposalStateHashesHandler.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network; - -import bisq.core.dao.monitoring.network.messages.GetProposalStateHashesRequest; -import bisq.core.dao.monitoring.network.messages.GetProposalStateHashesResponse; - -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.network.NetworkNode; -import bisq.network.p2p.peers.PeerManager; - -import bisq.common.proto.network.NetworkEnvelope; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class RequestProposalStateHashesHandler extends RequestStateHashesHandler { - RequestProposalStateHashesHandler(NetworkNode networkNode, - PeerManager peerManager, - NodeAddress nodeAddress, - Listener listener) { - super(networkNode, peerManager, nodeAddress, listener); - } - - @Override - protected GetProposalStateHashesRequest getGetStateHashesRequest(int fromHeight) { - return new GetProposalStateHashesRequest(fromHeight, nonce); - } - - @Override - protected GetProposalStateHashesResponse castToGetStateHashesResponse(NetworkEnvelope networkEnvelope) { - return (GetProposalStateHashesResponse) networkEnvelope; - } - - @Override - protected boolean isGetStateHashesResponse(NetworkEnvelope networkEnvelope) { - return networkEnvelope instanceof GetProposalStateHashesResponse; - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/RequestStateHashesHandler.java b/core/src/main/java/bisq/core/dao/monitoring/network/RequestStateHashesHandler.java deleted file mode 100644 index 602677121b..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/RequestStateHashesHandler.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network; - -import bisq.core.dao.monitoring.network.messages.GetStateHashesRequest; -import bisq.core.dao.monitoring.network.messages.GetStateHashesResponse; - -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.network.CloseConnectionReason; -import bisq.network.p2p.network.Connection; -import bisq.network.p2p.network.MessageListener; -import bisq.network.p2p.network.NetworkNode; -import bisq.network.p2p.peers.PeerManager; - -import bisq.common.Timer; -import bisq.common.UserThread; -import bisq.common.proto.network.NetworkEnvelope; - -import com.google.common.util.concurrent.FutureCallback; -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 lombok.extern.slf4j.Slf4j; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@Slf4j -abstract class RequestStateHashesHandler implements MessageListener { - private static final long TIMEOUT = 120; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Listener - /////////////////////////////////////////////////////////////////////////////////////////// - - public interface Listener { - void onComplete(Res getStateHashesResponse, Optional peersNodeAddress); - - @SuppressWarnings("UnusedParameters") - void onFault(String errorMessage, @SuppressWarnings("SameParameterValue") @Nullable Connection connection); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Class fields - /////////////////////////////////////////////////////////////////////////////////////////// - - private final NetworkNode networkNode; - private final PeerManager peerManager; - private final NodeAddress nodeAddress; - private final Listener listener; - private Timer timeoutTimer; - final int nonce = new Random().nextInt(); - private boolean stopped; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - RequestStateHashesHandler(NetworkNode networkNode, - PeerManager peerManager, - NodeAddress nodeAddress, - Listener listener) { - this.networkNode = networkNode; - this.peerManager = peerManager; - this.nodeAddress = nodeAddress; - this.listener = listener; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Abstract - /////////////////////////////////////////////////////////////////////////////////////////// - - protected abstract Req getGetStateHashesRequest(int fromHeight); - - protected abstract Res castToGetStateHashesResponse(NetworkEnvelope networkEnvelope); - - protected abstract boolean isGetStateHashesResponse(NetworkEnvelope networkEnvelope); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void requestStateHashes(int fromHeight) { - if (!stopped) { - Req getStateHashesRequest = getGetStateHashesRequest(fromHeight); - if (timeoutTimer == null) { - timeoutTimer = UserThread.runAfter(() -> { // setup before sending to avoid race conditions - if (!stopped) { - String errorMessage = "A timeout occurred at sending getStateHashesRequest:" + getStateHashesRequest + - " on peersNodeAddress:" + nodeAddress; - log.debug(errorMessage + " / RequestStateHashesHandler=" + RequestStateHashesHandler.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 a previous networkNode.sendMessage.onFailure."); - } - }, - TIMEOUT); - } - - log.debug("We send to peer {} a {}.", nodeAddress, getStateHashesRequest); - networkNode.addMessageListener(this); - SettableFuture future = networkNode.sendMessage(nodeAddress, getStateHashesRequest); - Futures.addCallback(future, new FutureCallback<>() { - @Override - public void onSuccess(Connection connection) { - if (!stopped) { - log.info("Sending of {} message to peer {} succeeded.", - getStateHashesRequest.getClass().getSimpleName(), - 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 getStateHashesRequest to " + nodeAddress + - " failed. That is expected if the peer is offline.\n\t" + - "getStateHashesRequest=" + getStateHashesRequest + "." + - "\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 requestProposalsHash call."); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // MessageListener implementation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { - if (isGetStateHashesResponse(networkEnvelope)) { - if (connection.getPeersNodeAddressOptional().isPresent() && connection.getPeersNodeAddressOptional().get().equals(nodeAddress)) { - if (!stopped) { - Res getStateHashesResponse = castToGetStateHashesResponse(networkEnvelope); - if (getStateHashesResponse.getRequestNonce() == nonce) { - stopTimeoutTimer(); - cleanup(); - log.info("We received from peer {} a {} with {} stateHashes", - nodeAddress.getFullAddress(), getStateHashesResponse.getClass().getSimpleName(), - getStateHashesResponse.getStateHashes().size()); - listener.onComplete(getStateHashesResponse, connection.getPeersNodeAddressOptional()); - } 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, getStateHashesResponse.getRequestNonce()); - } - } else { - log.warn("We have stopped already."); - } - } else if (connection.getPeersNodeAddressOptional().isPresent()) { - log.debug("{}: We got a message from another node. We ignore that.", - this.getClass().getSimpleName()); - } - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - @SuppressWarnings("UnusedParameters") - private void handleFault(String errorMessage, NodeAddress nodeAddress, CloseConnectionReason closeConnectionReason) { - cleanup(); - peerManager.handleConnectionFault(nodeAddress); - listener.onFault(errorMessage, null); - } - - private void cleanup() { - stopped = true; - networkNode.removeMessageListener(this); - stopTimeoutTimer(); - } - - private void stopTimeoutTimer() { - if (timeoutTimer != null) { - timeoutTimer.stop(); - timeoutTimer = null; - } - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/StateNetworkService.java b/core/src/main/java/bisq/core/dao/monitoring/network/StateNetworkService.java deleted file mode 100644 index 8300c76b23..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/StateNetworkService.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network; - -import bisq.core.dao.monitoring.model.StateHash; -import bisq.core.dao.monitoring.network.messages.GetStateHashesRequest; -import bisq.core.dao.monitoring.network.messages.GetStateHashesResponse; -import bisq.core.dao.monitoring.network.messages.NewStateHashMessage; - -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.network.Connection; -import bisq.network.p2p.network.MessageListener; -import bisq.network.p2p.network.NetworkNode; -import bisq.network.p2p.peers.Broadcaster; -import bisq.network.p2p.peers.PeerManager; - -import bisq.common.proto.network.NetworkEnvelope; - -import javax.inject.Inject; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.CopyOnWriteArrayList; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -@Slf4j -public abstract class StateNetworkService, - Han extends RequestStateHashesHandler, - StH extends StateHash> implements MessageListener { - - public interface Listener { - void onNewStateHashMessage(Msg newStateHashMessage, Connection connection); - - void onGetStateHashRequest(Connection connection, Req getStateHashRequest); - - void onPeersStateHashes(List stateHashes, Optional peersNodeAddress); - } - - protected final NetworkNode networkNode; - protected final PeerManager peerManager; - private final Broadcaster broadcaster; - - @Getter - private final Map requestStateHashHandlerMap = new HashMap<>(); - private final List> listeners = new CopyOnWriteArrayList<>(); - private boolean messageListenerAdded; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public StateNetworkService(NetworkNode networkNode, - PeerManager peerManager, - Broadcaster broadcaster) { - this.networkNode = networkNode; - this.peerManager = peerManager; - this.broadcaster = broadcaster; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Abstract - /////////////////////////////////////////////////////////////////////////////////////////// - - protected abstract Req castToGetStateHashRequest(NetworkEnvelope networkEnvelope); - - - protected abstract boolean isGetStateHashesRequest(NetworkEnvelope networkEnvelope); - - - protected abstract Msg castToNewStateHashMessage(NetworkEnvelope networkEnvelope); - - - protected abstract boolean isNewStateHashMessage(NetworkEnvelope networkEnvelope); - - protected abstract Res getGetStateHashesResponse(int nonce, List stateHashes); - - protected abstract Msg getNewStateHashMessage(StH myStateHash); - - protected abstract Han getRequestStateHashesHandler(NodeAddress nodeAddress, RequestStateHashesHandler.Listener listener); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // MessageListener implementation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { - if (isNewStateHashMessage(networkEnvelope)) { - Msg newStateHashMessage = castToNewStateHashMessage(networkEnvelope); - log.debug("We received a {} from peer {} with stateHash={} ", - newStateHashMessage.getClass().getSimpleName(), - connection.getPeersNodeAddressOptional(), - newStateHashMessage.getStateHash()); - listeners.forEach(e -> e.onNewStateHashMessage(newStateHashMessage, connection)); - } else if (isGetStateHashesRequest(networkEnvelope)) { - Req getStateHashRequest = castToGetStateHashRequest(networkEnvelope); - log.debug("We received a {} from peer {} for height={} ", - getStateHashRequest.getClass().getSimpleName(), - connection.getPeersNodeAddressOptional(), - getStateHashRequest.getHeight()); - listeners.forEach(e -> e.onGetStateHashRequest(connection, getStateHashRequest)); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void addListeners() { - if (!messageListenerAdded) { - networkNode.addMessageListener(this); - messageListenerAdded = true; - } - } - - public void sendGetStateHashesResponse(Connection connection, int nonce, List stateHashes) { - Res getStateHashesResponse = getGetStateHashesResponse(nonce, stateHashes); - log.info("Send {} with {} stateHashes to peer {}", getStateHashesResponse.getClass().getSimpleName(), - stateHashes.size(), connection.getPeersNodeAddressOptional()); - connection.sendMessage(getStateHashesResponse); - } - - public void requestHashesFromAllConnectedSeedNodes(int fromHeight) { - networkNode.getConfirmedConnections().stream() - .filter(peerManager::isSeedNode) - .forEach(connection -> connection.getPeersNodeAddressOptional() - .ifPresent(e -> requestHashesFromSeedNode(fromHeight, e))); - } - - public void broadcastMyStateHash(StH myStateHash) { - NewStateHashMessage newStateHashMessage = getNewStateHashMessage(myStateHash); - broadcaster.broadcast(newStateHashMessage, networkNode.getNodeAddress()); - } - - public void requestHashes(int fromHeight, String peersAddress) { - requestHashesFromSeedNode(fromHeight, new NodeAddress(peersAddress)); - } - - public void reset() { - requestStateHashHandlerMap.clear(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Listeners - /////////////////////////////////////////////////////////////////////////////////////////// - - public void addListener(Listener listener) { - listeners.add(listener); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void requestHashesFromSeedNode(int fromHeight, NodeAddress nodeAddress) { - RequestStateHashesHandler.Listener listener = new RequestStateHashesHandler.Listener<>() { - @Override - public void onComplete(Res getStateHashesResponse, Optional peersNodeAddress) { - requestStateHashHandlerMap.remove(nodeAddress); - List stateHashes = getStateHashesResponse.getStateHashes(); - listeners.forEach(e -> e.onPeersStateHashes(stateHashes, peersNodeAddress)); - } - - @Override - public void onFault(String errorMessage, @Nullable Connection connection) { - log.warn("requestDaoStateHashesHandler with outbound connection failed.\n\tnodeAddress={}\n\t" + - "ErrorMessage={}", nodeAddress, errorMessage); - requestStateHashHandlerMap.remove(nodeAddress); - } - }; - Han requestStateHashesHandler = getRequestStateHashesHandler(nodeAddress, listener); - requestStateHashHandlerMap.put(nodeAddress, requestStateHashesHandler); - requestStateHashesHandler.requestStateHashes(fromHeight); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetBlindVoteStateHashesRequest.java b/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetBlindVoteStateHashesRequest.java deleted file mode 100644 index 7505c3f9c4..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetBlindVoteStateHashesRequest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network.messages; - -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkEnvelope; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@EqualsAndHashCode(callSuper = true) -@Getter -public final class GetBlindVoteStateHashesRequest extends GetStateHashesRequest { - public GetBlindVoteStateHashesRequest(int fromCycleStartHeight, int nonce) { - super(fromCycleStartHeight, nonce, Version.getP2PMessageVersion()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private GetBlindVoteStateHashesRequest(int height, int nonce, int messageVersion) { - super(height, nonce, messageVersion); - } - - @Override - public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { - return getNetworkEnvelopeBuilder() - .setGetBlindVoteStateHashesRequest(protobuf.GetBlindVoteStateHashesRequest.newBuilder() - .setHeight(height) - .setNonce(nonce)) - .build(); - } - - public static NetworkEnvelope fromProto(protobuf.GetBlindVoteStateHashesRequest proto, int messageVersion) { - return new GetBlindVoteStateHashesRequest(proto.getHeight(), proto.getNonce(), messageVersion); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetBlindVoteStateHashesResponse.java b/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetBlindVoteStateHashesResponse.java deleted file mode 100644 index ead682dd1d..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetBlindVoteStateHashesResponse.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network.messages; - -import bisq.core.dao.monitoring.model.BlindVoteStateHash; - -import bisq.network.p2p.InitialDataRequest; - -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkEnvelope; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@EqualsAndHashCode(callSuper = true) -@Getter -public final class GetBlindVoteStateHashesResponse extends GetStateHashesResponse { - public GetBlindVoteStateHashesResponse(List stateHashes, int requestNonce) { - super(stateHashes, requestNonce, Version.getP2PMessageVersion()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private GetBlindVoteStateHashesResponse(List stateHashes, - int requestNonce, - int messageVersion) { - super(stateHashes, requestNonce, messageVersion); - } - - @Override - public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { - return getNetworkEnvelopeBuilder() - .setGetBlindVoteStateHashesResponse(protobuf.GetBlindVoteStateHashesResponse.newBuilder() - .addAllStateHashes(stateHashes.stream() - .map(BlindVoteStateHash::toProtoMessage) - .collect(Collectors.toList())) - .setRequestNonce(requestNonce)) - .build(); - } - - public static NetworkEnvelope fromProto(protobuf.GetBlindVoteStateHashesResponse proto, int messageVersion) { - return new GetBlindVoteStateHashesResponse(proto.getStateHashesList().isEmpty() ? - new ArrayList<>() : - proto.getStateHashesList().stream() - .map(BlindVoteStateHash::fromProto) - .collect(Collectors.toList()), - proto.getRequestNonce(), - messageVersion); - } - - @Override - public Class associatedRequest() { - return GetBlindVoteStateHashesRequest.class; - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetDaoStateHashesRequest.java b/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetDaoStateHashesRequest.java deleted file mode 100644 index c9dc3af482..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetDaoStateHashesRequest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network.messages; - -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkEnvelope; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@EqualsAndHashCode(callSuper = true) -@Getter -public final class GetDaoStateHashesRequest extends GetStateHashesRequest { - public GetDaoStateHashesRequest(int height, int nonce) { - super(height, nonce, Version.getP2PMessageVersion()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private GetDaoStateHashesRequest(int height, int nonce, int messageVersion) { - super(height, nonce, messageVersion); - } - - @Override - public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { - return getNetworkEnvelopeBuilder() - .setGetDaoStateHashesRequest(protobuf.GetDaoStateHashesRequest.newBuilder() - .setHeight(height) - .setNonce(nonce)) - .build(); - } - - public static NetworkEnvelope fromProto(protobuf.GetDaoStateHashesRequest proto, int messageVersion) { - return new GetDaoStateHashesRequest(proto.getHeight(), proto.getNonce(), messageVersion); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetDaoStateHashesResponse.java b/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetDaoStateHashesResponse.java deleted file mode 100644 index 899f10c612..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetDaoStateHashesResponse.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network.messages; - -import bisq.core.dao.monitoring.model.DaoStateHash; - -import bisq.network.p2p.InitialDataRequest; - -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkEnvelope; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@EqualsAndHashCode(callSuper = true) -@Getter -public final class GetDaoStateHashesResponse extends GetStateHashesResponse { - public GetDaoStateHashesResponse(List daoStateHashes, int requestNonce) { - super(daoStateHashes, requestNonce, Version.getP2PMessageVersion()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private GetDaoStateHashesResponse(List daoStateHashes, - int requestNonce, - int messageVersion) { - super(daoStateHashes, requestNonce, messageVersion); - } - - @Override - public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { - return getNetworkEnvelopeBuilder() - .setGetDaoStateHashesResponse(protobuf.GetDaoStateHashesResponse.newBuilder() - .addAllStateHashes(stateHashes.stream() - .map(DaoStateHash::toProtoMessage) - .collect(Collectors.toList())) - .setRequestNonce(requestNonce)) - .build(); - } - - public static NetworkEnvelope fromProto(protobuf.GetDaoStateHashesResponse proto, int messageVersion) { - return new GetDaoStateHashesResponse(proto.getStateHashesList().isEmpty() ? - new ArrayList<>() : - proto.getStateHashesList().stream() - .map(DaoStateHash::fromProto) - .collect(Collectors.toList()), - proto.getRequestNonce(), - messageVersion); - } - - @Override - public Class associatedRequest() { - return GetDaoStateHashesRequest.class; - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetProposalStateHashesRequest.java b/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetProposalStateHashesRequest.java deleted file mode 100644 index 34f80ad39b..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetProposalStateHashesRequest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network.messages; - -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkEnvelope; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@EqualsAndHashCode(callSuper = true) -@Getter -public final class GetProposalStateHashesRequest extends GetStateHashesRequest { - public GetProposalStateHashesRequest(int fromCycleStartHeight, int nonce) { - super(fromCycleStartHeight, nonce, Version.getP2PMessageVersion()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private GetProposalStateHashesRequest(int height, int nonce, int messageVersion) { - super(height, nonce, messageVersion); - } - - @Override - public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { - return getNetworkEnvelopeBuilder() - .setGetProposalStateHashesRequest(protobuf.GetProposalStateHashesRequest.newBuilder() - .setHeight(height) - .setNonce(nonce)) - .build(); - } - - public static NetworkEnvelope fromProto(protobuf.GetProposalStateHashesRequest proto, int messageVersion) { - return new GetProposalStateHashesRequest(proto.getHeight(), proto.getNonce(), messageVersion); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetProposalStateHashesResponse.java b/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetProposalStateHashesResponse.java deleted file mode 100644 index 1f5ce21c20..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetProposalStateHashesResponse.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network.messages; - -import bisq.core.dao.monitoring.model.ProposalStateHash; - -import bisq.network.p2p.InitialDataRequest; - -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkEnvelope; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@EqualsAndHashCode(callSuper = true) -@Getter -public final class GetProposalStateHashesResponse extends GetStateHashesResponse { - public GetProposalStateHashesResponse(List proposalStateHashes, int requestNonce) { - super(proposalStateHashes, requestNonce, Version.getP2PMessageVersion()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private GetProposalStateHashesResponse(List proposalStateHashes, - int requestNonce, - int messageVersion) { - super(proposalStateHashes, requestNonce, messageVersion); - } - - @Override - public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { - return getNetworkEnvelopeBuilder() - .setGetProposalStateHashesResponse(protobuf.GetProposalStateHashesResponse.newBuilder() - .addAllStateHashes(stateHashes.stream() - .map(ProposalStateHash::toProtoMessage) - .collect(Collectors.toList())) - .setRequestNonce(requestNonce)) - .build(); - } - - public static NetworkEnvelope fromProto(protobuf.GetProposalStateHashesResponse proto, int messageVersion) { - return new GetProposalStateHashesResponse(proto.getStateHashesList().isEmpty() ? - new ArrayList<>() : - proto.getStateHashesList().stream() - .map(ProposalStateHash::fromProto) - .collect(Collectors.toList()), - proto.getRequestNonce(), - messageVersion); - } - - @Override - public Class associatedRequest() { - return GetProposalStateHashesRequest.class; - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetStateHashesRequest.java b/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetStateHashesRequest.java deleted file mode 100644 index f62926040f..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetStateHashesRequest.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network.messages; - -import bisq.network.p2p.DirectMessage; -import bisq.network.p2p.InitialDataRequest; -import bisq.network.p2p.storage.payload.CapabilityRequiringPayload; - -import bisq.common.app.Capabilities; -import bisq.common.app.Capability; -import bisq.common.proto.network.NetworkEnvelope; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@EqualsAndHashCode(callSuper = true) -@Getter -public abstract class GetStateHashesRequest extends NetworkEnvelope implements DirectMessage, - CapabilityRequiringPayload, InitialDataRequest { - protected final int height; - protected final int nonce; - - protected GetStateHashesRequest(int height, int nonce, int messageVersion) { - super(messageVersion); - this.height = height; - this.nonce = nonce; - } - - @Override - public Capabilities getRequiredCapabilities() { - return new Capabilities(Capability.DAO_STATE); - } - - @Override - public String toString() { - return "GetStateHashesRequest{" + - ",\n height=" + height + - ",\n nonce=" + nonce + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetStateHashesResponse.java b/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetStateHashesResponse.java deleted file mode 100644 index b33cda4b4d..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/messages/GetStateHashesResponse.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network.messages; - -import bisq.core.dao.monitoring.model.StateHash; - -import bisq.network.p2p.DirectMessage; -import bisq.network.p2p.ExtendedDataSizePermission; -import bisq.network.p2p.InitialDataResponse; - -import bisq.common.proto.network.NetworkEnvelope; - -import java.util.List; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@EqualsAndHashCode(callSuper = true) -@Getter -public abstract class GetStateHashesResponse extends NetworkEnvelope implements DirectMessage, - ExtendedDataSizePermission, InitialDataResponse { - protected final List stateHashes; - protected final int requestNonce; - - protected GetStateHashesResponse(List stateHashes, - int requestNonce, - int messageVersion) { - super(messageVersion); - this.stateHashes = stateHashes; - this.requestNonce = requestNonce; - } - - @Override - public String toString() { - return "GetStateHashesResponse{" + - "\n stateHashes=" + stateHashes + - ",\n requestNonce=" + requestNonce + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/messages/NewBlindVoteStateHashMessage.java b/core/src/main/java/bisq/core/dao/monitoring/network/messages/NewBlindVoteStateHashMessage.java deleted file mode 100644 index 986371ce58..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/messages/NewBlindVoteStateHashMessage.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network.messages; - -import bisq.core.dao.monitoring.model.BlindVoteStateHash; - -import bisq.common.app.Capabilities; -import bisq.common.app.Capability; -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkEnvelope; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@EqualsAndHashCode(callSuper = true) -@Getter -public final class NewBlindVoteStateHashMessage extends NewStateHashMessage { - public NewBlindVoteStateHashMessage(BlindVoteStateHash stateHash) { - super(stateHash, Version.getP2PMessageVersion()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private NewBlindVoteStateHashMessage(BlindVoteStateHash stateHash, int messageVersion) { - super(stateHash, messageVersion); - } - - @Override - public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { - return getNetworkEnvelopeBuilder() - .setNewBlindVoteStateHashMessage(protobuf.NewBlindVoteStateHashMessage.newBuilder() - .setStateHash(stateHash.toProtoMessage())) - .build(); - } - - public static NetworkEnvelope fromProto(protobuf.NewBlindVoteStateHashMessage proto, int messageVersion) { - return new NewBlindVoteStateHashMessage(BlindVoteStateHash.fromProto(proto.getStateHash()), messageVersion); - } - - @Override - public Capabilities getRequiredCapabilities() { - return new Capabilities(Capability.DAO_STATE); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/messages/NewDaoStateHashMessage.java b/core/src/main/java/bisq/core/dao/monitoring/network/messages/NewDaoStateHashMessage.java deleted file mode 100644 index adc3523e44..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/messages/NewDaoStateHashMessage.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network.messages; - -import bisq.core.dao.monitoring.model.DaoStateHash; - -import bisq.common.app.Capabilities; -import bisq.common.app.Capability; -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkEnvelope; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@EqualsAndHashCode(callSuper = true) -@Getter -public final class NewDaoStateHashMessage extends NewStateHashMessage { - public NewDaoStateHashMessage(DaoStateHash daoStateHash) { - super(daoStateHash, Version.getP2PMessageVersion()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private NewDaoStateHashMessage(DaoStateHash daoStateHash, int messageVersion) { - super(daoStateHash, messageVersion); - } - - @Override - public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { - return getNetworkEnvelopeBuilder() - .setNewDaoStateHashMessage(protobuf.NewDaoStateHashMessage.newBuilder() - .setStateHash(stateHash.toProtoMessage())) - .build(); - } - - public static NetworkEnvelope fromProto(protobuf.NewDaoStateHashMessage proto, int messageVersion) { - return new NewDaoStateHashMessage(DaoStateHash.fromProto(proto.getStateHash()), messageVersion); - } - - @Override - public Capabilities getRequiredCapabilities() { - return new Capabilities(Capability.DAO_STATE); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/messages/NewProposalStateHashMessage.java b/core/src/main/java/bisq/core/dao/monitoring/network/messages/NewProposalStateHashMessage.java deleted file mode 100644 index eaf38180a2..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/messages/NewProposalStateHashMessage.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network.messages; - -import bisq.core.dao.monitoring.model.ProposalStateHash; - -import bisq.common.app.Capabilities; -import bisq.common.app.Capability; -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkEnvelope; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@EqualsAndHashCode(callSuper = true) -@Getter -public final class NewProposalStateHashMessage extends NewStateHashMessage { - public NewProposalStateHashMessage(ProposalStateHash proposalStateHash) { - super(proposalStateHash, Version.getP2PMessageVersion()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private NewProposalStateHashMessage(ProposalStateHash proposalStateHash, int messageVersion) { - super(proposalStateHash, messageVersion); - } - - @Override - public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { - return getNetworkEnvelopeBuilder() - .setNewProposalStateHashMessage(protobuf.NewProposalStateHashMessage.newBuilder() - .setStateHash(stateHash.toProtoMessage())) - .build(); - } - - public static NetworkEnvelope fromProto(protobuf.NewProposalStateHashMessage proto, int messageVersion) { - return new NewProposalStateHashMessage(ProposalStateHash.fromProto(proto.getStateHash()), messageVersion); - } - - @Override - public Capabilities getRequiredCapabilities() { - return new Capabilities(Capability.DAO_STATE); - } -} diff --git a/core/src/main/java/bisq/core/dao/monitoring/network/messages/NewStateHashMessage.java b/core/src/main/java/bisq/core/dao/monitoring/network/messages/NewStateHashMessage.java deleted file mode 100644 index 4d2f05d235..0000000000 --- a/core/src/main/java/bisq/core/dao/monitoring/network/messages/NewStateHashMessage.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.monitoring.network.messages; - -import bisq.core.dao.monitoring.model.StateHash; - -import bisq.network.p2p.storage.messages.BroadcastMessage; -import bisq.network.p2p.storage.payload.CapabilityRequiringPayload; - -import bisq.common.app.Capabilities; -import bisq.common.app.Capability; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@EqualsAndHashCode(callSuper = true) -@Getter -public abstract class NewStateHashMessage extends BroadcastMessage implements CapabilityRequiringPayload { - protected final T stateHash; - - protected NewStateHashMessage(T stateHash, int messageVersion) { - super(messageVersion); - this.stateHash = stateHash; - } - - @Override - public Capabilities getRequiredCapabilities() { - return new Capabilities(Capability.DAO_STATE); - } - - @Override - public String toString() { - return "NewStateHashMessage{" + - "\n stateHash=" + stateHash + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/BsqNode.java b/core/src/main/java/bisq/core/dao/node/BsqNode.java deleted file mode 100644 index 2e0bdfef4d..0000000000 --- a/core/src/main/java/bisq/core/dao/node/BsqNode.java +++ /dev/null @@ -1,296 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node; - -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.node.explorer.ExportJsonFilesService; -import bisq.core.dao.node.full.RawBlock; -import bisq.core.dao.node.parser.BlockParser; -import bisq.core.dao.node.parser.exceptions.BlockHashNotConnectingException; -import bisq.core.dao.node.parser.exceptions.BlockHeightNotConnectingException; -import bisq.core.dao.node.parser.exceptions.RequiredReorgFromSnapshotException; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.DaoStateSnapshotService; -import bisq.core.dao.state.model.blockchain.Block; - -import bisq.network.p2p.P2PService; -import bisq.network.p2p.P2PServiceListener; - -import com.google.inject.Inject; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Optional; -import java.util.function.Consumer; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -/** - * Base class for the lite and full node. - * It is responsible or the setup of the parser and snapshot management. - */ -@Slf4j -public abstract class BsqNode implements DaoSetupService { - private final BlockParser blockParser; - private final P2PService p2PService; - protected final DaoStateService daoStateService; - private final String genesisTxId; - private final int genesisBlockHeight; - private final ExportJsonFilesService exportJsonFilesService; - private final DaoStateSnapshotService daoStateSnapshotService; - private final P2PServiceListener p2PServiceListener; - protected boolean parseBlockchainComplete; - protected boolean p2pNetworkReady; - @Nullable - protected Consumer errorMessageHandler; - @Nullable - protected Consumer warnMessageHandler; - private final List pendingBlocks = new ArrayList<>(); - - // The chain height of the latest Block we either get reported by Bitcoin Core or from the seed node - // This property should not be used in consensus code but only for retrieving blocks as it is not in sync with the - // parsing and the daoState. It also does not represent the latest blockHeight but the currently received - // (not parsed) block. - @Getter - protected int chainTipHeight; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public BsqNode(BlockParser blockParser, - DaoStateService daoStateService, - DaoStateSnapshotService daoStateSnapshotService, - P2PService p2PService, - ExportJsonFilesService exportJsonFilesService) { - this.blockParser = blockParser; - this.daoStateService = daoStateService; - this.daoStateSnapshotService = daoStateSnapshotService; - this.p2PService = p2PService; - this.exportJsonFilesService = exportJsonFilesService; - - genesisTxId = daoStateService.getGenesisTxId(); - genesisBlockHeight = daoStateService.getGenesisBlockHeight(); - - p2PServiceListener = new P2PServiceListener() { - @Override - public void onTorNodeReady() { - } - - @Override - public void onHiddenServicePublished() { - } - - @Override - public void onSetupFailed(Throwable throwable) { - } - - @Override - public void onRequestCustomBridges() { - } - - @Override - public void onDataReceived() { - } - - @Override - public void onNoSeedNodeAvailable() { - onP2PNetworkReady(); - } - - @Override - public void onNoPeersAvailable() { - } - - @Override - public void onUpdatedDataReceived() { - onP2PNetworkReady(); - } - }; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - } - - @Override - public abstract void start(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void setErrorMessageHandler(@SuppressWarnings("NullableProblems") Consumer errorMessageHandler) { - this.errorMessageHandler = errorMessageHandler; - } - - public void setWarnMessageHandler(@SuppressWarnings("NullableProblems") Consumer warnMessageHandler) { - this.warnMessageHandler = warnMessageHandler; - } - - public void shutDown() { - exportJsonFilesService.shutDown(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Protected - /////////////////////////////////////////////////////////////////////////////////////////// - - @SuppressWarnings("WeakerAccess") - protected void onInitialized() { - daoStateSnapshotService.applySnapshot(false); - - if (p2PService.isBootstrapped()) { - log.info("onAllServicesInitialized: isBootstrapped"); - onP2PNetworkReady(); - } else { - p2PService.addP2PServiceListener(p2PServiceListener); - } - } - - @SuppressWarnings("WeakerAccess") - protected void onP2PNetworkReady() { - p2pNetworkReady = true; - p2PService.removeP2PServiceListener(p2PServiceListener); - } - - @SuppressWarnings("WeakerAccess") - protected int getStartBlockHeight() { - int chainHeight = daoStateService.getChainHeight(); - int startBlockHeight = chainHeight; - if (chainHeight > genesisBlockHeight) - startBlockHeight = chainHeight + 1; - - log.info("getStartBlockHeight:\n" + - " Start block height={}\n" + - " Genesis txId={}\n" + - " Genesis block height={}\n" + - " Block height={}\n", - startBlockHeight, - genesisTxId, - genesisBlockHeight, - chainHeight); - - return startBlockHeight; - } - - protected abstract void startParseBlocks(); - - protected void onParseBlockChainComplete() { - log.info("onParseBlockChainComplete"); - parseBlockchainComplete = true; - daoStateService.onParseBlockChainComplete(); - - maybeExportToJson(); - } - - @SuppressWarnings("WeakerAccess") - protected void startReOrgFromLastSnapshot() { - daoStateSnapshotService.applySnapshot(true); - } - - - protected Optional doParseBlock(RawBlock rawBlock) throws RequiredReorgFromSnapshotException { - // We check if we have a block with that height. If so we return. We do not use the chainHeight as with genesis - // 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.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); - - 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 - if (!pendingBlocks.isEmpty()) { - // We take only first element after sorting (so it is the block with the next height) to avoid that - // we would repeat calls in recursions in case we would iterate the list. - pendingBlocks.sort(Comparator.comparing(RawBlock::getHeight)); - RawBlock nextPending = pendingBlocks.get(0); - if (nextPending.getHeight() == daoStateService.getChainHeight() + 1) - doParseBlock(nextPending); - } - - return Optional.of(block); - } catch (BlockHeightNotConnectingException e) { - // There is no guaranteed order how we receive blocks. We could have received block 102 before 101. - // If block is in the future we move the block to the pendingBlocks list. At next block we look up the - // list if there is any potential candidate with the correct height and if so we remove that from that list. - - int heightForNextBlock = daoStateService.getChainHeight() + 1; - if (rawBlock.getHeight() > heightForNextBlock) { - if (!pendingBlocks.contains(rawBlock)) { - pendingBlocks.add(rawBlock); - log.info("We received a block with a future block height. We store it as pending and try to apply " + - "it at the next block. rawBlock: height/hash={}/{}", rawBlock.getHeight(), rawBlock.getHash()); - } else { - log.warn("We received a block with a future block height but we had it already added to our pendingBlocks."); - } - } else if (rawBlock.getHeight() >= daoStateService.getGenesisBlockHeight()) { - // We received an older block. We compare if we have it in our chain. - Optional optionalBlock = daoStateService.getBlockAtHeight(rawBlock.getHeight()); - if (optionalBlock.isPresent()) { - if (optionalBlock.get().getHash().equals(rawBlock.getPreviousBlockHash())) { - log.info("We received an old block we have already parsed and added. We ignore it."); - } else { - log.info("We received an old block with a different hash. We ignore it. Hash={}", rawBlock.getHash()); - } - } else { - log.info("In case we have reset from genesis height we would not find the block"); - } - } else { - log.info("We ignore it as it was before genesis height"); - } - } catch (BlockHashNotConnectingException throwable) { - Optional lastBlock = daoStateService.getLastBlock(); - log.warn("Block not connecting:\n" + - "New block height/hash/previousBlockHash={}/{}/{}, latest block height/hash={}/{}", - rawBlock.getHeight(), - rawBlock.getHash(), - rawBlock.getPreviousBlockHash(), - lastBlock.isPresent() ? lastBlock.get().getHeight() : "lastBlock not present", - lastBlock.isPresent() ? lastBlock.get().getHash() : "lastBlock not present"); - - pendingBlocks.clear(); - startReOrgFromLastSnapshot(); - throw new RequiredReorgFromSnapshotException(rawBlock); - } - return Optional.empty(); - } - - protected void maybeExportToJson() { - exportJsonFilesService.maybeExportToJson(); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/BsqNodeProvider.java b/core/src/main/java/bisq/core/dao/node/BsqNodeProvider.java deleted file mode 100644 index 933a941419..0000000000 --- a/core/src/main/java/bisq/core/dao/node/BsqNodeProvider.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node; - -import bisq.core.dao.node.full.FullNode; -import bisq.core.dao.node.lite.LiteNode; -import bisq.core.user.Preferences; - -import com.google.inject.Inject; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Returns a FullNode or LiteNode based on the Config.FULL_DAO_NODE option. - */ -@Slf4j -public class BsqNodeProvider { - @Getter - private final BsqNode bsqNode; - - @Inject - public BsqNodeProvider(LiteNode bsqLiteNode, - FullNode bsqFullNode, - Preferences preferences) { - - boolean rpcDataSet = preferences.getRpcUser() != null && - !preferences.getRpcUser().isEmpty() - && preferences.getRpcPw() != null && - !preferences.getRpcPw().isEmpty() && - preferences.getBlockNotifyPort() > 0; - boolean daoFullNode = preferences.isDaoFullNode(); - if (daoFullNode && !rpcDataSet) { - log.warn("daoFullNode is set in preferences but RPC user and pw are missing. We reset daoFullNode in preferences to false."); - preferences.setDaoFullNode(false); - } - bsqNode = daoFullNode && rpcDataSet ? bsqFullNode : bsqLiteNode; - } -} diff --git a/core/src/main/java/bisq/core/dao/node/explorer/ExportJsonFilesService.java b/core/src/main/java/bisq/core/dao/node/explorer/ExportJsonFilesService.java deleted file mode 100644 index 2d56e784dc..0000000000 --- a/core/src/main/java/bisq/core/dao/node/explorer/ExportJsonFilesService.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.explorer; - -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.DaoState; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.blockchain.PubKeyScript; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.blockchain.TxType; - -import bisq.common.config.Config; -import bisq.common.file.FileUtil; -import bisq.common.file.JsonFileManager; -import bisq.common.util.Utilities; - -import org.bitcoinj.core.Utils; - -import com.google.inject.Inject; - -import javax.inject.Named; - -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; - -import java.nio.file.Paths; - -import java.io.File; -import java.io.IOException; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class ExportJsonFilesService implements DaoSetupService { - private final DaoStateService daoStateService; - private final File storageDir; - private final boolean dumpBlockchainData; - - private final ListeningExecutorService executor = Utilities.getListeningExecutorService("JsonExporter", - 1, 1, 1200); - private JsonFileManager txFileManager, txOutputFileManager, bsqStateFileManager; - - @Inject - public ExportJsonFilesService(DaoStateService daoStateService, - @Named(Config.STORAGE_DIR) File storageDir, - @Named(Config.DUMP_BLOCKCHAIN_DATA) boolean dumpBlockchainData) { - this.daoStateService = daoStateService; - this.storageDir = storageDir; - this.dumpBlockchainData = dumpBlockchainData; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - } - - @Override - public void start() { - if (dumpBlockchainData) { - File jsonDir = new File(Paths.get(storageDir.getAbsolutePath(), "json").toString()); - File txDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "tx").toString()); - File txOutputDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "txo").toString()); - File bsqStateDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "all").toString()); - try { - if (txDir.exists()) - FileUtil.deleteDirectory(txDir); - if (txOutputDir.exists()) - FileUtil.deleteDirectory(txOutputDir); - if (bsqStateDir.exists()) - FileUtil.deleteDirectory(bsqStateDir); - if (jsonDir.exists()) - FileUtil.deleteDirectory(jsonDir); - } catch (IOException e) { - log.error(e.toString()); - e.printStackTrace(); - } - - if (!jsonDir.mkdir()) - log.warn("make jsonDir failed.\njsonDir=" + jsonDir.getAbsolutePath()); - - if (!txDir.mkdir()) - log.warn("make txDir failed.\ntxDir=" + txDir.getAbsolutePath()); - - if (!txOutputDir.mkdir()) - log.warn("make txOutputDir failed.\ntxOutputDir=" + txOutputDir.getAbsolutePath()); - - if (!bsqStateDir.mkdir()) - log.warn("make bsqStateDir failed.\nbsqStateDir=" + bsqStateDir.getAbsolutePath()); - - txFileManager = new JsonFileManager(txDir); - txOutputFileManager = new JsonFileManager(txOutputDir); - bsqStateFileManager = new JsonFileManager(bsqStateDir); - } - } - - public void shutDown() { - if (dumpBlockchainData && txFileManager != null) { - txFileManager.shutDown(); - txOutputFileManager.shutDown(); - bsqStateFileManager.shutDown(); - } - } - - public void maybeExportToJson() { - if (dumpBlockchainData && - daoStateService.isParseBlockChainComplete()) { - // We store the data we need once we write the data to disk (in the thread) locally. - // Access to daoStateService is single threaded, we must not access daoStateService from the thread. - List allJsonTxOutputs = new ArrayList<>(); - - List jsonTxs = daoStateService.getUnorderedTxStream() - .map(tx -> { - JsonTx jsonTx = getJsonTx(tx); - allJsonTxOutputs.addAll(jsonTx.getOutputs()); - return jsonTx; - }).collect(Collectors.toList()); - - DaoState daoState = daoStateService.getClone(); - List jsonBlockList = daoState.getBlocks().stream() - .map(this::getJsonBlock) - .collect(Collectors.toList()); - JsonBlocks jsonBlocks = new JsonBlocks(daoState.getChainHeight(), jsonBlockList); - - ListenableFuture future = executor.submit(() -> { - bsqStateFileManager.writeToDisc(Utilities.objectToJson(jsonBlocks), "blocks"); - allJsonTxOutputs.forEach(jsonTxOutput -> txOutputFileManager.writeToDisc(Utilities.objectToJson(jsonTxOutput), jsonTxOutput.getId())); - jsonTxs.forEach(jsonTx -> txFileManager.writeToDisc(Utilities.objectToJson(jsonTx), jsonTx.getId())); - return null; - }); - - Futures.addCallback(future, Utilities.failureCallback(throwable -> { - log.error(throwable.toString()); - throwable.printStackTrace(); - }), MoreExecutors.directExecutor()); - } - } - - private JsonBlock getJsonBlock(Block block) { - List jsonTxs = block.getTxs().stream() - .map(this::getJsonTx) - .collect(Collectors.toList()); - return new JsonBlock(block.getHeight(), - block.getTime(), - block.getHash(), - block.getPreviousBlockHash(), - jsonTxs); - } - - private JsonTx getJsonTx(Tx tx) { - JsonTxType jsonTxType = getJsonTxType(tx); - String jsonTxTypeDisplayString = getJsonTxTypeDisplayString(jsonTxType); - return new JsonTx(tx.getId(), - tx.getBlockHeight(), - tx.getBlockHash(), - tx.getTime(), - getJsonTxInputs(tx), - getJsonTxOutputs(tx), - jsonTxType, - jsonTxTypeDisplayString, - tx.getBurntFee(), - tx.getInvalidatedBsq(), - tx.getUnlockBlockHeight()); - } - - private List getJsonTxInputs(Tx tx) { - return tx.getTxInputs().stream() - .map(txInput -> { - Optional optionalTxOutput = daoStateService.getConnectedTxOutput(txInput); - if (optionalTxOutput.isPresent()) { - TxOutput connectedTxOutput = optionalTxOutput.get(); - boolean isBsqTxOutputType = daoStateService.isBsqTxOutputType(connectedTxOutput); - return new JsonTxInput(txInput.getConnectedTxOutputIndex(), - txInput.getConnectedTxOutputTxId(), - connectedTxOutput.getValue(), - isBsqTxOutputType, - connectedTxOutput.getAddress(), - tx.getTime()); - } else { - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - } - - private List getJsonTxOutputs(Tx tx) { - JsonTxType jsonTxType = getJsonTxType(tx); - String jsonTxTypeDisplayString = getJsonTxTypeDisplayString(jsonTxType); - return tx.getTxOutputs().stream() - .map(txOutput -> { - boolean isBsqTxOutputType = daoStateService.isBsqTxOutputType(txOutput); - long bsqAmount = isBsqTxOutputType ? txOutput.getValue() : 0; - long btcAmount = !isBsqTxOutputType ? txOutput.getValue() : 0; - PubKeyScript pubKeyScript = txOutput.getPubKeyScript(); - JsonScriptPubKey scriptPubKey = pubKeyScript != null ? new JsonScriptPubKey(pubKeyScript) : null; - JsonSpentInfo spentInfo = daoStateService.getSpentInfo(txOutput).map(JsonSpentInfo::new).orElse(null); - JsonTxOutputType txOutputType = JsonTxOutputType.valueOf(txOutput.getTxOutputType().name()); - int lockTime = txOutput.getLockTime(); - String opReturn = txOutput.getOpReturnData() != null ? Utils.HEX.encode(txOutput.getOpReturnData()) : null; - boolean isUnspent = daoStateService.isUnspent(txOutput.getKey()); - return new JsonTxOutput(tx.getId(), - txOutput.getIndex(), - bsqAmount, - btcAmount, - tx.getBlockHeight(), - isBsqTxOutputType, - tx.getBurntFee(), - tx.getInvalidatedBsq(), - txOutput.getAddress(), - scriptPubKey, - spentInfo, - tx.getTime(), - jsonTxType, - jsonTxTypeDisplayString, - txOutputType, - txOutputType.getDisplayString(), - opReturn, - lockTime, - isUnspent - ); - }) - .collect(Collectors.toList()); - } - - private String getJsonTxTypeDisplayString(JsonTxType jsonTxType) { - return jsonTxType != null ? jsonTxType.getDisplayString() : ""; - } - - private JsonTxType getJsonTxType(Tx tx) { - TxType txType = tx.getTxType(); - return txType != null ? JsonTxType.valueOf(txType.name()) : null; - } -} diff --git a/core/src/main/java/bisq/core/dao/node/explorer/JsonBlock.java b/core/src/main/java/bisq/core/dao/node/explorer/JsonBlock.java deleted file mode 100644 index da93f0c012..0000000000 --- a/core/src/main/java/bisq/core/dao/node/explorer/JsonBlock.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.explorer; - -import java.util.List; - -import lombok.Value; - -@Value -class JsonBlock { - protected final int height; - protected final long time; // in ms - protected final String hash; - protected final String previousBlockHash; - private final List txs; -} diff --git a/core/src/main/java/bisq/core/dao/node/explorer/JsonBlocks.java b/core/src/main/java/bisq/core/dao/node/explorer/JsonBlocks.java deleted file mode 100644 index f896b1f2fa..0000000000 --- a/core/src/main/java/bisq/core/dao/node/explorer/JsonBlocks.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.explorer; - -import java.util.List; - -import lombok.Value; - -@Value -class JsonBlocks { - private int chainHeight; - private final List blocks; -} diff --git a/core/src/main/java/bisq/core/dao/node/explorer/JsonScriptPubKey.java b/core/src/main/java/bisq/core/dao/node/explorer/JsonScriptPubKey.java deleted file mode 100644 index 60cecb41dd..0000000000 --- a/core/src/main/java/bisq/core/dao/node/explorer/JsonScriptPubKey.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.explorer; - -import bisq.core.dao.state.model.blockchain.PubKeyScript; - -import java.util.List; - -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@Value -class JsonScriptPubKey { - private final List addresses; - private final String asm; - private final String hex; - private final int reqSigs; - private final String type; - - JsonScriptPubKey(PubKeyScript pubKeyScript) { - addresses = pubKeyScript.getAddresses(); - asm = pubKeyScript.getAsm(); - hex = pubKeyScript.getHex(); - reqSigs = pubKeyScript.getReqSigs(); - type = pubKeyScript.getScriptType().toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/explorer/JsonSpentInfo.java b/core/src/main/java/bisq/core/dao/node/explorer/JsonSpentInfo.java deleted file mode 100644 index 0624fa5ac3..0000000000 --- a/core/src/main/java/bisq/core/dao/node/explorer/JsonSpentInfo.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.explorer; - -import bisq.core.dao.state.model.blockchain.SpentInfo; - -import lombok.Value; - -@Value -class JsonSpentInfo { - private final long height; - private final int inputIndex; - private final String txId; - - JsonSpentInfo(SpentInfo spentInfo) { - height = spentInfo.getBlockHeight(); - inputIndex = spentInfo.getInputIndex(); - txId = spentInfo.getTxId(); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/explorer/JsonTx.java b/core/src/main/java/bisq/core/dao/node/explorer/JsonTx.java deleted file mode 100644 index 145826bf60..0000000000 --- a/core/src/main/java/bisq/core/dao/node/explorer/JsonTx.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.explorer; - -import bisq.common.app.Version; - -import java.util.List; -import java.util.Objects; - -import lombok.Value; - -@Value -class JsonTx { - private final String txVersion = Version.BSQ_TX_VERSION; - private final String id; - private final int blockHeight; - private final String blockHash; - private final long time; - private final List inputs; - private final List outputs; - private final JsonTxType txType; - private final String txTypeDisplayString; - private final long burntFee; - private final long invalidatedBsq; - // If not set it is -1. LockTime of 0 is a valid value. - private final int unlockBlockHeight; - - JsonTx(String id, int blockHeight, String blockHash, long time, List inputs, - List outputs, JsonTxType txType, String txTypeDisplayString, long burntFee, - long invalidatedBsq, int unlockBlockHeight) { - this.id = id; - this.blockHeight = blockHeight; - this.blockHash = blockHash; - this.time = time; - this.inputs = inputs; - this.outputs = outputs; - this.txType = txType; - this.txTypeDisplayString = txTypeDisplayString; - this.burntFee = burntFee; - this.invalidatedBsq = invalidatedBsq; - this.unlockBlockHeight = unlockBlockHeight; - } - - // Enums must not be used directly for hashCode or equals as it delivers the Object.hashCode (internal address)! - // The equals and hashCode methods cannot be overwritten in Enums. - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof JsonTx)) return false; - if (!super.equals(o)) return false; - JsonTx jsonTx = (JsonTx) o; - return blockHeight == jsonTx.blockHeight && - time == jsonTx.time && - burntFee == jsonTx.burntFee && - invalidatedBsq == jsonTx.invalidatedBsq && - unlockBlockHeight == jsonTx.unlockBlockHeight && - Objects.equals(txVersion, jsonTx.txVersion) && - Objects.equals(id, jsonTx.id) && - Objects.equals(blockHash, jsonTx.blockHash) && - Objects.equals(inputs, jsonTx.inputs) && - Objects.equals(outputs, jsonTx.outputs) && - txType.name().equals(jsonTx.txType.name()) && - Objects.equals(txTypeDisplayString, jsonTx.txTypeDisplayString); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), txVersion, id, blockHeight, blockHash, time, inputs, outputs, - txType.name(), txTypeDisplayString, burntFee, invalidatedBsq, unlockBlockHeight); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxInput.java b/core/src/main/java/bisq/core/dao/node/explorer/JsonTxInput.java deleted file mode 100644 index 6c7ea14222..0000000000 --- a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxInput.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.explorer; - -import lombok.Value; - -import javax.annotation.concurrent.Immutable; - -@Value -@Immutable -class JsonTxInput { - private final int spendingTxOutputIndex; // connectedTxOutputIndex - private final String spendingTxId; // connectedTxOutputTxId - private final long bsqAmount; - private final boolean isVerified; // isBsqTxOutputType - private final String address; - private final long time; -} diff --git a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutput.java b/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutput.java deleted file mode 100644 index 65ce85374f..0000000000 --- a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutput.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.explorer; - -import bisq.common.app.Version; - -import java.util.Objects; - -import lombok.Value; - -import javax.annotation.Nullable; - -@Value -class JsonTxOutput { - private final String txVersion = Version.BSQ_TX_VERSION; - private final String txId; - private final int index; - private final long bsqAmount; - private final long btcAmount; - private final int height; - private final boolean isVerified; // isBsqTxOutputType - private final long burntFee; - private final long invalidatedBsq; - private final String address; - @Nullable - private final JsonScriptPubKey scriptPubKey; - @Nullable - private final JsonSpentInfo spentInfo; - private final long time; - private final JsonTxType txType; - private final String txTypeDisplayString; - private final JsonTxOutputType txOutputType; - private final String txOutputTypeDisplayString; - @Nullable - private final String opReturn; - private final int lockTime; - private final boolean isUnspent; - - JsonTxOutput(String txId, int index, long bsqAmount, long btcAmount, int height, boolean isVerified, long burntFee, - long invalidatedBsq, String address, JsonScriptPubKey scriptPubKey, JsonSpentInfo spentInfo, - long time, JsonTxType txType, String txTypeDisplayString, JsonTxOutputType txOutputType, - String txOutputTypeDisplayString, String opReturn, int lockTime, boolean isUnspent) { - this.txId = txId; - this.index = index; - this.bsqAmount = bsqAmount; - this.btcAmount = btcAmount; - this.height = height; - this.isVerified = isVerified; - this.burntFee = burntFee; - this.invalidatedBsq = invalidatedBsq; - this.address = address; - this.scriptPubKey = scriptPubKey; - this.spentInfo = spentInfo; - this.time = time; - this.txType = txType; - this.txTypeDisplayString = txTypeDisplayString; - this.txOutputType = txOutputType; - this.txOutputTypeDisplayString = txOutputTypeDisplayString; - this.opReturn = opReturn; - this.lockTime = lockTime; - this.isUnspent = isUnspent; - } - - String getId() { - return txId + ":" + index; - } - - // 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 - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof JsonTxOutput)) return false; - if (!super.equals(o)) return false; - JsonTxOutput that = (JsonTxOutput) o; - return index == that.index && - bsqAmount == that.bsqAmount && - btcAmount == that.btcAmount && - height == that.height && - isVerified == that.isVerified && - burntFee == that.burntFee && - invalidatedBsq == that.invalidatedBsq && - time == that.time && - lockTime == that.lockTime && - isUnspent == that.isUnspent && - Objects.equals(txVersion, that.txVersion) && - Objects.equals(txId, that.txId) && - Objects.equals(address, that.address) && - Objects.equals(scriptPubKey, that.scriptPubKey) && - Objects.equals(spentInfo, that.spentInfo) && - txType.name().equals(that.txType.name()) && - Objects.equals(txTypeDisplayString, that.txTypeDisplayString) && - txOutputType == that.txOutputType && - Objects.equals(txOutputTypeDisplayString, that.txOutputTypeDisplayString) && - Objects.equals(opReturn, that.opReturn); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), txVersion, txId, index, bsqAmount, btcAmount, height, isVerified, - burntFee, invalidatedBsq, address, scriptPubKey, spentInfo, time, txType.name(), txTypeDisplayString, - txOutputType, txOutputTypeDisplayString, opReturn, lockTime, isUnspent); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutputType.java b/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutputType.java deleted file mode 100644 index a5657bfaf8..0000000000 --- a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutputType.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.explorer; - -import lombok.Getter; - -// Need to be in sync with TxOutputType -enum JsonTxOutputType { - UNDEFINED("Undefined"), - UNDEFINED_OUTPUT("Undefined output"), - GENESIS_OUTPUT("Genesis"), - BSQ_OUTPUT("BSQ"), - BTC_OUTPUT("BTC"), - PROPOSAL_OP_RETURN_OUTPUT("Proposal opReturn"), - COMP_REQ_OP_RETURN_OUTPUT("Compensation request opReturn"), - REIMBURSEMENT_OP_RETURN_OUTPUT("Reimbursement request opReturn"), - CONFISCATE_BOND_OP_RETURN_OUTPUT("Confiscate bond opReturn"), - ISSUANCE_CANDIDATE_OUTPUT("Issuance candidate"), - BLIND_VOTE_LOCK_STAKE_OUTPUT("Blind vote lock stake"), - BLIND_VOTE_OP_RETURN_OUTPUT("Blind vote opReturn"), - VOTE_REVEAL_UNLOCK_STAKE_OUTPUT("Vote reveal unlock stake"), - VOTE_REVEAL_OP_RETURN_OUTPUT("Vote reveal opReturn"), - ASSET_LISTING_FEE_OP_RETURN_OUTPUT("Asset listing fee OpReturn"), - PROOF_OF_BURN_OP_RETURN_OUTPUT("Proof of burn opReturn"), - LOCKUP_OUTPUT("Lockup"), - LOCKUP_OP_RETURN_OUTPUT("Lockup opReturn"), - UNLOCK_OUTPUT("Unlock"), - INVALID_OUTPUT("Invalid"); - - @Getter - private final String displayString; - - JsonTxOutputType(String displayString) { - this.displayString = displayString; - } -} diff --git a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxType.java b/core/src/main/java/bisq/core/dao/node/explorer/JsonTxType.java deleted file mode 100644 index ee8ffd2527..0000000000 --- a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxType.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.explorer; - -import lombok.Getter; - -// Need to be in sync with TxOutputType -enum JsonTxType { - UNDEFINED("Undefined"), - UNDEFINED_TX_TYPE("Undefined tx type"), - UNVERIFIED("Unverified"), - INVALID("Invalid"), - GENESIS("Genesis"), - TRANSFER_BSQ("Transfer BSQ"), - PAY_TRADE_FEE("Pay trade fee"), - PROPOSAL("Proposal"), - COMPENSATION_REQUEST("Compensation request"), - REIMBURSEMENT_REQUEST("Reimbursement request"), - BLIND_VOTE("Blind vote"), - VOTE_REVEAL("Vote reveal"), - LOCKUP("Lockup"), - UNLOCK("Unlock"), - ASSET_LISTING_FEE("Asset listing fee"), - PROOF_OF_BURN("Proof of burn"), - IRREGULAR("Irregular"); - - @Getter - private final String displayString; - - JsonTxType(String displayString) { - this.displayString = displayString; - } -} diff --git a/core/src/main/java/bisq/core/dao/node/explorer/package-info.java b/core/src/main/java/bisq/core/dao/node/explorer/package-info.java deleted file mode 100644 index 30fc3a6f49..0000000000 --- a/core/src/main/java/bisq/core/dao/node/explorer/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -/** - * Contains classes which are only used for providing data to the BSQ explorer. - */ - -package bisq.core.dao.node.explorer; diff --git a/core/src/main/java/bisq/core/dao/node/full/FullNode.java b/core/src/main/java/bisq/core/dao/node/full/FullNode.java deleted file mode 100644 index 1bdd6b1ffb..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/FullNode.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full; - -import bisq.core.dao.node.BsqNode; -import bisq.core.dao.node.explorer.ExportJsonFilesService; -import bisq.core.dao.node.full.network.FullNodeNetworkService; -import bisq.core.dao.node.full.rpc.NotificationHandlerException; -import bisq.core.dao.node.parser.BlockParser; -import bisq.core.dao.node.parser.exceptions.BlockHashNotConnectingException; -import bisq.core.dao.node.parser.exceptions.BlockHeightNotConnectingException; -import bisq.core.dao.node.parser.exceptions.RequiredReorgFromSnapshotException; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.DaoStateSnapshotService; -import bisq.core.dao.state.model.blockchain.Block; - -import bisq.network.p2p.P2PService; -import bisq.network.p2p.network.ConnectionState; - -import bisq.common.UserThread; -import bisq.common.handlers.ResultHandler; - -import javax.inject.Inject; - -import java.net.ConnectException; - -import java.util.function.Consumer; - -import lombok.extern.slf4j.Slf4j; - -/** - * Main class for a full node which have Bitcoin Core with rpc running and does the blockchain lookup itself. - * It also provides the BSQ transactions to lite nodes on request and broadcasts new BSQ blocks. - *

- * TODO request p2p network data again after parsing is complete to be sure that in case we missed data during parsing - * we get it added. - */ -@Slf4j -public class FullNode extends BsqNode { - - private final RpcService rpcService; - private final FullNodeNetworkService fullNodeNetworkService; - private boolean addBlockHandlerAdded; - private int blocksToParseInBatch; - private long parseInBatchStartTime; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - private FullNode(BlockParser blockParser, - DaoStateService daoStateService, - DaoStateSnapshotService daoStateSnapshotService, - P2PService p2PService, - RpcService rpcService, - ExportJsonFilesService exportJsonFilesService, - FullNodeNetworkService fullNodeNetworkService) { - super(blockParser, daoStateService, daoStateSnapshotService, p2PService, exportJsonFilesService); - this.rpcService = rpcService; - - this.fullNodeNetworkService = fullNodeNetworkService; - ConnectionState.setExpectedRequests(5); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Public methods - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void start() { - fullNodeNetworkService.start(); - - rpcService.setup(() -> { - super.onInitialized(); - startParseBlocks(); - }, - this::handleError); - } - - public void shutDown() { - super.shutDown(); - fullNodeNetworkService.shutDown(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Protected - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void startParseBlocks() { - requestChainHeadHeightAndParseBlocks(getStartBlockHeight()); - } - - @Override - protected void startReOrgFromLastSnapshot() { - super.startReOrgFromLastSnapshot(); - - int startBlockHeight = getStartBlockHeight(); - rpcService.requestChainHeadHeight(chainHeight -> parseBlocksOnHeadHeight(startBlockHeight, chainHeight), - this::handleError); - } - - @Override - protected void onP2PNetworkReady() { - super.onP2PNetworkReady(); - - if (parseBlockchainComplete) { - addBlockHandler(); - int blockHeightOfLastBlock = daoStateService.getBlockHeightOfLastBlock(); - log.info("onP2PNetworkReady: We run parseBlocksIfNewBlockAvailable with latest block height {}.", blockHeightOfLastBlock); - parseBlocksIfNewBlockAvailable(blockHeightOfLastBlock); - } - } - - @Override - protected void onParseBlockChainComplete() { - super.onParseBlockChainComplete(); - - if (p2pNetworkReady) - addBlockHandler(); - else - log.info("onParseBlockChainComplete but P2P network is not ready yet."); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void addBlockHandler() { - if (!addBlockHandlerAdded) { - addBlockHandlerAdded = true; - rpcService.addNewDtoBlockHandler(rawBlock -> { - try { - // We need to call that before parsing to have set the chain tip correctly for clients - // which might listen for new blocks on daoStateService. DaoStateListener.onNewBlockHeight - // is called before the doParseBlock returns. - - // We only update chainTipHeight if we get a newer block - int blockHeight = rawBlock.getHeight(); - if (blockHeight > chainTipHeight) - chainTipHeight = blockHeight; - - doParseBlock(rawBlock).ifPresent(this::onNewBlock); - } catch (RequiredReorgFromSnapshotException ignore) { - } - }, - this::handleError); - } - } - - private void onNewBlock(Block block) { - maybeExportToJson(); - - if (p2pNetworkReady && parseBlockchainComplete) - fullNodeNetworkService.publishNewBlock(block); - } - - - private void parseBlocksIfNewBlockAvailable(int chainHeight) { - rpcService.requestChainHeadHeight(newChainHeight -> { - if (newChainHeight > chainHeight) { - log.info("During parsing new blocks have arrived. We parse again with those missing blocks." + - "ChainHeadHeight={}, newChainHeadHeight={}", chainHeight, newChainHeight); - parseBlocksOnHeadHeight(chainHeight + 1, newChainHeight); - } else { - log.info("parseBlocksIfNewBlockAvailable did not result in a new block, so we complete."); - log.info("parse {} blocks took {} seconds", blocksToParseInBatch, (System.currentTimeMillis() - parseInBatchStartTime) / 1000d); - if (!parseBlockchainComplete) { - onParseBlockChainComplete(); - } - } - }, - this::handleError); - } - - private void requestChainHeadHeightAndParseBlocks(int startBlockHeight) { - log.info("requestChainHeadHeightAndParseBlocks with startBlockHeight={}", startBlockHeight); - rpcService.requestChainHeadHeight(chainHeight -> parseBlocksOnHeadHeight(startBlockHeight, chainHeight), - this::handleError); - } - - private void parseBlocksOnHeadHeight(int startBlockHeight, int chainHeight) { - if (startBlockHeight <= chainHeight) { - blocksToParseInBatch = chainHeight - startBlockHeight; - parseInBatchStartTime = System.currentTimeMillis(); - log.info("parse {} blocks with startBlockHeight={} and chainHeight={}", blocksToParseInBatch, startBlockHeight, chainHeight); - chainTipHeight = chainHeight; - parseBlocks(startBlockHeight, - chainHeight, - this::onNewBlock, - () -> { - // We are done but it might be that new blocks have arrived in the meantime, - // so we try again with startBlockHeight set to current chainHeight - // We also set up the listener in the else main branch where we check - // if we are at chainTip, so do not include here another check as it would - // not trigger the listener registration. - parseBlocksIfNewBlockAvailable(chainHeight); - }, this::handleError); - } else { - log.warn("We are trying to start with a block which is above the chain height of Bitcoin Core. " + - "We need probably wait longer until Bitcoin Core has fully synced. " + - "We try again after a delay of 1 min."); - UserThread.runAfter(() -> requestChainHeadHeightAndParseBlocks(startBlockHeight), 60); - } - } - - private void parseBlocks(int startBlockHeight, - int chainHeight, - Consumer newBlockHandler, - ResultHandler resultHandler, - Consumer errorHandler) { - parseBlockRecursively(startBlockHeight, chainHeight, newBlockHandler, resultHandler, errorHandler); - } - - private void parseBlockRecursively(int blockHeight, - int chainHeight, - Consumer newBlockHandler, - ResultHandler resultHandler, - Consumer errorHandler) { - rpcService.requestDtoBlock(blockHeight, - rawBlock -> { - try { - doParseBlock(rawBlock).ifPresent(newBlockHandler); - - // Increment blockHeight and recursively call parseBlockAsync until we reach chainHeight - if (blockHeight < chainHeight) { - int newBlockHeight = blockHeight + 1; - parseBlockRecursively(newBlockHeight, chainHeight, newBlockHandler, resultHandler, errorHandler); - } else { - // We are done - resultHandler.handleResult(); - } - } catch (RequiredReorgFromSnapshotException ignore) { - // If we get a reorg we don't continue to call parseBlockRecursively - } - }, - errorHandler); - } - - private void handleError(Throwable throwable) { - if (throwable instanceof BlockHashNotConnectingException || throwable instanceof BlockHeightNotConnectingException) { - // We do not escalate that exception as it is handled with the snapshot manager to recover its state. - log.warn(throwable.toString()); - } else { - String errorMessage = "An error occurred: Error=" + throwable.toString(); - log.error(errorMessage); - throwable.printStackTrace(); - - if (throwable instanceof RpcException) { - Throwable cause = throwable.getCause(); - if (cause != null) { - if (cause instanceof ConnectException) { - if (warnMessageHandler != null) - warnMessageHandler.accept("You have configured Bisq to run as DAO full node but there is no " + - "localhost Bitcoin Core node detected. You need to have Bitcoin Core started and synced before " + - "starting Bisq. Please restart Bisq with proper DAO full node setup or switch to lite node mode."); - return; - } else if (cause instanceof NotificationHandlerException) { - log.error("Error from within block notification daemon: {}", cause.getCause().toString()); - startReOrgFromLastSnapshot(); - return; - } else if (cause instanceof Error) { - throw (Error) cause; - } - } - } - - if (errorMessageHandler != null) - errorMessageHandler.accept(errorMessage); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/node/full/RawBlock.java b/core/src/main/java/bisq/core/dao/node/full/RawBlock.java deleted file mode 100644 index 686ecb2dbe..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/RawBlock.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full; - -import bisq.core.dao.state.model.blockchain.BaseBlock; -import bisq.core.dao.state.model.blockchain.Block; - -import bisq.common.proto.network.NetworkPayload; - -import com.google.common.collect.ImmutableList; - -import java.util.ArrayList; -import java.util.stream.Collectors; - -import lombok.EqualsAndHashCode; -import lombok.Value; - -import javax.annotation.concurrent.Immutable; - -/** - * A block derived from the BTC blockchain and filtered for BSQ relevant transactions, though the transactions are not - * verified at that stage. That block is passed to lite nodes over the P2P network. The validation is done by the lite - * nodes themselves but the transactions are already filtered for BSQ only transactions to keep bandwidth requirements - * low. - * Sent over wire. - */ -@Immutable -@EqualsAndHashCode(callSuper = true) -@Value -public final class RawBlock extends BaseBlock implements NetworkPayload { - // Used when a full node sends a block over the P2P network - public static RawBlock fromBlock(Block block) { - ImmutableList txs = ImmutableList.copyOf(block.getTxs().stream().map(RawTx::fromTx).collect(Collectors.toList())); - return new RawBlock(block.getHeight(), - block.getTime(), - block.getHash(), - block.getPreviousBlockHash(), - txs); - } - - private final ImmutableList rawTxs; - - RawBlock(int height, - long time, - String hash, - String previousBlockHash, - ImmutableList rawTxs) { - super(height, - time, - hash, - previousBlockHash); - this.rawTxs = rawTxs; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.BaseBlock toProtoMessage() { - protobuf.RawBlock.Builder builder = protobuf.RawBlock.newBuilder() - .addAllRawTxs(rawTxs.stream() - .map(RawTx::toProtoMessage) - .collect(Collectors.toList())); - return getBaseBlockBuilder().setRawBlock(builder).build(); - } - - public static RawBlock fromProto(protobuf.BaseBlock proto) { - protobuf.RawBlock rawBlockProto = proto.getRawBlock(); - ImmutableList rawTxs = rawBlockProto.getRawTxsList().isEmpty() ? - ImmutableList.copyOf(new ArrayList<>()) : - ImmutableList.copyOf(rawBlockProto.getRawTxsList().stream() - .map(RawTx::fromProto) - .collect(Collectors.toList())); - return new RawBlock(proto.getHeight(), - proto.getTime(), - proto.getHash(), - proto.getPreviousBlockHash(), - rawTxs); - } - - - @Override - public String toString() { - return "RawBlock{" + - "\n rawTxs=" + rawTxs + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/full/RawTx.java b/core/src/main/java/bisq/core/dao/node/full/RawTx.java deleted file mode 100644 index bd291a0cd4..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/RawTx.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full; - -import bisq.core.dao.state.model.blockchain.BaseTx; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxInput; - -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkPayload; - -import com.google.common.collect.ImmutableList; - -import java.util.ArrayList; -import java.util.stream.Collectors; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -/** - * RawTx as we get it from the RPC service (full node) or from via the P2P network (lite node). - * It contains pure bitcoin blockchain data without any BSQ specific data. - * Sent over wire. - */ -@Immutable -@Slf4j -@EqualsAndHashCode(callSuper = true) -@Value -public final class RawTx extends BaseTx implements NetworkPayload { - // Used when a full node sends a block over the P2P network - public static RawTx fromTx(Tx tx) { - ImmutableList rawTxOutputs = ImmutableList.copyOf(tx.getTxOutputs().stream() - .map(RawTxOutput::fromTxOutput) - .collect(Collectors.toList())); - - return new RawTx(tx.getTxVersion(), - tx.getId(), - tx.getBlockHeight(), - tx.getBlockHash(), - tx.getTime(), - tx.getTxInputs(), - rawTxOutputs); - } - - private final ImmutableList rawTxOutputs; - - // The RPC service is creating a RawTx. - public RawTx(String id, - int blockHeight, - String blockHash, - long time, - ImmutableList txInputs, - ImmutableList rawTxOutputs) { - super(Version.BSQ_TX_VERSION, - id, - blockHeight, - blockHash, - time, - txInputs); - this.rawTxOutputs = rawTxOutputs; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private RawTx(String txVersion, - String id, - int blockHeight, - String blockHash, - long time, - ImmutableList txInputs, - ImmutableList rawTxOutputs) { - super(txVersion, - id, - blockHeight, - blockHash, - time, - txInputs); - this.rawTxOutputs = rawTxOutputs; - } - - @Override - public protobuf.BaseTx toProtoMessage() { - final protobuf.RawTx.Builder builder = protobuf.RawTx.newBuilder() - .addAllRawTxOutputs(rawTxOutputs.stream() - .map(RawTxOutput::toProtoMessage) - .collect(Collectors.toList())); - return getBaseTxBuilder().setRawTx(builder).build(); - } - - public static RawTx fromProto(protobuf.BaseTx protoBaseTx) { - ImmutableList txInputs = protoBaseTx.getTxInputsList().isEmpty() ? - ImmutableList.copyOf(new ArrayList<>()) : - ImmutableList.copyOf(protoBaseTx.getTxInputsList().stream() - .map(TxInput::fromProto) - .collect(Collectors.toList())); - protobuf.RawTx protoRawTx = protoBaseTx.getRawTx(); - ImmutableList outputs = protoRawTx.getRawTxOutputsList().isEmpty() ? - ImmutableList.copyOf(new ArrayList<>()) : - ImmutableList.copyOf(protoRawTx.getRawTxOutputsList().stream() - .map(RawTxOutput::fromProto) - .collect(Collectors.toList())); - return new RawTx(protoBaseTx.getTxVersion(), - protoBaseTx.getId(), - protoBaseTx.getBlockHeight(), - protoBaseTx.getBlockHash(), - protoBaseTx.getTime(), - txInputs, - outputs); - } - - @Override - public String toString() { - return "RawTx{" + - "\n rawTxOutputs=" + rawTxOutputs + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/full/RawTxOutput.java b/core/src/main/java/bisq/core/dao/node/full/RawTxOutput.java deleted file mode 100644 index 2d5599b2fb..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/RawTxOutput.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full; - -import bisq.core.dao.state.model.blockchain.BaseTxOutput; -import bisq.core.dao.state.model.blockchain.PubKeyScript; -import bisq.core.dao.state.model.blockchain.TxOutput; - -import bisq.common.proto.network.NetworkPayload; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** - * TxOutput used in RawTx. Containing only immutable bitcoin specific fields. - * Sent over wire. - */ -@Slf4j -@Immutable -@EqualsAndHashCode(callSuper = true) -@Value -public final class RawTxOutput extends BaseTxOutput implements NetworkPayload { - public static RawTxOutput fromTxOutput(TxOutput txOutput) { - return new RawTxOutput(txOutput.getIndex(), - txOutput.getValue(), - txOutput.getTxId(), - txOutput.getPubKeyScript(), - txOutput.getAddress(), - txOutput.getOpReturnData(), - txOutput.getBlockHeight()); - } - - public RawTxOutput(int index, - long value, - String txId, - @Nullable PubKeyScript pubKeyScript, - @Nullable String address, - @Nullable byte[] opReturnData, - int blockHeight) { - super(index, - value, - txId, - pubKeyScript, - address, - opReturnData, - blockHeight); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.BaseTxOutput toProtoMessage() { - return getRawTxOutputBuilder().setRawTxOutput(protobuf.RawTxOutput.newBuilder()).build(); - } - - public static RawTxOutput fromProto(protobuf.BaseTxOutput proto) { - return new RawTxOutput(proto.getIndex(), - proto.getValue(), - proto.getTxId(), - proto.hasPubKeyScript() ? PubKeyScript.fromProto(proto.getPubKeyScript()) : null, - proto.getAddress().isEmpty() ? null : proto.getAddress(), - proto.getOpReturnData().isEmpty() ? null : proto.getOpReturnData().toByteArray(), - proto.getBlockHeight()); - } - - - @Override - public String toString() { - return "RawTxOutput{} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/full/RpcException.java b/core/src/main/java/bisq/core/dao/node/full/RpcException.java deleted file mode 100644 index 92defd15c5..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/RpcException.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full; - -class RpcException extends Exception { - RpcException(String message, Throwable cause) { - super(message, cause); - } - - RpcException(Throwable cause) { - super(cause); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/full/RpcService.java b/core/src/main/java/bisq/core/dao/node/full/RpcService.java deleted file mode 100644 index 394f420de6..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/RpcService.java +++ /dev/null @@ -1,379 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full; - -import bisq.core.dao.node.full.rpc.BitcoindClient; -import bisq.core.dao.node.full.rpc.BitcoindDaemon; -import bisq.core.dao.node.full.rpc.dto.DtoPubKeyScript; -import bisq.core.dao.node.full.rpc.dto.RawDtoBlock; -import bisq.core.dao.node.full.rpc.dto.RawDtoInput; -import bisq.core.dao.node.full.rpc.dto.RawDtoTransaction; -import bisq.core.dao.state.model.blockchain.PubKeyScript; -import bisq.core.dao.state.model.blockchain.ScriptType; -import bisq.core.dao.state.model.blockchain.TxInput; -import bisq.core.user.Preferences; - -import bisq.common.UserThread; -import bisq.common.config.Config; -import bisq.common.handlers.ResultHandler; -import bisq.common.util.Utilities; - -import org.bitcoinj.core.Utils; - -import com.google.inject.Inject; - -import javax.inject.Named; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import com.google.common.collect.Range; -import com.google.common.primitives.Chars; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; - -import java.io.IOException; - -import java.math.BigDecimal; - -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - -import org.jetbrains.annotations.NotNull; - -/** - * Request blockchain data via RPC from Bitcoin Core for a FullNode. - * Runs in a custom thread. - * See the rpc.md file in the doc directory for more info about the setup. - */ -@Slf4j -public class RpcService { - private static final int ACTIVATE_HARD_FORK_2_HEIGHT_MAINNET = 680300; - private static final int ACTIVATE_HARD_FORK_2_HEIGHT_TESTNET = 1943000; - private static final int ACTIVATE_HARD_FORK_2_HEIGHT_REGTEST = 1; - private static final Range SUPPORTED_NODE_VERSION_RANGE = Range.closedOpen(180000, 210100); - - private final String rpcUser; - private final String rpcPassword; - private final String rpcHost; - private final int rpcPort; - private final int rpcBlockPort; - private final String rpcBlockHost; - - private BitcoindClient client; - private BitcoindDaemon daemon; - - // We could use multiple threads but then we need to support ordering of results in a queue - // Keep that for optimization after measuring performance differences - private final ListeningExecutorService executor = Utilities.getSingleThreadListeningExecutor("RpcService"); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - private RpcService(Preferences preferences, - @Named(Config.RPC_HOST) String rpcHost, - @Named(Config.RPC_PORT) int rpcPort, - @Named(Config.RPC_BLOCK_NOTIFICATION_PORT) int rpcBlockPort, - @Named(Config.RPC_BLOCK_NOTIFICATION_HOST) String rpcBlockHost) { - this.rpcUser = preferences.getRpcUser(); - this.rpcPassword = preferences.getRpcPw(); - - // mainnet is 8332, testnet 18332, regtest 18443 - boolean isHostSet = !rpcHost.isEmpty(); - boolean isPortSet = rpcPort != Config.UNSPECIFIED_PORT; - boolean isMainnet = Config.baseCurrencyNetwork().isMainnet(); - boolean isTestnet = Config.baseCurrencyNetwork().isTestnet(); - boolean isDaoBetaNet = Config.baseCurrencyNetwork().isDaoBetaNet(); - this.rpcHost = isHostSet ? rpcHost : "127.0.0.1"; - this.rpcPort = isPortSet ? rpcPort : - isMainnet || isDaoBetaNet ? 8332 : - isTestnet ? 18332 : - 18443; // regtest - boolean isBlockPortSet = rpcBlockPort != Config.UNSPECIFIED_PORT; - boolean isBlockHostSet = !rpcBlockHost.isEmpty(); - this.rpcBlockPort = isBlockPortSet ? rpcBlockPort : 5125; - this.rpcBlockHost = isBlockHostSet ? rpcBlockHost : "127.0.0.1"; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void shutDown() { - if (daemon != null) { - daemon.shutdown(); - log.info("daemon shut down"); - } - - executor.shutdown(); - } - - void setup(ResultHandler resultHandler, Consumer errorHandler) { - ListenableFuture future = executor.submit(() -> { - try { - log.info("Starting RpcService on {}:{} with user {}, listening for blocknotify on port {} from {}", - this.rpcHost, this.rpcPort, this.rpcUser, this.rpcBlockPort, this.rpcBlockHost); - - long startTs = System.currentTimeMillis(); - - client = BitcoindClient.builder() - .rpcHost(rpcHost) - .rpcPort(rpcPort) - .rpcUser(rpcUser) - .rpcPassword(rpcPassword) - .build(); - checkNodeVersionAndHealth(); - - daemon = new BitcoindDaemon(rpcBlockHost, rpcBlockPort, throwable -> { - log.error(throwable.toString()); - throwable.printStackTrace(); - UserThread.execute(() -> errorHandler.accept(new RpcException(throwable))); - }); - - log.info("Setup took {} ms", System.currentTimeMillis() - startTs); - } catch (Throwable e) { - log.error(e.toString()); - e.printStackTrace(); - throw new RpcException(e.toString(), e); - } - return null; - }); - - Futures.addCallback(future, new FutureCallback<>() { - public void onSuccess(Void ignore) { - UserThread.execute(resultHandler::handleResult); - } - - public void onFailure(@NotNull Throwable throwable) { - UserThread.execute(() -> errorHandler.accept(throwable)); - } - }, MoreExecutors.directExecutor()); - } - - private String decodeNodeVersion(Integer encodedVersion) { - var paddedEncodedVersion = Strings.padStart(encodedVersion.toString(), 8, '0'); - - return Lists.partition(Chars.asList(paddedEncodedVersion.toCharArray()), 2).stream() - .map(chars -> new String(Chars.toArray(chars)).replaceAll("^0", "")) - .collect(Collectors.joining(".")) - .replaceAll("\\.0$", ""); - } - - private void checkNodeVersionAndHealth() throws IOException { - var networkInfo = client.getNetworkInfo(); - var nodeVersion = decodeNodeVersion(networkInfo.getVersion()); - - if (SUPPORTED_NODE_VERSION_RANGE.contains(networkInfo.getVersion())) { - log.info("Got Bitcoin Core version: {}", nodeVersion); - } else { - log.warn("Server version mismatch - client optimized for '[{} .. {})', node responded with '{}'", - decodeNodeVersion(SUPPORTED_NODE_VERSION_RANGE.lowerEndpoint()), - decodeNodeVersion(SUPPORTED_NODE_VERSION_RANGE.upperEndpoint()), nodeVersion); - } - - var bestRawBlock = client.getBlock(client.getBestBlockHash(), 1); - long currentTime = System.currentTimeMillis() / 1000; - if ((currentTime - bestRawBlock.getTime()) > TimeUnit.HOURS.toSeconds(6)) { - log.warn("Last available block was mined >{} hours ago; please check your network connection", - ((currentTime - bestRawBlock.getTime()) / 3600)); - } - } - - void addNewDtoBlockHandler(Consumer dtoBlockHandler, - Consumer errorHandler) { - daemon.setBlockListener(blockHash -> { - try { - var rawDtoBlock = client.getBlock(blockHash, 2); - log.info("New block received: height={}, id={}", rawDtoBlock.getHeight(), rawDtoBlock.getHash()); - - var block = getBlockFromRawDtoBlock(rawDtoBlock); - UserThread.execute(() -> dtoBlockHandler.accept(block)); - } catch (Throwable t) { - errorHandler.accept(t); - } - }); - } - - void requestChainHeadHeight(Consumer resultHandler, Consumer errorHandler) { - ListenableFuture future = executor.submit(client::getBlockCount); - Futures.addCallback(future, new FutureCallback<>() { - public void onSuccess(Integer chainHeight) { - UserThread.execute(() -> resultHandler.accept(chainHeight)); - } - - public void onFailure(@NotNull Throwable throwable) { - UserThread.execute(() -> errorHandler.accept(throwable)); - } - }, MoreExecutors.directExecutor()); - } - - void requestDtoBlock(int blockHeight, - Consumer resultHandler, - Consumer errorHandler) { - ListenableFuture future = executor.submit(() -> { - long startTs = System.currentTimeMillis(); - String blockHash = client.getBlockHash(blockHeight); - var rawDtoBlock = client.getBlock(blockHash, 2); - var block = getBlockFromRawDtoBlock(rawDtoBlock); - log.info("requestDtoBlock from bitcoind at blockHeight {} with {} txs took {} ms", - blockHeight, block.getRawTxs().size(), System.currentTimeMillis() - startTs); - return block; - }); - - Futures.addCallback(future, new FutureCallback<>() { - @Override - public void onSuccess(RawBlock block) { - UserThread.execute(() -> resultHandler.accept(block)); - } - - @Override - public void onFailure(@NotNull Throwable throwable) { - log.error("Error at requestDtoBlock: blockHeight={}", blockHeight); - UserThread.execute(() -> errorHandler.accept(throwable)); - } - }, MoreExecutors.directExecutor()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private static RawBlock getBlockFromRawDtoBlock(RawDtoBlock rawDtoBlock) { - List txList = rawDtoBlock.getTx().stream() - .map(e -> getTxFromRawTransaction(e, rawDtoBlock)) - .collect(Collectors.toList()); - return new RawBlock(rawDtoBlock.getHeight(), - rawDtoBlock.getTime() * 1000, // rawDtoBlock.getTime() is in sec but we want ms - rawDtoBlock.getHash(), - rawDtoBlock.getPreviousBlockHash(), - ImmutableList.copyOf(txList)); - } - - private static RawTx getTxFromRawTransaction(RawDtoTransaction rawDtoTx, - RawDtoBlock rawDtoBlock) { - String txId = rawDtoTx.getTxId(); - long blockTime = rawDtoBlock.getTime() * 1000; // We convert block time from sec to ms - int blockHeight = rawDtoBlock.getHeight(); - String blockHash = rawDtoBlock.getHash(); - - // Extracting pubKeys for segwit (P2WPKH) inputs, instead of just P2PKH inputs as - // originally, changes the DAO state and thus represents a hard fork. We disallow - // it until the fork activates, which is determined by block height. - boolean allowSegwit = blockHeight >= getActivateHardFork2Height(); - - final List txInputs = rawDtoTx.getVIn() - .stream() - .filter(rawInput -> rawInput != null && rawInput.getVOut() != null && rawInput.getTxId() != null) - .map(rawInput -> { - String pubKeyAsHex = extractPubKeyAsHex(rawInput, allowSegwit); - if (pubKeyAsHex == null) { - log.debug("pubKeyAsHex is not set as we received a not supported sigScript. " + - "txId={}, asm={}, txInWitness={}", - rawDtoTx.getTxId(), rawInput.getScriptSig().getAsm(), rawInput.getTxInWitness()); - } - return new TxInput(rawInput.getTxId(), rawInput.getVOut(), pubKeyAsHex); - }) - .collect(Collectors.toList()); - - final List txOutputs = rawDtoTx.getVOut() - .stream() - .filter(e -> e != null && e.getN() != null && e.getValue() != null && e.getScriptPubKey() != null) - .map(rawDtoTxOutput -> { - byte[] opReturnData = null; - DtoPubKeyScript scriptPubKey = rawDtoTxOutput.getScriptPubKey(); - if (ScriptType.NULL_DATA.equals(scriptPubKey.getType()) && scriptPubKey.getAsm() != null) { - String[] chunks = scriptPubKey.getAsm().split(" "); - // We get on testnet a lot of "OP_RETURN 0" data, so we filter those away - if (chunks.length == 2 && "OP_RETURN".equals(chunks[0]) && !"0".equals(chunks[1])) { - try { - opReturnData = Utils.HEX.decode(chunks[1]); - } catch (Throwable t) { - log.debug("Error at Utils.HEX.decode(chunks[1]): " + t.toString() + - " / chunks[1]=" + chunks[1] + - "\nWe get sometimes exceptions with opReturn data, seems BitcoinJ " + - "cannot handle all " + - "existing OP_RETURN data, but we ignore them anyway as the OP_RETURN " + - "data used for DAO transactions are all valid in BitcoinJ"); - } - } - } - // We don't support raw MS which are the only case where scriptPubKey.getAddresses()>1 - String address = scriptPubKey.getAddresses() != null && - scriptPubKey.getAddresses().size() == 1 ? scriptPubKey.getAddresses().get(0) : null; - PubKeyScript pubKeyScript = new PubKeyScript(scriptPubKey); - return new RawTxOutput(rawDtoTxOutput.getN(), - BigDecimal.valueOf(rawDtoTxOutput.getValue()).movePointRight(8).longValueExact(), - rawDtoTx.getTxId(), - pubKeyScript, - address, - opReturnData, - blockHeight); - } - ) - .collect(Collectors.toList()); - - return new RawTx(txId, - blockHeight, - blockHash, - blockTime, - ImmutableList.copyOf(txInputs), - ImmutableList.copyOf(txOutputs)); - } - - private static int getActivateHardFork2Height() { - return Config.baseCurrencyNetwork().isMainnet() ? ACTIVATE_HARD_FORK_2_HEIGHT_MAINNET : - Config.baseCurrencyNetwork().isTestnet() ? ACTIVATE_HARD_FORK_2_HEIGHT_TESTNET : - ACTIVATE_HARD_FORK_2_HEIGHT_REGTEST; - } - - @VisibleForTesting - static String extractPubKeyAsHex(RawDtoInput rawInput, boolean allowSegwit) { - // We only allow inputs with a single SIGHASH_ALL signature. That is, multisig or - // signing of only some of the tx inputs/outputs is intentionally disallowed... - if (rawInput.getScriptSig() == null) { - // coinbase input - no pubKey to extract - return null; - } - String[] split = rawInput.getScriptSig().getAsm().split(" "); - if (split.length == 2 && split[0].endsWith("[ALL]")) { - // P2PKH input - return split[1]; - } - List txInWitness = rawInput.getTxInWitness() != null ? rawInput.getTxInWitness() : List.of(); - if (allowSegwit && split.length < 2 && txInWitness.size() == 2 && txInWitness.get(0).endsWith("01")) { - // P2WPKH or P2SH-P2WPKH input - return txInWitness.get(1); - } - // If we receive a pay to pubkey tx, the pubKey is not included as it is in the - // output already. - return null; - } -} diff --git a/core/src/main/java/bisq/core/dao/node/full/network/FullNodeNetworkService.java b/core/src/main/java/bisq/core/dao/node/full/network/FullNodeNetworkService.java deleted file mode 100644 index e8b6a0483f..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/network/FullNodeNetworkService.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full.network; - -import bisq.core.dao.governance.blindvote.network.messages.RepublishGovernanceDataRequest; -import bisq.core.dao.governance.voteresult.MissingDataRequestService; -import bisq.core.dao.node.full.RawBlock; -import bisq.core.dao.node.messages.GetBlocksRequest; -import bisq.core.dao.node.messages.NewBlockBroadcastMessage; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Block; - -import bisq.network.p2p.network.Connection; -import bisq.network.p2p.network.MessageListener; -import bisq.network.p2p.network.NetworkNode; -import bisq.network.p2p.peers.Broadcaster; -import bisq.network.p2p.peers.PeerManager; - -import bisq.common.UserThread; -import bisq.common.proto.network.NetworkEnvelope; - -import javax.inject.Inject; - -import java.util.HashMap; -import java.util.Map; - -import lombok.extern.slf4j.Slf4j; - -import org.jetbrains.annotations.Nullable; - -/** - * Responsible for handling requests for BSQ blocks from lite nodes and for broadcasting new blocks to the P2P network. - */ -@Slf4j -public class FullNodeNetworkService implements MessageListener, PeerManager.Listener { - - private static final long CLEANUP_TIMER = 120; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Class fields - /////////////////////////////////////////////////////////////////////////////////////////// - - private final NetworkNode networkNode; - private final PeerManager peerManager; - private final Broadcaster broadcaster; - private final MissingDataRequestService missingDataRequestService; - private final DaoStateService daoStateService; - - // Key is connection UID - private final Map getBlocksRequestHandlers = new HashMap<>(); - private boolean stopped; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public FullNodeNetworkService(NetworkNode networkNode, - PeerManager peerManager, - Broadcaster broadcaster, - MissingDataRequestService missingDataRequestService, - DaoStateService daoStateService) { - this.networkNode = networkNode; - this.peerManager = peerManager; - this.broadcaster = broadcaster; - this.missingDataRequestService = missingDataRequestService; - this.daoStateService = daoStateService; - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void start() { - networkNode.addMessageListener(this); - peerManager.addListener(this); - } - - @SuppressWarnings("Duplicates") - public void shutDown() { - stopped = true; - networkNode.removeMessageListener(this); - peerManager.removeListener(this); - } - - public void publishNewBlock(Block block) { - log.info("Publish new block at height={} and block hash={}", block.getHeight(), block.getHash()); - RawBlock rawBlock = RawBlock.fromBlock(block); - NewBlockBroadcastMessage newBlockBroadcastMessage = new NewBlockBroadcastMessage(rawBlock); - broadcaster.broadcast(newBlockBroadcastMessage, networkNode.getNodeAddress()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PeerManager.Listener implementation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onAllConnectionsLost() { - stopped = true; - } - - @Override - public void onNewConnectionAfterAllConnectionsLost() { - stopped = false; - } - - @Override - public void onAwakeFromStandby() { - stopped = false; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // MessageListener implementation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { - if (networkEnvelope instanceof GetBlocksRequest) { - handleGetBlocksRequest((GetBlocksRequest) networkEnvelope, connection); - } else if (networkEnvelope instanceof RepublishGovernanceDataRequest) { - 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(); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/full/network/GetBlocksRequestHandler.java b/core/src/main/java/bisq/core/dao/node/full/network/GetBlocksRequestHandler.java deleted file mode 100644 index dd8d6e9203..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/network/GetBlocksRequestHandler.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full.network; - -import bisq.core.dao.node.full.RawBlock; -import bisq.core.dao.node.messages.GetBlocksRequest; -import bisq.core.dao.node.messages.GetBlocksResponse; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Block; - -import bisq.network.p2p.network.CloseConnectionReason; -import bisq.network.p2p.network.Connection; -import bisq.network.p2p.network.NetworkNode; - -import bisq.common.Timer; -import bisq.common.UserThread; - -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.MoreExecutors; -import com.google.common.util.concurrent.SettableFuture; - -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - -import org.jetbrains.annotations.NotNull; - -/** - * Accepts a GetBlocksRequest from a lite node and sends back a corresponding GetBlocksResponse. - */ -@Slf4j -class GetBlocksRequestHandler { - private static final long TIMEOUT_MIN = 3; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Listener - /////////////////////////////////////////////////////////////////////////////////////////// - - public interface Listener { - void onComplete(); - - void onFault(String errorMessage, Connection connection); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Class fields - /////////////////////////////////////////////////////////////////////////////////////////// - - private final NetworkNode networkNode; - private final DaoStateService daoStateService; - private final Listener listener; - private Timer timeoutTimer; - private boolean stopped; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - public GetBlocksRequestHandler(NetworkNode networkNode, DaoStateService daoStateService, Listener listener) { - this.networkNode = networkNode; - this.daoStateService = daoStateService; - this.listener = listener; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - 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 blocks = new LinkedList<>(daoStateService.getBlocksFromBlockHeight(getBlocksRequest.getFromBlockHeight(), 6000)); - List 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 {}. " + - "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 future = networkNode.sendMessage(connection, getBlocksResponse); - Futures.addCallback(future, new FutureCallback<>() { - @Override - public void onSuccess(Connection connection) { - if (!stopped) { - log.info("Send DataResponse to {} succeeded. getBlocksResponse.getBlocks().size()={}", - connection.getPeersNodeAddressOptional(), getBlocksResponse.getBlocks().size()); - cleanup(); - listener.onComplete(); - } else { - log.trace("We have stopped already. We ignore that networkNode.sendMessage.onSuccess call."); - } - } - - @Override - public void onFailure(@NotNull Throwable throwable) { - if (!stopped) { - String errorMessage = "Sending getBlocksResponse to " + connection + - " failed. That is expected if the peer is offline. getBlocksResponse=" + getBlocksResponse + "." + - "Exception: " + throwable.getMessage(); - handleFault(errorMessage, CloseConnectionReason.SEND_MSG_FAILURE, connection); - } else { - log.trace("We have stopped already. We ignore that networkNode.sendMessage.onFailure call."); - } - } - }, MoreExecutors.directExecutor()); - } - - public void stop() { - cleanup(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void handleFault(String errorMessage, CloseConnectionReason closeConnectionReason, Connection connection) { - if (!stopped) { - log.warn("{}, closeConnectionReason={}", errorMessage, closeConnectionReason); - cleanup(); - listener.onFault(errorMessage, connection); - } else { - log.warn("We have already stopped (handleFault)"); - } - } - - private void cleanup() { - stopped = true; - if (timeoutTimer != null) { - timeoutTimer.stop(); - timeoutTimer = null; - } - } -} diff --git a/core/src/main/java/bisq/core/dao/node/full/rpc/BitcoindClient.java b/core/src/main/java/bisq/core/dao/node/full/rpc/BitcoindClient.java deleted file mode 100644 index dd9e97f9fc..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/rpc/BitcoindClient.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full.rpc; - -import bisq.core.dao.node.full.rpc.dto.DtoNetworkInfo; -import bisq.core.dao.node.full.rpc.dto.RawDtoBlock; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; - -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLStreamHandler; - -import java.nio.charset.StandardCharsets; - -import java.io.IOException; - -import java.util.Base64; -import java.util.Collections; -import java.util.Optional; - -import static com.google.common.base.Preconditions.checkNotNull; - - - -import com.googlecode.jsonrpc4j.JsonRpcHttpClient; -import com.googlecode.jsonrpc4j.JsonRpcMethod; -import com.googlecode.jsonrpc4j.ProxyUtil; -import com.googlecode.jsonrpc4j.RequestIDGenerator; - -public interface BitcoindClient { - @JsonRpcMethod("getblock") - RawDtoBlock getBlock(String headerHash, int verbosity) throws IOException; - - @JsonRpcMethod("getblockcount") - Integer getBlockCount() throws IOException; - - @JsonRpcMethod("getblockhash") - String getBlockHash(Integer blockHeight) throws IOException; - - @JsonRpcMethod("getbestblockhash") - String getBestBlockHash() throws IOException; - - @JsonRpcMethod("getnetworkinfo") - DtoNetworkInfo getNetworkInfo() throws IOException; - - static Builder builder() { - return new Builder(); - } - - class Builder { - private String rpcHost; - private int rpcPort = -1; - private String rpcUser; - private String rpcPassword; - private URLStreamHandler urlStreamHandler; - private RequestIDGenerator requestIDGenerator; - - public Builder rpcHost(String rpcHost) { - this.rpcHost = rpcHost; - return this; - } - - public Builder rpcPort(int rpcPort) { - this.rpcPort = rpcPort; - return this; - } - - public Builder rpcUser(String rpcUser) { - this.rpcUser = rpcUser; - return this; - } - - public Builder rpcPassword(String rpcPassword) { - this.rpcPassword = rpcPassword; - return this; - } - - public Builder urlStreamHandler(URLStreamHandler urlStreamHandler) { - this.urlStreamHandler = urlStreamHandler; - return this; - } - - public Builder requestIDGenerator(RequestIDGenerator requestIDGenerator) { - this.requestIDGenerator = requestIDGenerator; - return this; - } - - public BitcoindClient build() throws MalformedURLException { - var userPass = checkNotNull(rpcUser, "rpcUser not set") + - ":" + checkNotNull(rpcPassword, "rpcPassword not set"); - - var headers = Collections.singletonMap("Authorization", "Basic " + - Base64.getEncoder().encodeToString(userPass.getBytes(StandardCharsets.US_ASCII))); - - var httpClient = new JsonRpcHttpClient( - new ObjectMapper() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true), - new URL("http", rpcHost, rpcPort, "", urlStreamHandler), - headers); - Optional.ofNullable(requestIDGenerator).ifPresent(httpClient::setRequestIDGenerator); - return ProxyUtil.createClientProxy(getClass().getClassLoader(), BitcoindClient.class, httpClient); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/node/full/rpc/BitcoindDaemon.java b/core/src/main/java/bisq/core/dao/node/full/rpc/BitcoindDaemon.java deleted file mode 100644 index b607ac4e2a..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/rpc/BitcoindDaemon.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full.rpc; - -import bisq.common.util.Utilities; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; - -import org.apache.commons.io.IOUtils; - -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.SocketException; - -import java.nio.charset.StandardCharsets; - -import java.io.IOException; - -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class BitcoindDaemon { - private final ListeningExecutorService executor = Utilities.getSingleThreadListeningExecutor("block-notification-server"); - private final ListeningExecutorService workerPool = Utilities.getListeningExecutorService("block-notification-worker-%d", - 1, 10, 60, new ArrayBlockingQueue<>(100)); - private final ServerSocket serverSocket; - private final Consumer errorHandler; - private volatile boolean active; - private volatile BlockListener blockListener = blockHash -> { - }; - - public BitcoindDaemon(String host, int port, Consumer errorHandler) throws NotificationHandlerException { - this(newServerSocket(host, port), errorHandler); - } - - @VisibleForTesting - BitcoindDaemon(ServerSocket serverSocket, Consumer errorHandler) { - this.serverSocket = serverSocket; - this.errorHandler = errorHandler; - initialize(); - } - - private static ServerSocket newServerSocket(String host, int port) throws NotificationHandlerException { - try { - return new ServerSocket(port, 5, InetAddress.getByName(host)); - } catch (Exception e) { - throw new NotificationHandlerException(e); - } - } - - private void initialize() { - active = true; - var serverFuture = executor.submit((Callable) () -> { - try { - while (active) { - try (var socket = serverSocket.accept(); var is = socket.getInputStream()) { - var blockHash = IOUtils.toString(is, StandardCharsets.UTF_8).trim(); - var future = workerPool.submit((Callable) () -> { - try { - blockListener.blockDetected(blockHash); - return null; - } catch (RuntimeException e) { - throw new NotificationHandlerException(e); - } - }); - Futures.addCallback(future, Utilities.failureCallback(errorHandler), MoreExecutors.directExecutor()); - } - } - } catch (SocketException e) { - if (active) { - throw new NotificationHandlerException(e); - } - } catch (Exception e) { - throw new NotificationHandlerException(e); - } finally { - log.info("Shutting down block notification server"); - } - return null; - }); - Futures.addCallback(serverFuture, Utilities.failureCallback(errorHandler), MoreExecutors.directExecutor()); - } - - public void shutdown() { - active = false; - try { - serverSocket.close(); - } catch (IOException e) { - log.error("Error closing block notification server socket", e); - } finally { - Utilities.shutdownAndAwaitTermination(executor, 1, TimeUnit.SECONDS); - Utilities.shutdownAndAwaitTermination(workerPool, 5, TimeUnit.SECONDS); - } - } - - public void setBlockListener(BlockListener blockListener) { - this.blockListener = blockListener; - } - - public interface BlockListener { - void blockDetected(String blockHash); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/full/rpc/NotificationHandlerException.java b/core/src/main/java/bisq/core/dao/node/full/rpc/NotificationHandlerException.java deleted file mode 100644 index 3f0278eee9..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/rpc/NotificationHandlerException.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full.rpc; - -public class NotificationHandlerException extends Exception { - public NotificationHandlerException(Throwable cause) { - super(cause); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/full/rpc/dto/DtoNetworkInfo.java b/core/src/main/java/bisq/core/dao/node/full/rpc/dto/DtoNetworkInfo.java deleted file mode 100644 index 2b64073b93..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/rpc/dto/DtoNetworkInfo.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full.rpc.dto; - -import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonValue; - -import java.util.List; - -import lombok.Data; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.RequiredArgsConstructor; - -@Data -@NoArgsConstructor -@JsonInclude(JsonInclude.Include.NON_NULL) -@JsonPropertyOrder({"version", "subversion", "protocolversion", "localservices", "localservicesnames", "localrelay", - "timeoffset", "networkactive", "connections", "connections_in", "connections_out", "networks", "relayfee", - "incrementalfee", "localaddresses", "warnings"}) -public class DtoNetworkInfo { - private Integer version; - @JsonProperty("subversion") - private String subVersion; - @JsonProperty("protocolversion") - private Integer protocolVersion; - @JsonProperty("localservices") - private String localServices; - @JsonProperty("localservicesnames") - private List localServicesNames; - @JsonProperty("localrelay") - private Boolean localRelay; - @JsonProperty("timeoffset") - private Integer timeOffset; - @JsonProperty("networkactive") - private Boolean networkActive; - private Integer connections; - @JsonProperty("connections_in") - private Integer connectionsIn; - @JsonProperty("connections_out") - private Integer connectionsOut; - private List networks; - @JsonProperty("relayfee") - private Double relayFee; - @JsonProperty("incrementalfee") - private Double incrementalFee; - @JsonProperty("localaddresses") - private List localAddresses; - private String warnings; - - @Data - @NoArgsConstructor - @JsonInclude(JsonInclude.Include.NON_NULL) - @JsonPropertyOrder({"name", "limited", "reachable", "proxy", "proxy_randomize_credentials"}) - public static class Network { - private NetworkType name; - private Boolean limited; - private Boolean reachable; - private String proxy; - @JsonProperty("proxy_randomize_credentials") - private Boolean proxyRandomizeCredentials; - } - - @Data - @NoArgsConstructor - @JsonInclude(JsonInclude.Include.NON_NULL) - @JsonPropertyOrder({"address", "port", "score"}) - public static class LocalAddress { - private String address; - private Integer port; - private Integer score; - } - - @RequiredArgsConstructor - public enum NetworkType { - IPV4("ipv4"), IPV6("ipv6"), ONION("onion"); - - @Getter(onMethod_ = @JsonValue) - private final String name; - } - - @RequiredArgsConstructor - public enum ServiceFlag { - @JsonEnumDefaultValue - UNKNOWN(0), - // Taken from https://github.com/bitcoin/bitcoin/blob/master/src/protocol.h: - NETWORK(1), - BLOOM(1 << 2), - WITNESS(1 << 3), - COMPACT_FILTERS(1 << 6), - NETWORK_LIMITED(1 << 10); - - @Getter - private final int value; - } -} diff --git a/core/src/main/java/bisq/core/dao/node/full/rpc/dto/DtoPubKeyScript.java b/core/src/main/java/bisq/core/dao/node/full/rpc/dto/DtoPubKeyScript.java deleted file mode 100644 index a9fd9331c3..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/rpc/dto/DtoPubKeyScript.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full.rpc.dto; - -import bisq.core.dao.state.model.blockchain.ScriptType; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -import java.util.List; - -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@JsonInclude(JsonInclude.Include.NON_NULL) -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonPropertyOrder({"asm", "hex", "reqSigs", "type", "addresses"}) -public class DtoPubKeyScript { - private String asm; - private String hex; - private Integer reqSigs; - private ScriptType type; - private List addresses; -} diff --git a/core/src/main/java/bisq/core/dao/node/full/rpc/dto/DtoSignatureScript.java b/core/src/main/java/bisq/core/dao/node/full/rpc/dto/DtoSignatureScript.java deleted file mode 100644 index 77edaaa6e2..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/rpc/dto/DtoSignatureScript.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full.rpc.dto; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@JsonInclude(JsonInclude.Include.NON_NULL) -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonPropertyOrder({"asm", "hex"}) -public class DtoSignatureScript { - private String asm; - private String hex; -} diff --git a/core/src/main/java/bisq/core/dao/node/full/rpc/dto/RawDtoBlock.java b/core/src/main/java/bisq/core/dao/node/full/rpc/dto/RawDtoBlock.java deleted file mode 100644 index 91edb701b4..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/rpc/dto/RawDtoBlock.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full.rpc.dto; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonValue; - -import java.util.List; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@JsonInclude(JsonInclude.Include.NON_NULL) -@JsonIgnoreProperties(ignoreUnknown = true, value = "ntx") -@JsonPropertyOrder({"hash", "confirmations", "strippedsize", "size", "weight", "height", "version", "versionHex", - "merkleroot", "tx", "time", "mediantime", "nonce", "bits", "difficulty", "chainwork", "nTx", - "previousblockhash", "nextblockhash"}) -public class RawDtoBlock { - private String hash; - private Integer confirmations; - @JsonProperty("strippedsize") - private Integer strippedSize; - private Integer size; - private Integer weight; - private Integer height; - private Integer version; - private String versionHex; - @JsonProperty("merkleroot") - private String merkleRoot; - private List tx; - private Long time; - @JsonProperty("mediantime") - private Long medianTime; - private Long nonce; - private String bits; - private Double difficulty; - @JsonProperty("chainwork") - private String chainWork; - // There seems to be a bug in Jackson where it misses and/or duplicates this field without - // an explicit @JsonProperty annotation plus the @JsonIgnoreProperties 'ntx' term above: - @JsonProperty("nTx") - private Integer nTx; - @JsonProperty("previousblockhash") - private String previousBlockHash; - @JsonProperty("nextblockhash") - private String nextBlockHash; - - @JsonCreator - public static Summarized summarized(String hex) { - var result = new Summarized(); - result.setHex(hex); - return result; - } - - @Data - @EqualsAndHashCode(callSuper = true) - public static class Summarized extends RawDtoBlock { - @Getter(onMethod_ = @JsonValue) - private String hex; - } -} diff --git a/core/src/main/java/bisq/core/dao/node/full/rpc/dto/RawDtoInput.java b/core/src/main/java/bisq/core/dao/node/full/rpc/dto/RawDtoInput.java deleted file mode 100644 index a33c2b25e9..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/rpc/dto/RawDtoInput.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full.rpc.dto; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -import java.util.List; - -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@JsonInclude(JsonInclude.Include.NON_NULL) -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonPropertyOrder({"txid", "vout", "coinbase", "scriptSig", "txinwitness", "sequence"}) -public class RawDtoInput { - @JsonProperty("txid") - private String txId; - @JsonProperty("vout") - private Integer vOut; - private String coinbase; - private DtoSignatureScript scriptSig; - @JsonProperty("txinwitness") - private List txInWitness; - private Long sequence; -} diff --git a/core/src/main/java/bisq/core/dao/node/full/rpc/dto/RawDtoOutput.java b/core/src/main/java/bisq/core/dao/node/full/rpc/dto/RawDtoOutput.java deleted file mode 100644 index 4bd4d60569..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/rpc/dto/RawDtoOutput.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full.rpc.dto; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@JsonInclude(JsonInclude.Include.NON_NULL) -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonPropertyOrder({"value", "n", "scriptPubKey"}) -public class RawDtoOutput { - private Double value; - private Integer n; - private DtoPubKeyScript scriptPubKey; -} diff --git a/core/src/main/java/bisq/core/dao/node/full/rpc/dto/RawDtoTransaction.java b/core/src/main/java/bisq/core/dao/node/full/rpc/dto/RawDtoTransaction.java deleted file mode 100644 index e8e2c75206..0000000000 --- a/core/src/main/java/bisq/core/dao/node/full/rpc/dto/RawDtoTransaction.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full.rpc.dto; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonValue; - -import java.util.List; - -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@JsonInclude(JsonInclude.Include.NON_NULL) -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonPropertyOrder({"txid", "hash", "version", "size", "vsize", "weight", "locktime", "vin", "vout", "hex"}) -public class RawDtoTransaction { - @JsonProperty("in_active_chain") - private Boolean inActiveChain; - @JsonProperty("txid") - private String txId; - private String hash; - private Integer version; - private Integer size; - @JsonProperty("vsize") - private Integer vSize; - private Integer weight; - @JsonProperty("locktime") - private Long lockTime; - @JsonProperty("vin") - private List vIn; - @JsonProperty("vout") - private List vOut; - @JsonProperty("blockhash") - private String blockHash; - private Integer confirmations; - @JsonProperty("blocktime") - private Long blockTime; - private Long time; - private String hex; - - @JsonCreator - public static Summarized summarized(String hex) { - var result = new Summarized(); - result.setHex(hex); - return result; - } - - public static class Summarized extends RawDtoTransaction { - @Override - @JsonValue - public String getHex() { - return super.getHex(); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java b/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java deleted file mode 100644 index c0c8b64980..0000000000 --- a/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java +++ /dev/null @@ -1,274 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.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; -import bisq.core.dao.node.full.RawBlock; -import bisq.core.dao.node.lite.network.LiteNodeNetworkService; -import bisq.core.dao.node.messages.GetBlocksResponse; -import bisq.core.dao.node.messages.NewBlockBroadcastMessage; -import bisq.core.dao.node.parser.BlockParser; -import bisq.core.dao.node.parser.exceptions.RequiredReorgFromSnapshotException; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.DaoStateSnapshotService; - -import bisq.network.p2p.P2PService; -import bisq.network.p2p.network.Connection; - -import bisq.common.Timer; -import bisq.common.UserThread; - -import com.google.inject.Inject; - -import javafx.beans.value.ChangeListener; - -import java.util.ArrayList; -import java.util.List; - -import lombok.extern.slf4j.Slf4j; - -import org.jetbrains.annotations.Nullable; - -/** - * Main class for lite nodes which receive the BSQ transactions from a full node (e.g. seed nodes). - * Verification of BSQ transactions is done also by the lite node. - */ -@Slf4j -public class LiteNode extends BsqNode { - private static final int CHECK_FOR_BLOCK_RECEIVED_DELAY_SEC = 10; - - private final LiteNodeNetworkService liteNodeNetworkService; - private final BsqWalletService bsqWalletService; - private final WalletsSetup walletsSetup; - private Timer checkForBlockReceivedTimer; - private final ChangeListener blockDownloadListener; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @SuppressWarnings("WeakerAccess") - @Inject - public LiteNode(BlockParser blockParser, - DaoStateService daoStateService, - DaoStateSnapshotService daoStateSnapshotService, - 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(); - } - }; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Public methods - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void start() { - super.onInitialized(); - - liteNodeNetworkService.start(); - - // 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; - - if (checkForBlockReceivedTimer != null) { - // In case we received a new block before out timer gets called we stop the old timer - checkForBlockReceivedTimer.stop(); - } - - 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 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, daoChainHeight + 1); - liteNodeNetworkService.requestBlocks(daoChainHeight + 1); - } - }, CHECK_FOR_BLOCK_RECEIVED_DELAY_SEC); - }); - } - - @Override - public void shutDown() { - super.shutDown(); - liteNodeNetworkService.shutDown(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Protected - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void onP2PNetworkReady() { - super.onP2PNetworkReady(); - - liteNodeNetworkService.addListener(new LiteNodeNetworkService.Listener() { - @Override - public void onRequestedBlocksReceived(GetBlocksResponse getBlocksResponse, Runnable onParsingComplete) { - LiteNode.this.onRequestedBlocksReceived(new ArrayList<>(getBlocksResponse.getBlocks()), - onParsingComplete); - } - - @Override - public void onNewBlockReceived(NewBlockBroadcastMessage newBlockBroadcastMessage) { - LiteNode.this.onNewBlockReceived(newBlockBroadcastMessage.getBlock()); - } - - @Override - public void onNoSeedNodeAvailable() { - } - - @Override - public void onFault(String errorMessage, @Nullable Connection connection) { - } - }); - - if (!parseBlockchainComplete) - startParseBlocks(); - } - - // First we request the blocks from a full node - @Override - protected void startParseBlocks() { - liteNodeNetworkService.requestBlocks(getStartBlockHeight()); - } - - @Override - protected void startReOrgFromLastSnapshot() { - super.startReOrgFromLastSnapshot(); - - int startBlockHeight = getStartBlockHeight(); - liteNodeNetworkService.reset(); - liteNodeNetworkService.requestBlocks(startBlockHeight); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - // We received the missing blocks - private void onRequestedBlocksReceived(List blockList, Runnable onParsingComplete) { - if (!blockList.isEmpty()) { - chainTipHeight = blockList.get(blockList.size() - 1).getHeight(); - log.info("We received blocks from height {} to {}", blockList.get(0).getHeight(), chainTipHeight); - } - - // We delay the parsing to next render frame to avoid that the UI get blocked in case we parse a lot of blocks. - // Parsing itself is very fast (3 sec. for 7000 blocks) but creating the hash chain slows down batch processing a lot - // (30 sec for 7000 blocks). - // The updates at block height change are not much optimized yet, so that can be for sure improved - // 144 blocks a day would result in about 4000 in a month, so if a user downloads the app after 1 months latest - // release it will be a bit of a performance hit. It is a one time event as the snapshots gets created and be - // used at next startup. New users will get the shipped snapshot. Users who have not used Bisq for longer might - // experience longer durations for batch processing. - long ts = System.currentTimeMillis(); - - if (blockList.isEmpty()) { - onParseBlockChainComplete(); - return; - } - - runDelayedBatchProcessing(new ArrayList<>(blockList), - () -> { - 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(); - onParseBlockChainComplete(); - } - }); - } - - private void runDelayedBatchProcessing(List blocks, Runnable resultHandler) { - UserThread.execute(() -> { - if (blocks.isEmpty()) { - resultHandler.run(); - return; - } - - RawBlock block = blocks.remove(0); - try { - doParseBlock(block); - runDelayedBatchProcessing(blocks, resultHandler); - } catch (RequiredReorgFromSnapshotException e) { - resultHandler.run(); - } - }); - } - - // We received a new block - private void onNewBlockReceived(RawBlock block) { - int blockHeight = block.getHeight(); - 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) { - chainTipHeight = blockHeight; - } - - try { - doParseBlock(block); - } catch (RequiredReorgFromSnapshotException ignore) { - } - - maybeExportToJson(); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/lite/network/LiteNodeNetworkService.java b/core/src/main/java/bisq/core/dao/node/lite/network/LiteNodeNetworkService.java deleted file mode 100644 index 189afea334..0000000000 --- a/core/src/main/java/bisq/core/dao/node/lite/network/LiteNodeNetworkService.java +++ /dev/null @@ -1,414 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.lite.network; - -import bisq.core.dao.node.messages.GetBlocksResponse; -import bisq.core.dao.node.messages.NewBlockBroadcastMessage; -import bisq.core.dao.state.model.blockchain.BaseTx; - -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.network.CloseConnectionReason; -import bisq.network.p2p.network.Connection; -import bisq.network.p2p.network.ConnectionListener; -import bisq.network.p2p.network.MessageListener; -import bisq.network.p2p.network.NetworkNode; -import bisq.network.p2p.peers.Broadcaster; -import bisq.network.p2p.peers.PeerManager; -import bisq.network.p2p.seed.SeedNodeRepository; - -import bisq.common.Timer; -import bisq.common.UserThread; -import bisq.common.app.DevEnv; -import bisq.common.proto.network.NetworkEnvelope; -import bisq.common.util.Tuple2; - -import javax.inject.Inject; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - -import org.jetbrains.annotations.Nullable; - -/** - * Responsible for requesting BSQ blocks from a full node and for listening to new blocks broadcasted by full nodes. - */ -@Slf4j -public class LiteNodeNetworkService implements MessageListener, ConnectionListener, PeerManager.Listener { - - private static final long RETRY_DELAY_SEC = 10; - private static final long CLEANUP_TIMER = 120; - private static final int MAX_RETRY = 3; - - private int retryCounter = 0; - private int lastRequestedBlockHeight; - private int lastReceivedBlockHeight; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Listener - /////////////////////////////////////////////////////////////////////////////////////////// - - public interface Listener { - void onNoSeedNodeAvailable(); - - void onRequestedBlocksReceived(GetBlocksResponse getBlocksResponse, Runnable onParsingComplete); - - void onNewBlockReceived(NewBlockBroadcastMessage newBlockBroadcastMessage); - - void onFault(String errorMessage, @Nullable Connection connection); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Class fields - /////////////////////////////////////////////////////////////////////////////////////////// - - private final NetworkNode networkNode; - private final PeerManager peerManager; - private final Broadcaster broadcaster; - private final Collection seedNodeAddresses; - - private final List listeners = new CopyOnWriteArrayList<>(); - - // Key is tuple of seedNode address and requested blockHeight - private final Map, RequestBlocksHandler> requestBlocksHandlerMap = new HashMap<>(); - private Timer retryTimer; - private boolean stopped; - private final Set receivedBlocks = new HashSet<>(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public LiteNodeNetworkService(NetworkNode networkNode, - PeerManager peerManager, - Broadcaster broadcaster, - SeedNodeRepository seedNodesRepository) { - this.networkNode = networkNode; - this.peerManager = peerManager; - this.broadcaster = broadcaster; - // seedNodeAddresses can be empty (in case there is only 1 seed node, the seed node starting up has no other seed nodes) - this.seedNodeAddresses = new HashSet<>(seedNodesRepository.getSeedNodeAddresses()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void start() { - networkNode.addMessageListener(this); - networkNode.addConnectionListener(this); - peerManager.addListener(this); - } - - public void shutDown() { - stopped = true; - stopRetryTimer(); - networkNode.removeMessageListener(this); - networkNode.removeConnectionListener(this); - peerManager.removeListener(this); - closeAllHandlers(); - } - - public void addListener(Listener listener) { - listeners.add(listener); - } - - /** - * - * @param startBlockHeight Block height from where we expect new blocks (current block height in bsqState + 1) - */ - public void requestBlocks(int startBlockHeight) { - lastRequestedBlockHeight = startBlockHeight; - Optional connectionToSeedNodeOptional = networkNode.getConfirmedConnections().stream() - .filter(peerManager::isSeedNode) - .findAny(); - - 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::terminate); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // ConnectionListener implementation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onConnection(Connection connection) { - } - - @Override - public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) { - closeHandler(connection); - - if (peerManager.isPeerBanned(closeConnectionReason, connection)) { - connection.getPeersNodeAddressOptional().ifPresent(nodeAddress -> { - seedNodeAddresses.remove(nodeAddress); - removeFromRequestBlocksHandlerMap(nodeAddress); - }); - } - } - - @Override - public void onError(Throwable throwable) { - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PeerManager.Listener implementation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onAllConnectionsLost() { - closeAllHandlers(); - stopRetryTimer(); - stopped = true; - tryWithNewSeedNode(lastRequestedBlockHeight); - } - - @Override - public void onNewConnectionAfterAllConnectionsLost() { - closeAllHandlers(); - stopped = false; - tryWithNewSeedNode(lastRequestedBlockHeight); - } - - @Override - public void onAwakeFromStandby() { - log.info("onAwakeFromStandby"); - closeAllHandlers(); - stopped = false; - tryWithNewSeedNode(lastRequestedBlockHeight); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // MessageListener implementation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { - if (networkEnvelope instanceof NewBlockBroadcastMessage) { - NewBlockBroadcastMessage newBlockBroadcastMessage = (NewBlockBroadcastMessage) networkEnvelope; - // We combine blockHash and txId list in case we receive blocks with different transactions. - List 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)); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // RequestData - /////////////////////////////////////////////////////////////////////////////////////////// - - private void requestBlocks(NodeAddress peersNodeAddress, int startBlockHeight) { - if (stopped) { - log.warn("We have stopped already. We ignore that requestData call."); - return; - } - - Tuple2 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; - } - - 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, - () -> { - })); - } 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)); - - tryWithNewSeedNode(startBlockHeight); - } - }); - requestBlocksHandlerMap.put(key, requestBlocksHandler); - requestBlocksHandler.requestBlocks(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Utils - /////////////////////////////////////////////////////////////////////////////////////////// - - private void tryWithNewSeedNode(int startBlockHeight) { - 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 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() { - if (retryTimer != null) { - retryTimer.stop(); - retryTimer = null; - } - } - - private void closeHandler(Connection connection) { - Optional peersNodeAddressOptional = connection.getPeersNodeAddressOptional(); - if (peersNodeAddressOptional.isPresent()) { - NodeAddress nodeAddress = peersNodeAddressOptional.get(); - removeFromRequestBlocksHandlerMap(nodeAddress); - } else { - log.trace("closeHandler: nodeAddress not set in connection {}", connection); - } - } - - private void removeFromRequestBlocksHandlerMap(NodeAddress nodeAddress) { - requestBlocksHandlerMap.entrySet().stream() - .filter(e -> e.getKey().first.equals(nodeAddress)) - .findAny() - .ifPresent(e -> { - e.getValue().terminate(); - requestBlocksHandlerMap.remove(e.getKey()); - }); - } - - private void closeAllHandlers() { - requestBlocksHandlerMap.values().forEach(RequestBlocksHandler::terminate); - requestBlocksHandlerMap.clear(); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/lite/network/RequestBlocksHandler.java b/core/src/main/java/bisq/core/dao/node/lite/network/RequestBlocksHandler.java deleted file mode 100644 index 752e4829f8..0000000000 --- a/core/src/main/java/bisq/core/dao/node/lite/network/RequestBlocksHandler.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.lite.network; - -import bisq.core.dao.node.messages.GetBlocksRequest; -import bisq.core.dao.node.messages.GetBlocksResponse; - -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.network.CloseConnectionReason; -import bisq.network.p2p.network.Connection; -import bisq.network.p2p.network.MessageListener; -import bisq.network.p2p.network.NetworkNode; -import bisq.network.p2p.peers.PeerManager; - -import bisq.common.Timer; -import bisq.common.UserThread; -import bisq.common.proto.network.NetworkEnvelope; - -import com.google.common.util.concurrent.FutureCallback; -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; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * 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_MIN = 3; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Listener - /////////////////////////////////////////////////////////////////////////////////////////// - - public interface Listener { - void onComplete(GetBlocksResponse getBlocksResponse); - - @SuppressWarnings("UnusedParameters") - void onFault(String errorMessage, @SuppressWarnings("SameParameterValue") @Nullable Connection connection); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Class fields - /////////////////////////////////////////////////////////////////////////////////////////// - - private final NetworkNode networkNode; - private final PeerManager peerManager; - @Getter - private final NodeAddress nodeAddress; - @Getter - private final int startBlockHeight; - private final Listener listener; - private Timer timeoutTimer; - private final int nonce = new Random().nextInt(); - private boolean stopped; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - public RequestBlocksHandler(NetworkNode networkNode, - PeerManager peerManager, - NodeAddress nodeAddress, - int startBlockHeight, - Listener listener) { - this.networkNode = networkNode; - this.peerManager = peerManager; - this.nodeAddress = nodeAddress; - this.startBlockHeight = startBlockHeight; - this.listener = listener; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void requestBlocks() { - 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 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()); - } - - @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."); - } - } - }, MoreExecutors.directExecutor()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // MessageListener implementation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { - if (networkEnvelope instanceof GetBlocksResponse) { - if (stopped) { - log.warn("We have stopped already. We ignore that onDataRequest call."); - return; - } - - Optional 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 terminate() { - stopped = true; - networkNode.removeMessageListener(this); - stopTimeoutTimer(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - - @SuppressWarnings("UnusedParameters") - private void handleFault(String errorMessage, - NodeAddress nodeAddress, - CloseConnectionReason closeConnectionReason) { - terminate(); - peerManager.handleConnectionFault(nodeAddress); - listener.onFault(errorMessage, null); - } - - private void stopTimeoutTimer() { - if (timeoutTimer != null) { - timeoutTimer.stop(); - timeoutTimer = null; - } - } -} diff --git a/core/src/main/java/bisq/core/dao/node/messages/GetBlocksRequest.java b/core/src/main/java/bisq/core/dao/node/messages/GetBlocksRequest.java deleted file mode 100644 index f1d216e94c..0000000000 --- a/core/src/main/java/bisq/core/dao/node/messages/GetBlocksRequest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.messages; - -import bisq.network.p2p.DirectMessage; -import bisq.network.p2p.InitialDataRequest; -import bisq.network.p2p.NodeAddress; -import bisq.network.p2p.SendersNodeAddressMessage; -import bisq.network.p2p.SupportedCapabilitiesMessage; - -import bisq.common.app.Capabilities; -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkEnvelope; - -import java.util.Optional; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -// TODO We remove CapabilityRequiringPayload as it would cause problems if the lite node connects to a new seed node and -// they have not exchanged capabilities already. We need to improve capability handling the we can re-enable it again. -// As this message is sent any only to seed nodes it does not has any effect. Even if a lite node receives it it will be -// simply ignored. - -// This message is sent only to full DAO nodes -@EqualsAndHashCode(callSuper = true) -@Getter -@Slf4j -public final class GetBlocksRequest extends NetworkEnvelope implements DirectMessage, SendersNodeAddressMessage, - SupportedCapabilitiesMessage, InitialDataRequest { - private final int fromBlockHeight; - private final int nonce; - - // Added after version 1.0.1. Can be null if received from older clients. - @Nullable - private final NodeAddress senderNodeAddress; - - // Added after version 1.0.1. Can be null if received from older clients. - @Nullable - private final Capabilities supportedCapabilities; - - public GetBlocksRequest(int fromBlockHeight, - int nonce, - @Nullable NodeAddress senderNodeAddress) { - this(fromBlockHeight, - nonce, - senderNodeAddress, - Capabilities.app, - Version.getP2PMessageVersion()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private GetBlocksRequest(int fromBlockHeight, - int nonce, - @Nullable NodeAddress senderNodeAddress, - @Nullable Capabilities supportedCapabilities, - int messageVersion) { - super(messageVersion); - this.fromBlockHeight = fromBlockHeight; - this.nonce = nonce; - this.senderNodeAddress = senderNodeAddress; - this.supportedCapabilities = supportedCapabilities; - } - - @Override - public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { - protobuf.GetBlocksRequest.Builder builder = protobuf.GetBlocksRequest.newBuilder() - .setFromBlockHeight(fromBlockHeight) - .setNonce(nonce); - Optional.ofNullable(senderNodeAddress).ifPresent(e -> builder.setSenderNodeAddress(e.toProtoMessage())); - Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities))); - return getNetworkEnvelopeBuilder().setGetBlocksRequest(builder).build(); - } - - public static NetworkEnvelope fromProto(protobuf.GetBlocksRequest proto, int messageVersion) { - protobuf.NodeAddress protoNodeAddress = proto.getSenderNodeAddress(); - NodeAddress senderNodeAddress = protoNodeAddress.getHostName().isEmpty() ? - null : - NodeAddress.fromProto(protoNodeAddress); - Capabilities supportedCapabilities = proto.getSupportedCapabilitiesList().isEmpty() ? - null : - Capabilities.fromIntList(proto.getSupportedCapabilitiesList()); - return new GetBlocksRequest(proto.getFromBlockHeight(), - proto.getNonce(), - senderNodeAddress, - supportedCapabilities, - messageVersion); - } - -// @Override -// public Capabilities getRequiredCapabilities() { -// return new Capabilities(Capability.DAO_FULL_NODE); -// } - - @Override - public String toString() { - return "GetBlocksRequest{" + - "\n fromBlockHeight=" + fromBlockHeight + - ",\n nonce=" + nonce + - ",\n senderNodeAddress=" + senderNodeAddress + - ",\n supportedCapabilities=" + supportedCapabilities + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/messages/GetBlocksResponse.java b/core/src/main/java/bisq/core/dao/node/messages/GetBlocksResponse.java deleted file mode 100644 index 4d9876c09c..0000000000 --- a/core/src/main/java/bisq/core/dao/node/messages/GetBlocksResponse.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.messages; - -import bisq.core.dao.node.full.RawBlock; - -import bisq.network.p2p.DirectMessage; -import bisq.network.p2p.ExtendedDataSizePermission; -import bisq.network.p2p.InitialDataRequest; -import bisq.network.p2p.InitialDataResponse; - -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkEnvelope; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -@EqualsAndHashCode(callSuper = true) -@Getter -@Slf4j -public final class GetBlocksResponse extends NetworkEnvelope implements DirectMessage, - ExtendedDataSizePermission, InitialDataResponse { - private final List blocks; - private final int requestNonce; - - public GetBlocksResponse(List blocks, int requestNonce) { - this(blocks, requestNonce, Version.getP2PMessageVersion()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private GetBlocksResponse(List blocks, int requestNonce, int messageVersion) { - super(messageVersion); - this.blocks = blocks; - this.requestNonce = requestNonce; - } - - @Override - public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { - protobuf.NetworkEnvelope proto = getNetworkEnvelopeBuilder() - .setGetBlocksResponse(protobuf.GetBlocksResponse.newBuilder() - .addAllRawBlocks(blocks.stream() - .map(RawBlock::toProtoMessage) - .collect(Collectors.toList())) - .setRequestNonce(requestNonce)) - .build(); - log.info("Sending a GetBlocksResponse with {} kB", proto.getSerializedSize() / 1000d); - return proto; - } - - public static NetworkEnvelope fromProto(protobuf.GetBlocksResponse proto, int messageVersion) { - List list = proto.getRawBlocksList().stream() - .map(RawBlock::fromProto) - .collect(Collectors.toList()); - log.info("Received a GetBlocksResponse with {} blocks and {} kB size", list.size(), proto.getSerializedSize() / 1000d); - return new GetBlocksResponse(proto.getRawBlocksList().isEmpty() ? - new ArrayList<>() : - list, - proto.getRequestNonce(), - messageVersion); - } - - - @Override - public String toString() { - return "GetBlocksResponse{" + - "\n blocks=" + blocks + - ",\n requestNonce=" + requestNonce + - "\n} " + super.toString(); - } - - @Override - public Class associatedRequest() { - return GetBlocksRequest.class; - } -} diff --git a/core/src/main/java/bisq/core/dao/node/messages/NewBlockBroadcastMessage.java b/core/src/main/java/bisq/core/dao/node/messages/NewBlockBroadcastMessage.java deleted file mode 100644 index 658ace48e0..0000000000 --- a/core/src/main/java/bisq/core/dao/node/messages/NewBlockBroadcastMessage.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.messages; - -import bisq.core.dao.node.full.RawBlock; - -import bisq.network.p2p.storage.messages.BroadcastMessage; - -import bisq.common.app.Version; -import bisq.common.proto.network.NetworkEnvelope; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -// We remove the CapabilityRequiringPayload interface to avoid risks that new BSQ blocks are not well distributed in -// case the capability is not exchanged at the time when the message is sent. We need to improve the capability handling -// so that we can be sure that we know the actual capability of the peer. - -// This message is sent only to lite DAO nodes (full nodes get block from their local bitcoind) -@EqualsAndHashCode(callSuper = true) -@Getter -public final class NewBlockBroadcastMessage extends BroadcastMessage /*implements CapabilityRequiringPayload*/ { - private final RawBlock block; - - public NewBlockBroadcastMessage(RawBlock block) { - this(block, Version.getP2PMessageVersion()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private NewBlockBroadcastMessage(RawBlock block, int messageVersion) { - super(messageVersion); - this.block = block; - } - - @Override - public protobuf.NetworkEnvelope toProtoNetworkEnvelope() { - return getNetworkEnvelopeBuilder() - .setNewBlockBroadcastMessage(protobuf.NewBlockBroadcastMessage.newBuilder() - .setRawBlock(block.toProtoMessage())) - .build(); - } - - public static NetworkEnvelope fromProto(protobuf.NewBlockBroadcastMessage proto, int messageVersion) { - return new NewBlockBroadcastMessage(RawBlock.fromProto(proto.getRawBlock()), - messageVersion); - } - -// @Override -// public Capabilities getRequiredCapabilities() { -// return new Capabilities(Capability.RECEIVE_BSQ_BLOCK); -// } -} diff --git a/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java b/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java deleted file mode 100644 index 900634bfb0..0000000000 --- a/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.parser; - -import bisq.core.dao.node.full.RawBlock; -import bisq.core.dao.node.parser.exceptions.BlockHashNotConnectingException; -import bisq.core.dao.node.parser.exceptions.BlockHeightNotConnectingException; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Block; - -import bisq.common.app.DevEnv; - -import org.bitcoinj.core.Coin; - -import javax.inject.Inject; - -import java.util.LinkedList; - -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -/** - * Parse a rawBlock and creates a block from it with an empty tx list. - * Iterates all rawTx and if the tx is a BSQ tx it gets added to the tx list. - */ -@Slf4j -@Immutable -public class BlockParser { - private final TxParser txParser; - private final DaoStateService daoStateService; - private final String genesisTxId; - private final int genesisBlockHeight; - private final Coin genesisTotalSupply; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public BlockParser(TxParser txParser, - DaoStateService daoStateService) { - this.txParser = txParser; - this.daoStateService = daoStateService; - this.genesisTxId = daoStateService.getGenesisTxId(); - this.genesisBlockHeight = daoStateService.getGenesisBlockHeight(); - this.genesisTotalSupply = daoStateService.getGenesisTotalSupply(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - /** - * - * @param rawBlock Contains all transactions of a bitcoin block without any BSQ specific data - * @return Block: Gets created from the rawBlock but contains only BSQ specific transactions. - * @throws BlockHashNotConnectingException If new block does not connect to previous block - * @throws BlockHeightNotConnectingException If new block height is not current chain Height + 1 - */ - public Block parseBlock(RawBlock rawBlock) throws BlockHashNotConnectingException, BlockHeightNotConnectingException { - int blockHeight = rawBlock.getHeight(); - log.trace("Parse block at height={} ", blockHeight); - - validateIfBlockIsConnecting(rawBlock); - - daoStateService.onNewBlockHeight(blockHeight); - - // We create a block from the rawBlock but the transaction list is not set yet (is empty) - final Block block = new Block(blockHeight, - rawBlock.getTime(), - rawBlock.getHash(), - rawBlock.getPreviousBlockHash()); - - if (isBlockAlreadyAdded(rawBlock)) { - log.warn("Block was already added."); - DevEnv.logErrorAndThrowIfDevMode("Block was already added. rawBlock=" + rawBlock); - } else { - daoStateService.onNewBlockWithEmptyTxs(block); - } - - // Worst case is that all txs in a block are depending on another, so only one get resolved at each iteration. - // Min tx size is 189 bytes (normally about 240 bytes), 1 MB can contain max. about 5300 txs (usually 2000). - // Realistically we don't expect more than a few recursive calls. - // There are some blocks with testing such dependency chains like block 130768 where at each iteration only - // one get resolved. - // Lately there is a patter with 24 iterations observed - long startTs = System.currentTimeMillis(); - - rawBlock.getRawTxs().forEach(rawTx -> - txParser.findTx(rawTx, - genesisTxId, - genesisBlockHeight, - genesisTotalSupply) - .ifPresent(tx -> daoStateService.onNewTxForLastBlock(block, tx))); - - log.info("Parsing {} transactions at block height {} took {} ms", rawBlock.getRawTxs().size(), - blockHeight, System.currentTimeMillis() - startTs); - - daoStateService.onParseBlockComplete(block); - return block; - } - - private void validateIfBlockIsConnecting(RawBlock rawBlock) throws BlockHashNotConnectingException, BlockHeightNotConnectingException { - LinkedList blocks = daoStateService.getBlocks(); - - if (blocks.isEmpty()) - return; - - Block last = blocks.getLast(); - if (last.getHeight() + 1 != rawBlock.getHeight()) - throw new BlockHeightNotConnectingException(rawBlock); - - if (!last.getHash().equals(rawBlock.getPreviousBlockHash())) - throw new BlockHashNotConnectingException(rawBlock); - } - - private boolean isBlockAlreadyAdded(RawBlock rawBlock) { - return daoStateService.isBlockHashKnown(rawBlock.getHash()); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/parser/GenesisTxParser.java b/core/src/main/java/bisq/core/dao/node/parser/GenesisTxParser.java deleted file mode 100644 index 7b7a054bde..0000000000 --- a/core/src/main/java/bisq/core/dao/node/parser/GenesisTxParser.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.parser; - -import bisq.core.dao.node.full.RawTx; -import bisq.core.dao.node.parser.exceptions.InvalidGenesisTxException; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.blockchain.TxOutputType; -import bisq.core.dao.state.model.blockchain.TxType; - -import org.bitcoinj.core.Coin; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableList; - -import java.util.List; - -public class GenesisTxParser { - public static boolean isGenesis(RawTx rawTx, String genesisTxId, int genesisBlockHeight) { - return rawTx.getBlockHeight() == genesisBlockHeight && rawTx.getId().equals(genesisTxId); - } - - public static Tx getGenesisTx(RawTx rawTx, Coin genesisTotalSupply, DaoStateService daoStateService) { - TempTx genesisTx = getGenesisTempTx(rawTx, genesisTotalSupply); - commitUTXOs(daoStateService, genesisTx); - return Tx.fromTempTx(genesisTx); - } - - private static void commitUTXOs(DaoStateService daoStateService, TempTx genesisTx) { - ImmutableList outputs = genesisTx.getTempTxOutputs(); - for (int i = 0; i < outputs.size(); ++i) { - TempTxOutput tempTxOutput = outputs.get(i); - daoStateService.addUnspentTxOutput(TxOutput.fromTempOutput(tempTxOutput)); - } - } - - /** - * Parse and return the genesis transaction for bisq, if applicable. - * - * @param rawTx The candidate transaction. - * @param genesisTotalSupply The total supply of the genesis issuance for bisq. - * @return The genesis transaction. - */ - @VisibleForTesting - static TempTx getGenesisTempTx(RawTx rawTx, Coin genesisTotalSupply) { - TempTx tempTx = TempTx.fromRawTx(rawTx); - tempTx.setTxType(TxType.GENESIS); - long remainingInputValue = genesisTotalSupply.getValue(); - List tempTxOutputs = tempTx.getTempTxOutputs(); - //noinspection ForLoopReplaceableByForEach - for (int i = 0; i < tempTxOutputs.size(); ++i) { - TempTxOutput txOutput = tempTxOutputs.get(i); - long value = txOutput.getValue(); - boolean isValid = value <= remainingInputValue; - if (!isValid) - throw new InvalidGenesisTxException("Genesis tx is invalid; using more than available inputs. " + - "Remaining input value is " + remainingInputValue + " sat; tx info: " + tempTx.toString()); - - remainingInputValue -= value; - txOutput.setTxOutputType(TxOutputType.GENESIS_OUTPUT); - } - - if (remainingInputValue > 0) { - throw new InvalidGenesisTxException("Genesis tx is invalid; not using all available inputs. " + - "Remaining input value is " + remainingInputValue + " sat, tx info: " + tempTx.toString()); - } - - return tempTx; - } -} diff --git a/core/src/main/java/bisq/core/dao/node/parser/OpReturnParser.java b/core/src/main/java/bisq/core/dao/node/parser/OpReturnParser.java deleted file mode 100644 index 3b15bf322c..0000000000 --- a/core/src/main/java/bisq/core/dao/node/parser/OpReturnParser.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.parser; - -import bisq.core.dao.governance.asset.AssetConsensus; -import bisq.core.dao.governance.blindvote.BlindVoteConsensus; -import bisq.core.dao.governance.bond.BondConsensus; -import bisq.core.dao.governance.bond.lockup.LockupReason; -import bisq.core.dao.governance.proofofburn.ProofOfBurnConsensus; -import bisq.core.dao.governance.proposal.ProposalConsensus; -import bisq.core.dao.governance.voteresult.VoteResultConsensus; -import bisq.core.dao.node.parser.exceptions.InvalidParsingConditionException; -import bisq.core.dao.state.model.blockchain.OpReturnType; -import bisq.core.dao.state.model.blockchain.TxOutputType; - -import bisq.common.util.Utilities; - -import org.bitcoinj.core.Utils; - -import java.util.Optional; - -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Processes OpReturn output if valid and delegates validation to specific validators. - */ -@Slf4j -class OpReturnParser { - - /** - * Parse the type of OP_RETURN data and validate it. - * - * @param tempTxOutput The temporary transaction output to parse. - * @return The type of the transaction output, which will be either one of the - * {@code *_OP_RETURN_OUTPUT} values, or {@code UNDEFINED} in case of - * unexpected state. - */ - static TxOutputType getTxOutputType(TempTxOutput tempTxOutput) { - boolean nonZeroOutput = tempTxOutput.getValue() != 0; - byte[] opReturnData = tempTxOutput.getOpReturnData(); - checkNotNull(opReturnData, "opReturnData must not be null"); - - if (nonZeroOutput || opReturnData.length < 22) { - log.warn("OP_RETURN data does not match our rules. opReturnData={}", - Utils.HEX.encode(opReturnData)); - return TxOutputType.INVALID_OUTPUT; - } - Optional optionalOpReturnType = OpReturnType.getOpReturnType(opReturnData[0]); - if (!optionalOpReturnType.isPresent()) { - log.warn("OP_RETURN data does not match our defined types. opReturnData={}", - Utils.HEX.encode(opReturnData)); - return TxOutputType.INVALID_OUTPUT; - } - - switch (optionalOpReturnType.get()) { - case PROPOSAL: - if (ProposalConsensus.hasOpReturnDataValidLength(opReturnData)) - return TxOutputType.PROPOSAL_OP_RETURN_OUTPUT; - else - break; - case COMPENSATION_REQUEST: - if (ProposalConsensus.hasOpReturnDataValidLength(opReturnData)) - return TxOutputType.COMP_REQ_OP_RETURN_OUTPUT; - else - break; - case REIMBURSEMENT_REQUEST: - if (ProposalConsensus.hasOpReturnDataValidLength(opReturnData)) - return TxOutputType.REIMBURSEMENT_OP_RETURN_OUTPUT; - else - break; - case BLIND_VOTE: - if (BlindVoteConsensus.hasOpReturnDataValidLength(opReturnData)) - return TxOutputType.BLIND_VOTE_OP_RETURN_OUTPUT; - else - break; - case VOTE_REVEAL: - if (VoteResultConsensus.hasOpReturnDataValidLength(opReturnData)) - return TxOutputType.VOTE_REVEAL_OP_RETURN_OUTPUT; - else - break; - case LOCKUP: - if (!BondConsensus.hasOpReturnDataValidLength(opReturnData)) - return TxOutputType.INVALID_OUTPUT; - Optional optionalLockupReason = BondConsensus.getLockupReason(opReturnData); - if (!optionalLockupReason.isPresent()) { - log.warn("No lockupReason found for lockup tx, opReturnData=" + Utilities.encodeToHex(opReturnData)); - return TxOutputType.INVALID_OUTPUT; - } - - int lockTime = BondConsensus.getLockTime(opReturnData); - if (BondConsensus.isLockTimeInValidRange(lockTime)) { - return TxOutputType.LOCKUP_OP_RETURN_OUTPUT; - } else { - break; - } - case ASSET_LISTING_FEE: - if (AssetConsensus.hasOpReturnDataValidLength(opReturnData)) - return TxOutputType.ASSET_LISTING_FEE_OP_RETURN_OUTPUT; - else - break; - case PROOF_OF_BURN: - if (ProofOfBurnConsensus.hasOpReturnDataValidLength(opReturnData)) - return TxOutputType.PROOF_OF_BURN_OP_RETURN_OUTPUT; - else - break; - default: - throw new InvalidParsingConditionException("We must have a defined opReturnType as it was checked earlier in the caller."); - } - - log.info("We expected a compensation request op_return data but it did not " + - "match our rules."); - return TxOutputType.INVALID_OUTPUT; - } -} diff --git a/core/src/main/java/bisq/core/dao/node/parser/TempTx.java b/core/src/main/java/bisq/core/dao/node/parser/TempTx.java deleted file mode 100644 index b98d2f4579..0000000000 --- a/core/src/main/java/bisq/core/dao/node/parser/TempTx.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.parser; - -import bisq.core.dao.node.full.RawTx; -import bisq.core.dao.state.model.blockchain.BaseTx; -import bisq.core.dao.state.model.blockchain.TxInput; -import bisq.core.dao.state.model.blockchain.TxType; - -import com.google.common.collect.ImmutableList; - -import java.util.Objects; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.Setter; - -import javax.annotation.Nullable; - -/** - * Used only temporary during the transaction parsing process to support mutable data while parsing. - * 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. - */ -@Getter -@Setter -public class TempTx extends BaseTx { - static TempTx fromRawTx(RawTx rawTx) { - return new TempTx(rawTx.getTxVersion(), - rawTx.getId(), - rawTx.getBlockHeight(), - rawTx.getBlockHash(), - rawTx.getTime(), - rawTx.getTxInputs(), - ImmutableList.copyOf(rawTx.getRawTxOutputs().stream().map(TempTxOutput::fromRawTxOutput).collect(Collectors.toList())), - null, - 0); - } - - private final ImmutableList tempTxOutputs; - - // Mutable data - @Nullable - private TxType txType; - private long burntBsq; - - private TempTx(String txVersion, - String id, - int blockHeight, - String blockHash, - long time, - ImmutableList txInputs, - ImmutableList tempTxOutputs, - @Nullable TxType txType, - long burntBsq) { - super(txVersion, - id, - blockHeight, - blockHash, - time, - txInputs); - this.tempTxOutputs = tempTxOutputs; - this.txType = txType; - this.burntBsq = burntBsq; - } - - @Override - public String toString() { - return "TempTx{" + - "\n txOutputs=" + tempTxOutputs + - ",\n txType=" + txType + - ",\n burntBsq=" + burntBsq + - "\n} " + super.toString(); - } - - // 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 - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof TempTx)) return false; - if (!super.equals(o)) return false; - TempTx tempTx = (TempTx) o; - - String name = txType != null ? txType.name() : ""; - String name1 = tempTx.txType != null ? tempTx.txType.name() : ""; - boolean isTxTypeEquals = name.equals(name1); - return burntBsq == tempTx.burntBsq && - Objects.equals(tempTxOutputs, tempTx.tempTxOutputs) && - isTxTypeEquals; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), tempTxOutputs, txType, burntBsq); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/parser/TempTxOutput.java b/core/src/main/java/bisq/core/dao/node/parser/TempTxOutput.java deleted file mode 100644 index 8aa78c2b08..0000000000 --- a/core/src/main/java/bisq/core/dao/node/parser/TempTxOutput.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.parser; - -import bisq.core.dao.node.full.RawTxOutput; -import bisq.core.dao.state.model.blockchain.BaseTxOutput; -import bisq.core.dao.state.model.blockchain.PubKeyScript; -import bisq.core.dao.state.model.blockchain.TxOutputType; - -import java.util.Objects; - -import lombok.Getter; -import lombok.Setter; - -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. - */ -@Getter -@Setter -public class TempTxOutput extends BaseTxOutput { - static TempTxOutput fromRawTxOutput(RawTxOutput txOutput) { - return new TempTxOutput(txOutput.getIndex(), - txOutput.getValue(), - txOutput.getTxId(), - txOutput.getPubKeyScript(), - txOutput.getAddress(), - txOutput.getOpReturnData(), - txOutput.getBlockHeight(), - TxOutputType.UNDEFINED_OUTPUT, - -1, - 0); - } - - private TxOutputType txOutputType; - - // The lockTime is stored in the first output of the LOCKUP tx. - // If not set it is -1, 0 is a valid value. - private int lockTime; - // The unlockBlockHeight is stored in the first output of the UNLOCK tx. - private int unlockBlockHeight; - - private TempTxOutput(int index, - long value, - String txId, - @Nullable PubKeyScript pubKeyScript, - @Nullable String address, - @Nullable byte[] opReturnData, - int blockHeight, - TxOutputType txOutputType, - int lockTime, - int unlockBlockHeight) { - super(index, - value, - txId, - pubKeyScript, - address, - opReturnData, - blockHeight); - - this.txOutputType = txOutputType; - this.lockTime = lockTime; - 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() { - return "TempTxOutput{" + - "\n txOutputType=" + txOutputType + - "\n lockTime=" + lockTime + - "\n unlockBlockHeight=" + unlockBlockHeight + - "\n} " + super.toString(); - } - - // 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 - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof TempTxOutput)) return false; - if (!super.equals(o)) return false; - TempTxOutput that = (TempTxOutput) o; - return lockTime == that.lockTime && - unlockBlockHeight == that.unlockBlockHeight && - txOutputType.name().equals(that.txOutputType.name()); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), txOutputType.name(), lockTime, unlockBlockHeight); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/parser/TxInputParser.java b/core/src/main/java/bisq/core/dao/node/parser/TxInputParser.java deleted file mode 100644 index d5b9b58a53..0000000000 --- a/core/src/main/java/bisq/core/dao/node/parser/TxInputParser.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.node.parser; - -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.SpentInfo; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.blockchain.TxOutputKey; -import bisq.core.dao.state.model.blockchain.TxOutputType; - -import javax.inject.Inject; - -import java.util.Optional; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Processes TxInput and add input value to available balance if the input is a valid BSQ input. - */ -@Slf4j -public class TxInputParser { - private final DaoStateService daoStateService; - - // Getters - @Getter - private long accumulatedInputValue = 0; - @Getter - private long burntBondValue = 0; - @Getter - private int unlockBlockHeight; - @Getter - private Optional optionalSpentLockupTxOutput = Optional.empty(); - @Getter - private boolean isUnLockInputValid = true; - - // Private - private int numVoteRevealInputs = 0; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public TxInputParser(DaoStateService daoStateService) { - this.daoStateService = daoStateService; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - void process(TxOutputKey txOutputKey, int blockHeight, String txId, int inputIndex) { - if (!daoStateService.isConfiscatedOutput(txOutputKey)) { - daoStateService.getUnspentTxOutput(txOutputKey) - .ifPresent(connectedTxOutput -> { - long inputValue = connectedTxOutput.getValue(); - accumulatedInputValue += inputValue; - - // If we are spending an output from a blind vote tx marked as VOTE_STAKE_OUTPUT we save it in our parsingModel - // for later verification at the outputs of a reveal tx. - TxOutputType connectedTxOutputType = connectedTxOutput.getTxOutputType(); - switch (connectedTxOutputType) { - case UNDEFINED_OUTPUT: - case GENESIS_OUTPUT: - case BSQ_OUTPUT: - case BTC_OUTPUT: - case PROPOSAL_OP_RETURN_OUTPUT: - case COMP_REQ_OP_RETURN_OUTPUT: - case REIMBURSEMENT_OP_RETURN_OUTPUT: - case CONFISCATE_BOND_OP_RETURN_OUTPUT: - case ISSUANCE_CANDIDATE_OUTPUT: - break; - case BLIND_VOTE_LOCK_STAKE_OUTPUT: - numVoteRevealInputs++; - // The connected tx output of the blind vote tx is our input for the reveal tx. - // We allow only one input from any blind vote tx otherwise the vote reveal tx is invalid. - if (!isVoteRevealInputValid()) { - log.warn("We have a tx which has >1 connected txOutputs marked as BLIND_VOTE_LOCK_STAKE_OUTPUT. " + - "This is not a valid BSQ tx."); - } - break; - case BLIND_VOTE_OP_RETURN_OUTPUT: - case VOTE_REVEAL_UNLOCK_STAKE_OUTPUT: - case VOTE_REVEAL_OP_RETURN_OUTPUT: - case ASSET_LISTING_FEE_OP_RETURN_OUTPUT: - case PROOF_OF_BURN_OP_RETURN_OUTPUT: - break; - case LOCKUP_OUTPUT: - // A LOCKUP BSQ txOutput is spent to a corresponding UNLOCK - // txInput. The UNLOCK can only be spent after lockTime blocks has passed. - isUnLockInputValid = !optionalSpentLockupTxOutput.isPresent(); - if (isUnLockInputValid) { - optionalSpentLockupTxOutput = Optional.of(connectedTxOutput); - unlockBlockHeight = blockHeight + connectedTxOutput.getLockTime(); - } else { - log.warn("We have a tx which has >1 connected txOutputs marked as LOCKUP_OUTPUT. " + - "This is not a valid BSQ tx."); - } - break; - case LOCKUP_OP_RETURN_OUTPUT: - // Cannot happen - break; - case UNLOCK_OUTPUT: - // This txInput is Spending an UNLOCK txOutput - int unlockBlockHeight = connectedTxOutput.getUnlockBlockHeight(); - if (blockHeight < unlockBlockHeight) { - accumulatedInputValue -= inputValue; - burntBondValue += inputValue; - - log.warn("We got a tx which spends the output from an unlock tx but before the " + - "unlockTime has passed. That leads to burned BSQ! " + - "blockHeight={}, unLockHeight={}", blockHeight, unlockBlockHeight); - } - break; - case INVALID_OUTPUT: - default: - break; - } - - daoStateService.setSpentInfo(connectedTxOutput.getKey(), new SpentInfo(blockHeight, txId, inputIndex)); - daoStateService.removeUnspentTxOutput(connectedTxOutput); - }); - } else { - log.warn("Connected txOutput {} at input {} of txId {} is confiscated ", txOutputKey, inputIndex, txId); - } - } - - private boolean isVoteRevealInputValid() { - return numVoteRevealInputs == 1; - } -} diff --git a/core/src/main/java/bisq/core/dao/node/parser/TxOutputParser.java b/core/src/main/java/bisq/core/dao/node/parser/TxOutputParser.java deleted file mode 100644 index 7f311d6406..0000000000 --- a/core/src/main/java/bisq/core/dao/node/parser/TxOutputParser.java +++ /dev/null @@ -1,450 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.node.parser; - -import bisq.core.dao.governance.bond.BondConsensus; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.OpReturnType; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.blockchain.TxOutputType; - -import bisq.common.config.Config; - -import com.google.common.annotations.VisibleForTesting; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Checks if an output is a BSQ output and apply state change. - * - * With block 602500 (about 4 weeks after v1.2.0 release) we enforce a new rule which represents a - * hard fork. Not updated nodes would see an out of sync dao state hash if a relevant transaction would - * happen again. - * Further (highly unlikely) consequences could be: - * If the BSQ output would be sent to a BSQ address the old client would accept that even it is - * invalid according to the new rules. But sending such an output would require a manually crafted tx - * (not possible in the UI). Worst case a not updated user would buy invalid BSQ but that is not possible as we - * enforce update to 1.2.0 for trading a few days after release as that release introduced the new trade protocol - * and protection tool. Only if both traders would have deactivated filter messages they could trade. - * - * Problem description: - * We did not apply the check to not allow BSQ outputs after we had detected a BTC output. - * The supported BSQ transactions did not support such cases anyway but we missed an edge case: - * A trade fee tx in case when the BTC input matches exactly the BTC output - * (or BTC change was <= the miner fee) and the BSQ fee was > the miner fee. Then we - * create a change output after the BTC output (using an address from the BTC wallet) and as - * available BSQ was >= as spent BSQ it was considered a valid BSQ output. - * There have been observed 5 such transactions where 4 got spent later to a BTC address and by that burned - * the pending BSQ (spending amount was higher than sending amount). One was still unspent. - * The BSQ was sitting in the BTC wallet so not even visible as BSQ to the user. - * If the user would have crafted a custom BSQ tx he could have avoided that the full trade fee was burned. - * - * Not a universal rule: - * We cannot enforce the rule that no BSQ output is permitted to all possible transactions because there can be cases - * where we need to permit this case. - * For instance in case we confiscate a lockupTx we have usually 2 BSQ outputs: The first one is the bond which - * should be confiscated and the second one is the BSQ change output. - * At confiscating we set the first to TxOutputType.BTC_OUTPUT but we do not want to confiscate - * the second BSQ change output as well. So we do not apply the rule that no BSQ is allowed once a BTC output is - * found. Theoretically other transactions could be confiscated as well and all BSQ tx which allow > 1 BSQ outputs - * would have the same issue as well if the first output gets confiscated. - * We also don't enforce the rule for irregular or invalid txs which are usually set and detected at the end of - * the tx parsing which is done in the TxParser. Blind vote and LockupTx with invalid OpReturn would be such cases - * where we don't want to invalidate the change output (See comments in TxParser). - * - * Most transactions created in Bisq (Proposal, blind vote and lockup,...) have only 1 or 2 BSQ - * outputs but we do not enforce a limit of max. 2 transactions in the parser. - * We leave for now that flexibility but it should not be considered as a rule. We might strengthen - * it any time if we find a reason for that (e.g. attack risk) and add checks that no more - * BSQ outputs are permitted for those txs. - * Some transactions like issuance, vote reveal and unlock have exactly 1 BSQ output and that rule - * is enforced. - */ -@Slf4j -class TxOutputParser { - 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 - @Getter - @Setter - private long availableInputValue = 0; - @Setter - private int unlockBlockHeight; - @Setter - @Getter - private Optional optionalSpentLockupTxOutput = Optional.empty(); - - // Getters - @Getter - private boolean bsqOutputFound; - @Getter - private Optional optionalOpReturnType = Optional.empty(); - @Getter - private Optional optionalIssuanceCandidate = Optional.empty(); - @Getter - private Optional optionalBlindVoteLockStakeOutput = Optional.empty(); - @Getter - private Optional optionalVoteRevealUnlockStakeOutput = Optional.empty(); - @Getter - private Optional optionalLockupOutput = Optional.empty(); - private Optional optionalOpReturnIndex = Optional.empty(); - - // Private - private int lockTime; - private final List utxoCandidates = new ArrayList<>(); - private boolean prohibitMoreBsqOutputs = false; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - TxOutputParser(DaoStateService daoStateService) { - this.daoStateService = daoStateService; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - void processOpReturnOutput(TempTxOutput tempTxOutput) { - byte[] opReturnData = tempTxOutput.getOpReturnData(); - checkNotNull(opReturnData, "opReturnData must not be null"); - TxOutputType txOutputType = OpReturnParser.getTxOutputType(tempTxOutput); - tempTxOutput.setTxOutputType(txOutputType); - - optionalOpReturnType = getMappedOpReturnType(txOutputType); - - optionalOpReturnType.ifPresent(e -> optionalOpReturnIndex = Optional.of(tempTxOutput.getIndex())); - - // If we have a LOCKUP opReturn output we save the lockTime to apply it later to the LOCKUP output. - // We keep that data in that other output as it makes parsing of the UNLOCK tx easier. - optionalOpReturnType.filter(opReturnType -> opReturnType == OpReturnType.LOCKUP) - .ifPresent(opReturnType -> lockTime = BondConsensus.getLockTime(opReturnData)); - } - - void processTxOutput(TempTxOutput tempTxOutput) { - // We don not expect here an opReturn output as we do not get called on the last output. Any opReturn at - // another output index is invalid. - if (tempTxOutput.isOpReturnOutput()) { - tempTxOutput.setTxOutputType(TxOutputType.INVALID_OUTPUT); - return; - } - - if (!daoStateService.isConfiscatedOutput(tempTxOutput.getKey())) { - long txOutputValue = tempTxOutput.getValue(); - int index = tempTxOutput.getIndex(); - if (isUnlockBondTx(tempTxOutput.getValue(), index)) { - // We need to handle UNLOCK transactions separately as they don't follow the pattern on spending BSQ - // The LOCKUP BSQ is burnt unless the output exactly matches the input, that would cause the - // output to not be BSQ output at all - handleUnlockBondTx(tempTxOutput); - } else if (isBtcOutputOfBurnFeeTx(tempTxOutput)) { - // In case we have the opReturn for a burn fee tx all outputs after 1st output are considered BTC - handleBtcOutput(tempTxOutput, index); - } else if (isHardForkActivated(tempTxOutput) && isIssuanceCandidateTxOutput(tempTxOutput)) { - // After the hard fork activation we fix a bug with a transaction which would have interpreted the - // issuance output as BSQ if the availableInputValue was >= issuance amount. - // Such a tx was never created but as we don't know if it will happen before activation date we cannot - // enforce the bug fix which represents a rule change before the activation date. - handleIssuanceCandidateOutput(tempTxOutput); - } else if (availableInputValue > 0 && availableInputValue >= txOutputValue) { - if (isHardForkActivated(tempTxOutput) && prohibitMoreBsqOutputs) { - handleBtcOutput(tempTxOutput, index); - } else { - handleBsqOutput(tempTxOutput, index, txOutputValue); - } - } else { - handleBtcOutput(tempTxOutput, index); - } - } else { - log.warn("TxOutput {} is confiscated ", tempTxOutput.getKey()); - // We only burn that output - availableInputValue -= tempTxOutput.getValue(); - - // We must not set prohibitMoreBsqOutputs at confiscation transactions as optional - // BSQ change output (output 2) must not be confiscated. - tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT); - } - } - - void commitUTXOCandidates() { - utxoCandidates.forEach(output -> daoStateService.addUnspentTxOutput(TxOutput.fromTempOutput(output))); - } - - /** - * This sets all outputs to BTC_OUTPUT and doesn't add any txOutputs to the unspentTxOutput map in daoStateService - */ - void invalidateUTXOCandidates() { - // We do not need to apply prohibitMoreBsqOutputs as all spendable outputs are set to BTC_OUTPUT anyway. - utxoCandidates.forEach(output -> output.setTxOutputType(TxOutputType.BTC_OUTPUT)); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Whether a transaction is a valid unlock bond transaction or not. - * - * @param txOutputValue The value of the current output, in satoshis. - * @param index The index of the output. - * @return True if the transaction is an unlock transaction, false otherwise. - */ - private boolean isUnlockBondTx(long txOutputValue, int index) { - // We require that the input value is exact the available value and the output value - return index == 0 && - availableInputValue == txOutputValue && - optionalSpentLockupTxOutput.isPresent() && - optionalSpentLockupTxOutput.get().getValue() == txOutputValue; - } - - private void handleUnlockBondTx(TempTxOutput txOutput) { - checkArgument(optionalSpentLockupTxOutput.isPresent(), "optionalSpentLockupTxOutput must be present"); - availableInputValue -= optionalSpentLockupTxOutput.get().getValue(); - - txOutput.setTxOutputType(TxOutputType.UNLOCK_OUTPUT); - txOutput.setUnlockBlockHeight(unlockBlockHeight); - utxoCandidates.add(txOutput); - - bsqOutputFound = true; - - // We do not permit more BSQ outputs after the unlock txo as we don't expect additional BSQ outputs. - prohibitMoreBsqOutputs = true; - } - - private boolean isBtcOutputOfBurnFeeTx(TempTxOutput tempTxOutput) { - if (optionalOpReturnType.isPresent()) { - int index = tempTxOutput.getIndex(); - switch (optionalOpReturnType.get()) { - case UNDEFINED: - break; - case PROPOSAL: - if (isHardForkActivated(tempTxOutput)) { - // We enforce a mandatory BSQ change output. - // We need that as similar to ASSET_LISTING_FEE and PROOF_OF_BURN - // we could not distinguish between 2 structurally same transactions otherwise (only way here - // would be to check the proposal fee as that is known from the params). - return index >= 1; - } - break; - case COMPENSATION_REQUEST: - break; - case REIMBURSEMENT_REQUEST: - break; - case BLIND_VOTE: - if (isHardForkActivated(tempTxOutput)) { - // After the hard fork activation we fix a bug with a transaction which would have interpreted the - // burned vote fee output as BSQ if the vote fee was >= miner fee. - // Such a tx was never created but as we don't know if it will happen before activation date we cannot - // enforce the bug fix which represents a rule change before the activation date. - - // If it is the vote stake output we return false. - if (index == 0) { - return false; - } - - // There must be a vote fee left - if (availableInputValue <= 0) { - return false; - } - - // Burned BSQ output is last output before opReturn. - // We could have also a BSQ change output as last output before opReturn but that will - // be detected at blindVoteFee check. - // We always have the BSQ change before the burned BSQ output if both are present. - checkArgument(optionalOpReturnIndex.isPresent()); - if (index != optionalOpReturnIndex.get() - 1) { - return false; - } - - // Without checking the fee we would not be able to distinguish between 2 structurally same transactions, one - // where the output is burned BSQ and one where it is a BSQ change output. - long blindVoteFee = daoStateService.getParamValueAsCoin(Param.BLIND_VOTE_FEE, tempTxOutput.getBlockHeight()).value; - return availableInputValue == blindVoteFee; - } - break; - case VOTE_REVEAL: - break; - case LOCKUP: - break; - case ASSET_LISTING_FEE: - case PROOF_OF_BURN: - // Asset listing fee and proof of burn tx are structurally the same. - - // We need to require one BSQ change output as we could otherwise not be able to distinguish between 2 - // structurally same transactions where only the BSQ fee is different. In case of asset listing fee and proof of - // burn it is a user input, so it is not known to the parser, instead we derive the burned fee from the parser. - // In case of proposal fee we could derive it from the params. - - // Case 1: 10 BSQ fee to burn - // In: 17 BSQ - // Out: BSQ change 7 BSQ -> valid BSQ - // Out: OpReturn - // Miner fee: 1000 sat (10 BSQ burned) - - - // Case 2: 17 BSQ fee to burn - // In: 17 BSQ - // Out: burned BSQ change 7 BSQ -> BTC (7 BSQ burned) - // Out: OpReturn - // Miner fee: 1000 sat (10 BSQ burned) - return index >= 1; - } - } - return false; - } - - private boolean isIssuanceCandidateTxOutput(TempTxOutput tempTxOutput) { - // If we have BSQ left as fee and we are at the second output we interpret it as a compensation request output. - return availableInputValue > 0 && - tempTxOutput.getIndex() == 1 && - optionalOpReturnType.isPresent() && - (optionalOpReturnType.get() == OpReturnType.COMPENSATION_REQUEST || - optionalOpReturnType.get() == OpReturnType.REIMBURSEMENT_REQUEST); - } - - private void handleIssuanceCandidateOutput(TempTxOutput tempTxOutput) { - // We do not permit more BSQ outputs after the issuance candidate. - prohibitMoreBsqOutputs = true; - - // We store the candidate but we don't apply the TxOutputType yet as we need to verify the fee after all - // outputs are parsed and check the phase. The TxParser will do that.... - optionalIssuanceCandidate = Optional.of(tempTxOutput); - } - - private void handleBsqOutput(TempTxOutput txOutput, int index, long txOutputValue) { - // Update the input balance. - availableInputValue -= txOutputValue; - - boolean isFirstOutput = index == 0; - - OpReturnType opReturnTypeCandidate = null; - if (optionalOpReturnType.isPresent()) - opReturnTypeCandidate = optionalOpReturnType.get(); - - TxOutputType txOutputType; - if (isFirstOutput && opReturnTypeCandidate == OpReturnType.BLIND_VOTE) { - txOutputType = TxOutputType.BLIND_VOTE_LOCK_STAKE_OUTPUT; - optionalBlindVoteLockStakeOutput = Optional.of(txOutput); - } else if (isFirstOutput && opReturnTypeCandidate == OpReturnType.VOTE_REVEAL) { - txOutputType = TxOutputType.VOTE_REVEAL_UNLOCK_STAKE_OUTPUT; - optionalVoteRevealUnlockStakeOutput = Optional.of(txOutput); - - // We do not permit more BSQ outputs after the VOTE_REVEAL_UNLOCK_STAKE_OUTPUT. - prohibitMoreBsqOutputs = true; - } else if (isFirstOutput && opReturnTypeCandidate == OpReturnType.LOCKUP) { - txOutputType = TxOutputType.LOCKUP_OUTPUT; - - // We store the lockTime in the output which will be used as input for a unlock tx. - // That makes parsing of that data easier as if we would need to access it from the opReturn output of - // that tx. - txOutput.setLockTime(lockTime); - optionalLockupOutput = Optional.of(txOutput); - } else { - txOutputType = TxOutputType.BSQ_OUTPUT; - } - txOutput.setTxOutputType(txOutputType); - utxoCandidates.add(txOutput); - - bsqOutputFound = true; - } - - private void handleBtcOutput(TempTxOutput txOutput, int index) { - if (isHardForkActivated(txOutput)) { - txOutput.setTxOutputType(TxOutputType.BTC_OUTPUT); - - // For regular transactions we don't permit BSQ outputs after a BTC output was detected. - prohibitMoreBsqOutputs = true; - } else { - // If we have BSQ left as fee and we are at the second output it might be a compensation request output. - // We store the candidate but we don't apply the TxOutputType yet as we need to verify the fee after all - // outputs are parsed and check the phase. The TxParser will do that.... - if (availableInputValue > 0 && - index == 1 && - optionalOpReturnType.isPresent() && - (optionalOpReturnType.get() == OpReturnType.COMPENSATION_REQUEST || - optionalOpReturnType.get() == OpReturnType.REIMBURSEMENT_REQUEST)) { - optionalIssuanceCandidate = Optional.of(txOutput); - - // We do not permit more BSQ outputs after the issuance candidate. - prohibitMoreBsqOutputs = true; - } else { - txOutput.setTxOutputType(TxOutputType.BTC_OUTPUT); - - // For regular transactions we don't permit BSQ outputs after a BTC output was detected. - prohibitMoreBsqOutputs = true; - } - } - } - - private boolean isHardForkActivated(TempTxOutput tempTxOutput) { - return tempTxOutput.getBlockHeight() >= getActivateHardFork1Height(); - } - - private int getActivateHardFork1Height() { - return Config.baseCurrencyNetwork().isMainnet() ? ACTIVATE_HARD_FORK_1_HEIGHT_MAINNET : - Config.baseCurrencyNetwork().isTestnet() ? ACTIVATE_HARD_FORK_1_HEIGHT_TESTNET : - ACTIVATE_HARD_FORK_1_HEIGHT_REGTEST; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Static - /////////////////////////////////////////////////////////////////////////////////////////// - - @SuppressWarnings("WeakerAccess") - @VisibleForTesting - static Optional getMappedOpReturnType(TxOutputType outputType) { - switch (outputType) { - case PROPOSAL_OP_RETURN_OUTPUT: - return Optional.of(OpReturnType.PROPOSAL); - case COMP_REQ_OP_RETURN_OUTPUT: - return Optional.of(OpReturnType.COMPENSATION_REQUEST); - case REIMBURSEMENT_OP_RETURN_OUTPUT: - return Optional.of(OpReturnType.REIMBURSEMENT_REQUEST); - case BLIND_VOTE_OP_RETURN_OUTPUT: - return Optional.of(OpReturnType.BLIND_VOTE); - case VOTE_REVEAL_OP_RETURN_OUTPUT: - return Optional.of(OpReturnType.VOTE_REVEAL); - case LOCKUP_OP_RETURN_OUTPUT: - return Optional.of(OpReturnType.LOCKUP); - case ASSET_LISTING_FEE_OP_RETURN_OUTPUT: - return Optional.of(OpReturnType.ASSET_LISTING_FEE); - case PROOF_OF_BURN_OP_RETURN_OUTPUT: - return Optional.of(OpReturnType.PROOF_OF_BURN); - default: - return Optional.empty(); - } - } -} diff --git a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java b/core/src/main/java/bisq/core/dao/node/parser/TxParser.java deleted file mode 100644 index c2d38ebebc..0000000000 --- a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java +++ /dev/null @@ -1,451 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.node.parser; - -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.node.full.RawTx; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.OpReturnType; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxInput; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.blockchain.TxOutputKey; -import bisq.core.dao.state.model.blockchain.TxOutputType; -import bisq.core.dao.state.model.blockchain.TxType; -import bisq.core.dao.state.model.governance.DaoPhase; - -import org.bitcoinj.core.Coin; - -import javax.inject.Inject; - -import com.google.common.annotations.VisibleForTesting; - -import java.util.List; -import java.util.Optional; - -import lombok.extern.slf4j.Slf4j; - -/** - * Verifies if a given transaction is a BSQ transaction. - */ -@Slf4j -public class TxParser { - private final PeriodService periodService; - private final DaoStateService daoStateService; - private TxOutputParser txOutputParser; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public TxParser(PeriodService periodService, - DaoStateService daoStateService) { - this.periodService = periodService; - this.daoStateService = daoStateService; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public Optional findTx(RawTx rawTx, String genesisTxId, int genesisBlockHeight, Coin genesisTotalSupply) { - if (GenesisTxParser.isGenesis(rawTx, genesisTxId, genesisBlockHeight)) - return Optional.of(GenesisTxParser.getGenesisTx(rawTx, genesisTotalSupply, daoStateService)); - else - return findTx(rawTx); - } - - // Apply state changes to tx, inputs and outputs - // return Tx if any input contained BSQ - // Any tx with BSQ input is a BSQ tx. - // There might be txs without any valid BSQ txOutput but we still keep track of it, - // for instance to calculate the total burned BSQ. - private Optional findTx(RawTx rawTx) { - int blockHeight = rawTx.getBlockHeight(); - TempTx tempTx = TempTx.fromRawTx(rawTx); - - //**************************************************************************************** - // Parse Inputs - //**************************************************************************************** - - TxInputParser txInputParser = new TxInputParser(daoStateService); - for (int inputIndex = 0; inputIndex < tempTx.getTxInputs().size(); inputIndex++) { - TxInput input = tempTx.getTxInputs().get(inputIndex); - TxOutputKey outputKey = input.getConnectedTxOutputKey(); - txInputParser.process(outputKey, blockHeight, rawTx.getId(), inputIndex); - } - - // Results from txInputParser - long accumulatedInputValue = txInputParser.getAccumulatedInputValue(); - long burntBondValue = txInputParser.getBurntBondValue(); - boolean unLockInputValid = txInputParser.isUnLockInputValid(); - int unlockBlockHeight = txInputParser.getUnlockBlockHeight(); - Optional optionalSpentLockupTxOutput = txInputParser.getOptionalSpentLockupTxOutput(); - - boolean hasBsqInputs = accumulatedInputValue > 0; - boolean hasBurntBond = burntBondValue > 0; - - // If we don't have any BSQ in our input and we don't have burnt bonds we do not consider the tx as a BSQ tx. - if (!hasBsqInputs && !hasBurntBond) - return Optional.empty(); - - - //**************************************************************************************** - // Parse Outputs - //**************************************************************************************** - - txOutputParser = new TxOutputParser(daoStateService); - txOutputParser.setAvailableInputValue(accumulatedInputValue); - txOutputParser.setUnlockBlockHeight(unlockBlockHeight); - txOutputParser.setOptionalSpentLockupTxOutput(optionalSpentLockupTxOutput); - - List outputs = tempTx.getTempTxOutputs(); - // We start with last output as that might be an OP_RETURN output and gives us the specific tx type, so it is - // easier and cleaner at parsing the other outputs to detect which kind of tx we deal with. - int lastIndex = outputs.size() - 1; - int lastNonOpReturnIndex = lastIndex; - if (outputs.get(lastIndex).isOpReturnOutput()) { - txOutputParser.processOpReturnOutput(outputs.get(lastIndex)); - lastNonOpReturnIndex -= 1; - } - - // We need to consider the order of the outputs. An output is a BSQ utxo as long there is enough input value - // We iterate all outputs (excluding an optional opReturn). - for (int index = 0; index <= lastNonOpReturnIndex; index++) { - txOutputParser.processTxOutput(outputs.get(index)); - } - - // Results from txOutputParser - long remainingInputValue = txOutputParser.getAvailableInputValue(); - Optional optionalOpReturnType = txOutputParser.getOptionalOpReturnType(); - boolean bsqOutputFound = txOutputParser.isBsqOutputFound(); - - long burntBsq = remainingInputValue + burntBondValue; - boolean hasBurntBsq = burntBsq > 0; - if (hasBurntBsq) - tempTx.setBurntBsq(burntBsq); - - - //**************************************************************************************** - // Verify and apply txType and txOutputTypes after we have all outputs parsed - //**************************************************************************************** - - applyTxTypeAndTxOutputType(blockHeight, tempTx, remainingInputValue); - TxType txType; - if (tempTx.getTxType() != TxType.IRREGULAR && tempTx.getTxType() != TxType.INVALID) { - txType = evaluateTxType(tempTx, optionalOpReturnType, hasBurntBsq, unLockInputValid); - tempTx.setTxType(txType); - } else { - txType = tempTx.getTxType(); - } - - if (isTxInvalid(tempTx, bsqOutputFound, hasBurntBond)) { - tempTx.setTxType(TxType.INVALID); - // We consider all BSQ inputs as burned if the tx is invalid. - tempTx.setBurntBsq(accumulatedInputValue); - txOutputParser.invalidateUTXOCandidates(); - log.warn("We have destroyed BSQ because of an invalid tx. Burned BSQ={}. tx={}", accumulatedInputValue / 100D, tempTx); - } else if (txType == TxType.IRREGULAR) { - log.warn("We have an irregular tx {}", tempTx); - txOutputParser.commitUTXOCandidates(); - } else { - txOutputParser.commitUTXOCandidates(); - } - - return Optional.of(Tx.fromTempTx(tempTx)); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - /** - * This method verifies after all outputs are parsed if the opReturn type and the optional txOutputs required for - * certain use cases are valid. - * It verifies also if the fee is correct (if required) and if the phase is correct (if relevant). - * We set the txType as well as the txOutputType of the relevant outputs. - */ - private void applyTxTypeAndTxOutputType(int blockHeight, TempTx tempTx, long bsqFee) { - OpReturnType opReturnType = null; - Optional optionalOpReturnType = txOutputParser.getOptionalOpReturnType(); - if (optionalOpReturnType.isPresent()) { - opReturnType = optionalOpReturnType.get(); - - switch (opReturnType) { - case PROPOSAL: - processProposal(blockHeight, tempTx, bsqFee); - break; - case COMPENSATION_REQUEST: - case REIMBURSEMENT_REQUEST: - processIssuance(blockHeight, tempTx, bsqFee); - break; - case BLIND_VOTE: - processBlindVote(blockHeight, tempTx, bsqFee); - break; - case VOTE_REVEAL: - // We do not check phase or cycle as a late voteReveal tx is considered a valid BSQ tx. - // The vote result though will ignore such votes. - break; - case LOCKUP: - case ASSET_LISTING_FEE: - case PROOF_OF_BURN: - // do nothing - break; - } - } - - // We need to check if any tempTxOutput is available and if so and the OpReturn data is invalid we - // set the output to a BTC output. We must not use `if else` cases here! - if (opReturnType != OpReturnType.COMPENSATION_REQUEST && opReturnType != OpReturnType.REIMBURSEMENT_REQUEST) { - // We applied already the check to not permit further BSQ outputs after the issuanceCandidate in the - // txOutputParser so we don't need to do any additional check here when we change to BTC_OUTPUT. - txOutputParser.getOptionalIssuanceCandidate().ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT)); - } - - if (opReturnType != OpReturnType.BLIND_VOTE) { - txOutputParser.getOptionalBlindVoteLockStakeOutput().ifPresent(tempTxOutput -> { - // We cannot apply the rule to not allow BSQ outputs after a BTC output as the 2nd output is an - // optional BSQ change output and we don't want to burn that in case the opReturn is invalid. - tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT); - }); - } - - if (opReturnType != OpReturnType.VOTE_REVEAL) { - txOutputParser.getOptionalVoteRevealUnlockStakeOutput().ifPresent(tempTxOutput -> { - // We do not apply the rule to not allow BSQ outputs after a BTC output here because we expect only - // one BSQ output anyway. - tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT); - }); - } - - if (opReturnType != OpReturnType.LOCKUP) { - txOutputParser.getOptionalLockupOutput().ifPresent(tempTxOutput -> { - // We cannot apply the rule to not allow BSQ outputs after a BTC output as the 2nd output is an - // optional BSQ change output and we don't want to burn that in case the opReturn is invalid. - tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT); - }); - } - } - - private void processProposal(int blockHeight, TempTx tempTx, long bsqFee) { - boolean isFeeAndPhaseValid = isFeeAndPhaseValid(tempTx.getId(), blockHeight, bsqFee, DaoPhase.Phase.PROPOSAL, Param.PROPOSAL_FEE); - if (!isFeeAndPhaseValid) { - // We tolerate such an incorrect tx and do not burn the BSQ - tempTx.setTxType(TxType.IRREGULAR); - } - } - - private void processIssuance(int blockHeight, TempTx tempTx, long bsqFee) { - boolean isFeeAndPhaseValid = isFeeAndPhaseValid(tempTx.getId(), blockHeight, bsqFee, DaoPhase.Phase.PROPOSAL, Param.PROPOSAL_FEE); - Optional optionalIssuanceCandidate = txOutputParser.getOptionalIssuanceCandidate(); - if (isFeeAndPhaseValid) { - if (optionalIssuanceCandidate.isPresent()) { - // Now after we have validated the fee and phase we will apply the TxOutputType - optionalIssuanceCandidate.get().setTxOutputType(TxOutputType.ISSUANCE_CANDIDATE_OUTPUT); - } else { - log.warn("It can be that we have a opReturn which is correct from its structure but the whole tx " + - "in not valid as the issuanceCandidate in not there. " + - "As the BSQ fee is set it must be either a buggy tx or a manually crafted invalid tx."); - // Even though the request part if invalid the BSQ transfer and change output should still be valid - // as long as the BSQ change <= BSQ inputs. - // We tolerate such an incorrect tx and do not burn the BSQ - tempTx.setTxType(TxType.IRREGULAR); - } - } else { - // This could be a valid compensation request that failed to be included in a block during the - // correct phase due to no fault of the user. We must not burn the change as long as the BSQ inputs - // cover the value of the outputs. - // We tolerate such an incorrect tx and do not burn the BSQ - tempTx.setTxType(TxType.IRREGULAR); - - // Make sure the optionalIssuanceCandidate is set to BTC - // We applied already the check to not permit further BSQ outputs after the issuanceCandidate in the - // txOutputParser so we don't need to do any additional check here when we change to BTC_OUTPUT. - optionalIssuanceCandidate.ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT)); - // Empty Optional case is a possible valid case where a random tx matches our opReturn rules but it is not a - // valid BSQ tx. - } - } - - private void processBlindVote(int blockHeight, TempTx tempTx, long bsqFee) { - boolean isFeeAndPhaseValid = isFeeAndPhaseValid(tempTx.getId(), blockHeight, bsqFee, DaoPhase.Phase.BLIND_VOTE, Param.BLIND_VOTE_FEE); - if (!isFeeAndPhaseValid) { - // We tolerate such an incorrect tx and do not burn the BSQ - tempTx.setTxType(TxType.IRREGULAR); - - // Set the stake output from BLIND_VOTE_LOCK_STAKE_OUTPUT to BSQ - txOutputParser.getOptionalBlindVoteLockStakeOutput().ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BSQ_OUTPUT)); - // Empty Optional case is a possible valid case where a random tx matches our opReturn rules but it is not a - // valid BSQ tx. - } - } - - /** - * Whether the BSQ fee and phase is valid for a transaction. - * - * @param blockHeight The height of the block that the transaction is in. - * @param bsqFee The fee in BSQ, in satoshi. - * @param phase The current phase of the DAO, e.g {@code DaoPhase.Phase.PROPOSAL}. - * @param param The parameter for the fee, e.g {@code Param.PROPOSAL_FEE}. - * @return True if the fee and phase was valid, false otherwise. - */ - private boolean isFeeAndPhaseValid(String txId, int blockHeight, long bsqFee, DaoPhase.Phase phase, Param param) { - // The leftover BSQ balance from the inputs is the BSQ fee in case we are in an OP_RETURN output - - if (!periodService.isInPhase(blockHeight, phase)) { - log.warn("Tx with ID {} is not in required phase ({}). blockHeight={}", txId, phase, blockHeight); - return false; - } - long paramValue = daoStateService.getParamValueAsCoin(param, blockHeight).value; - boolean isFeeCorrect = bsqFee == paramValue; - if (!isFeeCorrect) { - log.warn("Invalid fee. used fee={}, required fee={}, txId={}", bsqFee, paramValue, txId); - } - return isFeeCorrect; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Static methods - /////////////////////////////////////////////////////////////////////////////////////////// - - @VisibleForTesting - // Performs various checks for an invalid tx - static boolean isTxInvalid(TempTx tempTx, boolean bsqOutputFound, boolean burntBondValue) { - if (tempTx.getTxType() == TxType.INVALID) { - // We got already set the invalid type in earlier checks and return early. - return true; - } - - // We don't allow multiple opReturn outputs (they are non-standard but to be safe lets check it) - long numOpReturnOutputs = tempTx.getTempTxOutputs().stream() - .filter(TempTxOutput::isOpReturnOutput) - .count(); - if (numOpReturnOutputs > 1) { - log.warn("Invalid tx. We have multiple opReturn outputs. tx=" + tempTx); - return true; - } - - if ((tempTx.getTxType() == TxType.COMPENSATION_REQUEST || - tempTx.getTxType() == TxType.REIMBURSEMENT_REQUEST) - && !bsqOutputFound) { - log.warn("Invalid Tx: A compensation or reimbursement tx requires 1 BSQ output. Tx=" + tempTx); - return true; - } - - if (burntBondValue) { - log.warn("Invalid Tx: Bond value was burnt. tx=" + tempTx); - return true; - } - - if (tempTx.getTempTxOutputs().stream() - .anyMatch(txOutput -> TxOutputType.UNDEFINED_OUTPUT == txOutput.getTxOutputType() || - TxOutputType.INVALID_OUTPUT == txOutput.getTxOutputType())) { - log.warn("Invalid Tx: We have undefined or invalid txOutput types. tx=" + tempTx); - return true; - } - - return false; - } - - /** - * Retrieve the type of the transaction, assuming it is relevant to bisq. - * - * @param tempTx The temporary transaction. - * @param optionalOpReturnType The optional OP_RETURN type of the transaction. - * @param hasBurntBSQ If there have been remaining value from the inputs which got not spent in outputs. - * Might be valid BSQ fees or burned BSQ from an invalid tx. - * @return The type of the transaction, if it is relevant to bisq. - */ - @VisibleForTesting - static TxType evaluateTxType(TempTx tempTx, Optional optionalOpReturnType, - boolean hasBurntBSQ, boolean isUnLockInputValid) { - if (optionalOpReturnType.isPresent()) { - // We use the opReturnType to find the txType - return evaluateTxTypeFromOpReturnType(tempTx, optionalOpReturnType.get()); - } - - // No opReturnType, so we check for the remaining possible cases - if (hasBurntBSQ) { - // PAY_TRADE_FEE tx has a fee and no opReturn - return TxType.PAY_TRADE_FEE; - } - - // UNLOCK tx has no fee, no opReturn but an UNLOCK_OUTPUT at first output. - if (tempTx.getTempTxOutputs().get(0).getTxOutputType() == TxOutputType.UNLOCK_OUTPUT) { - // We check if there have been invalid inputs - if (!isUnLockInputValid) - return TxType.INVALID; - - return TxType.UNLOCK; - } - - // TRANSFER_BSQ has no fee, no opReturn and no UNLOCK_OUTPUT at first output - log.trace("No burned fee and no OP_RETURN, so this is a TRANSFER_BSQ tx."); - return TxType.TRANSFER_BSQ; - } - - @VisibleForTesting - static TxType evaluateTxTypeFromOpReturnType(TempTx tempTx, OpReturnType opReturnType) { - switch (opReturnType) { - case PROPOSAL: - return TxType.PROPOSAL; - case COMPENSATION_REQUEST: - case REIMBURSEMENT_REQUEST: - boolean hasCorrectNumOutputs = tempTx.getTempTxOutputs().size() >= 3; - if (!hasCorrectNumOutputs) { - log.warn("Compensation/reimbursement request tx need to have at least 3 outputs"); - // Such a transaction cannot be created by the Bisq client and is considered invalid. - return TxType.INVALID; - } - - TempTxOutput issuanceTxOutput = tempTx.getTempTxOutputs().get(1); - boolean hasIssuanceOutput = issuanceTxOutput.getTxOutputType() == TxOutputType.ISSUANCE_CANDIDATE_OUTPUT; - if (!hasIssuanceOutput) { - log.warn("Compensation/reimbursement request txOutput type of output at index 1 need to be ISSUANCE_CANDIDATE_OUTPUT. " + - "TxOutputType={}", issuanceTxOutput.getTxOutputType()); - // Such a transaction cannot be created by the Bisq client and is considered invalid. - return TxType.INVALID; - } - - return opReturnType == OpReturnType.COMPENSATION_REQUEST ? - TxType.COMPENSATION_REQUEST : - TxType.REIMBURSEMENT_REQUEST; - case BLIND_VOTE: - return TxType.BLIND_VOTE; - case VOTE_REVEAL: - return TxType.VOTE_REVEAL; - case LOCKUP: - return TxType.LOCKUP; - case ASSET_LISTING_FEE: - return TxType.ASSET_LISTING_FEE; - case PROOF_OF_BURN: - return TxType.PROOF_OF_BURN; - default: - log.warn("We got a BSQ tx with an unknown OP_RETURN. tx={}, opReturnType={}", tempTx, opReturnType); - // We tolerate such an incorrect tx and do not burn the BSQ. We might need that in case we add new - // opReturn types in future. - return TxType.IRREGULAR; - } - } -} diff --git a/core/src/main/java/bisq/core/dao/node/parser/exceptions/BlockHashNotConnectingException.java b/core/src/main/java/bisq/core/dao/node/parser/exceptions/BlockHashNotConnectingException.java deleted file mode 100644 index c97ee28e11..0000000000 --- a/core/src/main/java/bisq/core/dao/node/parser/exceptions/BlockHashNotConnectingException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.parser.exceptions; - -import bisq.core.dao.node.full.RawBlock; - -import lombok.Getter; - -@Getter -public class BlockHashNotConnectingException extends Exception { - - private final RawBlock rawBlock; - - public BlockHashNotConnectingException(RawBlock rawBlock) { - this.rawBlock = rawBlock; - } - - @Override - public String toString() { - return "BlockHashNotConnectingException{" + - "\n rawBlock.getHash=" + rawBlock.getHash() + - "\n rawBlock.getHeight=" + rawBlock.getHeight() + - "\n rawBlock.getPreviousBlockHash=" + rawBlock.getPreviousBlockHash() + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/parser/exceptions/BlockHeightNotConnectingException.java b/core/src/main/java/bisq/core/dao/node/parser/exceptions/BlockHeightNotConnectingException.java deleted file mode 100644 index 09b39be647..0000000000 --- a/core/src/main/java/bisq/core/dao/node/parser/exceptions/BlockHeightNotConnectingException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.parser.exceptions; - -import bisq.core.dao.node.full.RawBlock; - -import lombok.Getter; - -@Getter -public class BlockHeightNotConnectingException extends Exception { - - private final RawBlock rawBlock; - - public BlockHeightNotConnectingException(RawBlock rawBlock) { - this.rawBlock = rawBlock; - } - - @Override - public String toString() { - return "BlockHeightNotConnectingException{" + - "\n rawBlock.getHash=" + rawBlock.getHash() + - "\n rawBlock.getHeight=" + rawBlock.getHeight() + - "\n rawBlock.getPreviousBlockHash=" + rawBlock.getPreviousBlockHash() + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/parser/exceptions/InvalidGenesisTxException.java b/core/src/main/java/bisq/core/dao/node/parser/exceptions/InvalidGenesisTxException.java deleted file mode 100644 index e99d6e2977..0000000000 --- a/core/src/main/java/bisq/core/dao/node/parser/exceptions/InvalidGenesisTxException.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.parser.exceptions; - -import lombok.Getter; - -@Getter -public class InvalidGenesisTxException extends RuntimeException { - - public InvalidGenesisTxException(String msg) { - super(msg); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/parser/exceptions/InvalidParsingConditionException.java b/core/src/main/java/bisq/core/dao/node/parser/exceptions/InvalidParsingConditionException.java deleted file mode 100644 index 81e2aff020..0000000000 --- a/core/src/main/java/bisq/core/dao/node/parser/exceptions/InvalidParsingConditionException.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.parser.exceptions; - -public class InvalidParsingConditionException extends RuntimeException { - public InvalidParsingConditionException(String message) { - super(message); - } -} diff --git a/core/src/main/java/bisq/core/dao/node/parser/exceptions/RequiredReorgFromSnapshotException.java b/core/src/main/java/bisq/core/dao/node/parser/exceptions/RequiredReorgFromSnapshotException.java deleted file mode 100644 index 184676078c..0000000000 --- a/core/src/main/java/bisq/core/dao/node/parser/exceptions/RequiredReorgFromSnapshotException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.parser.exceptions; - -import bisq.core.dao.node.full.RawBlock; - -import lombok.Getter; - -@Getter -public class RequiredReorgFromSnapshotException extends Exception { - - private final RawBlock rawBlock; - - public RequiredReorgFromSnapshotException(RawBlock rawBlock) { - this.rawBlock = rawBlock; - } -} diff --git a/core/src/main/java/bisq/core/dao/presentation/DaoUtil.java b/core/src/main/java/bisq/core/dao/presentation/DaoUtil.java deleted file mode 100644 index 44a02536e4..0000000000 --- a/core/src/main/java/bisq/core/dao/presentation/DaoUtil.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.presentation; - -import bisq.core.dao.DaoFacade; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.locale.Res; -import bisq.core.util.FormattingUtils; - -import java.text.SimpleDateFormat; - -import java.util.Date; -import java.util.Locale; - -/** - * Util class for shared presentation code. - */ -public class DaoUtil { - - public static String getNextPhaseDuration(int height, DaoPhase.Phase phase, DaoFacade daoFacade) { - final int currentCycleDuration = daoFacade.getCurrentCycleDuration(); - long start = daoFacade.getFirstBlockOfPhaseForDisplay(height, phase) + currentCycleDuration; - long end = daoFacade.getLastBlockOfPhaseForDisplay(height, phase) + currentCycleDuration; - - long now = new Date().getTime(); - SimpleDateFormat dateFormatter = new SimpleDateFormat("dd MMM", Locale.getDefault()); - SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm", Locale.getDefault()); - String startDateTime = FormattingUtils.formatDateTime(new Date(now + (start - height) * 10 * 60 * 1000L), dateFormatter, timeFormatter); - String endDateTime = FormattingUtils.formatDateTime(new Date(now + (end - height) * 10 * 60 * 1000L), dateFormatter, timeFormatter); - - return Res.get("dao.cycle.phaseDurationWithoutBlocks", start, end, startDateTime, endDateTime); - } - - public static String getPhaseDuration(int height, DaoPhase.Phase phase, DaoFacade daoFacade) { - long start = daoFacade.getFirstBlockOfPhaseForDisplay(height, phase); - long end = daoFacade.getLastBlockOfPhaseForDisplay(height, phase); - long duration = daoFacade.getDurationForPhaseForDisplay(phase); - long now = new Date().getTime(); - SimpleDateFormat dateFormatter = new SimpleDateFormat("dd MMM", Locale.getDefault()); - SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm", Locale.getDefault()); - String startDateTime = FormattingUtils.formatDateTime(new Date(now + (start - height) * 10 * 60 * 1000L), dateFormatter, timeFormatter); - String endDateTime = FormattingUtils.formatDateTime(new Date(now + (end - height) * 10 * 60 * 1000L), dateFormatter, timeFormatter); - String durationTime = FormattingUtils.formatDurationAsWords(duration * 10 * 60 * 1000, false, false); - return Res.get("dao.cycle.phaseDuration", duration, durationTime, start, end, startDateTime, endDateTime); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/DaoStateListener.java b/core/src/main/java/bisq/core/dao/state/DaoStateListener.java deleted file mode 100644 index 02b19da71a..0000000000 --- a/core/src/main/java/bisq/core/dao/state/DaoStateListener.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state; - -import bisq.core.dao.state.model.blockchain.Block; - -public interface DaoStateListener { - default void onNewBlockHeight(int blockHeight) { - } - - default void onParseBlockChainComplete() { - } - - // Called before onParseTxsCompleteAfterBatchProcessing in case batch processing is complete - default void onParseBlockComplete(Block block) { - } - - default void onParseBlockCompleteAfterBatchProcessing(Block block) { - } - - // Called after the parsing of a block is complete and we do not allow any change in the daoState until the next - // block arrives. - default void onDaoStateChanged(Block block) { - } -} diff --git a/core/src/main/java/bisq/core/dao/state/DaoStateService.java b/core/src/main/java/bisq/core/dao/state/DaoStateService.java deleted file mode 100644 index ebfeddf781..0000000000 --- a/core/src/main/java/bisq/core/dao/state/DaoStateService.java +++ /dev/null @@ -1,1073 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state; - -import bisq.core.dao.DaoSetupService; -import bisq.core.dao.governance.bond.BondConsensus; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.state.model.DaoState; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.blockchain.SpentInfo; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxInput; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.blockchain.TxOutputKey; -import bisq.core.dao.state.model.blockchain.TxOutputType; -import bisq.core.dao.state.model.blockchain.TxType; -import bisq.core.dao.state.model.governance.Cycle; -import bisq.core.dao.state.model.governance.DecryptedBallotsWithMerits; -import bisq.core.dao.state.model.governance.EvaluatedProposal; -import bisq.core.dao.state.model.governance.Issuance; -import bisq.core.dao.state.model.governance.IssuanceType; -import bisq.core.dao.state.model.governance.ParamChange; -import bisq.core.util.ParsingUtils; -import bisq.core.util.coin.BsqFormatter; - -import org.bitcoinj.core.Coin; - -import javax.inject.Inject; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.TreeMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -import static com.google.common.base.Preconditions.checkArgument; - -/** - * Provides access methods to DaoState data. - */ -@Slf4j -public class DaoStateService implements DaoSetupService { - private final DaoState daoState; - private final GenesisTxInfo genesisTxInfo; - private final BsqFormatter bsqFormatter; - private final List daoStateListeners = new CopyOnWriteArrayList<>(); - @Getter - private boolean parseBlockChainComplete; - private boolean allowDaoStateChange; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public DaoStateService(DaoState daoState, GenesisTxInfo genesisTxInfo, BsqFormatter bsqFormatter) { - this.daoState = daoState; - this.genesisTxInfo = genesisTxInfo; - this.bsqFormatter = bsqFormatter; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoSetupService - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void addListeners() { - } - - @Override - public void start() { - allowDaoStateChange = true; - assertDaoStateChange(); - daoState.setChainHeight(genesisTxInfo.getGenesisBlockHeight()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Snapshot - /////////////////////////////////////////////////////////////////////////////////////////// - - public void applySnapshot(DaoState snapshot) { - allowDaoStateChange = true; - assertDaoStateChange(); - - log.info("Apply snapshot with chain height {}", snapshot.getChainHeight()); - - daoState.setChainHeight(snapshot.getChainHeight()); - - daoState.setTxCache(snapshot.getTxCache()); - - daoState.getBlocks().clear(); - daoState.getBlocks().addAll(snapshot.getBlocks()); - - daoState.getCycles().clear(); - daoState.getCycles().addAll(snapshot.getCycles()); - - daoState.getUnspentTxOutputMap().clear(); - daoState.getUnspentTxOutputMap().putAll(snapshot.getUnspentTxOutputMap()); - - daoState.getSpentInfoMap().clear(); - daoState.getSpentInfoMap().putAll(snapshot.getSpentInfoMap()); - - daoState.getConfiscatedLockupTxList().clear(); - daoState.getConfiscatedLockupTxList().addAll(snapshot.getConfiscatedLockupTxList()); - - daoState.getIssuanceMap().clear(); - daoState.getIssuanceMap().putAll(snapshot.getIssuanceMap()); - - daoState.getParamChangeList().clear(); - daoState.getParamChangeList().addAll(snapshot.getParamChangeList()); - - daoState.getEvaluatedProposalList().clear(); - daoState.getEvaluatedProposalList().addAll(snapshot.getEvaluatedProposalList()); - - daoState.getDecryptedBallotsWithMeritsList().clear(); - daoState.getDecryptedBallotsWithMeritsList().addAll(snapshot.getDecryptedBallotsWithMeritsList()); - } - - public DaoState getClone() { - return DaoState.getClone(daoState); - } - - public byte[] getSerializedStateForHashChain() { - return daoState.getSerializedStateForHashChain(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // ChainHeight - /////////////////////////////////////////////////////////////////////////////////////////// - - public int getChainHeight() { - return daoState.getChainHeight(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Cycle - /////////////////////////////////////////////////////////////////////////////////////////// - - public LinkedList getCycles() { - return daoState.getCycles(); - } - - public void addCycle(Cycle cycle) { - assertDaoStateChange(); - getCycles().add(cycle); - } - - @Nullable - public Cycle getCurrentCycle() { - return !getCycles().isEmpty() ? getCycles().getLast() : null; - } - - public Optional getCycle(int height) { - return getCycles().stream() - .filter(cycle -> cycle.getHeightOfFirstBlock() <= height) - .filter(cycle -> cycle.getHeightOfLastBlock() >= height) - .findAny(); - } - - public Optional getStartHeightOfNextCycle(int blockHeight) { - return getCycle(blockHeight).map(cycle -> cycle.getHeightOfLastBlock() + 1); - } - - public Optional getStartHeightOfCurrentCycle(int blockHeight) { - return getCycle(blockHeight).map(cycle -> cycle.getHeightOfFirstBlock()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Block - /////////////////////////////////////////////////////////////////////////////////////////// - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Parser events - /////////////////////////////////////////////////////////////////////////////////////////// - - // First we get the blockHeight set - public void onNewBlockHeight(int blockHeight) { - allowDaoStateChange = true; - daoState.setChainHeight(blockHeight); - daoStateListeners.forEach(listener -> listener.onNewBlockHeight(blockHeight)); - } - - // Second we get the block added with empty txs - public void onNewBlockWithEmptyTxs(Block block) { - assertDaoStateChange(); - if (daoState.getBlocks().isEmpty() && block.getHeight() != getGenesisBlockHeight()) { - log.warn("We don't have any blocks yet and we received a block which is not the genesis block. " + - "We ignore that block as the first block need to be the genesis block. " + - "That might happen in edge cases at reorgs. Received block={}", block); - } else { - daoState.getBlocks().add(block); - - if (parseBlockChainComplete) - log.info("New Block added at blockHeight {}", block.getHeight()); - } - } - - // Third we add each successfully parsed BSQ tx to the last block - public void onNewTxForLastBlock(Block block, Tx tx) { - assertDaoStateChange(); - - getLastBlock().ifPresent(lastBlock -> { - if (block == lastBlock) { - // We need to ensure that the txs in all blocks are in sync with the txs in our txMap (cache). - block.addTx(tx); - daoState.addToTxCache(tx); - } else { - // Not clear if this case can happen but at onNewBlockWithEmptyTxs we handle such a potential edge - // case as well, so we need to reflect that here as well. - log.warn("Block for parsing does not match last block. That might happen in edge cases at reorgs. " + - "Received block={}", block); - } - }); - } - - // Fourth we get the onParseBlockComplete called after all rawTxs of blocks have been parsed - public void onParseBlockComplete(Block block) { - if (parseBlockChainComplete) - log.info("Parse block completed: Block height {}, {} BSQ transactions.", block.getHeight(), block.getTxs().size()); - - // Need to be called before onParseTxsCompleteAfterBatchProcessing as we use it in - // VoteResult and other listeners like balances usually listen on onParseTxsCompleteAfterBatchProcessing - // so we need to make sure that vote result calculation is completed before (e.g. for comp. request to - // update balance). - daoStateListeners.forEach(l -> l.onParseBlockComplete(block)); - - // We use 2 different handlers as we don't want to update domain listeners during batch processing of all - // blocks as that causes performance issues. In earlier versions when we updated at each block it took - // 50 sec. for 4000 blocks, after that change it was about 4 sec. - // Clients - if (parseBlockChainComplete) - daoStateListeners.forEach(l -> l.onParseBlockCompleteAfterBatchProcessing(block)); - - // Here listeners must not trigger any state change in the DAO as we trigger the validation service to - // generate a hash of the state. - allowDaoStateChange = false; - daoStateListeners.forEach(l -> l.onDaoStateChanged(block)); - } - - // Called after parsing of all pending blocks is completed - public void onParseBlockChainComplete() { - log.info("Parse blockchain completed"); - parseBlockChainComplete = true; - - getLastBlock().ifPresent(block -> { - daoStateListeners.forEach(l -> l.onParseBlockCompleteAfterBatchProcessing(block)); - }); - - daoStateListeners.forEach(DaoStateListener::onParseBlockChainComplete); - } - - - public LinkedList getBlocks() { - return daoState.getBlocks(); - } - - /** - * Whether specified block hash belongs to a block we already know about. - * - * @param blockHash The hash of a {@link Block}. - * @return True if the hash belongs to a {@link Block} we know about, otherwise - * {@code false}. - */ - public boolean isBlockHashKnown(String blockHash) { - return getBlocks().stream().anyMatch(block -> block.getHash().equals(blockHash)); - } - - public Optional getLastBlock() { - if (!getBlocks().isEmpty()) - return Optional.of(getBlocks().getLast()); - else - return Optional.empty(); - } - - public int getBlockHeightOfLastBlock() { - return getLastBlock().map(Block::getHeight).orElse(0); - } - - public Optional getBlockAtHeight(int height) { - return getBlocks().stream() - .filter(block -> block.getHeight() == height) - .findAny(); - } - - public boolean containsBlock(Block block) { - return getBlocks().contains(block); - } - - public boolean containsBlockHash(String blockHash) { - return getBlocks().stream().anyMatch(block -> block.getHash().equals(blockHash)); - } - - public long getBlockTime(int height) { - return getBlockAtHeight(height).map(Block::getTime).orElse(0L); - } - - public List getBlocksFromBlockHeight(int fromBlockHeight, int numMaxBlocks) { - // We limit requests to numMaxBlocks blocks, to avoid performance issues and too - // large network data in case a node requests too far back in history. - return getBlocks().stream() - .filter(block -> block.getHeight() >= fromBlockHeight) - .sorted(Comparator.comparing(Block::getHeight)) - .limit(numMaxBlocks) - .collect(Collectors.toList()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Genesis - /////////////////////////////////////////////////////////////////////////////////////////// - - public String getGenesisTxId() { - return genesisTxInfo.getGenesisTxId(); - } - - public int getGenesisBlockHeight() { - return genesisTxInfo.getGenesisBlockHeight(); - } - - public Coin getGenesisTotalSupply() { - return Coin.valueOf(genesisTxInfo.getGenesisTotalSupply()); - } - - public Optional getGenesisTx() { - return getTx(getGenesisTxId()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Tx - /////////////////////////////////////////////////////////////////////////////////////////// - - public Stream getUnorderedTxStream() { - return daoState.getTxCache().values().stream(); - } - - public int getNumTxs() { - return daoState.getTxCache().size(); - } - - public List getInvalidTxs() { - return getUnorderedTxStream().filter(tx -> tx.getTxType() == TxType.INVALID).collect(Collectors.toList()); - } - - public List getIrregularTxs() { - return getUnorderedTxStream().filter(tx -> tx.getTxType() == TxType.IRREGULAR).collect(Collectors.toList()); - } - - public Optional getTx(String txId) { - return Optional.ofNullable(daoState.getTxCache().get(txId)); - } - - public boolean containsTx(String txId) { - return getTx(txId).isPresent(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // TxType - /////////////////////////////////////////////////////////////////////////////////////////// - - public Optional getOptionalTxType(String txId) { - return getTx(txId).map(Tx::getTxType); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // BurntFee (trade fee and fee burned at proof of burn) - /////////////////////////////////////////////////////////////////////////////////////////// - - public long getBurntFee(String txId) { - return getTx(txId).map(Tx::getBurntFee).orElse(0L); - } - - public boolean hasTxBurntFee(String txId) { - return getBurntFee(txId) > 0; - } - - public Set getTradeFeeTxs() { - return getUnorderedTxStream() - .filter(tx -> tx.getTxType() == TxType.PAY_TRADE_FEE) - .collect(Collectors.toSet()); - } - - public Set getProofOfBurnTxs() { - return getUnorderedTxStream() - .filter(tx -> tx.getTxType() == TxType.PROOF_OF_BURN) - .collect(Collectors.toSet()); - } - - // Any tx with burned BSQ - public Set getBurntFeeTxs() { - return getUnorderedTxStream() - .filter(tx -> tx.getBurntFee() > 0) - .collect(Collectors.toSet()); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // TxInput - /////////////////////////////////////////////////////////////////////////////////////////// - - public Optional getConnectedTxOutput(TxInput txInput) { - return getTx(txInput.getConnectedTxOutputTxId()) - .map(tx -> tx.getTxOutputs().get(txInput.getConnectedTxOutputIndex())); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // TxOutput - /////////////////////////////////////////////////////////////////////////////////////////// - - private Stream getUnorderedTxOutputStream() { - return getUnorderedTxStream() - .flatMap(tx -> tx.getTxOutputs().stream()); - } - - public boolean existsTxOutput(TxOutputKey key) { - return getUnorderedTxOutputStream().anyMatch(txOutput -> txOutput.getKey().equals(key)); - } - - public Optional getTxOutput(TxOutputKey txOutputKey) { - return getUnorderedTxOutputStream() - .filter(txOutput -> txOutput.getKey().equals(txOutputKey)) - .findAny(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // UnspentTxOutput - /////////////////////////////////////////////////////////////////////////////////////////// - - public TreeMap getUnspentTxOutputMap() { - return daoState.getUnspentTxOutputMap(); - } - - public void addUnspentTxOutput(TxOutput txOutput) { - assertDaoStateChange(); - getUnspentTxOutputMap().put(txOutput.getKey(), txOutput); - } - - public void removeUnspentTxOutput(TxOutput txOutput) { - assertDaoStateChange(); - getUnspentTxOutputMap().remove(txOutput.getKey()); - } - - public boolean isUnspent(TxOutputKey key) { - return getUnspentTxOutputMap().containsKey(key); - } - - public Set getUnspentTxOutputs() { - return new HashSet<>(getUnspentTxOutputMap().values()); - } - - public Optional getUnspentTxOutput(TxOutputKey key) { - return Optional.ofNullable(getUnspentTxOutputMap().getOrDefault(key, null)); - } - - public boolean isTxOutputSpendable(TxOutputKey key) { - if (!isUnspent(key)) - return false; - - Optional optionalTxOutput = getUnspentTxOutput(key); - // The above isUnspent call satisfies optionalTxOutput.isPresent() - checkArgument(optionalTxOutput.isPresent(), "optionalTxOutput must be present"); - TxOutput txOutput = optionalTxOutput.get(); - - switch (txOutput.getTxOutputType()) { - case UNDEFINED_OUTPUT: - return false; - case GENESIS_OUTPUT: - case BSQ_OUTPUT: - return true; - case BTC_OUTPUT: - return false; - case PROPOSAL_OP_RETURN_OUTPUT: - case COMP_REQ_OP_RETURN_OUTPUT: - case REIMBURSEMENT_OP_RETURN_OUTPUT: - case ISSUANCE_CANDIDATE_OUTPUT: - return true; - case BLIND_VOTE_LOCK_STAKE_OUTPUT: - return false; - case BLIND_VOTE_OP_RETURN_OUTPUT: - case VOTE_REVEAL_UNLOCK_STAKE_OUTPUT: - case VOTE_REVEAL_OP_RETURN_OUTPUT: - return true; - case ASSET_LISTING_FEE_OP_RETURN_OUTPUT: - case PROOF_OF_BURN_OP_RETURN_OUTPUT: - return false; - case LOCKUP_OUTPUT: - return false; - case LOCKUP_OP_RETURN_OUTPUT: - return true; - case UNLOCK_OUTPUT: - return isLockTimeOverForUnlockTxOutput(txOutput); - case INVALID_OUTPUT: - return false; - default: - return false; - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // TxOutputType - /////////////////////////////////////////////////////////////////////////////////////////// - - private Set getTxOutputsByTxOutputType(TxOutputType txOutputType) { - return getUnorderedTxOutputStream() - .filter(txOutput -> txOutput.getTxOutputType() == txOutputType) - .collect(Collectors.toSet()); - } - - public boolean isBsqTxOutputType(TxOutput txOutput) { - final TxOutputType txOutputType = txOutput.getTxOutputType(); - switch (txOutputType) { - case UNDEFINED_OUTPUT: - return false; - case GENESIS_OUTPUT: - case BSQ_OUTPUT: - return true; - case BTC_OUTPUT: - return false; - case PROPOSAL_OP_RETURN_OUTPUT: - case COMP_REQ_OP_RETURN_OUTPUT: - case REIMBURSEMENT_OP_RETURN_OUTPUT: - return true; - case ISSUANCE_CANDIDATE_OUTPUT: - return isIssuanceTx(txOutput.getTxId()); - case BLIND_VOTE_LOCK_STAKE_OUTPUT: - case BLIND_VOTE_OP_RETURN_OUTPUT: - case VOTE_REVEAL_UNLOCK_STAKE_OUTPUT: - case VOTE_REVEAL_OP_RETURN_OUTPUT: - case ASSET_LISTING_FEE_OP_RETURN_OUTPUT: - case PROOF_OF_BURN_OP_RETURN_OUTPUT: - case LOCKUP_OUTPUT: - case LOCKUP_OP_RETURN_OUTPUT: - case UNLOCK_OUTPUT: - return true; - case INVALID_OUTPUT: - return false; - default: - return false; - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // TxOutputType - Voting - /////////////////////////////////////////////////////////////////////////////////////////// - - public Set getUnspentBlindVoteStakeTxOutputs() { - return getTxOutputsByTxOutputType(TxOutputType.BLIND_VOTE_LOCK_STAKE_OUTPUT).stream() - .filter(txOutput -> isUnspent(txOutput.getKey())) - .collect(Collectors.toSet()); - } - - public Set getVoteRevealOpReturnTxOutputs() { - return getTxOutputsByTxOutputType(TxOutputType.VOTE_REVEAL_OP_RETURN_OUTPUT); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // TxOutputType - Issuance - /////////////////////////////////////////////////////////////////////////////////////////// - - public Set getIssuanceCandidateTxOutputs() { - return getTxOutputsByTxOutputType(TxOutputType.ISSUANCE_CANDIDATE_OUTPUT); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Issuance - /////////////////////////////////////////////////////////////////////////////////////////// - - public void addIssuance(Issuance issuance) { - assertDaoStateChange(); - daoState.getIssuanceMap().put(issuance.getTxId(), issuance); - } - - public Set getIssuanceSetForType(IssuanceType issuanceType) { - return daoState.getIssuanceMap().values().stream() - .filter(issuance -> issuance.getIssuanceType() == issuanceType) - .collect(Collectors.toSet()); - } - - public Optional getIssuance(String txId, IssuanceType issuanceType) { - return getIssuance(txId).filter(issuance -> issuance.getIssuanceType() == issuanceType); - } - - public Optional getIssuance(String txId) { - return Optional.ofNullable(daoState.getIssuanceMap().get(txId)); - } - - public boolean isIssuanceTx(String txId) { - return getIssuance(txId).isPresent(); - } - - public boolean isIssuanceTx(String txId, IssuanceType issuanceType) { - return getIssuance(txId, issuanceType).isPresent(); - } - - public int getIssuanceBlockHeight(String txId) { - return getIssuance(txId) - .map(Issuance::getChainHeight) - .orElse(0); - } - - public long getTotalIssuedAmount(IssuanceType issuanceType) { - return getIssuanceCandidateTxOutputs().stream() - .filter(txOutput -> isIssuanceTx(txOutput.getTxId(), issuanceType)) - .mapToLong(TxOutput::getValue) - .sum(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Not accepted issuance candidate outputs of past cycles - /////////////////////////////////////////////////////////////////////////////////////////// - - public boolean isRejectedIssuanceOutput(TxOutputKey txOutputKey) { - Cycle currentCycle = getCurrentCycle(); - return currentCycle != null && - getIssuanceCandidateTxOutputs().stream() - .filter(txOutput -> txOutput.getKey().equals(txOutputKey)) - .filter(txOutput -> !currentCycle.isInCycle(txOutput.getBlockHeight())) - .anyMatch(txOutput -> !isIssuanceTx(txOutput.getTxId())); - - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Bond - /////////////////////////////////////////////////////////////////////////////////////////// - - // Terminology - // HashOfBondId - 20 bytes hash of the bond ID - // Lockup - txOutputs of LOCKUP type - // Unlocking - UNLOCK txOutputs that are not yet spendable due to lock time - // Unlocked - UNLOCK txOutputs that are spendable since the lock time has passed - // LockTime - 0 means that the funds are spendable at the same block of the UNLOCK tx. For the user that is not - // supported as we do not expose unconfirmed BSQ txs so lockTime of 1 is the smallest the user can actually use. - - // LockTime - public Optional getLockTime(String txId) { - return getTx(txId).map(Tx::getLockTime); - } - - public Optional getLockupHash(TxOutput txOutput) { - Optional lockupTx = Optional.empty(); - String txId = txOutput.getTxId(); - if (txOutput.getTxOutputType() == TxOutputType.LOCKUP_OUTPUT) { - lockupTx = getTx(txId); - } else if (isUnlockTxOutputAndLockTimeNotOver(txOutput)) { - if (getTx(txId).isPresent()) { - Tx unlockTx = getTx(txId).get(); - lockupTx = getTx(unlockTx.getTxInputs().get(0).getConnectedTxOutputTxId()); - } - } - if (lockupTx.isPresent()) { - byte[] opReturnData = lockupTx.get().getLastTxOutput().getOpReturnData(); - if (opReturnData != null) - return Optional.of(BondConsensus.getHashFromOpReturnData(opReturnData)); - } - return Optional.empty(); - } - - /* public Set getHashOfBondIdSet() { - return getTxOutputStream() - .filter(txOutput -> isUnspent(txOutput.getKey())) - .filter(txOutput -> txOutput.getTxOutputType() == TxOutputType.LOCKUP || - isUnlockTxOutputAndLockTimeNotOver(txOutput)) - .map(txOutput -> getHash(txOutput).orElse(null)) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); - }*/ - - public boolean isUnlockTxOutputAndLockTimeNotOver(TxOutput txOutput) { - return txOutput.getTxOutputType() == TxOutputType.UNLOCK_OUTPUT && !isLockTimeOverForUnlockTxOutput(txOutput); - } - - // Lockup - public boolean isLockupOutput(TxOutputKey key) { - Optional opTxOutput = getUnspentTxOutput(key); - return opTxOutput.isPresent() && isLockupOutput(opTxOutput.get()); - } - - public boolean isLockupOutput(TxOutput txOutput) { - return txOutput.getTxOutputType() == TxOutputType.LOCKUP_OUTPUT; - } - - public Set getLockupTxOutputs() { - return getTxOutputsByTxOutputType(TxOutputType.LOCKUP_OUTPUT); - } - - public Set getUnlockTxOutputs() { - return getTxOutputsByTxOutputType(TxOutputType.UNLOCK_OUTPUT); - } - - public Set getUnspentLockUpTxOutputs() { - return getTxOutputsByTxOutputType(TxOutputType.LOCKUP_OUTPUT).stream() - .filter(txOutput -> isUnspent(txOutput.getKey())) - .collect(Collectors.toSet()); - } - - public Optional getLockupTxOutput(String txId) { - return getTx(txId).flatMap(tx -> tx.getTxOutputs().stream() - .filter(this::isLockupOutput) - .findFirst()); - } - - public Optional getLockupOpReturnTxOutput(String txId) { - return getTx(txId).map(Tx::getLastTxOutput).filter(txOutput -> txOutput.getOpReturnData() != null); - } - - // Returns amount of all LOCKUP txOutputs (they might have been unlocking or unlocked in the meantime) - public long getTotalAmountOfLockupTxOutputs() { - return getLockupTxOutputs().stream() - .filter(txOutput -> !isConfiscatedLockupTxOutput(txOutput.getTxId())) - .mapToLong(TxOutput::getValue) - .sum(); - } - - // Returns the current locked up amount (excluding unlocking and unlocked) - public long getTotalLockupAmount() { - return getTotalAmountOfLockupTxOutputs() - getTotalAmountOfUnLockingTxOutputs() - getTotalAmountOfUnLockedTxOutputs(); - } - - - // Unlock - public boolean isUnspentUnlockOutput(TxOutputKey key) { - Optional opTxOutput = getUnspentTxOutput(key); - return opTxOutput.isPresent() && isUnlockOutput(opTxOutput.get()); - } - - public boolean isUnlockOutput(TxOutput txOutput) { - return txOutput.getTxOutputType() == TxOutputType.UNLOCK_OUTPUT; - } - - // Unlocking - // Return UNLOCK TxOutputs that are not yet spendable as lockTime is not over - public Stream getUnspentUnlockingTxOutputsStream() { - return getTxOutputsByTxOutputType(TxOutputType.UNLOCK_OUTPUT).stream() - .filter(txOutput -> isUnspent(txOutput.getKey())) - .filter(txOutput -> !isLockTimeOverForUnlockTxOutput(txOutput)); - } - - public long getTotalAmountOfUnLockingTxOutputs() { - return getUnspentUnlockingTxOutputsStream() - .filter(txOutput -> !isConfiscatedUnlockTxOutput(txOutput.getTxId())) - .mapToLong(TxOutput::getValue) - .sum(); - } - - public boolean isUnlockingAndUnspent(TxOutputKey key) { - Optional opTxOutput = getUnspentTxOutput(key); - return opTxOutput.isPresent() && isUnlockingAndUnspent(opTxOutput.get()); - } - - public boolean isUnlockingAndUnspent(String unlockTxId) { - Optional optionalTx = getTx(unlockTxId); - return optionalTx.isPresent() && isUnlockingAndUnspent(optionalTx.get().getTxOutputs().get(0)); - } - - public boolean isUnlockingAndUnspent(TxOutput unlockTxOutput) { - return unlockTxOutput.getTxOutputType() == TxOutputType.UNLOCK_OUTPUT && - isUnspent(unlockTxOutput.getKey()) && - !isLockTimeOverForUnlockTxOutput(unlockTxOutput); - } - - public Optional getLockupTxFromUnlockTxId(String unlockTxId) { - return getTx(unlockTxId).flatMap(tx -> getTx(tx.getTxInputs().get(0).getConnectedTxOutputTxId())); - } - - public Optional getUnlockTxFromLockupTxId(String lockupTxId) { - return getTx(lockupTxId).flatMap(tx -> getSpentInfo(tx.getTxOutputs().get(0))).flatMap(spentInfo -> getTx(spentInfo.getTxId())); - } - - // Unlocked - public Optional getUnlockBlockHeight(String txId) { - return getTx(txId).map(Tx::getUnlockBlockHeight); - } - - public boolean isLockTimeOverForUnlockTxOutput(TxOutput unlockTxOutput) { - checkArgument(isUnlockOutput(unlockTxOutput), "txOutput must be of type UNLOCK"); - return getUnlockBlockHeight(unlockTxOutput.getTxId()) - .map(unlockBlockHeight -> BondConsensus.isLockTimeOver(unlockBlockHeight, getChainHeight())) - .orElse(false); - } - - // We don't care here about the unspent state - public Stream getUnlockedTxOutputsStream() { - return getTxOutputsByTxOutputType(TxOutputType.UNLOCK_OUTPUT).stream() - .filter(txOutput -> !isConfiscatedUnlockTxOutput(txOutput.getTxId())) - .filter(this::isLockTimeOverForUnlockTxOutput); - } - - public long getTotalAmountOfUnLockedTxOutputs() { - return getUnlockedTxOutputsStream() - .mapToLong(TxOutput::getValue) - .sum(); - } - - public long getTotalAmountOfConfiscatedTxOutputs() { - return daoState.getConfiscatedLockupTxList() - .stream() - .flatMap(e -> getTx(e).stream()) - .mapToLong(tx -> tx.getLockupOutput().getValue()) - .sum(); - } - - public long getTotalAmountOfInvalidatedBsq() { - return getUnorderedTxStream().mapToLong(Tx::getInvalidatedBsq).sum(); - } - - // Contains burnt fee and invalidated bsq due invalid txs - public long getTotalAmountOfBurntBsq() { - return getUnorderedTxStream().mapToLong(Tx::getBurntBsq).sum(); - } - - // Confiscate bond - public void confiscateBond(String lockupTxId) { - Optional optionalTxOutput = getLockupTxOutput(lockupTxId); - if (optionalTxOutput.isPresent()) { - TxOutput lockupTxOutput = optionalTxOutput.get(); - if (isUnspent(lockupTxOutput.getKey())) { - log.warn("confiscateBond: lockupTxOutput {} is still unspent so we can confiscate it.", lockupTxOutput.getKey()); - doConfiscateBond(lockupTxId); - } else { - // We lookup for the unlock tx which need to be still in unlocking state - Optional optionalSpentInfo = getSpentInfo(lockupTxOutput); - checkArgument(optionalSpentInfo.isPresent(), "optionalSpentInfo must be present"); - String unlockTxId = optionalSpentInfo.get().getTxId(); - if (isUnlockingAndUnspent(unlockTxId)) { - // We found the unlock tx is still not spend - log.warn("confiscateBond: lockupTxOutput {} is still unspent so we can We confiscate it.", lockupTxOutput.getKey()); - doConfiscateBond(lockupTxId); - } else { - // We could be more radical here and confiscate the output if it is unspent but lock time is over, - // but it's probably better to stick to the rules that confiscation can only happen before lock time - // is over. - log.warn("We could not confiscate the bond because the unlock tx was already spent or lock time " + - "has exceeded. unlockTxId={}", unlockTxId); - } - } - } else { - log.warn("No lockupTxOutput found for lockupTxId {}", lockupTxId); - } - } - - private void doConfiscateBond(String lockupTxId) { - assertDaoStateChange(); - log.warn("TxId {} added to confiscatedLockupTxIdList.", lockupTxId); - daoState.getConfiscatedLockupTxList().add(lockupTxId); - } - - public boolean isConfiscatedOutput(TxOutputKey txOutputKey) { - if (isLockupOutput(txOutputKey)) - return isConfiscatedLockupTxOutput(txOutputKey.getTxId()); - else if (isUnspentUnlockOutput(txOutputKey)) - return isConfiscatedUnlockTxOutput(txOutputKey.getTxId()); - return false; - } - - public boolean isConfiscatedLockupTxOutput(String lockupTxId) { - return daoState.getConfiscatedLockupTxList().contains(lockupTxId); - } - - public boolean isConfiscatedUnlockTxOutput(String unlockTxId) { - return getLockupTxFromUnlockTxId(unlockTxId). - map(lockupTx -> isConfiscatedLockupTxOutput(lockupTx.getId())). - orElse(false); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Param - /////////////////////////////////////////////////////////////////////////////////////////// - - public void setNewParam(int blockHeight, Param param, String paramValue) { - assertDaoStateChange(); - List paramChangeList = daoState.getParamChangeList(); - getStartHeightOfNextCycle(blockHeight) - .ifPresent(heightOfNewCycle -> { - ParamChange paramChange = new ParamChange(param.name(), paramValue, heightOfNewCycle); - paramChangeList.add(paramChange); - // Addition with older height should not be possible but to ensure correct sorting lets run a sort. - paramChangeList.sort(Comparator.comparingInt(ParamChange::getActivationHeight)); - }); - } - - public String getParamValue(Param param, int blockHeight) { - List paramChangeList = new ArrayList<>(daoState.getParamChangeList()); - if (!paramChangeList.isEmpty()) { - // List is sorted by height, we start from latest entries to find most recent entry. - for (int i = paramChangeList.size() - 1; i >= 0; i--) { - ParamChange paramChange = paramChangeList.get(i); - if (paramChange.getParamName().equals(param.name()) && - blockHeight >= paramChange.getActivationHeight()) { - return paramChange.getValue(); - } - } - } - - // If no value found we use default values - return param.getDefaultValue(); - } - - public List getParamChangeList(Param param) { - List values = new ArrayList<>(); - for (ParamChange paramChange : daoState.getParamChangeList()) { - if (paramChange.getParamName().equals(param.name())) { - values.add(getParamValueAsCoin(param, paramChange.getValue())); - } - } - return values; - } - - public Coin getParamValueAsCoin(Param param, String paramValue) { - return bsqFormatter.parseParamValueToCoin(param, paramValue); - } - - public double getParamValueAsPercentDouble(String paramValue) { - return ParsingUtils.parsePercentStringToDouble(paramValue); - } - - public int getParamValueAsBlock(String paramValue) { - return Integer.parseInt(paramValue); - } - - public Coin getParamValueAsCoin(Param param, int blockHeight) { - return getParamValueAsCoin(param, getParamValue(param, blockHeight)); - } - - public double getParamValueAsPercentDouble(Param param, int blockHeight) { - return getParamValueAsPercentDouble(getParamValue(param, blockHeight)); - } - - public int getParamValueAsBlock(Param param, int blockHeight) { - return getParamValueAsBlock(getParamValue(param, blockHeight)); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // SpentInfo - /////////////////////////////////////////////////////////////////////////////////////////// - - public void setSpentInfo(TxOutputKey txOutputKey, SpentInfo spentInfo) { - assertDaoStateChange(); - daoState.getSpentInfoMap().put(txOutputKey, spentInfo); - } - - public Optional getSpentInfo(TxOutput txOutput) { - return Optional.ofNullable(daoState.getSpentInfoMap().getOrDefault(txOutput.getKey(), null)); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Vote result data - /////////////////////////////////////////////////////////////////////////////////////////// - - public List getEvaluatedProposalList() { - return daoState.getEvaluatedProposalList(); - } - - public void addEvaluatedProposalSet(Set evaluatedProposals) { - assertDaoStateChange(); - - evaluatedProposals.stream() - .filter(e -> !daoState.getEvaluatedProposalList().contains(e)) - .forEach(daoState.getEvaluatedProposalList()::add); - - // We need deterministic order for the hash chain - daoState.getEvaluatedProposalList().sort(Comparator.comparing(EvaluatedProposal::getProposalTxId)); - } - - public List getDecryptedBallotsWithMeritsList() { - return daoState.getDecryptedBallotsWithMeritsList(); - } - - public void addDecryptedBallotsWithMeritsSet(Set decryptedBallotsWithMeritsSet) { - assertDaoStateChange(); - - decryptedBallotsWithMeritsSet.stream() - .filter(e -> !daoState.getDecryptedBallotsWithMeritsList().contains(e)) - .forEach(daoState.getDecryptedBallotsWithMeritsList()::add); - - // We need deterministic order for the hash chain - daoState.getDecryptedBallotsWithMeritsList().sort(Comparator.comparing(DecryptedBallotsWithMerits::getBlindVoteTxId)); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Asset listing fee - /////////////////////////////////////////////////////////////////////////////////////////// - - public Set getAssetListingFeeOpReturnTxOutputs() { - return getTxOutputsByTxOutputType(TxOutputType.ASSET_LISTING_FEE_OP_RETURN_OUTPUT); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Proof of burn - /////////////////////////////////////////////////////////////////////////////////////////// - - public Set getProofOfBurnOpReturnTxOutputs() { - return getTxOutputsByTxOutputType(TxOutputType.PROOF_OF_BURN_OP_RETURN_OUTPUT); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Listeners - /////////////////////////////////////////////////////////////////////////////////////////// - - public void addDaoStateListener(DaoStateListener listener) { - daoStateListeners.add(listener); - } - - public void removeDaoStateListener(DaoStateListener listener) { - daoStateListeners.remove(listener); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Utils - /////////////////////////////////////////////////////////////////////////////////////////// - - public String daoStateToString() { - return daoState.toString(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void assertDaoStateChange() { - if (!allowDaoStateChange) - throw new RuntimeException("We got a call which would change the daoState outside of the allowed event phase"); - } -} - diff --git a/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java b/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java deleted file mode 100644 index 1752119b26..0000000000 --- a/core/src/main/java/bisq/core/dao/state/DaoStateSnapshotService.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.state; - -import bisq.core.dao.monitoring.DaoStateMonitoringService; -import bisq.core.dao.monitoring.model.DaoStateHash; -import bisq.core.dao.state.model.DaoState; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.storage.DaoStateStorageService; - -import bisq.common.config.Config; - -import javax.inject.Inject; -import javax.inject.Named; - -import com.google.common.annotations.VisibleForTesting; - -import java.io.File; -import java.io.IOException; - -import java.util.LinkedList; - -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -/** - * Manages periodical snapshots of the DaoState. - * At startup we apply a snapshot if available. - * At each trigger height we persist the latest snapshot candidate and set the current daoState as new candidate. - * The trigger height is determined by the SNAPSHOT_GRID. The latest persisted snapshot is min. the height of - * SNAPSHOT_GRID old not less than 2 times the SNAPSHOT_GRID old. - */ -@Slf4j -public class DaoStateSnapshotService { - private static final int SNAPSHOT_GRID = 20; - - private final DaoStateService daoStateService; - private final GenesisTxInfo genesisTxInfo; - private final DaoStateStorageService daoStateStorageService; - private final DaoStateMonitoringService daoStateMonitoringService; - private final File storageDir; - - private DaoState daoStateSnapshotCandidate; - private LinkedList daoStateHashChainSnapshotCandidate = new LinkedList<>(); - private int chainHeightOfLastApplySnapshot; - @Setter - @Nullable - private Runnable daoRequiresRestartHandler; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public DaoStateSnapshotService(DaoStateService daoStateService, - GenesisTxInfo genesisTxInfo, - DaoStateStorageService daoStateStorageService, - DaoStateMonitoringService daoStateMonitoringService, - @Named(Config.STORAGE_DIR) File storageDir) { - this.daoStateService = daoStateService; - this.genesisTxInfo = genesisTxInfo; - this.daoStateStorageService = daoStateStorageService; - this.daoStateMonitoringService = daoStateMonitoringService; - this.storageDir = storageDir; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - // We do not use DaoStateListener.onDaoStateChanged but let the DaoEventCoordinator call maybeCreateSnapshot to ensure the - // correct order of execution. - // We need to process during batch processing as well to write snapshots during that process. - public void maybeCreateSnapshot(Block block) { - int chainHeight = block.getHeight(); - - // Either we don't have a snapshot candidate yet, or if we have one the height at that snapshot candidate must be - // different to our current height. - boolean noSnapshotCandidateOrDifferentHeight = daoStateSnapshotCandidate == null || - daoStateSnapshotCandidate.getChainHeight() != chainHeight; - if (isSnapshotHeight(chainHeight) && - !daoStateService.getBlocks().isEmpty() && - isValidHeight(daoStateService.getBlocks().getLast().getHeight()) && - noSnapshotCandidateOrDifferentHeight) { - // At trigger event we store the latest snapshotCandidate to disc - long ts = System.currentTimeMillis(); - if (daoStateSnapshotCandidate != null) { - // Serialisation happens on the userThread so we do not need to clone the data. Write to disk happens - // in a thread but does not interfere with our objects as they got already serialized when passed to the - // write thread. We use requestPersistence so we do not write immediately but at next scheduled interval. - // This avoids frequent write at dao sync and better performance. - daoStateStorageService.requestPersistence(daoStateSnapshotCandidate, daoStateHashChainSnapshotCandidate); - log.info("Serializing snapshotCandidate for writing to Disc with height {} at height {} took {} ms", - daoStateSnapshotCandidate.getChainHeight(), chainHeight, System.currentTimeMillis() - ts); - } - - ts = System.currentTimeMillis(); - // Now we clone and keep it in memory for the next trigger event - daoStateSnapshotCandidate = daoStateService.getClone(); - daoStateHashChainSnapshotCandidate = new LinkedList<>(daoStateMonitoringService.getDaoStateHashChain()); - - log.debug("Cloned new snapshotCandidate at height {} took {} ms", chainHeight, System.currentTimeMillis() - ts); - } - } - - public void applySnapshot(boolean fromReorg) { - DaoState persistedBsqState = daoStateStorageService.getPersistedBsqState(); - LinkedList persistedDaoStateHashChain = daoStateStorageService.getPersistedDaoStateHashChain(); - if (persistedBsqState != null) { - LinkedList blocks = persistedBsqState.getBlocks(); - int chainHeightOfPersisted = persistedBsqState.getChainHeight(); - if (!blocks.isEmpty()) { - int heightOfLastBlock = blocks.getLast().getHeight(); - log.debug("applySnapshot from persistedBsqState daoState with height of last block {}", heightOfLastBlock); - if (isValidHeight(heightOfLastBlock)) { - if (chainHeightOfLastApplySnapshot != chainHeightOfPersisted) { - chainHeightOfLastApplySnapshot = chainHeightOfPersisted; - daoStateService.applySnapshot(persistedBsqState); - daoStateMonitoringService.applySnapshot(persistedDaoStateHashChain); - } else { - // The reorg might have been caused by the previous parsing which might contains a range of - // blocks. - log.warn("We applied already a snapshot with chainHeight {}. " + - "We remove all dao store files and shutdown. After a restart resource files will " + - "be applied if available.", - chainHeightOfLastApplySnapshot); - resyncDaoStateFromResources(); - } - } - } else if (fromReorg) { - log.info("We got a reorg and we want to apply the snapshot but it is empty. " + - "That is expected in the first blocks until the first snapshot has been created. " + - "We remove all dao store files and shutdown. " + - "After a restart resource files will be applied if available."); - resyncDaoStateFromResources(); - } - } else { - log.info("Try to apply snapshot but no stored snapshot available. That is expected at first blocks."); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private boolean isValidHeight(int heightOfLastBlock) { - return heightOfLastBlock >= genesisTxInfo.getGenesisBlockHeight(); - } - - private void resyncDaoStateFromResources() { - log.info("resyncDaoStateFromResources called"); - try { - daoStateStorageService.resyncDaoStateFromResources(storageDir); - - if (daoRequiresRestartHandler != null) { - daoRequiresRestartHandler.run(); - } - } catch (IOException e) { - log.error("Error at resyncDaoStateFromResources: {}", e.toString()); - } - } - - @VisibleForTesting - int getSnapshotHeight(int genesisHeight, int height, int grid) { - return Math.round(Math.max(genesisHeight + 3 * grid, height) / grid) * grid - grid; - } - - @VisibleForTesting - boolean isSnapshotHeight(int genesisHeight, int height, int grid) { - return height % grid == 0 && height >= getSnapshotHeight(genesisHeight, height, grid); - } - - private boolean isSnapshotHeight(int height) { - return isSnapshotHeight(genesisTxInfo.getGenesisBlockHeight(), height, SNAPSHOT_GRID); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java b/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java deleted file mode 100644 index f1c696ca41..0000000000 --- a/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state; - -import bisq.common.config.BaseCurrencyNetwork; -import bisq.common.config.Config; - -import org.bitcoinj.core.Coin; - -import javax.inject.Inject; -import javax.inject.Named; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - - -/** - * Encapsulate the genesis txId and height. - * As we don't persist those data we don't want to have it in the DaoState directly and moved it to a separate class. - * Using a static final field in DaoState would not work well as we want to support that the data can be overwritten by - * program arguments for development testing and therefore it is set in the constructor via Guice. - */ -@SuppressWarnings("SpellCheckingInspection") -@Slf4j -public class GenesisTxInfo { - - /////////////////////////////////////////////////////////////////////////////////////////// - // Static - /////////////////////////////////////////////////////////////////////////////////////////// - - private static final String MAINNET_GENESIS_TX_ID = "4b5417ec5ab6112bedf539c3b4f5a806ed539542d8b717e1c4470aa3180edce5"; - private static final int MAINNET_GENESIS_BLOCK_HEIGHT = 571747; // 2019-04-15 - private static final Coin MAINNET_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("3.65748"); - - private static final String TESTNET_GENESIS_TX_ID = "f35b62930b16a680ba6bc8ba8fecc4f1db65c5635b5a4b4b0445544649acf4f6"; - private static final int TESTNET_GENESIS_BLOCK_HEIGHT = 1564395; // 2019-06-21 - private static final Coin TESTNET_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5"); // 2.5M BSQ / 2.50000000 BTC - - private static final String DAO_TESTNET_GENESIS_TX_ID = "cb316a186b9e88d1b8e1ce8dc79cc6a2080cc7bbc6df94f2be325d8253417af1"; - private static final int DAO_TESTNET_GENESIS_BLOCK_HEIGHT = 104; // 2019-02-19 - private static final Coin DAO_TESTNET_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5"); // 2.5M BSQ / 2.50000000 BTC - - private static final String DAO_BETANET_GENESIS_TX_ID = "0bd66d8ff26476b55dfaf2a5db0c659a5d8635566488244df25606db63a08bd9"; - private static final int DAO_BETANET_GENESIS_BLOCK_HEIGHT = 567405; // 2019-03-16 - private static final Coin DAO_BETANET_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("0.49998644"); // 499 986.44 BSQ / 0.49998644 BTC - - private static final String DAO_REGTEST_GENESIS_TX_ID = "d594ad0c5de53e261b5784e5eb2acec8b807c45b74450401f488d36b8acf2e14"; - private static final int DAO_REGTEST_GENESIS_BLOCK_HEIGHT = 104; // 2019-03-26 - private static final Coin DAO_REGTEST_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5"); // 2.5M BSQ / 2.50000000 BTC - - private static final String REGTEST_GENESIS_TX_ID = "30af0050040befd8af25068cc697e418e09c2d8ebd8d411d2240591b9ec203cf"; - private static final int REGTEST_GENESIS_BLOCK_HEIGHT = 111; - private static final Coin REGTEST_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5"); // 2.5M BSQ / 2.50000000 BTC - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Instance fields - /////////////////////////////////////////////////////////////////////////////////////////// - - // We cannot use a static final as we want to set the txId also via program argument for development and then it - // is getting passed via Guice in the constructor. - @Getter - private final String genesisTxId; - @Getter - private final int genesisBlockHeight; - @Getter - private final long genesisTotalSupply; - - // mainnet - // this tx has a lot of outputs - // https://blockchain.info/de/tx/ee921650ab3f978881b8fe291e0c025e0da2b7dc684003d7a03d9649dfee2e15 - // BLOCK_HEIGHT 411779 - // 411812 has 693 recursions - // block 376078 has 2843 recursions and caused once a StackOverflowError, a second run worked. Took 1,2 sec. - - - // BTC MAIN NET - // new: --genesisBlockHeight=524717 --genesisTxId=81855816eca165f17f0668898faa8724a105196e90ffc4993f4cac980176674e - // private static final String DEFAULT_GENESIS_TX_ID = "e5c8313c4144d219b5f6b2dacf1d36f2d43a9039bb2fcd1bd57f8352a9c9809a"; - // private static final int DEFAULT_GENESIS_BLOCK_HEIGHT = 477865; // 2017-07-28 - - - // private static final String DEFAULT_GENESIS_TX_ID = "--"; - // private static final int DEFAULT_GENESIS_BLOCK_HEIGHT = 499000; // recursive test 137298, 499000 dec 2017 - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public GenesisTxInfo(@Named(Config.GENESIS_TX_ID) String genesisTxId, - @Named(Config.GENESIS_BLOCK_HEIGHT) int genesisBlockHeight, - @Named(Config.GENESIS_TOTAL_SUPPLY) long genesisTotalSupply) { - BaseCurrencyNetwork baseCurrencyNetwork = Config.baseCurrencyNetwork(); - boolean isMainnet = baseCurrencyNetwork.isMainnet(); - boolean isTestnet = baseCurrencyNetwork.isTestnet(); - boolean isDaoTestNet = baseCurrencyNetwork.isDaoTestNet(); - boolean isDaoBetaNet = baseCurrencyNetwork.isDaoBetaNet(); - boolean isDaoRegTest = baseCurrencyNetwork.isDaoRegTest(); - boolean isRegtest = baseCurrencyNetwork.isStagenet(); - if (!genesisTxId.isEmpty()) { - this.genesisTxId = genesisTxId; - } else if (isMainnet) { - this.genesisTxId = MAINNET_GENESIS_TX_ID; - } else if (isTestnet) { - this.genesisTxId = TESTNET_GENESIS_TX_ID; - } else if (isDaoTestNet) { - this.genesisTxId = DAO_TESTNET_GENESIS_TX_ID; - } else if (isDaoBetaNet) { - this.genesisTxId = DAO_BETANET_GENESIS_TX_ID; - } else if (isDaoRegTest) { - this.genesisTxId = DAO_REGTEST_GENESIS_TX_ID; - } else if (isRegtest) { - this.genesisTxId = REGTEST_GENESIS_TX_ID; - } else { - this.genesisTxId = "genesisTxId is undefined"; - } - - if (genesisBlockHeight > -1) { - this.genesisBlockHeight = genesisBlockHeight; - } else if (isMainnet) { - this.genesisBlockHeight = MAINNET_GENESIS_BLOCK_HEIGHT; - } else if (isTestnet) { - this.genesisBlockHeight = TESTNET_GENESIS_BLOCK_HEIGHT; - } else if (isDaoTestNet) { - this.genesisBlockHeight = DAO_TESTNET_GENESIS_BLOCK_HEIGHT; - } else if (isDaoBetaNet) { - this.genesisBlockHeight = DAO_BETANET_GENESIS_BLOCK_HEIGHT; - } else if (isDaoRegTest) { - this.genesisBlockHeight = DAO_REGTEST_GENESIS_BLOCK_HEIGHT; - } else if (isRegtest) { - this.genesisBlockHeight = REGTEST_GENESIS_BLOCK_HEIGHT; - } else { - this.genesisBlockHeight = 0; - } - - if (genesisTotalSupply > -1) { - this.genesisTotalSupply = genesisTotalSupply; - } else if (isMainnet) { - this.genesisTotalSupply = MAINNET_GENESIS_TOTAL_SUPPLY.value; - } else if (isTestnet) { - this.genesisTotalSupply = TESTNET_GENESIS_TOTAL_SUPPLY.value; - } else if (isDaoTestNet) { - this.genesisTotalSupply = DAO_TESTNET_GENESIS_TOTAL_SUPPLY.value; - } else if (isDaoBetaNet) { - this.genesisTotalSupply = DAO_BETANET_GENESIS_TOTAL_SUPPLY.value; - } else if (isDaoRegTest) { - this.genesisTotalSupply = DAO_REGTEST_GENESIS_TOTAL_SUPPLY.value; - } else if (isRegtest) { - this.genesisTotalSupply = REGTEST_GENESIS_TOTAL_SUPPLY.value; - } else { - this.genesisTotalSupply = 0; - } - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/DaoState.java b/core/src/main/java/bisq/core/dao/state/model/DaoState.java deleted file mode 100644 index bb65f7b30d..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/DaoState.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model; - -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.blockchain.SpentInfo; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.blockchain.TxOutputKey; -import bisq.core.dao.state.model.governance.Cycle; -import bisq.core.dao.state.model.governance.DecryptedBallotsWithMerits; -import bisq.core.dao.state.model.governance.EvaluatedProposal; -import bisq.core.dao.state.model.governance.Issuance; -import bisq.core.dao.state.model.governance.ParamChange; - -import bisq.common.proto.persistable.PersistablePayload; -import bisq.common.util.JsonExclude; - -import com.google.protobuf.Message; - -import javax.inject.Inject; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import java.util.function.Function; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Root class for mutable state of the DAO. - * Holds both blockchain data as well as data derived from the governance process (voting). - *

- * One BSQ block with empty txs adds 152 bytes which results in about 8 MB/year - * - * For supporting the hashChain we need to ensure deterministic sorting behaviour of all collections so we use a - * TreeMap which is sorted by the key. - */ -@Slf4j -public class DaoState implements PersistablePayload { - - /////////////////////////////////////////////////////////////////////////////////////////// - // Static - /////////////////////////////////////////////////////////////////////////////////////////// - - public static DaoState getClone(DaoState daoState) { - return DaoState.fromProto(daoState.getBsqStateBuilder().build()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Fields - /////////////////////////////////////////////////////////////////////////////////////////// - - @Getter - private int chainHeight; // Is set initially to genesis height - @Getter - private final LinkedList blocks; - @Getter - private final LinkedList 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 unspentTxOutputMap; - @Getter - private final TreeMap spentInfoMap; - - // These maps are related to state change triggered by voting - @Getter - private final List confiscatedLockupTxList; - @Getter - private final TreeMap issuanceMap; // key is txId - @Getter - private final List paramChangeList; - - // Vote result data - // All evaluated proposals which get added at the result phase - @Getter - private final List evaluatedProposalList; - // All voting data which get added at the result phase - @Getter - private final List decryptedBallotsWithMeritsList; - - // Transient data used only as an index - must be kept in sync with the block list - @JsonExclude - private transient final Map txCache; // key is txId - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public DaoState() { - this(0, - new LinkedList<>(), - new LinkedList<>(), - new TreeMap<>(), - new TreeMap<>(), - new ArrayList<>(), - new TreeMap<>(), - new ArrayList<>(), - new ArrayList<>(), - new ArrayList<>() - ); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private DaoState(int chainHeight, - LinkedList blocks, - LinkedList cycles, - TreeMap unspentTxOutputMap, - TreeMap spentInfoMap, - List confiscatedLockupTxList, - TreeMap issuanceMap, - List paramChangeList, - List evaluatedProposalList, - List decryptedBallotsWithMeritsList) { - this.chainHeight = chainHeight; - this.blocks = blocks; - this.cycles = cycles; - - this.unspentTxOutputMap = unspentTxOutputMap; - this.spentInfoMap = spentInfoMap; - - this.confiscatedLockupTxList = confiscatedLockupTxList; - this.issuanceMap = issuanceMap; - this.paramChangeList = paramChangeList; - this.evaluatedProposalList = evaluatedProposalList; - this.decryptedBallotsWithMeritsList = decryptedBallotsWithMeritsList; - - txCache = blocks.stream() - .flatMap(block -> block.getTxs().stream()) - .collect(Collectors.toMap(Tx::getId, Function.identity(), (x, y) -> x, HashMap::new)); - } - - @Override - public Message toProtoMessage() { - return getBsqStateBuilder().build(); - } - - public protobuf.DaoState.Builder getBsqStateBuilder() { - return getBsqStateBuilderExcludingBlocks().addAllBlocks(blocks.stream() - .map(Block::toProtoMessage) - .collect(Collectors.toList())); - } - - private protobuf.DaoState.Builder getBsqStateBuilderExcludingBlocks() { - protobuf.DaoState.Builder builder = protobuf.DaoState.newBuilder(); - builder.setChainHeight(chainHeight) - .addAllCycles(cycles.stream().map(Cycle::toProtoMessage).collect(Collectors.toList())) - .putAllUnspentTxOutputMap(unspentTxOutputMap.entrySet().stream() - .collect(Collectors.toMap(e -> e.getKey().toString(), e -> e.getValue().toProtoMessage()))) - .putAllSpentInfoMap(spentInfoMap.entrySet().stream() - .collect(Collectors.toMap(e -> e.getKey().toString(), entry -> entry.getValue().toProtoMessage()))) - .addAllConfiscatedLockupTxList(confiscatedLockupTxList) - .putAllIssuanceMap(issuanceMap.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().toProtoMessage()))) - .addAllParamChangeList(paramChangeList.stream().map(ParamChange::toProtoMessage).collect(Collectors.toList())) - .addAllEvaluatedProposalList(evaluatedProposalList.stream().map(EvaluatedProposal::toProtoMessage).collect(Collectors.toList())) - .addAllDecryptedBallotsWithMeritsList(decryptedBallotsWithMeritsList.stream().map(DecryptedBallotsWithMerits::toProtoMessage).collect(Collectors.toList())); - return builder; - } - - public static DaoState fromProto(protobuf.DaoState proto) { - LinkedList blocks = proto.getBlocksList().stream() - .map(Block::fromProto) - .collect(Collectors.toCollection(LinkedList::new)); - LinkedList cycles = proto.getCyclesList().stream() - .map(Cycle::fromProto).collect(Collectors.toCollection(LinkedList::new)); - TreeMap unspentTxOutputMap = new TreeMap<>(proto.getUnspentTxOutputMapMap().entrySet().stream() - .collect(Collectors.toMap(e -> TxOutputKey.getKeyFromString(e.getKey()), e -> TxOutput.fromProto(e.getValue())))); - TreeMap spentInfoMap = new TreeMap<>(proto.getSpentInfoMapMap().entrySet().stream() - .collect(Collectors.toMap(e -> TxOutputKey.getKeyFromString(e.getKey()), e -> SpentInfo.fromProto(e.getValue())))); - List confiscatedLockupTxList = new ArrayList<>(proto.getConfiscatedLockupTxListList()); - TreeMap issuanceMap = new TreeMap<>(proto.getIssuanceMapMap().entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, e -> Issuance.fromProto(e.getValue())))); - List paramChangeList = proto.getParamChangeListList().stream() - .map(ParamChange::fromProto).collect(Collectors.toCollection(ArrayList::new)); - List evaluatedProposalList = proto.getEvaluatedProposalListList().stream() - .map(EvaluatedProposal::fromProto).collect(Collectors.toCollection(ArrayList::new)); - List decryptedBallotsWithMeritsList = proto.getDecryptedBallotsWithMeritsListList().stream() - .map(DecryptedBallotsWithMerits::fromProto).collect(Collectors.toCollection(ArrayList::new)); - return new DaoState(proto.getChainHeight(), - blocks, - cycles, - unspentTxOutputMap, - spentInfoMap, - confiscatedLockupTxList, - issuanceMap, - paramChangeList, - evaluatedProposalList, - decryptedBallotsWithMeritsList); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void setChainHeight(int chainHeight) { - this.chainHeight = chainHeight; - } - - public byte[] getSerializedStateForHashChain() { - // We only add last block as for the hash chain we include the prev. hash in the new hash so the state of the - // earlier blocks is included in the hash. The past blocks cannot be changed anyway when a new block arrives. - // Reorgs are handled by rebuilding the hash chain from last snapshot. - // Using the full blocks list becomes quite heavy. 7000 blocks are - // about 1.4 MB and creating the hash takes 30 sec. By using just the last block we reduce the time to 7 sec. - return getBsqStateBuilderExcludingBlocks().addBlocks(getBlocks().getLast().toProtoMessage()).build().toByteArray(); - } - - public void addToTxCache(Tx tx) { - // We shouldn't get duplicate txIds, but use putIfAbsent instead of put for consistency with the map merge - // function used in the constructor to initialise txCache (and to exactly match the pre-caching behaviour). - txCache.putIfAbsent(tx.getId(), tx); - } - - public void setTxCache(Map txCache) { - this.txCache.clear(); - this.txCache.putAll(txCache); - } - - public Map getTxCache() { - return Collections.unmodifiableMap(txCache); - } - - @Override - public String toString() { - return "DaoState{" + - "\n chainHeight=" + chainHeight + - ",\n blocks=" + blocks + - ",\n cycles=" + cycles + - ",\n unspentTxOutputMap=" + unspentTxOutputMap + - ",\n spentInfoMap=" + spentInfoMap + - ",\n confiscatedLockupTxList=" + confiscatedLockupTxList + - ",\n issuanceMap=" + issuanceMap + - ",\n paramChangeList=" + paramChangeList + - ",\n evaluatedProposalList=" + evaluatedProposalList + - ",\n decryptedBallotsWithMeritsList=" + decryptedBallotsWithMeritsList + - ",\n txCache=" + txCache + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/ImmutableDaoStateModel.java b/core/src/main/java/bisq/core/dao/state/model/ImmutableDaoStateModel.java deleted file mode 100644 index 010873ad6f..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/ImmutableDaoStateModel.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model; - -/** - * Marker interface for objects which are stored in the daoState and therefore they need to be immutable. - */ -public interface ImmutableDaoStateModel { -} diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/BaseBlock.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/BaseBlock.java deleted file mode 100644 index d993cdfaba..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/BaseBlock.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.blockchain; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import java.util.Optional; - -import lombok.Data; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** - * The base class for RawBlock and Block containing the common immutable bitcoin - * blockchain specific data. - */ -@Immutable -@Data -public abstract class BaseBlock implements ImmutableDaoStateModel { - protected final int height; - protected final long time; // in ms - protected final String hash; - @Nullable // in case of first block in the blockchain - protected final String previousBlockHash; - - protected BaseBlock(int height, long time, String hash, @SuppressWarnings("NullableProblems") String previousBlockHash) { - this.height = height; - this.time = time; - this.hash = hash; - this.previousBlockHash = previousBlockHash; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - protected protobuf.BaseBlock.Builder getBaseBlockBuilder() { - protobuf.BaseBlock.Builder builder = protobuf.BaseBlock.newBuilder() - .setHeight(height) - .setTime(time) - .setHash(hash); - Optional.ofNullable(previousBlockHash).ifPresent(builder::setPreviousBlockHash); - return builder; - - } - - @Override - public String toString() { - return "BaseBlock{" + - "\n height=" + height + - ",\n time=" + time + - ",\n hash='" + hash + '\'' + - ",\n previousBlockHash='" + previousBlockHash + '\'' + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/BaseTx.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/BaseTx.java deleted file mode 100644 index 19c9754808..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/BaseTx.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.blockchain; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import com.google.common.collect.ImmutableList; - -import java.util.stream.Collectors; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -/** - * The base class for the Tx classes with all common immutable data fields. - * - * TxOutputs are not added here as the sub classes use different data types. - * As not all subclasses implement PersistablePayload we leave it to the sub classes to implement the interface. - * A getBaseTxBuilder method though is available. - */ -@Immutable -@Slf4j -@Getter -@EqualsAndHashCode -public abstract class BaseTx implements ImmutableDaoStateModel { - protected final String txVersion; - protected final String id; - protected final int blockHeight; - protected final String blockHash; - protected final long time; - protected final ImmutableList txInputs; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - protected BaseTx(String txVersion, - String id, - int blockHeight, - String blockHash, - long time, - ImmutableList txInputs) { - this.txVersion = txVersion; - this.id = id; - this.blockHeight = blockHeight; - this.blockHash = blockHash; - this.time = time; - this.txInputs = txInputs; - } - - protected protobuf.BaseTx.Builder getBaseTxBuilder() { - return protobuf.BaseTx.newBuilder() - .setTxVersion(txVersion) - .setId(id) - .setBlockHeight(blockHeight) - .setBlockHash(blockHash) - .setTime(time) - .addAllTxInputs(txInputs.stream() - .map(TxInput::toProtoMessage) - .collect(Collectors.toList())); - } - - @Override - public String toString() { - return "BaseTx{" + - "\n txVersion='" + txVersion + '\'' + - ",\n id='" + id + '\'' + - ",\n blockHeight=" + blockHeight + - ",\n blockHash='" + blockHash + '\'' + - ",\n time=" + time + - ",\n txInputs=" + txInputs + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/BaseTxOutput.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/BaseTxOutput.java deleted file mode 100644 index 2e0cc6a2c3..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/BaseTxOutput.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.blockchain; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.util.JsonExclude; -import bisq.common.util.Utilities; - -import com.google.protobuf.ByteString; - -import java.util.Optional; - -import lombok.Data; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** - * Base class for TxOutput classes containing the immutable bitcoin specific blockchain data. - */ -@Slf4j -@Immutable -@Data -public abstract class BaseTxOutput implements ImmutableDaoStateModel { - protected final int index; - protected final long value; - protected final String txId; - - // Before v0.9.6 it was only set if dumpBlockchainData was set to true but we changed that with 0.9.6 - // so that it is always set. We still need to support it because of backward compatibility. - @Nullable - protected final PubKeyScript pubKeyScript; // Has about 50 bytes, total size of TxOutput is about 300 bytes. - @Nullable - protected final String address; - @Nullable - @JsonExclude - protected final byte[] opReturnData; - protected final int blockHeight; - - protected BaseTxOutput(int index, - long value, - String txId, - @Nullable PubKeyScript pubKeyScript, - @Nullable String address, - @Nullable byte[] opReturnData, - int blockHeight) { - this.index = index; - this.value = value; - this.txId = txId; - this.pubKeyScript = pubKeyScript; - this.address = address; - this.opReturnData = opReturnData; - this.blockHeight = blockHeight; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - protected protobuf.BaseTxOutput.Builder getRawTxOutputBuilder() { - final protobuf.BaseTxOutput.Builder builder = protobuf.BaseTxOutput.newBuilder() - .setIndex(index) - .setValue(value) - .setTxId(txId) - .setBlockHeight(blockHeight); - - Optional.ofNullable(pubKeyScript).ifPresent(e -> builder.setPubKeyScript(pubKeyScript.toProtoMessage())); - Optional.ofNullable(address).ifPresent(e -> builder.setAddress(address)); - Optional.ofNullable(opReturnData).ifPresent(e -> builder.setOpReturnData(ByteString.copyFrom(opReturnData))); - - return builder; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Util - /////////////////////////////////////////////////////////////////////////////////////////// - - public TxOutputKey getKey() { - return new TxOutputKey(txId, index); - } - - @Override - public String toString() { - return "BaseTxOutput{" + - "\n index=" + index + - ",\n value=" + value + - ",\n txId='" + txId + '\'' + - ",\n pubKeyScript=" + pubKeyScript + - ",\n address='" + address + '\'' + - ",\n opReturnData=" + Utilities.bytesAsHexString(opReturnData) + - ",\n blockHeight=" + blockHeight + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/Block.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/Block.java deleted file mode 100644 index e438197e93..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/Block.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.blockchain; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.persistable.PersistablePayload; - -import com.google.common.collect.ImmutableList; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.EqualsAndHashCode; - -/** - * The Block which gets persisted in the DaoState. During parsing transactions can be - * added to the txs list, therefore it is not an immutable list. - * - * It is difficult to make the block immutable by using the same pattern we use with Tx or TxOutput because we add the - * block at the beginning of the parsing to the daoState and add transactions during parsing. We need to have the state - * updated during parsing. If we would set then after the parsing the immutable block we might have inconsistent data. - * There might be a way to do it but it comes with high complexity and risks so for now we prefer to have that known - * issue with not being fully immutable at that level. - * - * An empty block (no BSQ txs) has 146 bytes in Protobuffer serialized form. - * - */ -@EqualsAndHashCode(callSuper = true) -public final class Block extends BaseBlock implements PersistablePayload, ImmutableDaoStateModel { - // We do not expose txs with a Lombok getter. We cannot make it immutable as we add transactions during parsing. - private final List txs; - - public Block(int height, long time, String hash, String previousBlockHash) { - this(height, time, hash, previousBlockHash, new ArrayList<>()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private Block(int height, - long time, - String hash, - String previousBlockHash, - List txs) { - super(height, - time, - hash, - previousBlockHash); - this.txs = txs; - } - - - @Override - public protobuf.BaseBlock toProtoMessage() { - protobuf.Block.Builder builder = protobuf.Block.newBuilder() - .addAllTxs(txs.stream() - .map(Tx::toProtoMessage) - .collect(Collectors.toList())); - return getBaseBlockBuilder().setBlock(builder).build(); - } - - public static Block fromProto(protobuf.BaseBlock proto) { - protobuf.Block blockProto = proto.getBlock(); - ImmutableList txs = blockProto.getTxsList().isEmpty() ? - ImmutableList.copyOf(new ArrayList<>()) : - ImmutableList.copyOf(blockProto.getTxsList().stream() - .map(Tx::fromProto) - .collect(Collectors.toList())); - return new Block(proto.getHeight(), - proto.getTime(), - proto.getHash(), - proto.getPreviousBlockHash(), - txs); - } - - public void addTx(Tx tx) { - txs.add(tx); - } - - // We want to guarantee that no client can modify the list. We use unmodifiableList and not ImmutableList as - // we want that clients reflect any change to the source list. Also ImmutableList is more expensive as it - // creates a copy. - public List getTxs() { - return Collections.unmodifiableList(txs); - } - - @Override - public String toString() { - return "Block{" + - "\n txs=" + txs + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/OpReturnType.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/OpReturnType.java deleted file mode 100644 index 812288a684..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/OpReturnType.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.blockchain; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import java.util.Arrays; -import java.util.Optional; - -import lombok.Getter; - -import javax.annotation.concurrent.Immutable; - -/** - * Provides byte constants for distinguishing the type of a DAO transaction used in the OP_RETURN data. - */ -@Immutable -public enum OpReturnType implements ImmutableDaoStateModel { - UNDEFINED((byte) 0x00), - PROPOSAL((byte) 0x10), - COMPENSATION_REQUEST((byte) 0x11), - REIMBURSEMENT_REQUEST((byte) 0x12), - BLIND_VOTE((byte) 0x13), - VOTE_REVEAL((byte) 0x14), - LOCKUP((byte) 0x15), - ASSET_LISTING_FEE((byte) 0x16), - PROOF_OF_BURN((byte) 0x17); - - @Getter - private byte type; - - OpReturnType(byte type) { - this.type = type; - } - - public static Optional getOpReturnType(byte type) { - return Arrays.stream(OpReturnType.values()) - .filter(opReturnType -> opReturnType.type == type) - .map(Optional::of) - .findAny() - .orElse(Optional.empty()); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/PubKeyScript.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/PubKeyScript.java deleted file mode 100644 index adde8ae8d9..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/PubKeyScript.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.blockchain; - -import bisq.core.dao.node.full.rpc.dto.DtoPubKeyScript; -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.persistable.PersistablePayload; - -import com.google.common.collect.ImmutableList; - -import java.util.Objects; -import java.util.Optional; - -import lombok.AllArgsConstructor; -import lombok.Value; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -@Immutable -@Value -@AllArgsConstructor -public class PubKeyScript implements PersistablePayload, ImmutableDaoStateModel { - private final int reqSigs; - private final ScriptType scriptType; - @Nullable - private final ImmutableList addresses; - private final String asm; - private final String hex; - - public PubKeyScript(DtoPubKeyScript scriptPubKey) { - this(scriptPubKey.getReqSigs() != null ? scriptPubKey.getReqSigs() : 0, - scriptPubKey.getType(), - scriptPubKey.getAddresses() != null ? ImmutableList.copyOf(scriptPubKey.getAddresses()) : null, - scriptPubKey.getAsm(), - scriptPubKey.getHex()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public protobuf.PubKeyScript toProtoMessage() { - final protobuf.PubKeyScript.Builder builder = protobuf.PubKeyScript.newBuilder() - .setReqSigs(reqSigs) - .setScriptType(scriptType.toProtoMessage()) - .setAsm(asm) - .setHex(hex); - Optional.ofNullable(addresses).ifPresent(builder::addAllAddresses); - return builder.build(); - } - - public static PubKeyScript fromProto(protobuf.PubKeyScript proto) { - return new PubKeyScript(proto.getReqSigs(), - ScriptType.fromProto(proto.getScriptType()), - proto.getAddressesList().isEmpty() ? null : ImmutableList.copyOf(proto.getAddressesList()), - proto.getAsm(), - proto.getHex()); - } - - // 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 - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof PubKeyScript)) return false; - if (!super.equals(o)) return false; - PubKeyScript that = (PubKeyScript) o; - return reqSigs == that.reqSigs && - scriptType.name().equals(that.scriptType.name()) && - Objects.equals(addresses, that.addresses) && - Objects.equals(asm, that.asm) && - Objects.equals(hex, that.hex); - } - - @Override - public int hashCode() { - - return Objects.hash(super.hashCode(), reqSigs, scriptType.name(), addresses, asm, hex); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/ScriptType.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/ScriptType.java deleted file mode 100644 index 6c560f1b88..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/ScriptType.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.blockchain; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.ProtoUtil; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonValue; - -import lombok.AllArgsConstructor; -import lombok.ToString; - -import javax.annotation.concurrent.Immutable; - -@ToString -@Immutable -@AllArgsConstructor -@JsonInclude(JsonInclude.Include.NON_NULL) -@JsonIgnoreProperties(ignoreUnknown = true) -public enum ScriptType implements ImmutableDaoStateModel { - UNDEFINED("undefined"), - // https://github.com/bitcoin/bitcoin/blob/master/src/script/standard.cpp - NONSTANDARD("nonstandard"), - PUB_KEY("pubkey"), - PUB_KEY_HASH("pubkeyhash"), - SCRIPT_HASH("scripthash"), - MULTISIG("multisig"), - NULL_DATA("nulldata"), - WITNESS_V0_KEYHASH("witness_v0_keyhash"), - WITNESS_V0_SCRIPTHASH("witness_v0_scripthash"), - WITNESS_V1_TAPROOT("witness_v1_taproot"), - WITNESS_UNKNOWN("witness_unknown"); - - private final String name; - - @JsonValue - private String getName() { - return name; - } - - @JsonCreator - public static ScriptType forName(String name) { - if (name != null) { - for (ScriptType scriptType : ScriptType.values()) { - if (name.equals(scriptType.getName())) { - return scriptType; - } - } - } - throw new IllegalArgumentException("Expected the argument to be a valid 'bitcoind' script type, " - + "but was invalid/unsupported instead. Received scriptType=" + name); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public static ScriptType fromProto(protobuf.ScriptType scriptType) { - return ProtoUtil.enumFromProto(ScriptType.class, scriptType.name()); - } - - public protobuf.ScriptType toProtoMessage() { - return protobuf.ScriptType.valueOf(name()); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/SpentInfo.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/SpentInfo.java deleted file mode 100644 index fb01985ae2..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/SpentInfo.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.blockchain; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.persistable.PersistablePayload; - -import lombok.Value; - -import javax.annotation.concurrent.Immutable; - -@Immutable -@Value -public final class SpentInfo implements PersistablePayload, ImmutableDaoStateModel { - private final long blockHeight; - // Spending tx - private final String txId; - private final int inputIndex; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public static SpentInfo fromProto(protobuf.SpentInfo proto) { - return new SpentInfo(proto.getBlockHeight(), - proto.getTxId(), - proto.getInputIndex()); - } - - public protobuf.SpentInfo toProtoMessage() { - return protobuf.SpentInfo.newBuilder() - .setBlockHeight(blockHeight) - .setTxId(txId) - .setInputIndex(inputIndex) - .build(); - } - - @Override - public String toString() { - return "SpentInfo{" + - "\n blockHeight=" + blockHeight + - ",\n txId='" + txId + '\'' + - ",\n inputIndex=" + inputIndex + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/Tx.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/Tx.java deleted file mode 100644 index a90db56048..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/Tx.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.blockchain; - -import bisq.core.dao.node.parser.TempTx; -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.persistable.PersistablePayload; - -import com.google.common.collect.ImmutableList; - -import java.util.ArrayList; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -import lombok.Getter; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** - * Immutable class for a Bsq transaction. - * Gets persisted. - */ -@Immutable -@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) { - ImmutableList txOutputs = ImmutableList.copyOf(tempTx.getTempTxOutputs().stream() - .map(TxOutput::fromTempOutput) - .collect(Collectors.toList())); - - return new Tx(tempTx.getTxVersion(), - tempTx.getId(), - tempTx.getBlockHeight(), - tempTx.getBlockHash(), - tempTx.getTime(), - tempTx.getTxInputs(), - txOutputs, - tempTx.getTxType(), - tempTx.getBurntBsq()); - } - - private final ImmutableList txOutputs; - @Nullable - private final TxType txType; - // Can be burned fee or in case of an invalid tx the burned BSQ from all BSQ inputs - private final long burntBsq; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private Tx(String txVersion, - String id, - int blockHeight, - String blockHash, - long time, - ImmutableList txInputs, - ImmutableList txOutputs, - @Nullable TxType txType, - long burntBsq) { - super(txVersion, - id, - blockHeight, - blockHash, - time, - txInputs); - this.txOutputs = txOutputs; - this.txType = txType; - this.burntBsq = burntBsq; - - } - - @Override - public protobuf.BaseTx toProtoMessage() { - final protobuf.Tx.Builder builder = protobuf.Tx.newBuilder() - .addAllTxOutputs(txOutputs.stream() - .map(TxOutput::toProtoMessage) - .collect(Collectors.toList())) - .setBurntBsq(burntBsq); - Optional.ofNullable(txType).ifPresent(txType -> builder.setTxType(txType.toProtoMessage())); - return getBaseTxBuilder().setTx(builder).build(); - } - - public static Tx fromProto(protobuf.BaseTx protoBaseTx) { - ImmutableList txInputs = protoBaseTx.getTxInputsList().isEmpty() ? - ImmutableList.copyOf(new ArrayList<>()) : - ImmutableList.copyOf(protoBaseTx.getTxInputsList().stream() - .map(TxInput::fromProto) - .collect(Collectors.toList())); - protobuf.Tx protoTx = protoBaseTx.getTx(); - ImmutableList outputs = protoTx.getTxOutputsList().isEmpty() ? - ImmutableList.copyOf(new ArrayList<>()) : - ImmutableList.copyOf(protoTx.getTxOutputsList().stream() - .map(TxOutput::fromProto) - .collect(Collectors.toList())); - return new Tx(protoBaseTx.getTxVersion(), - protoBaseTx.getId(), - protoBaseTx.getBlockHeight(), - protoBaseTx.getBlockHash(), - protoBaseTx.getTime(), - txInputs, - outputs, - TxType.fromProto(protoTx.getTxType()), - protoTx.getBurntBsq()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Utils - /////////////////////////////////////////////////////////////////////////////////////////// - - public TxOutput getLastTxOutput() { - return txOutputs.get(txOutputs.size() - 1); - } - - - public long getBurntBsq() { - return burntBsq; - } - - public long getBurntFee() { - return txType == TxType.INVALID ? 0 : burntBsq; - } - - public long getInvalidatedBsq() { - return txType == TxType.INVALID ? burntBsq : 0; - } - - public int getLockTime() { - return getLockupOutput().getLockTime(); - } - - public long getLockedAmount() { - return getLockupOutput().getValue(); - } - - // The lockTime is stored in the first output of the LOCKUP tx. - public TxOutput getLockupOutput() { - return txOutputs.get(0); - } - - // The unlockBlockHeight is stored in the first output of the UNLOCK tx. - public int getUnlockBlockHeight() { - return getLockupOutput().getUnlockBlockHeight(); - } - - @Override - public String toString() { - return "Tx{" + - "\n txOutputs=" + txOutputs + - ",\n txType=" + txType + - ",\n burntBsq=" + burntBsq + - "\n} " + super.toString(); - } - - // 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 - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Tx)) return false; - if (!super.equals(o)) return false; - Tx tx = (Tx) o; - - String name = txType != null ? txType.name() : ""; - String name1 = tx.txType != null ? tx.txType.name() : ""; - boolean isTxTypeEquals = name.equals(name1); - - return burntBsq == tx.burntBsq && - Objects.equals(txOutputs, tx.txOutputs) && - isTxTypeEquals; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), txOutputs, txType, burntBsq); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxInput.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/TxInput.java deleted file mode 100644 index 0ebcddf937..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxInput.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.blockchain; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.persistable.PersistablePayload; - -import java.util.Optional; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** - * An input is really just a reference to the spending output. It gets identified by the - * txId and the index of that output. We use TxOutputKey to encapsulate that. - */ -@Immutable -@Value -@EqualsAndHashCode -@Slf4j -public final class TxInput implements PersistablePayload, ImmutableDaoStateModel { - private final String connectedTxOutputTxId; - private final int connectedTxOutputIndex; - @Nullable - private final String pubKey; // as hex - - public TxInput(String connectedTxOutputTxId, int connectedTxOutputIndex, @Nullable String pubKey) { - this.connectedTxOutputTxId = connectedTxOutputTxId; - this.connectedTxOutputIndex = connectedTxOutputIndex; - this.pubKey = pubKey; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public protobuf.TxInput toProtoMessage() { - final protobuf.TxInput.Builder builder = protobuf.TxInput.newBuilder() - .setConnectedTxOutputTxId(connectedTxOutputTxId) - .setConnectedTxOutputIndex(connectedTxOutputIndex); - - Optional.ofNullable(pubKey).ifPresent(builder::setPubKey); - - return builder.build(); - } - - public static TxInput fromProto(protobuf.TxInput proto) { - return new TxInput(proto.getConnectedTxOutputTxId(), - proto.getConnectedTxOutputIndex(), - proto.getPubKey().isEmpty() ? null : proto.getPubKey()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public TxOutputKey getConnectedTxOutputKey() { - return new TxOutputKey(connectedTxOutputTxId, connectedTxOutputIndex); - } - - @Override - public String toString() { - return "TxInput{" + - "\n connectedTxOutputTxId='" + connectedTxOutputTxId + '\'' + - ",\n connectedTxOutputIndex=" + connectedTxOutputIndex + - ",\n pubKey=" + pubKey + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxOutput.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/TxOutput.java deleted file mode 100644 index 566216314e..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxOutput.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.blockchain; - -import bisq.core.dao.node.parser.TempTxOutput; -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.persistable.PersistablePayload; - -import java.util.Objects; - -import lombok.Getter; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** - * Contains immutable BSQ specific data (TxOutputType) and is used to get - * stored in the list of immutable Transactions (Tx) in a block. - * TempTxOutput get converted to immutable TxOutput after tx parsing is completed. - * Gets persisted. - */ -@Immutable -@Getter -public class TxOutput extends BaseTxOutput implements PersistablePayload, ImmutableDaoStateModel { - public static TxOutput fromTempOutput(TempTxOutput tempTxOutput) { - return new TxOutput(tempTxOutput.getIndex(), - tempTxOutput.getValue(), - tempTxOutput.getTxId(), - tempTxOutput.getPubKeyScript(), - tempTxOutput.getAddress(), - tempTxOutput.getOpReturnData(), - tempTxOutput.getBlockHeight(), - tempTxOutput.getTxOutputType(), - tempTxOutput.getLockTime(), - tempTxOutput.getUnlockBlockHeight()); - } - - private final TxOutputType txOutputType; - - // The lockTime is stored in the first output of the LOCKUP tx. - // If not set it is -1, 0 is a valid value. - private final int lockTime; - // The unlockBlockHeight is stored in the first output of the UNLOCK tx. - private final int unlockBlockHeight; - - private TxOutput(int index, - long value, - String txId, - @Nullable PubKeyScript pubKeyScript, - @Nullable String address, - @Nullable byte[] opReturnData, - int blockHeight, - TxOutputType txOutputType, - int lockTime, - int unlockBlockHeight) { - super(index, - value, - txId, - pubKeyScript, - address, - opReturnData, - blockHeight); - - this.txOutputType = txOutputType; - this.lockTime = lockTime; - this.unlockBlockHeight = unlockBlockHeight; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.BaseTxOutput toProtoMessage() { - protobuf.TxOutput.Builder builder = protobuf.TxOutput.newBuilder() - .setTxOutputType(txOutputType.toProtoMessage()) - .setLockTime(lockTime) - .setUnlockBlockHeight(unlockBlockHeight); - return getRawTxOutputBuilder().setTxOutput(builder).build(); - } - - public static TxOutput fromProto(protobuf.BaseTxOutput proto) { - protobuf.TxOutput protoTxOutput = proto.getTxOutput(); - return new TxOutput(proto.getIndex(), - proto.getValue(), - proto.getTxId(), - proto.hasPubKeyScript() ? PubKeyScript.fromProto(proto.getPubKeyScript()) : null, - proto.getAddress().isEmpty() ? null : proto.getAddress(), - proto.getOpReturnData().isEmpty() ? null : proto.getOpReturnData().toByteArray(), - proto.getBlockHeight(), - TxOutputType.fromProto(protoTxOutput.getTxOutputType()), - protoTxOutput.getLockTime(), - protoTxOutput.getUnlockBlockHeight()); - } - - - @Override - public String toString() { - return "TxOutput{" + - "\n txOutputType=" + txOutputType + - "\n lockTime=" + lockTime + - ",\n unlockBlockHeight=" + unlockBlockHeight + - "\n} " + super.toString(); - } - - // 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 - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof TxOutput)) return false; - if (!super.equals(o)) return false; - TxOutput txOutput = (TxOutput) o; - return lockTime == txOutput.lockTime && - unlockBlockHeight == txOutput.unlockBlockHeight && - txOutputType.name().equals(txOutput.txOutputType.name()); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), txOutputType.name(), lockTime, unlockBlockHeight); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxOutputKey.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/TxOutputKey.java deleted file mode 100644 index 282d69d278..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxOutputKey.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.blockchain; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import lombok.Value; - -import org.jetbrains.annotations.NotNull; - -import javax.annotation.concurrent.Immutable; - -/** - * Convenience object for identifying a TxOutput. - * Used as key in maps in the daoState. - */ -@Immutable -@Value -public final class TxOutputKey implements ImmutableDaoStateModel, Comparable { - private final String txId; - private final int index; - - public TxOutputKey(String txId, int index) { - this.txId = txId; - this.index = index; - } - - @Override - public String toString() { - return txId + ":" + index; - } - - public static TxOutputKey getKeyFromString(String keyAsString) { - final String[] tokens = keyAsString.split(":"); - return new TxOutputKey(tokens[0], Integer.valueOf(tokens[1])); - } - - @Override - public int compareTo(@NotNull Object o) { - return toString().compareTo(o.toString()); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxOutputType.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/TxOutputType.java deleted file mode 100644 index 51bfbd0197..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxOutputType.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.blockchain; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.ProtoUtil; - -import javax.annotation.concurrent.Immutable; - -@Immutable -public enum TxOutputType implements ImmutableDaoStateModel { - UNDEFINED, // only fallback for backward compatibility in case we add a new value and old clients fall back to UNDEFINED - UNDEFINED_OUTPUT, - GENESIS_OUTPUT, - BSQ_OUTPUT, - BTC_OUTPUT, - PROPOSAL_OP_RETURN_OUTPUT, - COMP_REQ_OP_RETURN_OUTPUT, - REIMBURSEMENT_OP_RETURN_OUTPUT, - CONFISCATE_BOND_OP_RETURN_OUTPUT, - ISSUANCE_CANDIDATE_OUTPUT, - BLIND_VOTE_LOCK_STAKE_OUTPUT, - BLIND_VOTE_OP_RETURN_OUTPUT, - VOTE_REVEAL_UNLOCK_STAKE_OUTPUT, - VOTE_REVEAL_OP_RETURN_OUTPUT, - ASSET_LISTING_FEE_OP_RETURN_OUTPUT, - PROOF_OF_BURN_OP_RETURN_OUTPUT, - LOCKUP_OUTPUT, - LOCKUP_OP_RETURN_OUTPUT, - UNLOCK_OUTPUT, - INVALID_OUTPUT; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public static TxOutputType fromProto(protobuf.TxOutputType txOutputType) { - return ProtoUtil.enumFromProto(TxOutputType.class, txOutputType.name()); - } - - public protobuf.TxOutputType toProtoMessage() { - return protobuf.TxOutputType.valueOf(name()); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxType.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/TxType.java deleted file mode 100644 index ff1f782df9..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxType.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.blockchain; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.ProtoUtil; - -import lombok.Getter; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -@Immutable -public enum TxType implements ImmutableDaoStateModel { - UNDEFINED(false, false), // only fallback for backward compatibility in case we add a new value and old clients fall back to UNDEFINED - UNDEFINED_TX_TYPE(false, false), - UNVERIFIED(false, false), - INVALID(false, false), - GENESIS(false, false), - TRANSFER_BSQ(false, false), - PAY_TRADE_FEE(false, true), - PROPOSAL(true, true), - COMPENSATION_REQUEST(true, true), - REIMBURSEMENT_REQUEST(true, true), - BLIND_VOTE(true, true), - VOTE_REVEAL(true, false), - LOCKUP(true, false), - UNLOCK(true, false), - ASSET_LISTING_FEE(true, true), - PROOF_OF_BURN(true, true), - IRREGULAR(false, false); // the params are irrelevant here as we can have any tx that violated the rules set to irregular - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Getter - private final boolean hasOpReturn; - @Getter - private final boolean requiresFee; - - TxType(boolean hasOpReturn, boolean requiresFee) { - this.hasOpReturn = hasOpReturn; - this.requiresFee = requiresFee; - } - - @Nullable - public static TxType fromProto(protobuf.TxType txType) { - return ProtoUtil.enumFromProto(TxType.class, txType.name()); - } - - public protobuf.TxType toProtoMessage() { - return protobuf.TxType.valueOf(name()); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/package-info.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/package-info.java deleted file mode 100644 index ce91db4133..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -/** - * Holds all blockchain specific data which are used in the daoState. - */ - -package bisq.core.dao.state.model.blockchain; diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/Ballot.java b/core/src/main/java/bisq/core/dao/state/model/governance/Ballot.java deleted file mode 100644 index 7a8ad92078..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/Ballot.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.persistable.PersistablePayload; - -import java.util.Optional; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** - * Base class for all ballots like compensation request, generic request, remove asset ballots and - * change param ballots. - * It contains the Proposal and the Vote. If a Proposal is ignored for voting the vote object is null. - * - * One proposal has about 278 bytes - */ -@Immutable -@Slf4j -@Getter -@EqualsAndHashCode -public final class Ballot implements PersistablePayload, ConsensusCritical, ImmutableDaoStateModel { - protected final Proposal proposal; - - @Nullable - protected Vote vote; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - public Ballot(Proposal proposal) { - this(proposal, null); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public Ballot(Proposal proposal, @Nullable Vote vote) { - this.proposal = proposal; - this.vote = vote; - } - - @Override - public protobuf.Ballot toProtoMessage() { - final protobuf.Ballot.Builder builder = protobuf.Ballot.newBuilder() - .setProposal(proposal.getProposalBuilder()); - Optional.ofNullable(vote).ifPresent(e -> builder.setVote((protobuf.Vote) e.toProtoMessage())); - return builder.build(); - } - - public static Ballot fromProto(protobuf.Ballot proto) { - return new Ballot(Proposal.fromProto(proto.getProposal()), - proto.hasVote() ? Vote.fromProto(proto.getVote()) : null); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void setVote(@Nullable Vote vote) { - this.vote = vote; - } - - public String getTxId() { - return proposal.getTxId(); - } - - public Optional getVoteAsOptional() { - return Optional.ofNullable(vote); - } - - @Override - public String toString() { - return "Ballot{" + - "\n proposal=" + proposal + - ",\n vote=" + vote + - "\n}"; - } - - public String info() { - return "Ballot{" + - "\n proposalTxId=" + proposal.getTxId() + - ",\n vote=" + vote + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/BallotList.java b/core/src/main/java/bisq/core/dao/state/model/governance/BallotList.java deleted file mode 100644 index 25eaa415e6..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/BallotList.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.persistable.PersistableList; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.EqualsAndHashCode; - -import javax.annotation.concurrent.Immutable; - -/** - * PersistableEnvelope wrapper for list of ballots. - */ -@Immutable -@EqualsAndHashCode(callSuper = true) -public class BallotList extends PersistableList implements ConsensusCritical, ImmutableDaoStateModel { - - public BallotList(List list) { - super(list); - } - - public BallotList() { - super(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.PersistableEnvelope toProtoMessage() { - return protobuf.PersistableEnvelope.newBuilder().setBallotList(getBuilder()).build(); - } - - public protobuf.BallotList.Builder getBuilder() { - return protobuf.BallotList.newBuilder() - .addAllBallot(getList().stream() - .map(Ballot::toProtoMessage) - .collect(Collectors.toList())); - } - - public static BallotList fromProto(protobuf.BallotList proto) { - return new BallotList(new ArrayList<>(proto.getBallotList().stream() - .map(Ballot::fromProto) - .collect(Collectors.toList()))); - } - - @Override - public String toString() { - return "BallotList: " + getList().stream() - .map(Ballot::info) - .collect(Collectors.toList()); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/BondedRoleType.java b/core/src/main/java/bisq/core/dao/state/model/governance/BondedRoleType.java deleted file mode 100644 index 1f53a587a2..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/BondedRoleType.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.locale.Res; - -import bisq.common.config.Config; - -import lombok.Getter; - - -/** - * Data here must not be changed as it would break backward compatibility! In case we need to change we need to add a - * new entry and maintain the old one. Once all the role holders of an old deprecated role have revoked the - * role might get removed. - * - * Add entry to translation file "dao.bond.bondedRoleType...." - * - * Name of the BondedRoleType must not change as that is used for serialisation in Protobuffer. The data fields are not part of - * the PB serialisation so changes for those would not change the hash for the dao state hash chain. - * As the data is not used in consensus critical code yet changing fields can be tolerated. - * For mediators and arbitrators we will use automated verification of the bond so there might be issues when we change - * the values. So let's avoid changing anything here beside adding new entries. - * - */ -public enum BondedRoleType { - UNDEFINED(0, 0, "N/A", false), - // admins - GITHUB_ADMIN(50, 110, "https://bisq.network/roles/16", true), - FORUM_ADMIN(20, 110, "https://bisq.network/roles/19", true), - TWITTER_ADMIN(20, 110, "https://bisq.network/roles/21", true), - ROCKET_CHAT_ADMIN(20, 110, "https://bisq.network/roles/79", true),// Now Keybase Admin - YOUTUBE_ADMIN(10, 110, "https://bisq.network/roles/56", true), - - // maintainers - BISQ_MAINTAINER(50, 110, "https://bisq.network/roles/63", true), - BITCOINJ_MAINTAINER(20, 110, "https://bisq.network/roles/8", true), - NETLAYER_MAINTAINER(20, 110, "https://bisq.network/roles/81", true), - - // operators - WEBSITE_OPERATOR(50, 110, "https://bisq.network/roles/12", true), - FORUM_OPERATOR(50, 110, "https://bisq.network/roles/19", true), - SEED_NODE_OPERATOR(20, 110, "https://bisq.network/roles/15", true), - DATA_RELAY_NODE_OPERATOR(20, 110, "https://bisq.network/roles/14", true), - BTC_NODE_OPERATOR(5, 110, "https://bisq.network/roles/67", true), - MARKETS_OPERATOR(20, 110, "https://bisq.network/roles/9", true), - BSQ_EXPLORER_OPERATOR(20, 110, "https://bisq.network/roles/11", true), - MOBILE_NOTIFICATIONS_RELAY_OPERATOR(20, 110, "https://bisq.network/roles/82", true), - - // other - DOMAIN_NAME_HOLDER(50, 110, "https://bisq.network/roles/77", false), - DNS_ADMIN(20, 110, "https://bisq.network/roles/18", false), - MEDIATOR(10, 110, "https://bisq.network/roles/83", true), - ARBITRATOR(200, 110, "https://bisq.network/roles/13", true), - BTC_DONATION_ADDRESS_OWNER(50, 110, "https://bisq.network/roles/80", true); - - - // Will be multiplied with PARAM.BONDED_ROLE_FACTOR to get BSQ amount. - // As BSQ is volatile we need to adjust the bonds over time. - // To avoid changing the Enum we use the BONDED_ROLE_FACTOR param to react on BSQ price changes. - // Required bond = requiredBondUnit * PARAM.BONDED_ROLE_FACTOR.value - @Getter - private final long requiredBondUnit; - - // Unlock time in blocks - @Getter - private final int unlockTimeInBlocks; - @Getter - private final String link; - @Getter - private final boolean allowMultipleHolders; - - /** - * @param requiredBondUnit // requiredBondUnit for lockup tx (will be multiplied with PARAM.BONDED_ROLE_FACTOR for BSQ value) - * @param unlockTimeInDays // unlockTime in days - * @param link // Link to GitHub for role description - * @param allowMultipleHolders // If role can be held by multiple persons (e.g. seed nodes vs. domain name) - */ - BondedRoleType(long requiredBondUnit, int unlockTimeInDays, String link, boolean allowMultipleHolders) { - this.requiredBondUnit = requiredBondUnit; - this.unlockTimeInBlocks = Config.baseCurrencyNetwork().isMainnet() ? - unlockTimeInDays * 144 : // mainnet (144 blocks per day) - Config.baseCurrencyNetwork().isStagenet() ? - 5 : // regtest (arbitrarily low value for dev testing) - 144; // testnet (relatively short time for testing purposes) - this.link = link; - this.allowMultipleHolders = allowMultipleHolders; - } - - public String getDisplayString() { - return Res.get("dao.bond.bondedRoleType." + name()); - } - - @Override - public String toString() { - return "BondedRoleType{" + - "\n requiredBondUnit=" + requiredBondUnit + - ",\n unlockTime=" + unlockTimeInBlocks + - ",\n link='" + link + '\'' + - ",\n allowMultipleHolders=" + allowMultipleHolders + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/ChangeParamProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/ChangeParamProposal.java deleted file mode 100644 index d39fc72604..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/ChangeParamProposal.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.proposal.ProposalType; -import bisq.core.dao.state.model.ImmutableDaoStateModel; -import bisq.core.dao.state.model.blockchain.TxType; - -import bisq.common.app.Version; -import bisq.common.util.CollectionUtils; - -import java.util.Date; -import java.util.Map; -import java.util.Objects; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -@Immutable -@Slf4j -@Getter -public final class ChangeParamProposal extends Proposal implements ImmutableDaoStateModel { - private final Param param; - private final String paramValue; - - public ChangeParamProposal(String name, - String link, - Param param, - String paramValue, - Map extraDataMap) { - this(name, - link, - param, - paramValue, - Version.PROPOSAL, - new Date().getTime(), - null, - extraDataMap); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private ChangeParamProposal(String name, - String link, - Param param, - String paramValue, - byte version, - long creationDate, - String txId, - Map extraDataMap) { - super(name, - link, - version, - creationDate, - txId, - extraDataMap); - - this.param = param; - this.paramValue = paramValue; - } - - @Override - public protobuf.Proposal.Builder getProposalBuilder() { - final protobuf.ChangeParamProposal.Builder builder = protobuf.ChangeParamProposal.newBuilder() - .setParam(param.name()) - .setParamValue(paramValue); - return super.getProposalBuilder().setChangeParamProposal(builder); - } - - public static ChangeParamProposal fromProto(protobuf.Proposal proto) { - final protobuf.ChangeParamProposal proposalProto = proto.getChangeParamProposal(); - return new ChangeParamProposal(proto.getName(), - proto.getLink(), - Param.fromProto(proposalProto), - proposalProto.getParamValue(), - (byte) proto.getVersion(), - proto.getCreationDate(), - proto.getTxId(), - CollectionUtils.isEmpty(proto.getExtraDataMap()) ? - null : proto.getExtraDataMap()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Getters - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public ProposalType getType() { - return ProposalType.CHANGE_PARAM; - } - - @Override - public Param getQuorumParam() { - return Param.QUORUM_CHANGE_PARAM; - } - - @Override - public Param getThresholdParam() { - return Param.THRESHOLD_CHANGE_PARAM; - } - - @Override - public TxType getTxType() { - return TxType.PROPOSAL; - } - - @Override - public Proposal cloneProposalAndAddTxId(String txId) { - return new ChangeParamProposal(getName(), - getLink(), - getParam(), - getParamValue(), - getVersion(), - getCreationDate(), - txId, - extraDataMap); - } - - @Override - public String toString() { - return "ChangeParamProposal{" + - "\n param=" + param + - ",\n paramValue=" + paramValue + - "\n} " + super.toString(); - } - - // 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 - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ChangeParamProposal)) return false; - if (!super.equals(o)) return false; - ChangeParamProposal that = (ChangeParamProposal) o; - boolean paramTypeNameIsEquals = param.getParamType().name().equals(that.param.getParamType().name()); - boolean paramNameIsEquals = param.name().equals(that.param.name()); - return paramNameIsEquals && paramTypeNameIsEquals && - Objects.equals(paramValue, that.paramValue); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), param.getParamType().name(), param.name(), paramValue); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/CompensationProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/CompensationProposal.java deleted file mode 100644 index d23249b6c4..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/CompensationProposal.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.proposal.IssuanceProposal; -import bisq.core.dao.governance.proposal.ProposalType; -import bisq.core.dao.state.model.ImmutableDaoStateModel; -import bisq.core.dao.state.model.blockchain.TxType; - -import bisq.common.app.Version; -import bisq.common.config.Config; -import bisq.common.util.CollectionUtils; - -import org.bitcoinj.core.AddressFormatException; -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.LegacyAddress; - -import java.util.Date; -import java.util.Map; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -@Immutable -@Slf4j -@EqualsAndHashCode(callSuper = true) -@Value -public final class CompensationProposal extends Proposal implements IssuanceProposal, ImmutableDaoStateModel { - private final long requestedBsq; - private final String bsqAddress; - - public CompensationProposal(String name, - String link, - Coin requestedBsq, - String bsqAddress, - Map extraDataMap) { - this(name, - link, - bsqAddress, - requestedBsq.value, - Version.COMPENSATION_REQUEST, - new Date().getTime(), - null, - extraDataMap); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private CompensationProposal(String name, - String link, - String bsqAddress, - long requestedBsq, - byte version, - long creationDate, - String txId, - Map extraDataMap) { - super(name, - link, - version, - creationDate, - txId, - extraDataMap); - - this.requestedBsq = requestedBsq; - this.bsqAddress = bsqAddress; - } - - @Override - public protobuf.Proposal.Builder getProposalBuilder() { - final protobuf.CompensationProposal.Builder builder = protobuf.CompensationProposal.newBuilder() - .setBsqAddress(bsqAddress) - .setRequestedBsq(requestedBsq); - return super.getProposalBuilder().setCompensationProposal(builder); - } - - public static CompensationProposal fromProto(protobuf.Proposal proto) { - final protobuf.CompensationProposal proposalProto = proto.getCompensationProposal(); - return new CompensationProposal(proto.getName(), - proto.getLink(), - proposalProto.getBsqAddress(), - proposalProto.getRequestedBsq(), - (byte) proto.getVersion(), - proto.getCreationDate(), - proto.getTxId(), - CollectionUtils.isEmpty(proto.getExtraDataMap()) ? - null : proto.getExtraDataMap()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Getters - /////////////////////////////////////////////////////////////////////////////////////////// - - public Coin getRequestedBsq() { - return Coin.valueOf(requestedBsq); - } - - public LegacyAddress getAddress() throws AddressFormatException { - // Remove leading 'B' - String underlyingBtcAddress = bsqAddress.substring(1, bsqAddress.length()); - return LegacyAddress.fromBase58(Config.baseCurrencyNetworkParameters(), underlyingBtcAddress); - } - - - @Override - public ProposalType getType() { - return ProposalType.COMPENSATION_REQUEST; - } - - @Override - public Param getQuorumParam() { - return Param.QUORUM_COMP_REQUEST; - } - - @Override - public Param getThresholdParam() { - return Param.THRESHOLD_COMP_REQUEST; - } - - @Override - public TxType getTxType() { - return TxType.COMPENSATION_REQUEST; - } - - @Override - public Proposal cloneProposalAndAddTxId(String txId) { - return new CompensationProposal(getName(), - getLink(), - getBsqAddress(), - getRequestedBsq().value, - getVersion(), - getCreationDate(), - txId, - extraDataMap); - } - - @Override - public String toString() { - return "CompensationProposal{" + - "\n requestedBsq=" + requestedBsq + - ",\n bsqAddress='" + bsqAddress + '\'' + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/ConfiscateBondProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/ConfiscateBondProposal.java deleted file mode 100644 index e442454ab7..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/ConfiscateBondProposal.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.proposal.ProposalType; -import bisq.core.dao.state.model.ImmutableDaoStateModel; -import bisq.core.dao.state.model.blockchain.TxType; - -import bisq.common.app.Version; -import bisq.common.util.CollectionUtils; - -import java.util.Date; -import java.util.Map; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -@Immutable -@Slf4j -@EqualsAndHashCode(callSuper = true) -@Value -public final class ConfiscateBondProposal extends Proposal implements ImmutableDaoStateModel { - private final String lockupTxId; - - public ConfiscateBondProposal(String name, - String link, - String lockupTxId, - Map extraDataMap) { - this(name, - link, - lockupTxId, - Version.PROPOSAL, - new Date().getTime(), - null, - extraDataMap); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private ConfiscateBondProposal(String name, - String link, - String lockupTxId, - byte version, - long creationDate, - String txId, - Map extraDataMap) { - super(name, - link, - version, - creationDate, - txId, - extraDataMap); - this.lockupTxId = lockupTxId; - } - - @Override - public protobuf.Proposal.Builder getProposalBuilder() { - final protobuf.ConfiscateBondProposal.Builder builder = protobuf.ConfiscateBondProposal.newBuilder() - .setLockupTxId(lockupTxId); - return super.getProposalBuilder().setConfiscateBondProposal(builder); - } - - public static ConfiscateBondProposal fromProto(protobuf.Proposal proto) { - final protobuf.ConfiscateBondProposal proposalProto = proto.getConfiscateBondProposal(); - return new ConfiscateBondProposal(proto.getName(), - proto.getLink(), - proposalProto.getLockupTxId(), - (byte) proto.getVersion(), - proto.getCreationDate(), - proto.getTxId(), - CollectionUtils.isEmpty(proto.getExtraDataMap()) ? - null : proto.getExtraDataMap()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Getters - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public ProposalType getType() { - return ProposalType.CONFISCATE_BOND; - } - - @Override - public Param getQuorumParam() { - return Param.QUORUM_CONFISCATION; - } - - @Override - public Param getThresholdParam() { - return Param.THRESHOLD_CONFISCATION; - } - - @Override - public TxType getTxType() { - return TxType.PROPOSAL; - } - - @Override - public Proposal cloneProposalAndAddTxId(String txId) { - return new ConfiscateBondProposal(getName(), - getLink(), - getLockupTxId(), - getVersion(), - getCreationDate(), - txId, - extraDataMap); - } - - @Override - public String toString() { - return "ConfiscateBondProposal{" + - "\n lockupTxId=" + lockupTxId + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/Cycle.java b/core/src/main/java/bisq/core/dao/state/model/governance/Cycle.java deleted file mode 100644 index a1066757a0..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/Cycle.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.persistable.PersistablePayload; - -import com.google.common.collect.ImmutableList; - -import java.util.Optional; -import java.util.stream.Collectors; - -import lombok.Value; - -import javax.annotation.concurrent.Immutable; - -/** - * Cycle represents the monthly period for proposals and voting. - * It consists of a ordered list of phases represented by the phaseWrappers. - */ -@Immutable -@Value -public class Cycle implements PersistablePayload, ImmutableDaoStateModel { - // List is ordered according to the Phase enum. - private final ImmutableList daoPhaseList; - private final int heightOfFirstBlock; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - public Cycle(int heightOfFirstBlock, ImmutableList daoPhaseList) { - this.heightOfFirstBlock = heightOfFirstBlock; - this.daoPhaseList = daoPhaseList; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.Cycle toProtoMessage() { - return protobuf.Cycle.newBuilder() - .setHeightOfFirstLock(heightOfFirstBlock) - .addAllDaoPhase(daoPhaseList.stream() - .map(DaoPhase::toProtoMessage) - .collect(Collectors.toList())) - .build(); - } - - public static Cycle fromProto(protobuf.Cycle proto) { - final ImmutableList daoPhaseList = ImmutableList.copyOf(proto.getDaoPhaseList().stream() - .map(DaoPhase::fromProto) - .collect(Collectors.toList())); - return new Cycle(proto.getHeightOfFirstLock(), daoPhaseList); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public int getHeightOfLastBlock() { - return heightOfFirstBlock + getDuration() - 1; - } - - public boolean isInPhase(int height, DaoPhase.Phase phase) { - return height >= getFirstBlockOfPhase(phase) && height <= getLastBlockOfPhase(phase); - } - - public boolean isInCycle(int height) { - return height >= getHeightOfFirstBlock() && height <= getHeightOfLastBlock(); - } - - public int getFirstBlockOfPhase(DaoPhase.Phase phase) { - return heightOfFirstBlock + daoPhaseList.stream() - .filter(item -> item.getPhase().ordinal() < phase.ordinal()) - .mapToInt(DaoPhase::getDuration).sum(); - } - - public int getLastBlockOfPhase(DaoPhase.Phase phase) { - return getFirstBlockOfPhase(phase) + getDuration(phase) - 1; - } - - public int getDurationOfPhase(DaoPhase.Phase phase) { - return daoPhaseList.stream() - .filter(item -> item.getPhase() == phase) - .mapToInt(DaoPhase::getDuration) - .sum(); - } - - public Optional getPhaseForHeight(int height) { - return daoPhaseList.stream() - .filter(item -> isInPhase(height, item.getPhase())) - .map(DaoPhase::getPhase) - .findAny(); - } - - private Optional getPhaseWrapper(DaoPhase.Phase phase) { - return daoPhaseList.stream().filter(item -> item.getPhase() == phase).findAny(); - } - - private int getDuration(DaoPhase.Phase phase) { - return getPhaseWrapper(phase).map(DaoPhase::getDuration).orElse(0); - } - - public int getDuration() { - return daoPhaseList.stream().mapToInt(DaoPhase::getDuration).sum(); - } - - @Override - public String toString() { - return "Cycle{" + - "\n daoPhaseList=" + daoPhaseList + - ",\n heightOfFirstBlock=" + heightOfFirstBlock + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/DaoPhase.java b/core/src/main/java/bisq/core/dao/state/model/governance/DaoPhase.java deleted file mode 100644 index ccd7091d1b..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/DaoPhase.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.persistable.PersistablePayload; - -import java.util.Objects; - -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -/** - * Encapsulated the phase enum with the duration. - * As the duration can change by voting we don't want to put the duration property in the enum but use that wrapper. - */ -@Immutable -@Value -@Slf4j -public class DaoPhase implements PersistablePayload, ImmutableDaoStateModel { - - /** - * Enum for phase of a cycle. - * - * We don't want to use an enum with the duration as field because the duration can change by voting and enums - * should be considered immutable. - */ - @Immutable - public enum Phase implements ImmutableDaoStateModel { - UNDEFINED, - PROPOSAL, - BREAK1, - BLIND_VOTE, - BREAK2, - VOTE_REVEAL, - BREAK3, - RESULT - } - - - private final Phase phase; - private final int duration; - - public DaoPhase(Phase phase, int duration) { - this.phase = phase; - this.duration = duration; - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.DaoPhase toProtoMessage() { - return protobuf.DaoPhase.newBuilder() - .setPhaseOrdinal(phase.ordinal()) - .setDuration(duration) - .build(); - } - - public static DaoPhase fromProto(protobuf.DaoPhase proto) { - int ordinal = proto.getPhaseOrdinal(); - if (ordinal >= Phase.values().length) { - log.warn("We tried to access a ordinal outside of the DaoPhase.Phase enum bounds and set it to " + - "UNDEFINED. ordinal={}", ordinal); - return new DaoPhase(Phase.UNDEFINED, 0); - } - - return new DaoPhase(Phase.values()[ordinal], proto.getDuration()); - } - - - @Override - public String toString() { - return "DaoPhase{" + - "\n phase=" + phase + - ",\n duration=" + duration + - "\n}"; - } - - // 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 - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof DaoPhase)) return false; - if (!super.equals(o)) return false; - DaoPhase daoPhase = (DaoPhase) o; - return duration == daoPhase.duration && - phase.name().equals(daoPhase.phase.name()); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), phase.name(), duration); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/DecryptedBallotsWithMerits.java b/core/src/main/java/bisq/core/dao/state/model/governance/DecryptedBallotsWithMerits.java deleted file mode 100644 index 489ba012f7..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/DecryptedBallotsWithMerits.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.governance.merit.MeritConsensus; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.persistable.PersistablePayload; -import bisq.common.util.Utilities; - -import com.google.protobuf.ByteString; - -import java.util.Optional; - -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -/** - * Holds all data from a decrypted vote item. - */ -@Immutable -@Slf4j -@Value -public class DecryptedBallotsWithMerits implements PersistablePayload, ImmutableDaoStateModel { - private final byte[] hashOfBlindVoteList; - private final String blindVoteTxId; - private final String voteRevealTxId; - private final long stake; - - // BallotList and meritList can be empty list in case we don't have a blind vote payload - private final BallotList ballotList; - private final MeritList meritList; - - public DecryptedBallotsWithMerits(byte[] hashOfBlindVoteList, String blindVoteTxId, String voteRevealTxId, long stake, - BallotList ballotList, MeritList meritList) { - this.hashOfBlindVoteList = hashOfBlindVoteList; - this.blindVoteTxId = blindVoteTxId; - this.voteRevealTxId = voteRevealTxId; - this.stake = stake; - this.ballotList = ballotList; - this.meritList = meritList; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.DecryptedBallotsWithMerits toProtoMessage() { - return getBuilder().build(); - } - - private protobuf.DecryptedBallotsWithMerits.Builder getBuilder() { - return protobuf.DecryptedBallotsWithMerits.newBuilder() - .setHashOfBlindVoteList(ByteString.copyFrom(hashOfBlindVoteList)) - .setBlindVoteTxId(blindVoteTxId) - .setVoteRevealTxId(voteRevealTxId) - .setStake(stake) - .setBallotList(ballotList.getBuilder()) - .setMeritList(meritList.getBuilder()); - } - - public static DecryptedBallotsWithMerits fromProto(protobuf.DecryptedBallotsWithMerits proto) { - return new DecryptedBallotsWithMerits(proto.getHashOfBlindVoteList().toByteArray(), - proto.getBlindVoteTxId(), - proto.getVoteRevealTxId(), - proto.getStake(), - BallotList.fromProto(proto.getBallotList()), - MeritList.fromProto(proto.getMeritList())); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public Optional getVote(String proposalTxId) { - return ballotList.stream() - .filter(ballot -> ballot.getTxId().equals(proposalTxId)) - .map(Ballot::getVote) - .findAny(); - } - - public long getMerit(DaoStateService daoStateService) { - return MeritConsensus.getMeritStake(blindVoteTxId, meritList, daoStateService); - } - - @Override - public String toString() { - return "DecryptedBallotsWithMerits{" + - "\n hashOfBlindVoteList=" + Utilities.bytesAsHexString(hashOfBlindVoteList) + - ",\n blindVoteTxId='" + blindVoteTxId + '\'' + - ",\n voteRevealTxId='" + voteRevealTxId + '\'' + - ",\n stake=" + stake + - ",\n ballotList=" + ballotList + - ",\n meritList=" + meritList + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/EvaluatedProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/EvaluatedProposal.java deleted file mode 100644 index c06bba0053..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/EvaluatedProposal.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.persistable.PersistablePayload; - -import lombok.Value; - -import javax.annotation.concurrent.Immutable; - -@Immutable -@Value -public class EvaluatedProposal implements PersistablePayload, ImmutableDaoStateModel { - private final boolean isAccepted; - private final ProposalVoteResult proposalVoteResult; - - public EvaluatedProposal(boolean isAccepted, ProposalVoteResult proposalVoteResult) { - this.isAccepted = isAccepted; - this.proposalVoteResult = proposalVoteResult; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.EvaluatedProposal toProtoMessage() { - protobuf.EvaluatedProposal.Builder builder = protobuf.EvaluatedProposal.newBuilder() - .setIsAccepted(isAccepted) - .setProposalVoteResult(proposalVoteResult.toProtoMessage()); - return builder.build(); - } - - public static EvaluatedProposal fromProto(protobuf.EvaluatedProposal proto) { - return new EvaluatedProposal(proto.getIsAccepted(), - ProposalVoteResult.fromProto(proto.getProposalVoteResult())); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public Proposal getProposal() { - return proposalVoteResult.getProposal(); - } - - public String getProposalTxId() { - return getProposal().getTxId(); - } - - @Override - public String toString() { - return "EvaluatedProposal{" + - "\n isAccepted=" + isAccepted + - ",\n proposalVoteResult=" + proposalVoteResult + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/GenericProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/GenericProposal.java deleted file mode 100644 index fa8c71b556..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/GenericProposal.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.proposal.ProposalType; -import bisq.core.dao.state.model.ImmutableDaoStateModel; -import bisq.core.dao.state.model.blockchain.TxType; - -import bisq.common.app.Version; -import bisq.common.util.CollectionUtils; - -import java.util.Date; -import java.util.Map; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -@Immutable -@Slf4j -@EqualsAndHashCode(callSuper = true) -@Value -public final class GenericProposal extends Proposal implements ImmutableDaoStateModel { - - public GenericProposal(String name, - String link, - Map extraDataMap) { - this(name, - link, - Version.PROPOSAL, - new Date().getTime(), - null, - extraDataMap); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private GenericProposal(String name, - String link, - byte version, - long creationDate, - String txId, - Map extraDataMap) { - super(name, - link, - version, - creationDate, - txId, - extraDataMap); - } - - @Override - public protobuf.Proposal.Builder getProposalBuilder() { - final protobuf.GenericProposal.Builder builder = protobuf.GenericProposal.newBuilder(); - return super.getProposalBuilder().setGenericProposal(builder); - } - - public static GenericProposal fromProto(protobuf.Proposal proto) { - return new GenericProposal(proto.getName(), - proto.getLink(), - (byte) proto.getVersion(), - proto.getCreationDate(), - proto.getTxId(), - CollectionUtils.isEmpty(proto.getExtraDataMap()) ? - null : proto.getExtraDataMap()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Getters - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public ProposalType getType() { - return ProposalType.GENERIC; - } - - @Override - public Param getQuorumParam() { - return Param.QUORUM_GENERIC; - } - - @Override - public Param getThresholdParam() { - return Param.THRESHOLD_GENERIC; - } - - @Override - public TxType getTxType() { - return TxType.PROPOSAL; - } - - @Override - public Proposal cloneProposalAndAddTxId(String txId) { - return new GenericProposal(getName(), - getLink(), - getVersion(), - getCreationDate(), - txId, - extraDataMap); - } - - @Override - public String toString() { - return "GenericProposal{" + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/Issuance.java b/core/src/main/java/bisq/core/dao/state/model/governance/Issuance.java deleted file mode 100644 index 45888ac722..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/Issuance.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.ProtoUtil; -import bisq.common.proto.network.NetworkPayload; -import bisq.common.proto.persistable.PersistablePayload; - -import java.util.Objects; -import java.util.Optional; - -import lombok.Value; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** - * Holds the issuance data (compensation request which was accepted in voting). - */ -@Immutable -@Value -public class Issuance implements PersistablePayload, NetworkPayload, ImmutableDaoStateModel { - private final String txId; // comp. request txId - private final int chainHeight; // of issuance (first block of result phase) - private final long amount; - - // sig key as hex of first input in issuance tx used for signing the merits - // Can be null (payToPubKey tx) but in our case it will never be null. Still keep it nullable to be safe. - @Nullable - private final String pubKey; - - private final IssuanceType issuanceType; - - public Issuance(String txId, int chainHeight, long amount, @Nullable String pubKey, IssuanceType issuanceType) { - this.txId = txId; - this.chainHeight = chainHeight; - this.amount = amount; - this.pubKey = pubKey; - this.issuanceType = issuanceType; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public protobuf.Issuance toProtoMessage() { - final protobuf.Issuance.Builder builder = protobuf.Issuance.newBuilder() - .setTxId(txId) - .setChainHeight(chainHeight) - .setAmount(amount) - .setIssuanceType(issuanceType.name()); - Optional.ofNullable(pubKey).ifPresent(e -> builder.setPubKey(pubKey)); - return builder.build(); - } - - public static Issuance fromProto(protobuf.Issuance proto) { - return new Issuance(proto.getTxId(), - proto.getChainHeight(), - proto.getAmount(), - proto.getPubKey().isEmpty() ? null : proto.getPubKey(), - proto.getIssuanceType().isEmpty() ? IssuanceType.UNDEFINED : ProtoUtil.enumFromProto(IssuanceType.class, proto.getIssuanceType())); - } - - @Override - public String toString() { - return "Issuance{" + - "\n txId='" + txId + '\'' + - ",\n chainHeight=" + chainHeight + - ",\n amount=" + amount + - ",\n pubKey='" + pubKey + '\'' + - ",\n issuanceType='" + issuanceType + '\'' + - "\n}"; - } - - // 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 - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Issuance)) return false; - if (!super.equals(o)) return false; - Issuance issuance = (Issuance) o; - return chainHeight == issuance.chainHeight && - amount == issuance.amount && - Objects.equals(txId, issuance.txId) && - Objects.equals(pubKey, issuance.pubKey) && - issuanceType.name().equals(issuance.issuanceType.name()); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), txId, chainHeight, amount, pubKey, issuanceType.name()); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/IssuanceType.java b/core/src/main/java/bisq/core/dao/state/model/governance/IssuanceType.java deleted file mode 100644 index 1d0b14224a..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/IssuanceType.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import javax.annotation.concurrent.Immutable; - -@Immutable -public enum IssuanceType implements ImmutableDaoStateModel { - UNDEFINED, - COMPENSATION, - REIMBURSEMENT -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/Merit.java b/core/src/main/java/bisq/core/dao/state/model/governance/Merit.java deleted file mode 100644 index 7b449934b3..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/Merit.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.network.NetworkPayload; -import bisq.common.proto.persistable.PersistablePayload; -import bisq.common.util.Utilities; - -import com.google.protobuf.ByteString; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -import javax.annotation.concurrent.Immutable; - -@Immutable -@EqualsAndHashCode -public class Merit implements PersistablePayload, NetworkPayload, ConsensusCritical, ImmutableDaoStateModel { - @Getter - private final Issuance issuance; - @Getter - private final byte[] signature; - - public Merit(Issuance issuance, byte[] signature) { - this.issuance = issuance; - this.signature = signature; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.Merit toProtoMessage() { - final protobuf.Merit.Builder builder = protobuf.Merit.newBuilder() - .setIssuance(issuance.toProtoMessage()) - .setSignature(ByteString.copyFrom(signature)); - return builder.build(); - } - - public static Merit fromProto(protobuf.Merit proto) { - return new Merit(Issuance.fromProto(proto.getIssuance()), - proto.getSignature().toByteArray()); - } - - public String getIssuanceTxId() { - return issuance.getTxId(); - } - - @Override - public String toString() { - return "Merit{" + - "\n issuance=" + issuance + - ",\n signature=" + Utilities.bytesAsHexString(signature) + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/MeritList.java b/core/src/main/java/bisq/core/dao/state/model/governance/MeritList.java deleted file mode 100644 index 334ac98872..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/MeritList.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.Proto; - -import com.google.protobuf.InvalidProtocolBufferException; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.Value; - -@Value -public class MeritList implements Proto, ConsensusCritical, ImmutableDaoStateModel { - private final List list; - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.MeritList toProtoMessage() { - return getBuilder().build(); - } - - public protobuf.MeritList.Builder getBuilder() { - return protobuf.MeritList.newBuilder() - .addAllMerit(getList().stream() - .map(Merit::toProtoMessage) - .collect(Collectors.toList())); - } - - public static MeritList fromProto(protobuf.MeritList proto) { - return new MeritList(new ArrayList<>(proto.getMeritList().stream() - .map(Merit::fromProto) - .collect(Collectors.toList()))); - } - - public static MeritList getMeritListFromBytes(byte[] bytes) throws InvalidProtocolBufferException { - return MeritList.fromProto(protobuf.MeritList.parseFrom(bytes)); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/ParamChange.java b/core/src/main/java/bisq/core/dao/state/model/governance/ParamChange.java deleted file mode 100644 index 622d3272f1..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/ParamChange.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.persistable.PersistablePayload; - -import lombok.Value; - -import javax.annotation.concurrent.Immutable; - -/** - * Holds the data for a parameter change. Gets persisted with the DaoState. - */ -@Immutable -@Value -public class ParamChange implements PersistablePayload, ImmutableDaoStateModel { - // We use the enum name instead of the enum to be more flexible with changes at updates - private final String paramName; - private final String value; - private final int activationHeight; - - public ParamChange(String paramName, String value, int activationHeight) { - this.paramName = paramName; - this.value = value; - this.activationHeight = activationHeight; - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - - @Override - public protobuf.ParamChange toProtoMessage() { - return protobuf.ParamChange.newBuilder() - .setParamName(paramName) - .setParamValue(value) - .setActivationHeight(activationHeight) - .build(); - } - - public static ParamChange fromProto(protobuf.ParamChange proto) { - return new ParamChange(proto.getParamName(), - proto.getParamValue(), - proto.getActivationHeight()); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java deleted file mode 100644 index 585ffd9bc8..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.proposal.ProposalType; -import bisq.core.dao.state.model.ImmutableDaoStateModel; -import bisq.core.dao.state.model.blockchain.TxType; - -import bisq.common.proto.ProtobufferRuntimeException; -import bisq.common.proto.network.NetworkPayload; -import bisq.common.proto.persistable.PersistablePayload; -import bisq.common.util.ExtraDataMapValidator; - -import java.util.Date; -import java.util.Map; -import java.util.Optional; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** - * Base class for proposals. - */ -@Immutable -@Slf4j -@Getter -@EqualsAndHashCode -public abstract class Proposal implements PersistablePayload, NetworkPayload, ConsensusCritical, ImmutableDaoStateModel { - protected final String name; - protected final String link; - protected final byte version; - protected final long creationDate; - @Nullable - protected final String txId; - - // This hash map allows addition of data in future versions without breaking consensus - @Nullable - protected final Map extraDataMap; - - protected Proposal(String name, - String link, - byte version, - long creationDate, - @Nullable String txId, - @Nullable Map extraDataMap) { - this.name = name; - this.link = link; - this.version = version; - this.creationDate = creationDate; - this.txId = txId; - this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public protobuf.Proposal.Builder getProposalBuilder() { - final protobuf.Proposal.Builder builder = protobuf.Proposal.newBuilder() - .setName(name) - .setLink(link) - .setVersion(version) - .setCreationDate(creationDate); - Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData); - Optional.ofNullable(txId).ifPresent(builder::setTxId); - return builder; - } - - @Override - public protobuf.Proposal toProtoMessage() { - return getProposalBuilder().build(); - } - - public static Proposal fromProto(protobuf.Proposal proto) { - switch (proto.getMessageCase()) { - case COMPENSATION_PROPOSAL: - return CompensationProposal.fromProto(proto); - case REIMBURSEMENT_PROPOSAL: - return ReimbursementProposal.fromProto(proto); - case CHANGE_PARAM_PROPOSAL: - return ChangeParamProposal.fromProto(proto); - case ROLE_PROPOSAL: - return RoleProposal.fromProto(proto); - case CONFISCATE_BOND_PROPOSAL: - return ConfiscateBondProposal.fromProto(proto); - case GENERIC_PROPOSAL: - return GenericProposal.fromProto(proto); - case REMOVE_ASSET_PROPOSAL: - return RemoveAssetProposal.fromProto(proto); - case MESSAGE_NOT_SET: - default: - throw new ProtobufferRuntimeException("Unknown message case: " + proto); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Utils - /////////////////////////////////////////////////////////////////////////////////////////// - - public Date getCreationDateAsDate() { - return new Date(getCreationDate()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Abstract - /////////////////////////////////////////////////////////////////////////////////////////// - - public abstract Proposal cloneProposalAndAddTxId(String txId); - - public abstract ProposalType getType(); - - public abstract TxType getTxType(); - - public abstract Param getQuorumParam(); - - public abstract Param getThresholdParam(); - - @Override - public String toString() { - return "Proposal{" + - "\n txId='" + txId + '\'' + - ",\n name='" + name + '\'' + - ",\n link='" + link + '\'' + - ",\n txId='" + txId + '\'' + - ",\n version=" + version + - ",\n creationDate=" + new Date(creationDate) + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/ProposalVoteResult.java b/core/src/main/java/bisq/core/dao/state/model/governance/ProposalVoteResult.java deleted file mode 100644 index 765c97e9d6..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/ProposalVoteResult.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.persistable.PersistablePayload; - -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -import static com.google.common.base.Preconditions.checkArgument; - -@Immutable -@Value -@Slf4j -public class ProposalVoteResult implements PersistablePayload, ImmutableDaoStateModel { - private final Proposal proposal; - private final long stakeOfAcceptedVotes; - private final long stakeOfRejectedVotes; - private final int numAcceptedVotes; - private final int numRejectedVotes; - private final int numIgnoredVotes; - - public ProposalVoteResult(Proposal proposal, long stakeOfAcceptedVotes, long stakeOfRejectedVotes, - int numAcceptedVotes, int numRejectedVotes, int numIgnoredVotes) { - this.proposal = proposal; - this.stakeOfAcceptedVotes = stakeOfAcceptedVotes; - this.stakeOfRejectedVotes = stakeOfRejectedVotes; - this.numAcceptedVotes = numAcceptedVotes; - this.numRejectedVotes = numRejectedVotes; - this.numIgnoredVotes = numIgnoredVotes; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public protobuf.ProposalVoteResult toProtoMessage() { - protobuf.ProposalVoteResult.Builder builder = protobuf.ProposalVoteResult.newBuilder() - .setProposal(proposal.toProtoMessage()) - .setStakeOfAcceptedVotes(stakeOfAcceptedVotes) - .setStakeOfRejectedVotes(stakeOfRejectedVotes) - .setNumAcceptedVotes(numAcceptedVotes) - .setNumRejectedVotes(numRejectedVotes) - .setNumIgnoredVotes(numIgnoredVotes); - return builder.build(); - } - - public static ProposalVoteResult fromProto(protobuf.ProposalVoteResult proto) { - return new ProposalVoteResult(Proposal.fromProto(proto.getProposal()), - proto.getStakeOfAcceptedVotes(), - proto.getStakeOfRejectedVotes(), - proto.getNumAcceptedVotes(), - proto.getNumRejectedVotes(), - proto.getNumIgnoredVotes()); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public int getNumActiveVotes() { - return numAcceptedVotes + numRejectedVotes; - } - - public long getQuorum() { - // Quorum is sum of all votes independent if accepted or rejected. - log.debug("Quorum: proposalTxId: {}, totalStake: {}, stakeOfAcceptedVotes: {}, stakeOfRejectedVotes: {}", - proposal.getTxId(), getTotalStake(), stakeOfAcceptedVotes, stakeOfRejectedVotes); - return getTotalStake(); - } - - public long getThreshold() { - checkArgument(stakeOfAcceptedVotes >= 0, "stakeOfAcceptedVotes must not be negative"); - checkArgument(stakeOfRejectedVotes >= 0, "stakeOfRejectedVotes must not be negative"); - if (stakeOfAcceptedVotes == 0) { - return 0; - } - return stakeOfAcceptedVotes * 10_000 / getTotalStake(); - } - - @Override - public String toString() { - return "ProposalVoteResult{" + - "\n proposal=" + proposal + - ",\n stakeOfAcceptedVotes=" + stakeOfAcceptedVotes + - ",\n stakeOfRejectedVotes=" + stakeOfRejectedVotes + - ",\n numAcceptedVotes=" + numAcceptedVotes + - ",\n numRejectedVotes=" + numRejectedVotes + - ",\n numIgnoredVotes=" + numIgnoredVotes + - "\n}"; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private long getTotalStake() { - return stakeOfAcceptedVotes + stakeOfRejectedVotes; - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/ReimbursementProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/ReimbursementProposal.java deleted file mode 100644 index fb83ab23d5..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/ReimbursementProposal.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.proposal.IssuanceProposal; -import bisq.core.dao.governance.proposal.ProposalType; -import bisq.core.dao.state.model.ImmutableDaoStateModel; -import bisq.core.dao.state.model.blockchain.TxType; - -import bisq.common.app.Version; -import bisq.common.config.Config; -import bisq.common.util.CollectionUtils; - -import org.bitcoinj.core.AddressFormatException; -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.LegacyAddress; - -import java.util.Date; -import java.util.Map; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -@Immutable -@Slf4j -@EqualsAndHashCode(callSuper = true) -@Value -public final class ReimbursementProposal extends Proposal implements IssuanceProposal, ImmutableDaoStateModel { - private final long requestedBsq; - private final String bsqAddress; - - public ReimbursementProposal(String name, - String link, - Coin requestedBsq, - String bsqAddress, - Map extraDataMap) { - this(name, - link, - bsqAddress, - requestedBsq.value, - Version.REIMBURSEMENT_REQUEST, - new Date().getTime(), - null, - extraDataMap); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private ReimbursementProposal(String name, - String link, - String bsqAddress, - long requestedBsq, - byte version, - long creationDate, - String txId, - Map extraDataMap) { - super(name, - link, - version, - creationDate, - txId, - extraDataMap); - - this.requestedBsq = requestedBsq; - this.bsqAddress = bsqAddress; - } - - @Override - public protobuf.Proposal.Builder getProposalBuilder() { - final protobuf.ReimbursementProposal.Builder builder = protobuf.ReimbursementProposal.newBuilder() - .setBsqAddress(bsqAddress) - .setRequestedBsq(requestedBsq); - return super.getProposalBuilder().setReimbursementProposal(builder); - } - - public static ReimbursementProposal fromProto(protobuf.Proposal proto) { - final protobuf.ReimbursementProposal proposalProto = proto.getReimbursementProposal(); - return new ReimbursementProposal(proto.getName(), - proto.getLink(), - proposalProto.getBsqAddress(), - proposalProto.getRequestedBsq(), - (byte) proto.getVersion(), - proto.getCreationDate(), - proto.getTxId(), - CollectionUtils.isEmpty(proto.getExtraDataMap()) ? - null : proto.getExtraDataMap()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Getters - /////////////////////////////////////////////////////////////////////////////////////////// - - public Coin getRequestedBsq() { - return Coin.valueOf(requestedBsq); - } - - public LegacyAddress getAddress() throws AddressFormatException { - // Remove leading 'B' - String underlyingBtcAddress = bsqAddress.substring(1, bsqAddress.length()); - return LegacyAddress.fromBase58(Config.baseCurrencyNetworkParameters(), underlyingBtcAddress); - } - - - @Override - public ProposalType getType() { - return ProposalType.REIMBURSEMENT_REQUEST; - } - - @Override - public Param getQuorumParam() { - return Param.QUORUM_REIMBURSEMENT; - } - - @Override - public Param getThresholdParam() { - return Param.THRESHOLD_REIMBURSEMENT; - } - - @Override - public TxType getTxType() { - return TxType.REIMBURSEMENT_REQUEST; - } - - @Override - public Proposal cloneProposalAndAddTxId(String txId) { - return new ReimbursementProposal(getName(), - getLink(), - getBsqAddress(), - getRequestedBsq().value, - getVersion(), - getCreationDate(), - txId, - extraDataMap); - } - - @Override - public String toString() { - return "ReimbursementProposal{" + - "\n requestedBsq=" + requestedBsq + - ",\n bsqAddress='" + bsqAddress + '\'' + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/RemoveAssetProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/RemoveAssetProposal.java deleted file mode 100644 index db9cbf7522..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/RemoveAssetProposal.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.proposal.ProposalType; -import bisq.core.dao.state.model.ImmutableDaoStateModel; -import bisq.core.dao.state.model.blockchain.TxType; - -import bisq.common.app.Version; -import bisq.common.util.CollectionUtils; - -import java.util.Date; -import java.util.Map; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -@Immutable -@Slf4j -@EqualsAndHashCode(callSuper = true) -@Value -public final class RemoveAssetProposal extends Proposal implements ImmutableDaoStateModel { - private final String tickerSymbol; - - public RemoveAssetProposal(String name, - String link, - String tickerSymbol, - Map extraDataMap) { - this(name, - link, - tickerSymbol, - Version.PROPOSAL, - new Date().getTime(), - null, - extraDataMap); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private RemoveAssetProposal(String name, - String link, - String tickerSymbol, - byte version, - long creationDate, - String txId, - Map extraDataMap) { - super(name, - link, - version, - creationDate, - txId, - extraDataMap); - - this.tickerSymbol = tickerSymbol; - } - - @Override - public protobuf.Proposal.Builder getProposalBuilder() { - final protobuf.RemoveAssetProposal.Builder builder = protobuf.RemoveAssetProposal.newBuilder() - .setTickerSymbol(tickerSymbol); - return super.getProposalBuilder().setRemoveAssetProposal(builder); - } - - public static RemoveAssetProposal fromProto(protobuf.Proposal proto) { - final protobuf.RemoveAssetProposal proposalProto = proto.getRemoveAssetProposal(); - return new RemoveAssetProposal(proto.getName(), - proto.getLink(), - proposalProto.getTickerSymbol(), - (byte) proto.getVersion(), - proto.getCreationDate(), - proto.getTxId(), - CollectionUtils.isEmpty(proto.getExtraDataMap()) ? - null : proto.getExtraDataMap()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Getters - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public ProposalType getType() { - return ProposalType.REMOVE_ASSET; - } - - @Override - public Param getQuorumParam() { - return Param.QUORUM_REMOVE_ASSET; - } - - @Override - public Param getThresholdParam() { - return Param.THRESHOLD_REMOVE_ASSET; - } - - @Override - public TxType getTxType() { - return TxType.PROPOSAL; - } - - @Override - public Proposal cloneProposalAndAddTxId(String txId) { - return new RemoveAssetProposal(getName(), - getLink(), - getTickerSymbol(), - getVersion(), - getCreationDate(), - txId, - extraDataMap); - } - - @Override - public String toString() { - return "GenericProposal{" + - "\n tickerSymbol=" + tickerSymbol + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/Role.java b/core/src/main/java/bisq/core/dao/state/model/governance/Role.java deleted file mode 100644 index 4850b31541..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/Role.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.governance.bond.BondedAsset; -import bisq.core.dao.state.model.ImmutableDaoStateModel; -import bisq.core.locale.Res; - -import bisq.common.crypto.Hash; -import bisq.common.proto.ProtoUtil; -import bisq.common.proto.network.NetworkPayload; -import bisq.common.proto.persistable.PersistablePayload; - -import java.util.Objects; -import java.util.UUID; - -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -/** - * Immutable data for a role. Is stored in the DaoState as part of the evaluated proposals. - */ -@Immutable -@Slf4j -@Value -public final class Role implements PersistablePayload, NetworkPayload, BondedAsset, ImmutableDaoStateModel { - private final String uid; - private final String name; - private final String link; - private final BondedRoleType bondedRoleType; - - // Only used as cache - transient private final byte[] hash; - - /** - * @param name Full name or nickname - * @param link GitHub account or forum account of user - * @param bondedRoleType BondedRoleType - */ - public Role(String name, - String link, - BondedRoleType bondedRoleType) { - this(UUID.randomUUID().toString(), - name, - link, - bondedRoleType - ); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private Role(String uid, - String name, - String link, - BondedRoleType bondedRoleType) { - this.uid = uid; - this.name = name; - this.link = link; - this.bondedRoleType = bondedRoleType; - - hash = Hash.getSha256Ripemd160hash(toProtoMessage().toByteArray()); - } - - @Override - public protobuf.Role toProtoMessage() { - protobuf.Role.Builder builder = protobuf.Role.newBuilder() - .setUid(uid) - .setName(name) - .setLink(link) - .setBondedRoleType(bondedRoleType.name()); - return builder.build(); - } - - public static Role fromProto(protobuf.Role proto) { - return new Role(proto.getUid(), - proto.getName(), - proto.getLink(), - ProtoUtil.enumFromProto(BondedRoleType.class, proto.getBondedRoleType())); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // BondedAsset implementation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public byte[] getHash() { - return hash; - } - - @Override - public String getDisplayString() { - return Res.get("dao.bond.bondedRoleType." + bondedRoleType.name()) + ": " + name; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - // We use only the immutable data - // bondedRoleType 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 - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Role that = (Role) o; - return Objects.equals(uid, that.uid) && - Objects.equals(name, that.name) && - Objects.equals(link, that.link) && - bondedRoleType.name().equals(that.bondedRoleType.name()); - } - - @Override - public int hashCode() { - return Objects.hash(uid, name, link, bondedRoleType.name()); - } - - @Override - public String toString() { - return "Role{" + - "\n uid='" + uid + '\'' + - ",\n name='" + name + '\'' + - ",\n link='" + link + '\'' + - ",\n bondedRoleType=" + bondedRoleType + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/RoleProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/RoleProposal.java deleted file mode 100644 index a24a0b9542..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/RoleProposal.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.proposal.ProposalType; -import bisq.core.dao.state.model.ImmutableDaoStateModel; -import bisq.core.dao.state.model.blockchain.TxType; - -import bisq.common.app.Version; -import bisq.common.util.CollectionUtils; - -import java.util.Date; -import java.util.Map; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -@Immutable -@Slf4j -@EqualsAndHashCode(callSuper = true) -@Value -public final class RoleProposal extends Proposal implements ImmutableDaoStateModel { - private final Role role; - private final long requiredBondUnit; - private final int unlockTime; // in blocks - - public RoleProposal(Role role, Map extraDataMap) { - this(role.getName(), - role.getLink(), - role, - role.getBondedRoleType().getRequiredBondUnit(), - role.getBondedRoleType().getUnlockTimeInBlocks(), - Version.PROPOSAL, - new Date().getTime(), - null, - extraDataMap); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private RoleProposal(String name, - String link, - Role role, - long requiredBondUnit, - int unlockTime, - byte version, - long creationDate, - String txId, - Map extraDataMap) { - super(name, - link, - version, - creationDate, - txId, - extraDataMap); - - this.role = role; - this.requiredBondUnit = requiredBondUnit; - this.unlockTime = unlockTime; - } - - @Override - public protobuf.Proposal.Builder getProposalBuilder() { - final protobuf.RoleProposal.Builder builder = protobuf.RoleProposal.newBuilder() - .setRole(role.toProtoMessage()) - .setRequiredBondUnit(requiredBondUnit) - .setUnlockTime(unlockTime); - return super.getProposalBuilder().setRoleProposal(builder); - } - - public static RoleProposal fromProto(protobuf.Proposal proto) { - final protobuf.RoleProposal proposalProto = proto.getRoleProposal(); - return new RoleProposal(proto.getName(), - proto.getLink(), - Role.fromProto(proposalProto.getRole()), - proposalProto.getRequiredBondUnit(), - proposalProto.getUnlockTime(), - (byte) proto.getVersion(), - proto.getCreationDate(), - proto.getTxId(), - CollectionUtils.isEmpty(proto.getExtraDataMap()) ? - null : proto.getExtraDataMap()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Getters - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public ProposalType getType() { - return ProposalType.BONDED_ROLE; - } - - @Override - public Param getQuorumParam() { - return Param.QUORUM_ROLE; - } - - @Override - public Param getThresholdParam() { - return Param.THRESHOLD_ROLE; - } - - @Override - public TxType getTxType() { - return TxType.PROPOSAL; - } - - @Override - public Proposal cloneProposalAndAddTxId(String txId) { - return new RoleProposal(name, - link, - role, - requiredBondUnit, - unlockTime, - version, - creationDate, - txId, - extraDataMap); - } - - @Override - public String toString() { - return "RoleProposal{" + - "\n role=" + role + - "\n requiredBondUnit=" + requiredBondUnit + - "\n unlockTime=" + unlockTime + - "\n} " + super.toString(); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/Vote.java b/core/src/main/java/bisq/core/dao/state/model/governance/Vote.java deleted file mode 100644 index cb29cfbadd..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/Vote.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.state.model.governance; - -import bisq.core.dao.governance.ConsensusCritical; -import bisq.core.dao.state.model.ImmutableDaoStateModel; - -import bisq.common.proto.network.NetworkPayload; -import bisq.common.proto.persistable.PersistablePayload; - -import com.google.protobuf.Message; - -import lombok.Value; - -import javax.annotation.concurrent.Immutable; - -@Immutable -@Value -public class Vote implements PersistablePayload, NetworkPayload, ConsensusCritical, ImmutableDaoStateModel { - private boolean accepted; - - public Vote(boolean accepted) { - this.accepted = accepted; - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public Message toProtoMessage() { - return protobuf.Vote.newBuilder() - .setAccepted(accepted) - .build(); - } - - public static Vote fromProto(protobuf.Vote proto) { - return new Vote(proto.getAccepted()); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/package-info.java b/core/src/main/java/bisq/core/dao/state/model/governance/package-info.java deleted file mode 100644 index 171cf8dc03..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/governance/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -/** - * Contains all governance specific data used in the daoState. - */ - -package bisq.core.dao.state.model.governance; diff --git a/core/src/main/java/bisq/core/dao/state/model/package-info.java b/core/src/main/java/bisq/core/dao/state/model/package-info.java deleted file mode 100644 index 7d344b2a1c..0000000000 --- a/core/src/main/java/bisq/core/dao/state/model/package-info.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -/** - * This package contains all immutable model classes used in the daoState. DaoState is persisted and it can be seen as - * the immutable ledger of the Bisq DAO. - * We want to make very clear which data are contained here that's we we break up grouping by domains and moved all - * model classes here. - */ - -package bisq.core.dao.state.model; diff --git a/core/src/main/java/bisq/core/dao/state/storage/DaoStateStorageService.java b/core/src/main/java/bisq/core/dao/state/storage/DaoStateStorageService.java deleted file mode 100644 index ece0166da8..0000000000 --- a/core/src/main/java/bisq/core/dao/state/storage/DaoStateStorageService.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This file is part of Bisq. - * - * bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with bisq. If not, see . - */ - -package bisq.core.dao.state.storage; - -import bisq.core.dao.monitoring.DaoStateMonitoringService; -import bisq.core.dao.monitoring.model.DaoStateHash; -import bisq.core.dao.state.model.DaoState; - -import bisq.network.p2p.storage.persistence.ResourceDataStoreService; -import bisq.network.p2p.storage.persistence.StoreService; - -import bisq.common.config.Config; -import bisq.common.file.FileUtil; -import bisq.common.persistence.PersistenceManager; - -import javax.inject.Inject; -import javax.inject.Named; - -import java.io.File; -import java.io.IOException; - -import java.util.LinkedList; - -import lombok.extern.slf4j.Slf4j; - -/** - * Manages persistence of the daoState. - */ -@Slf4j -public class DaoStateStorageService extends StoreService { - private static final String FILE_NAME = "DaoStateStore"; - - private final DaoState daoState; - private final DaoStateMonitoringService daoStateMonitoringService; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public DaoStateStorageService(ResourceDataStoreService resourceDataStoreService, - DaoState daoState, - DaoStateMonitoringService daoStateMonitoringService, - @Named(Config.STORAGE_DIR) File storageDir, - PersistenceManager persistenceManager) { - super(storageDir, persistenceManager); - this.daoState = daoState; - this.daoStateMonitoringService = daoStateMonitoringService; - - resourceDataStoreService.addService(this); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public String getFileName() { - return FILE_NAME; - } - - public void requestPersistence(DaoState daoState, LinkedList daoStateHashChain) { - store.setDaoState(daoState); - store.setDaoStateHashChain(daoStateHashChain); - persistenceManager.requestPersistence(); - } - - public DaoState getPersistedBsqState() { - return store.getDaoState(); - } - - public LinkedList getPersistedDaoStateHashChain() { - return store.getDaoStateHashChain(); - } - - public void resyncDaoStateFromGenesis(Runnable resultHandler) { - store.setDaoState(new DaoState()); - store.setDaoStateHashChain(new LinkedList<>()); - persistenceManager.persistNow(resultHandler); - } - - public void resyncDaoStateFromResources(File storageDir) throws IOException { - // We delete all DAO consensus payload data and remove the daoState so it will rebuild from latest - // resource files. - long currentTime = System.currentTimeMillis(); - String backupDirName = "out_of_sync_dao_data"; - String newFileName = "BlindVoteStore_" + currentTime; - FileUtil.removeAndBackupFile(storageDir, new File(storageDir, "BlindVoteStore"), newFileName, backupDirName); - - newFileName = "ProposalStore_" + currentTime; - FileUtil.removeAndBackupFile(storageDir, new File(storageDir, "ProposalStore"), newFileName, backupDirName); - - // We also need to remove ballot list as it contains the proposals as well. It will be recreated at resync - newFileName = "BallotList_" + currentTime; - FileUtil.removeAndBackupFile(storageDir, new File(storageDir, "BallotList"), newFileName, backupDirName); - - newFileName = "UnconfirmedBsqChangeOutputList_" + currentTime; - FileUtil.removeAndBackupFile(storageDir, new File(storageDir, "UnconfirmedBsqChangeOutputList"), newFileName, backupDirName); - - newFileName = "DaoStateStore_" + currentTime; - FileUtil.removeAndBackupFile(storageDir, new File(storageDir, "DaoStateStore"), newFileName, backupDirName); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Protected - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected DaoStateStore createStore() { - return new DaoStateStore(DaoState.getClone(daoState), new LinkedList<>(daoStateMonitoringService.getDaoStateHashChain())); - } - - @Override - protected void initializePersistenceManager() { - persistenceManager.initialize(store, PersistenceManager.Source.NETWORK); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/storage/DaoStateStore.java b/core/src/main/java/bisq/core/dao/state/storage/DaoStateStore.java deleted file mode 100644 index fe99e39724..0000000000 --- a/core/src/main/java/bisq/core/dao/state/storage/DaoStateStore.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.storage; - -import bisq.core.dao.monitoring.model.DaoStateHash; -import bisq.core.dao.state.model.DaoState; - -import bisq.common.proto.persistable.PersistableEnvelope; - -import com.google.protobuf.Message; - -import java.util.LinkedList; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkNotNull; - - -@Slf4j -public class DaoStateStore implements PersistableEnvelope { - // DaoState is always a clone and must not be used for read access beside initial read from disc when we apply - // the snapshot! - @Getter - @Setter - private DaoState daoState; - @Getter - @Setter - private LinkedList daoStateHashChain; - - DaoStateStore(DaoState daoState, LinkedList daoStateHashChain) { - this.daoState = daoState; - this.daoStateHashChain = daoStateHashChain; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public Message toProtoMessage() { - checkNotNull(daoState, "daoState must not be null when toProtoMessage is invoked"); - protobuf.DaoStateStore.Builder builder = protobuf.DaoStateStore.newBuilder() - .setDaoState(daoState.getBsqStateBuilder()) - .addAllDaoStateHash(daoStateHashChain.stream() - .map(DaoStateHash::toProtoMessage) - .collect(Collectors.toList())); - return protobuf.PersistableEnvelope.newBuilder() - .setDaoStateStore(builder) - .build(); - } - - public static DaoStateStore fromProto(protobuf.DaoStateStore proto) { - LinkedList daoStateHashList = proto.getDaoStateHashList().isEmpty() ? - new LinkedList<>() : - new LinkedList<>(proto.getDaoStateHashList().stream() - .map(DaoStateHash::fromProto) - .collect(Collectors.toList())); - return new DaoStateStore(DaoState.fromProto(proto.getDaoState()), daoStateHashList); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/unconfirmed/UnconfirmedBsqChangeOutputList.java b/core/src/main/java/bisq/core/dao/state/unconfirmed/UnconfirmedBsqChangeOutputList.java deleted file mode 100644 index 2deae25697..0000000000 --- a/core/src/main/java/bisq/core/dao/state/unconfirmed/UnconfirmedBsqChangeOutputList.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.unconfirmed; - -import bisq.common.proto.persistable.PersistableList; - -import com.google.protobuf.Message; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import lombok.EqualsAndHashCode; - -@EqualsAndHashCode(callSuper = true) -public class UnconfirmedBsqChangeOutputList extends PersistableList { - - UnconfirmedBsqChangeOutputList() { - super(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - private UnconfirmedBsqChangeOutputList(List list) { - super(list); - } - - @Override - public Message toProtoMessage() { - return protobuf.PersistableEnvelope.newBuilder() - .setUnconfirmedBsqChangeOutputList(protobuf.UnconfirmedBsqChangeOutputList.newBuilder() - .addAllUnconfirmedTxOutput(getList().stream().map(UnconfirmedTxOutput::toProtoMessage).collect(Collectors.toList()))) - .build(); - } - - public static UnconfirmedBsqChangeOutputList fromProto(protobuf.UnconfirmedBsqChangeOutputList proto) { - return new UnconfirmedBsqChangeOutputList(new ArrayList<>(proto.getUnconfirmedTxOutputList().stream() - .map(UnconfirmedTxOutput::fromProto) - .collect(Collectors.toList()))); - } - - public boolean containsTxOutput(UnconfirmedTxOutput txOutput) { - return getList().stream().anyMatch(output -> output.getKey().equals(txOutput.getKey())); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/unconfirmed/UnconfirmedBsqChangeOutputListService.java b/core/src/main/java/bisq/core/dao/state/unconfirmed/UnconfirmedBsqChangeOutputListService.java deleted file mode 100644 index 4436c2bcb8..0000000000 --- a/core/src/main/java/bisq/core/dao/state/unconfirmed/UnconfirmedBsqChangeOutputListService.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.unconfirmed; - -import bisq.core.dao.state.model.blockchain.TxType; - -import bisq.common.app.DevEnv; -import bisq.common.persistence.PersistenceManager; -import bisq.common.proto.persistable.PersistedDataHost; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.Transaction; -import org.bitcoinj.core.TransactionConfidence; -import org.bitcoinj.core.TransactionInput; -import org.bitcoinj.core.TransactionOutput; -import org.bitcoinj.wallet.Wallet; - -import javax.inject.Inject; - -import java.util.List; -import java.util.Objects; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class UnconfirmedBsqChangeOutputListService implements PersistedDataHost { - private final UnconfirmedBsqChangeOutputList unconfirmedBsqChangeOutputList = new UnconfirmedBsqChangeOutputList(); - private final PersistenceManager persistenceManager; - - @Inject - public UnconfirmedBsqChangeOutputListService(PersistenceManager persistenceManager) { - this.persistenceManager = persistenceManager; - - this.persistenceManager.initialize(unconfirmedBsqChangeOutputList, PersistenceManager.Source.PRIVATE); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PersistedDataHost - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void readPersisted(Runnable completeHandler) { - if (DevEnv.isDaoActivated()) { - persistenceManager.readPersisted(persisted -> { - unconfirmedBsqChangeOutputList.setAll(persisted.getList()); - completeHandler.run(); - }, - completeHandler); - } else { - completeHandler.run(); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Once a tx gets committed to our BSQ wallet we store the change output for allowing it to be spent in follow-up - * transactions. - */ - public void onCommitTx(Transaction tx, TxType txType, Wallet wallet) { - // We remove all potential connected outputs from our inputs as they would have been spent. - removeConnectedOutputsOfInputsOfTx(tx); - - int changeOutputIndex; - switch (txType) { - case UNDEFINED_TX_TYPE: - case UNVERIFIED: - case INVALID: - case GENESIS: - return; - case TRANSFER_BSQ: - changeOutputIndex = 1; // output 0 is receiver's address - break; - case PAY_TRADE_FEE: - changeOutputIndex = 0; - break; - case PROPOSAL: - changeOutputIndex = 0; - break; - case COMPENSATION_REQUEST: - case REIMBURSEMENT_REQUEST: - changeOutputIndex = 0; - break; - case BLIND_VOTE: - changeOutputIndex = 1; // output 0 is stake - break; - case VOTE_REVEAL: - changeOutputIndex = 0; - break; - case LOCKUP: - changeOutputIndex = 1; // output 0 is lockup amount - break; - case UNLOCK: - // We don't allow to spend the unlocking funds as there is the lock time which need to pass, - // otherwise the funds get burned! - return; - case ASSET_LISTING_FEE: - changeOutputIndex = 0; - break; - case PROOF_OF_BURN: - changeOutputIndex = 0; - break; - case IRREGULAR: - return; - default: - return; - } - - // It can be that we don't have a BSQ and a BTC change output. - // If no BSQ change but a BTC change the index points to the BTC output and then - // we detect that it is not part of our wallet. - // If there is a BSQ change but no BTC change it has no effect as we ignore BTC outputs anyway. - // If both change outputs do not exist then we might point to an index outside - // of the list and we return at our scope check. - - // If no BTC output (unlikely but - // possible) the index points to the BTC output and then we detect that it is not part of our wallet. - // - List outputs = tx.getOutputs(); - if (changeOutputIndex > outputs.size() - 1) - return; - - TransactionOutput change = outputs.get(changeOutputIndex); - if (!change.isMine(wallet)) - return; - - UnconfirmedTxOutput txOutput = UnconfirmedTxOutput.fromTransactionOutput(change); - if (unconfirmedBsqChangeOutputList.containsTxOutput(txOutput)) - return; - - unconfirmedBsqChangeOutputList.add(txOutput); - requestPersistence(); - } - - public void onReorganize() { - reset(); - } - - public void onSpvResync() { - reset(); - } - - public void onTransactionConfidenceChanged(Transaction tx) { - if (tx != null && - tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) { - removeConnectedOutputsOfInputsOfTx(tx); - - tx.getOutputs().forEach(transactionOutput -> { - UnconfirmedTxOutput txOutput = UnconfirmedTxOutput.fromTransactionOutput(transactionOutput); - if (unconfirmedBsqChangeOutputList.containsTxOutput(txOutput)) { - unconfirmedBsqChangeOutputList.remove(txOutput); - } - }); - } - } - - public boolean hasTransactionOutput(TransactionOutput output) { - return unconfirmedBsqChangeOutputList.containsTxOutput(UnconfirmedTxOutput.fromTransactionOutput(output)); - } - - public Coin getBalance() { - return Coin.valueOf(unconfirmedBsqChangeOutputList.stream().mapToLong(UnconfirmedTxOutput::getValue).sum()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void removeConnectedOutputsOfInputsOfTx(Transaction tx) { - tx.getInputs().stream() - .map(TransactionInput::getConnectedOutput) - .filter(Objects::nonNull) - .map(UnconfirmedTxOutput::fromTransactionOutput) - .filter(unconfirmedBsqChangeOutputList::containsTxOutput) - .forEach(txOutput -> { - unconfirmedBsqChangeOutputList.remove(txOutput); - requestPersistence(); - }); - } - - private void reset() { - unconfirmedBsqChangeOutputList.clear(); - requestPersistence(); - } - - private void requestPersistence() { - persistenceManager.requestPersistence(); - } -} diff --git a/core/src/main/java/bisq/core/dao/state/unconfirmed/UnconfirmedTxOutput.java b/core/src/main/java/bisq/core/dao/state/unconfirmed/UnconfirmedTxOutput.java deleted file mode 100644 index 48f7550dc9..0000000000 --- a/core/src/main/java/bisq/core/dao/state/unconfirmed/UnconfirmedTxOutput.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state.unconfirmed; - -import bisq.core.dao.state.model.ImmutableDaoStateModel; -import bisq.core.dao.state.model.blockchain.TxOutputKey; - -import bisq.common.proto.persistable.PersistablePayload; - -import org.bitcoinj.core.Transaction; -import org.bitcoinj.core.TransactionOutput; - -import lombok.Data; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.concurrent.Immutable; - -/** - * Used for tracking unconfirmed change outputs to allow them to be spent in follow up - * transactions in txType permits it. We can assume that the user is not intending to - * double spend own transactions as well that he does not try to spend an invalid BSQ - * output to a BSQ address. - * We do not allow spending unconfirmed BSQ outputs received from elsewhere. - */ -@Slf4j -@Immutable -@Data -public final class UnconfirmedTxOutput implements PersistablePayload, ImmutableDaoStateModel { - - public static UnconfirmedTxOutput fromTransactionOutput(TransactionOutput transactionOutput) { - Transaction parentTransaction = transactionOutput.getParentTransaction(); - if (parentTransaction != null) { - return new UnconfirmedTxOutput(transactionOutput.getIndex(), - transactionOutput.getValue().value, - parentTransaction.getTxId().toString()); - } else { - log.warn("parentTransaction of transactionOutput is null. " + - "This must not happen. " + - "We could throw an exception as well " + - "here but we prefer to be for now more tolerant and just " + - "assign the value 0 if that would be the case. transactionOutput={}", - transactionOutput); - return new UnconfirmedTxOutput(transactionOutput.getIndex(), - 0, - "null"); - } - } - - protected final int index; - protected final long value; - protected final String txId; - - private UnconfirmedTxOutput(int index, - long value, - String txId) { - this.index = index; - this.value = value; - this.txId = txId; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - public protobuf.UnconfirmedTxOutput toProtoMessage() { - return protobuf.UnconfirmedTxOutput.newBuilder() - .setIndex(index) - .setValue(value) - .setTxId(txId).build(); - } - - public static UnconfirmedTxOutput fromProto(protobuf.UnconfirmedTxOutput proto) { - return new UnconfirmedTxOutput(proto.getIndex(), - proto.getValue(), - proto.getTxId()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public TxOutputKey getKey() { - return new TxOutputKey(txId, index); - } - - @Override - public String toString() { - return "UnconfirmedTxOutput{" + - "\n index=" + index + - ",\n value=" + value + - ",\n txId='" + txId + '\'' + - "\n}"; - } -} diff --git a/core/src/main/java/bisq/core/filter/Filter.java b/core/src/main/java/bisq/core/filter/Filter.java index be43c3f746..5f91ee806a 100644 --- a/core/src/main/java/bisq/core/filter/Filter.java +++ b/core/src/main/java/bisq/core/filter/Filter.java @@ -69,8 +69,6 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload { // The pub key used for the data protection in the p2p storage private final byte[] ownerPubKeyBytes; - private final boolean disableDao; - private final String disableDaoBelowVersion; private final String disableTradeBelowVersion; private final List mediators; private final List refundAgents; @@ -113,8 +111,6 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload { filter.getPriceRelayNodes(), filter.isPreventPublicBtcNetwork(), filter.getBtcNodes(), - filter.isDisableDao(), - filter.getDisableDaoBelowVersion(), filter.getDisableTradeBelowVersion(), filter.getMediators(), filter.getRefundAgents(), @@ -145,8 +141,6 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload { filter.getPriceRelayNodes(), filter.isPreventPublicBtcNetwork(), filter.getBtcNodes(), - filter.isDisableDao(), - filter.getDisableDaoBelowVersion(), filter.getDisableTradeBelowVersion(), filter.getMediators(), filter.getRefundAgents(), @@ -175,8 +169,6 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload { List priceRelayNodes, boolean preventPublicBtcNetwork, List btcNodes, - boolean disableDao, - String disableDaoBelowVersion, String disableTradeBelowVersion, List mediators, List refundAgents, @@ -200,8 +192,6 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload { priceRelayNodes, preventPublicBtcNetwork, btcNodes, - disableDao, - disableDaoBelowVersion, disableTradeBelowVersion, mediators, refundAgents, @@ -236,8 +226,6 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload { List priceRelayNodes, boolean preventPublicBtcNetwork, List btcNodes, - boolean disableDao, - String disableDaoBelowVersion, String disableTradeBelowVersion, List mediators, List refundAgents, @@ -264,8 +252,6 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload { this.priceRelayNodes = priceRelayNodes; this.preventPublicBtcNetwork = preventPublicBtcNetwork; this.btcNodes = btcNodes; - this.disableDao = disableDao; - this.disableDaoBelowVersion = disableDaoBelowVersion; this.disableTradeBelowVersion = disableTradeBelowVersion; this.mediators = mediators; this.refundAgents = refundAgents; @@ -307,8 +293,6 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload { .addAllPriceRelayNodes(priceRelayNodes) .setPreventPublicBtcNetwork(preventPublicBtcNetwork) .addAllBtcNodes(btcNodes) - .setDisableDao(disableDao) - .setDisableDaoBelowVersion(disableDaoBelowVersion) .setDisableTradeBelowVersion(disableTradeBelowVersion) .addAllMediators(mediators) .addAllRefundAgents(refundAgents) @@ -346,8 +330,6 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload { ProtoUtil.protocolStringListToList(proto.getPriceRelayNodesList()), proto.getPreventPublicBtcNetwork(), ProtoUtil.protocolStringListToList(proto.getBtcNodesList()), - proto.getDisableDao(), - proto.getDisableDaoBelowVersion(), proto.getDisableTradeBelowVersion(), ProtoUtil.protocolStringListToList(proto.getMediatorsList()), ProtoUtil.protocolStringListToList(proto.getRefundAgentsList()), @@ -394,8 +376,6 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload { ",\n signatureAsBase64='" + signatureAsBase64 + '\'' + ",\n signerPubKeyAsHex='" + signerPubKeyAsHex + '\'' + ",\n ownerPubKeyBytes=" + Utilities.bytesAsHexString(ownerPubKeyBytes) + - ",\n disableDao=" + disableDao + - ",\n disableDaoBelowVersion='" + disableDaoBelowVersion + '\'' + ",\n disableTradeBelowVersion='" + disableTradeBelowVersion + '\'' + ",\n mediators=" + mediators + ",\n refundAgents=" + refundAgents + diff --git a/core/src/main/java/bisq/core/filter/FilterManager.java b/core/src/main/java/bisq/core/filter/FilterManager.java index 163ddbfee3..542d73d152 100644 --- a/core/src/main/java/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/bisq/core/filter/FilterManager.java @@ -242,13 +242,6 @@ public class FilterManager { if (requireUpdateToNewVersionForTrading()) { filterWarningHandler.accept(Res.get("popup.warning.mandatoryUpdate.trading")); } - - if (requireUpdateToNewVersionForDAO()) { - filterWarningHandler.accept(Res.get("popup.warning.mandatoryUpdate.dao")); - } - if (filter.isDisableDao()) { - filterWarningHandler.accept(Res.get("popup.warning.disable.dao")); - } } }); } @@ -436,20 +429,6 @@ public class FilterManager { return requireUpdateToNewVersion; } - public boolean requireUpdateToNewVersionForDAO() { - if (getFilter() == null) { - return false; - } - - boolean requireUpdateToNewVersion = false; - String disableDaoBelowVersion = getFilter().getDisableDaoBelowVersion(); - if (disableDaoBelowVersion != null && !disableDaoBelowVersion.isEmpty()) { - requireUpdateToNewVersion = Version.isNewVersion(disableDaoBelowVersion); - } - - return requireUpdateToNewVersion; - } - public boolean arePeersPaymentAccountDataBanned(PaymentAccountPayload paymentAccountPayload) { return getFilter() != null && getFilter().getBannedPaymentAccounts().stream() diff --git a/core/src/main/java/bisq/core/locale/CurrencyUtil.java b/core/src/main/java/bisq/core/locale/CurrencyUtil.java index 6bd9c65cc3..a755a697a0 100644 --- a/core/src/main/java/bisq/core/locale/CurrencyUtil.java +++ b/core/src/main/java/bisq/core/locale/CurrencyUtil.java @@ -17,14 +17,12 @@ package bisq.core.locale; -import bisq.core.dao.governance.asset.AssetService; import bisq.core.filter.FilterManager; import bisq.asset.Asset; import bisq.asset.AssetRegistry; import bisq.asset.Coin; import bisq.asset.Token; -import bisq.asset.coins.BSQ; import bisq.common.app.DevEnv; import bisq.common.config.BaseCurrencyNetwork; @@ -130,7 +128,6 @@ public class CurrencyUtil { public static Stream getSortedAssetStream() { return assetRegistry.stream() .filter(CurrencyUtil::assetIsNotBaseCurrency) - .filter(asset -> isNotBsqOrBsqTradingActivated(asset, Config.baseCurrencyNetwork(), DevEnv.isDaoTradingActivated())) .filter(asset -> assetMatchesNetworkIfMainnet(asset, Config.baseCurrencyNetwork())) .sorted(Comparator.comparing(Asset::getName)); } @@ -138,10 +135,6 @@ public class CurrencyUtil { public static List getMainCryptoCurrencies() { final List result = new ArrayList<>(); result.add(new CryptoCurrency("XRC", "Bitcoin Rhodium")); - - if (DevEnv.isDaoTradingActivated()) - result.add(new CryptoCurrency("BSQ", "BSQ")); - result.add(new CryptoCurrency("BEAM", "Beam")); result.add(new CryptoCurrency("DASH", "Dash")); result.add(new CryptoCurrency("DCR", "Decred")); @@ -585,19 +578,14 @@ public class CurrencyUtil { return new CryptoCurrency(asset.getTickerSymbol(), asset.getName(), asset instanceof Token); } - private static boolean isNotBsqOrBsqTradingActivated(Asset asset, - BaseCurrencyNetwork baseCurrencyNetwork, - boolean daoTradingActivated) { - return !(asset instanceof BSQ) || - daoTradingActivated && assetMatchesNetwork(asset, baseCurrencyNetwork); - } + public static boolean assetMatchesCurrencyCode(Asset asset, String currencyCode) { return currencyCode.equals(asset.getTickerSymbol()); } public static Optional findAsset(AssetRegistry assetRegistry, String currencyCode, - BaseCurrencyNetwork baseCurrencyNetwork, boolean daoTradingActivated) { + BaseCurrencyNetwork baseCurrencyNetwork) { List assets = assetRegistry.stream() .filter(asset -> assetMatchesCurrencyCode(asset, currencyCode)).collect(Collectors.toList()); @@ -605,8 +593,6 @@ public class CurrencyUtil { if (!assets.stream().findFirst().isPresent()) return Optional.empty(); - if (currencyCode.equals("BSQ") && baseCurrencyNetwork.isMainnet() && !daoTradingActivated) - return Optional.empty(); // We check for exact match with network, e.g. BTC$TESTNET Optional optionalAssetMatchesNetwork = assets.stream() @@ -641,11 +627,9 @@ public class CurrencyUtil { .findAny(); } - // Excludes all assets which got removed by DAO voting - public static List getActiveSortedCryptoCurrencies(AssetService assetService, - FilterManager filterManager) { + // Excludes all assets which got removed by voting + public static List getActiveSortedCryptoCurrencies(FilterManager filterManager) { return getAllSortedCryptoCurrencies().stream() - .filter(e -> e.getCode().equals("BSQ") || assetService.isActive(e.getCode())) .filter(e -> !filterManager.isCurrencyBanned(e.getCode())) .collect(Collectors.toList()); } diff --git a/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequestHandler.java b/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequestHandler.java index 6bd646bc29..f1ae40999f 100644 --- a/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequestHandler.java +++ b/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequestHandler.java @@ -17,13 +17,6 @@ package bisq.core.network.p2p.inventory; -import bisq.core.dao.monitoring.BlindVoteStateMonitoringService; -import bisq.core.dao.monitoring.DaoStateMonitoringService; -import bisq.core.dao.monitoring.ProposalStateMonitoringService; -import bisq.core.dao.monitoring.model.BlindVoteStateBlock; -import bisq.core.dao.monitoring.model.DaoStateBlock; -import bisq.core.dao.monitoring.model.ProposalStateBlock; -import bisq.core.dao.state.DaoStateService; import bisq.core.filter.Filter; import bisq.core.filter.FilterManager; import bisq.core.network.p2p.inventory.messages.GetInventoryRequest; @@ -65,10 +58,6 @@ public class GetInventoryRequestHandler implements MessageListener { private final NetworkNode networkNode; private final PeerManager peerManager; private final P2PDataStorage p2PDataStorage; - private final DaoStateService daoStateService; - private final DaoStateMonitoringService daoStateMonitoringService; - private final ProposalStateMonitoringService proposalStateMonitoringService; - private final BlindVoteStateMonitoringService blindVoteStateMonitoringService; private final FilterManager filterManager; private final int maxConnections; @@ -76,19 +65,11 @@ public class GetInventoryRequestHandler implements MessageListener { public GetInventoryRequestHandler(NetworkNode networkNode, PeerManager peerManager, P2PDataStorage p2PDataStorage, - DaoStateService daoStateService, - DaoStateMonitoringService daoStateMonitoringService, - ProposalStateMonitoringService proposalStateMonitoringService, - BlindVoteStateMonitoringService blindVoteStateMonitoringService, FilterManager filterManager, @Named(Config.MAX_CONNECTIONS) int maxConnections) { this.networkNode = networkNode; this.peerManager = peerManager; this.p2PDataStorage = p2PDataStorage; - this.daoStateService = daoStateService; - this.daoStateMonitoringService = daoStateMonitoringService; - this.proposalStateMonitoringService = proposalStateMonitoringService; - this.blindVoteStateMonitoringService = blindVoteStateMonitoringService; this.filterManager = filterManager; this.maxConnections = maxConnections; @@ -111,30 +92,7 @@ public class GetInventoryRequestHandler implements MessageListener { Map inventory = new HashMap<>(); dataObjects.forEach((key, value) -> inventory.put(key, String.valueOf(value))); - // DAO - int numBsqBlocks = daoStateService.getBlocks().size(); - inventory.put(InventoryItem.numBsqBlocks, String.valueOf(numBsqBlocks)); - int daoStateChainHeight = daoStateService.getChainHeight(); - inventory.put(InventoryItem.daoStateChainHeight, String.valueOf(daoStateChainHeight)); - - LinkedList daoStateBlockChain = daoStateMonitoringService.getDaoStateBlockChain(); - if (!daoStateBlockChain.isEmpty()) { - String daoStateHash = Utilities.bytesAsHexString(daoStateBlockChain.getLast().getMyStateHash().getHash()); - inventory.put(InventoryItem.daoStateHash, daoStateHash); - } - - LinkedList proposalStateBlockChain = proposalStateMonitoringService.getProposalStateBlockChain(); - if (!proposalStateBlockChain.isEmpty()) { - String proposalHash = Utilities.bytesAsHexString(proposalStateBlockChain.getLast().getMyStateHash().getHash()); - inventory.put(InventoryItem.proposalHash, proposalHash); - } - - LinkedList blindVoteStateBlockChain = blindVoteStateMonitoringService.getBlindVoteStateBlockChain(); - if (!blindVoteStateBlockChain.isEmpty()) { - String blindVoteHash = Utilities.bytesAsHexString(blindVoteStateBlockChain.getLast().getMyStateHash().getHash()); - inventory.put(InventoryItem.blindVoteHash, blindVoteHash); - } // network inventory.put(InventoryItem.maxConnections, String.valueOf(maxConnections)); diff --git a/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationOfHashes.java b/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationOfHashes.java index 6ca4afc76f..fe2c2402af 100644 --- a/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationOfHashes.java +++ b/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationOfHashes.java @@ -33,8 +33,7 @@ import org.jetbrains.annotations.Nullable; public class DeviationOfHashes implements DeviationType { public DeviationSeverity getDeviationSeverity(Collection> collection, @Nullable String value, - InventoryItem inventoryItem, - String currentBlockHeight) { + InventoryItem inventoryItem) { DeviationSeverity deviationSeverity = DeviationSeverity.OK; if (value == null) { return deviationSeverity; @@ -45,7 +44,6 @@ public class DeviationOfHashes implements DeviationType { .filter(list -> !list.isEmpty()) .map(list -> list.get(list.size() - 1)) // We use last item only .map(RequestInfo::getDataMap) - .filter(map -> currentBlockHeight.equals(map.get(InventoryItem.daoStateChainHeight).getValue())) .map(map -> map.get(inventoryItem).getValue()) .filter(Objects::nonNull) .forEach(v -> { diff --git a/core/src/main/java/bisq/core/network/p2p/inventory/model/InventoryItem.java b/core/src/main/java/bisq/core/network/p2p/inventory/model/InventoryItem.java index 4f91a2023b..fa8976c167 100644 --- a/core/src/main/java/bisq/core/network/p2p/inventory/model/InventoryItem.java +++ b/core/src/main/java/bisq/core/network/p2p/inventory/model/InventoryItem.java @@ -60,36 +60,6 @@ public enum InventoryItem { true, new DeviationByIntegerDiff(1, 1), 2), - // Should be very close values - TempProposalPayload("TempProposalPayload", - true, - new DeviationByIntegerDiff(3, 5), 2), - ProposalPayload("ProposalPayload", - true, - new DeviationByIntegerDiff(1, 2), 2), - BlindVotePayload("BlindVotePayload", - true, - new DeviationByIntegerDiff(1, 2), 2), - - // Should be very close values - daoStateChainHeight("daoStateChainHeight", - true, - new DeviationByIntegerDiff(2, 4), 3), - numBsqBlocks("numBsqBlocks", - true, - new DeviationByIntegerDiff(2, 4), 3), - - // Has to be same values at same block - daoStateHash("daoStateHash", - false, - new DeviationOfHashes(), 1), - proposalHash("proposalHash", - false, - new DeviationOfHashes(), 1), - blindVoteHash("blindVoteHash", - false, - new DeviationOfHashes(), 1), - // Percentage deviation maxConnections("maxConnections", true, @@ -172,8 +142,7 @@ public enum InventoryItem { public DeviationSeverity getDeviationSeverity(Double deviation, Collection> collection, - @Nullable String value, - String currentBlockHeight) { + @Nullable String value) { if (deviationType == null || deviation == null || value == null) { return DeviationSeverity.OK; } @@ -183,7 +152,7 @@ public enum InventoryItem { } else if (deviationType instanceof DeviationByIntegerDiff) { return ((DeviationByIntegerDiff) deviationType).getDeviationSeverity(collection, value, this); } else if (deviationType instanceof DeviationOfHashes) { - return ((DeviationOfHashes) deviationType).getDeviationSeverity(collection, value, this, currentBlockHeight); + return ((DeviationOfHashes) deviationType).getDeviationSeverity(collection, value, this); } else { return DeviationSeverity.OK; } diff --git a/core/src/main/java/bisq/core/offer/CreateOfferService.java b/core/src/main/java/bisq/core/offer/CreateOfferService.java index f30412a037..9d6c917f41 100644 --- a/core/src/main/java/bisq/core/offer/CreateOfferService.java +++ b/core/src/main/java/bisq/core/offer/CreateOfferService.java @@ -168,7 +168,6 @@ public class CreateOfferService { Coin txFeeFromFeeService = getEstimatedFeeAndTxVsize(amount, direction, buyerSecurityDepositAsDouble, sellerSecurityDeposit).first; Coin txFeeToUse = txFee.isPositive() ? txFee : txFeeFromFeeService; Coin makerFeeAsCoin = offerUtil.getMakerFee(amount); - boolean isCurrencyForMakerFeeBtc = offerUtil.isCurrencyForMakerFeeBtc(amount); Coin buyerSecurityDepositAsCoin = getBuyerSecurityDeposit(amount, buyerSecurityDepositAsDouble); Coin sellerSecurityDepositAsCoin = getSellerSecurityDeposit(amount, sellerSecurityDeposit); long maxTradeLimit = offerUtil.getMaxTradeLimit(paymentAccount, currencyCode, direction); @@ -218,7 +217,6 @@ public class CreateOfferService { btcWalletService.getLastBlockSeenHeight(), txFeeToUse.value, makerFeeAsCoin.value, - isCurrencyForMakerFeeBtc, buyerSecurityDepositAsCoin.value, sellerSecurityDepositAsCoin.value, maxTradeLimit, diff --git a/core/src/main/java/bisq/core/offer/Offer.java b/core/src/main/java/bisq/core/offer/Offer.java index ce1a51062d..b96e7ac3c3 100644 --- a/core/src/main/java/bisq/core/offer/Offer.java +++ b/core/src/main/java/bisq/core/offer/Offer.java @@ -286,10 +286,6 @@ public class Offer implements NetworkPayload, PersistablePayload { return Coin.valueOf(offerPayload.getMakerFee()); } - public boolean isCurrencyForMakerFeeBtc() { - return offerPayload.isCurrencyForMakerFeeBtc(); - } - public Coin getBuyerSecurityDeposit() { return Coin.valueOf(offerPayload.getBuyerSecurityDeposit()); } diff --git a/core/src/main/java/bisq/core/offer/OfferPayload.java b/core/src/main/java/bisq/core/offer/OfferPayload.java index b44b8b0f2b..9c98429c7e 100644 --- a/core/src/main/java/bisq/core/offer/OfferPayload.java +++ b/core/src/main/java/bisq/core/offer/OfferPayload.java @@ -137,7 +137,6 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay private final long blockHeightAtOfferCreation; private final long txFee; private final long makerFee; - private final boolean isCurrencyForMakerFeeBtc; private final long buyerSecurityDeposit; private final long sellerSecurityDeposit; private final long maxTradeLimit; @@ -206,7 +205,6 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay long blockHeightAtOfferCreation, long txFee, long makerFee, - boolean isCurrencyForMakerFeeBtc, long buyerSecurityDeposit, long sellerSecurityDeposit, long maxTradeLimit, @@ -245,7 +243,6 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay this.blockHeightAtOfferCreation = blockHeightAtOfferCreation; this.txFee = txFee; this.makerFee = makerFee; - this.isCurrencyForMakerFeeBtc = isCurrencyForMakerFeeBtc; this.buyerSecurityDeposit = buyerSecurityDeposit; this.sellerSecurityDeposit = sellerSecurityDeposit; this.maxTradeLimit = maxTradeLimit; @@ -288,7 +285,6 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay .setBlockHeightAtOfferCreation(blockHeightAtOfferCreation) .setTxFee(txFee) .setMakerFee(makerFee) - .setIsCurrencyForMakerFeeBtc(isCurrencyForMakerFeeBtc) .setBuyerSecurityDeposit(buyerSecurityDeposit) .setSellerSecurityDeposit(sellerSecurityDeposit) .setMaxTradeLimit(maxTradeLimit) @@ -349,7 +345,6 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay proto.getBlockHeightAtOfferCreation(), proto.getTxFee(), proto.getMakerFee(), - proto.getIsCurrencyForMakerFeeBtc(), proto.getBuyerSecurityDeposit(), proto.getSellerSecurityDeposit(), proto.getMaxTradeLimit(), @@ -417,7 +412,6 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay ",\n blockHeightAtOfferCreation=" + blockHeightAtOfferCreation + ",\n txFee=" + txFee + ",\n makerFee=" + makerFee + - ",\n isCurrencyForMakerFeeBtc=" + isCurrencyForMakerFeeBtc + ",\n buyerSecurityDeposit=" + buyerSecurityDeposit + ",\n sellerSecurityDeposit=" + sellerSecurityDeposit + ",\n maxTradeLimit=" + maxTradeLimit + diff --git a/core/src/main/java/bisq/core/offer/OfferUtil.java b/core/src/main/java/bisq/core/offer/OfferUtil.java index 714a27f5b9..fd5f343f5d 100644 --- a/core/src/main/java/bisq/core/offer/OfferUtil.java +++ b/core/src/main/java/bisq/core/offer/OfferUtil.java @@ -18,7 +18,6 @@ package bisq.core.offer; import bisq.core.account.witness.AccountAgeWitnessService; -import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.filter.FilterManager; import bisq.core.locale.CurrencyUtil; @@ -32,18 +31,14 @@ import bisq.core.provider.fee.FeeService; import bisq.core.provider.price.MarketPrice; import bisq.core.provider.price.PriceFeedService; import bisq.core.trade.statistics.ReferralIdService; -import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.user.AutoConfirmSettings; import bisq.core.user.Preferences; -import bisq.core.util.AveragePriceUtil; -import bisq.core.util.coin.CoinFormatter; import bisq.core.util.coin.CoinUtil; import bisq.network.p2p.P2PService; import bisq.common.app.Capabilities; import bisq.common.util.MathUtils; -import bisq.common.util.Tuple2; import org.bitcoinj.core.Coin; import org.bitcoinj.core.Transaction; @@ -67,8 +62,6 @@ import static bisq.common.util.MathUtils.roundDoubleToLong; import static bisq.common.util.MathUtils.scaleUpByPowerOf10; import static bisq.core.btc.wallet.Restrictions.getMaxBuyerSecurityDepositAsPercent; import static bisq.core.btc.wallet.Restrictions.getMinBuyerSecurityDepositAsPercent; -import static bisq.core.btc.wallet.Restrictions.getMinNonDustOutput; -import static bisq.core.btc.wallet.Restrictions.isDust; import static bisq.core.offer.OfferPayload.*; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -81,48 +74,26 @@ import static java.lang.String.format; @Singleton public class OfferUtil { - private final AccountAgeWitnessService accountAgeWitnessService; - private final BsqWalletService bsqWalletService; - private final FilterManager filterManager; - private final Preferences preferences; - private final PriceFeedService priceFeedService; - private final P2PService p2PService; - private final ReferralIdService referralIdService; - private final TradeStatisticsManager tradeStatisticsManager; - - private final Predicate isValidFeePaymentCurrencyCode = (c) -> - c.equalsIgnoreCase("XMR"); + private final AccountAgeWitnessService accountAgeWitnessService; + private final FilterManager filterManager; + private final Preferences preferences; + private final PriceFeedService priceFeedService; + private final P2PService p2PService; + private final ReferralIdService referralIdService; @Inject public OfferUtil(AccountAgeWitnessService accountAgeWitnessService, - BsqWalletService bsqWalletService, FilterManager filterManager, Preferences preferences, PriceFeedService priceFeedService, P2PService p2PService, - ReferralIdService referralIdService, - TradeStatisticsManager tradeStatisticsManager) { + ReferralIdService referralIdService) { this.accountAgeWitnessService = accountAgeWitnessService; - this.bsqWalletService = bsqWalletService; this.filterManager = filterManager; this.preferences = preferences; this.priceFeedService = priceFeedService; this.p2PService = p2PService; this.referralIdService = referralIdService; - this.tradeStatisticsManager = tradeStatisticsManager; - } - - public void maybeSetFeePaymentCurrencyPreference(String feeCurrencyCode) { - if (!feeCurrencyCode.isEmpty()) { - if (!isValidFeePaymentCurrencyCode.test(feeCurrencyCode)) - throw new IllegalStateException(format("%s cannot be used to pay trade fees", - feeCurrencyCode.toUpperCase())); - - if (feeCurrencyCode.equalsIgnoreCase("BSQ") && preferences.isPayFeeInBtc()) - preferences.setPayFeeInBtc(false); - else if (feeCurrencyCode.equalsIgnoreCase("BTC") && !preferences.isPayFeeInBtc()) - preferences.setPayFeeInBtc(true); - } } /** @@ -172,18 +143,6 @@ public class OfferUtil { } } - /** - * Returns the usable BSQ balance. - * - * @return Coin the usable BSQ balance - */ - public Coin getUsableBsqBalance() { - // We have to keep a minimum amount of BSQ == bitcoin dust limit, otherwise there - // would be dust violations for change UTXOs; essentially means the minimum usable - // balance of BSQ is 5.46. - Coin usableBsqBalance = bsqWalletService.getAvailableConfirmedBalance().subtract(getMinNonDustOutput()); - return usableBsqBalance.isNegative() ? Coin.ZERO : usableBsqBalance; - } public double calculateManualPrice(double volumeAsDouble, double amountAsDouble) { return volumeAsDouble / amountAsDouble; @@ -194,7 +153,7 @@ public class OfferUtil { } /** - * Returns the makerFee as Coin, this can be priced in BTC or BSQ. + * Returns the makerFee as Coin, this can be priced in BTC. * * @param amount the amount of BTC to trade * @return the maker fee for the given trade amount, or {@code null} if the amount @@ -202,8 +161,7 @@ public class OfferUtil { */ @Nullable public Coin getMakerFee(@Nullable Coin amount) { - boolean isCurrencyForMakerFeeBtc = isCurrencyForMakerFeeBtc(amount); - return CoinUtil.getMakerFee(isCurrencyForMakerFeeBtc, amount); + return CoinUtil.getMakerFee(amount); } public Coin getTxFeeByVsize(Coin txFeePerVbyteFromFeeService, int vsizeInVbytes) { @@ -219,84 +177,21 @@ public class OfferUtil { return (txVsize + 233) / 2; } - /** - * Checks if the maker fee should be paid in BTC, this can be the case due to user - * preference or because the user doesn't have enough BSQ. - * - * @param amount the amount of BTC to trade - * @return {@code true} if BTC is preferred or the trade amount is nonnull and there - * isn't enough BSQ for it. - */ - public boolean isCurrencyForMakerFeeBtc(@Nullable Coin amount) { - boolean payFeeInBtc = preferences.getPayFeeInBtc(); - boolean bsqForFeeAvailable = isBsqForMakerFeeAvailable(amount); - return payFeeInBtc || !bsqForFeeAvailable; - } - - /** - * Checks if the available BSQ balance is sufficient to pay for the offer's maker fee. - * - * @param amount the amount of BTC to trade - * @return {@code true} if the balance is sufficient, {@code false} otherwise - */ - public boolean isBsqForMakerFeeAvailable(@Nullable Coin amount) { - Coin availableBalance = bsqWalletService.getAvailableConfirmedBalance(); - Coin makerFee = CoinUtil.getMakerFee(false, amount); - - // If we don't know yet the maker fee (amount is not set) we return true, - // otherwise we would disable BSQ fee each time we open the create offer screen - // as there the amount is not set. - if (makerFee == null) - return true; - - Coin surplusFunds = availableBalance.subtract(makerFee); - if (isDust(surplusFunds)) { - return false; // we can't be left with dust - } - return !availableBalance.subtract(makerFee).isNegative(); - } - - @Nullable - public Coin getTakerFee(boolean isCurrencyForTakerFeeBtc, @Nullable Coin amount) { + public Coin getTakerFee(@Nullable Coin amount) { if (amount != null) { - Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getTakerFeePerBtc(isCurrencyForTakerFeeBtc), amount); - return CoinUtil.maxCoin(feePerBtc, FeeService.getMinTakerFee(isCurrencyForTakerFeeBtc)); + Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getTakerFeePerBtc(), amount); + return CoinUtil.maxCoin(feePerBtc, FeeService.getMinTakerFee()); } else { return null; } } - public boolean isCurrencyForTakerFeeBtc(Coin amount) { - boolean payFeeInBtc = preferences.getPayFeeInBtc(); - boolean bsqForFeeAvailable = isBsqForTakerFeeAvailable(amount); - return payFeeInBtc || !bsqForFeeAvailable; - } - - public boolean isBsqForTakerFeeAvailable(@Nullable Coin amount) { - Coin availableBalance = bsqWalletService.getAvailableConfirmedBalance(); - Coin takerFee = getTakerFee(false, amount); - - // If we don't know yet the maker fee (amount is not set) we return true, - // otherwise we would disable BSQ fee each time we open the create offer screen - // as there the amount is not set. - if (takerFee == null) - return true; - - Coin surplusFunds = availableBalance.subtract(takerFee); - if (isDust(surplusFunds)) { - return false; // we can't be left with dust - } - return !availableBalance.subtract(takerFee).isNegative(); - } - public boolean isBlockChainPaymentMethod(Offer offer) { return offer != null && offer.getPaymentMethod().isAsset(); } - public Optional getFeeInUserFiatCurrency(Coin makerFee, - boolean isCurrencyForMakerFeeBtc, - CoinFormatter bsqFormatter) { + public Optional getFeeInUserFiatCurrency(Coin makerFee) { String userCurrencyCode = preferences.getPreferredTradeCurrency().getCode(); if (CurrencyUtil.isCryptoCurrency(userCurrencyCode)) { // In case the user has selected a altcoin as preferredTradeCurrency @@ -305,10 +200,7 @@ public class OfferUtil { userCurrencyCode = CurrencyUtil.getCurrencyByCountryCode(countryCode).getCode(); } - return getFeeInUserFiatCurrency(makerFee, - isCurrencyForMakerFeeBtc, - userCurrencyCode, - bsqFormatter); + return getFeeInUserFiatCurrency(makerFee, userCurrencyCode); } public Map getExtraDataMap(PaymentAccount paymentAccount, @@ -365,33 +257,13 @@ public class OfferUtil { } private Optional getFeeInUserFiatCurrency(Coin makerFee, - boolean isCurrencyForMakerFeeBtc, - String userCurrencyCode, - CoinFormatter bsqFormatter) { + String userCurrencyCode) { MarketPrice marketPrice = priceFeedService.getMarketPrice(userCurrencyCode); if (marketPrice != null && makerFee != null) { long marketPriceAsLong = roundDoubleToLong( scaleUpByPowerOf10(marketPrice.getPrice(), Fiat.SMALLEST_UNIT_EXPONENT)); Price userCurrencyPrice = Price.valueOf(userCurrencyCode, marketPriceAsLong); - - if (isCurrencyForMakerFeeBtc) { - return Optional.of(userCurrencyPrice.getVolumeByAmount(makerFee)); - } else { - // We use the current market price for the fiat currency and the 30 day average BSQ price - Tuple2 tuple = AveragePriceUtil.getAveragePriceTuple(preferences, - tradeStatisticsManager, - 30); - Price bsqPrice = tuple.second; - if (bsqPrice.isPositive()) { - String inputValue = bsqFormatter.formatCoin(makerFee); - Volume makerFeeAsVolume = Volume.parse(inputValue, "BSQ"); - Coin requiredBtc = bsqPrice.getAmountByVolume(makerFeeAsVolume); - Volume volumeByAmount = userCurrencyPrice.getVolumeByAmount(requiredBtc); - return Optional.of(volumeByAmount); - } else { - return Optional.empty(); - } - } + return Optional.of(userCurrencyPrice.getVolumeByAmount(makerFee)); } else { return Optional.empty(); } diff --git a/core/src/main/java/bisq/core/offer/OpenOfferManager.java b/core/src/main/java/bisq/core/offer/OpenOfferManager.java index af1ed84d13..d39d432f31 100644 --- a/core/src/main/java/bisq/core/offer/OpenOfferManager.java +++ b/core/src/main/java/bisq/core/offer/OpenOfferManager.java @@ -18,11 +18,9 @@ package bisq.core.offer; import bisq.core.api.CoreContext; -import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.TradeWalletService; import bisq.core.btc.wallet.XmrWalletService; -import bisq.core.dao.DaoFacade; import bisq.core.exceptions.TradePriceOutOfToleranceException; import bisq.core.filter.FilterManager; import bisq.core.locale.Res; @@ -37,7 +35,6 @@ import bisq.core.provider.price.PriceFeedService; import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager; import bisq.core.support.dispute.mediation.mediator.Mediator; import bisq.core.support.dispute.mediation.mediator.MediatorManager; -import bisq.core.support.dispute.refund.refundagent.RefundAgentManager; import bisq.core.trade.TradableList; import bisq.core.trade.TradeUtils; import bisq.core.trade.closed.ClosedTradableManager; @@ -117,7 +114,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe private final BtcWalletService btcWalletService; private final XmrWalletService xmrWalletService; private final TradeWalletService tradeWalletService; - private final BsqWalletService bsqWalletService; private final OfferBookService offerBookService; private final ClosedTradableManager closedTradableManager; private final PriceFeedService priceFeedService; @@ -125,7 +121,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe private final TradeStatisticsManager tradeStatisticsManager; private final ArbitratorManager arbitratorManager; private final MediatorManager mediatorManager; - private final DaoFacade daoFacade; private final FilterManager filterManager; private final Broadcaster broadcaster; private final PersistenceManager> persistenceManager; @@ -153,7 +148,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe BtcWalletService btcWalletService, XmrWalletService xmrWalletService, TradeWalletService tradeWalletService, - BsqWalletService bsqWalletService, OfferBookService offerBookService, ClosedTradableManager closedTradableManager, PriceFeedService priceFeedService, @@ -161,8 +155,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe TradeStatisticsManager tradeStatisticsManager, ArbitratorManager arbitratorManager, MediatorManager mediatorManager, - RefundAgentManager refundAgentManager, - DaoFacade daoFacade, FilterManager filterManager, Broadcaster broadcaster, PersistenceManager> persistenceManager, @@ -175,7 +167,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe this.btcWalletService = btcWalletService; this.xmrWalletService = xmrWalletService; this.tradeWalletService = tradeWalletService; - this.bsqWalletService = bsqWalletService; this.offerBookService = offerBookService; this.closedTradableManager = closedTradableManager; this.priceFeedService = priceFeedService; @@ -183,7 +174,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe this.tradeStatisticsManager = tradeStatisticsManager; this.arbitratorManager = arbitratorManager; this.mediatorManager = mediatorManager; - this.daoFacade = daoFacade; this.filterManager = filterManager; this.broadcaster = broadcaster; this.persistenceManager = persistenceManager; @@ -415,12 +405,10 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe btcWalletService, xmrWalletService, tradeWalletService, - bsqWalletService, offerBookService, arbitratorManager, mediatorManager, tradeStatisticsManager, - daoFacade, user, keyRing, filterManager); @@ -851,7 +839,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe availabilityResult = AvailabilityResult.OFFER_TAKEN; } - if (btcWalletService.isUnconfirmedTransactionsLimitHit() || bsqWalletService.isUnconfirmedTransactionsLimitHit()) { + if (btcWalletService.isUnconfirmedTransactionsLimitHit()) { errorMessage = Res.get("shared.unconfirmedTransactionsLimitReached"); log.warn(errorMessage); availabilityResult = AvailabilityResult.UNCONF_TX_LIMIT_HIT; @@ -1019,7 +1007,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe originalOfferPayload.getBlockHeightAtOfferCreation(), originalOfferPayload.getTxFee(), originalOfferPayload.getMakerFee(), - originalOfferPayload.isCurrencyForMakerFeeBtc(), originalOfferPayload.getBuyerSecurityDeposit(), originalOfferPayload.getSellerSecurityDeposit(), originalOfferPayload.getMaxTradeLimit(), diff --git a/core/src/main/java/bisq/core/offer/availability/tasks/SendOfferAvailabilityRequest.java b/core/src/main/java/bisq/core/offer/availability/tasks/SendOfferAvailabilityRequest.java index 9728a084d2..03c310f4c4 100644 --- a/core/src/main/java/bisq/core/offer/availability/tasks/SendOfferAvailabilityRequest.java +++ b/core/src/main/java/bisq/core/offer/availability/tasks/SendOfferAvailabilityRequest.java @@ -47,7 +47,7 @@ public class SendOfferAvailabilityRequest extends Task { protected void run() { try { runInterceptHook(); - + // collect fields Offer offer = model.getOffer(); User user = model.getUser(); @@ -57,10 +57,10 @@ public class SendOfferAvailabilityRequest extends Task { String paymentAccountId = model.getPaymentAccountId(); String paymentMethodId = user.getPaymentAccount(paymentAccountId).getPaymentAccountPayload().getPaymentMethodId(); String payoutAddress = walletService.getOrCreateAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString(); // reserve new payout address - + // taker signs offer using offer id as nonce to avoid challenge protocol byte[] sig = Sig.sign(model.getP2PService().getKeyRing().getSignatureKeyPair().getPrivate(), offer.getId().getBytes(Charsets.UTF_8)); - + // send InitTradeRequest to maker to sign InitTradeRequest tradeRequest = new InitTradeRequest( offer.getId(), @@ -68,7 +68,7 @@ public class SendOfferAvailabilityRequest extends Task { p2PService.getKeyRing().getPubKeyRing(), offer.getAmount().value, offer.getPrice().getValue(), - offerUtil.getTakerFee(true, offer.getAmount()).value, + offerUtil.getTakerFee(offer.getAmount()).value, user.getAccountId(), paymentAccountId, paymentMethodId, @@ -84,10 +84,10 @@ public class SendOfferAvailabilityRequest extends Task { null, payoutAddress, null); - + // save trade request to later send to arbitrator model.setTradeRequest(tradeRequest); - + OfferAvailabilityRequest message = new OfferAvailabilityRequest(model.getOffer().getId(), model.getPubKeyRing(), model.getTakersTradePrice(), model.isTakerApiUser(), tradeRequest); log.info("Send {} with offerId {} and uid {} to peer {}", diff --git a/core/src/main/java/bisq/core/offer/placeoffer/PlaceOfferModel.java b/core/src/main/java/bisq/core/offer/placeoffer/PlaceOfferModel.java index c7ebe7ac4a..c9f74bec47 100644 --- a/core/src/main/java/bisq/core/offer/placeoffer/PlaceOfferModel.java +++ b/core/src/main/java/bisq/core/offer/placeoffer/PlaceOfferModel.java @@ -17,11 +17,9 @@ package bisq.core.offer.placeoffer; -import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.TradeWalletService; import bisq.core.btc.wallet.XmrWalletService; -import bisq.core.dao.DaoFacade; import bisq.core.filter.FilterManager; import bisq.core.offer.Offer; import bisq.core.offer.OfferBookService; @@ -57,12 +55,10 @@ public class PlaceOfferModel implements Model { private final BtcWalletService walletService; private final XmrWalletService xmrWalletService; private final TradeWalletService tradeWalletService; - private final BsqWalletService bsqWalletService; private final OfferBookService offerBookService; private final ArbitratorManager arbitratorManager; private final MediatorManager mediatorManager; private final TradeStatisticsManager tradeStatisticsManager; - private final DaoFacade daoFacade; private final User user; private final KeyRing keyRing; @Getter @@ -85,12 +81,10 @@ public class PlaceOfferModel implements Model { BtcWalletService walletService, XmrWalletService xmrWalletService, TradeWalletService tradeWalletService, - BsqWalletService bsqWalletService, OfferBookService offerBookService, ArbitratorManager arbitratorManager, MediatorManager mediatorManager, TradeStatisticsManager tradeStatisticsManager, - DaoFacade daoFacade, User user, KeyRing keyRing, FilterManager filterManager) { @@ -101,12 +95,10 @@ public class PlaceOfferModel implements Model { this.walletService = walletService; this.xmrWalletService = xmrWalletService; this.tradeWalletService = tradeWalletService; - this.bsqWalletService = bsqWalletService; this.offerBookService = offerBookService; this.arbitratorManager = arbitratorManager; this.mediatorManager = mediatorManager; this.tradeStatisticsManager = tradeStatisticsManager; - this.daoFacade = daoFacade; this.user = user; this.keyRing = keyRing; this.filterManager = filterManager; diff --git a/core/src/main/java/bisq/core/offer/placeoffer/tasks/CreateMakerFeeTx.java b/core/src/main/java/bisq/core/offer/placeoffer/tasks/CreateMakerFeeTx.java index 849319690d..f225d2c8e2 100644 --- a/core/src/main/java/bisq/core/offer/placeoffer/tasks/CreateMakerFeeTx.java +++ b/core/src/main/java/bisq/core/offer/placeoffer/tasks/CreateMakerFeeTx.java @@ -19,16 +19,13 @@ package bisq.core.offer.placeoffer.tasks; import bisq.core.btc.exceptions.TxBroadcastException; import bisq.core.btc.model.AddressEntry; -import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.TradeWalletService; import bisq.core.btc.wallet.TxBroadcaster; import bisq.core.btc.wallet.WalletService; -import bisq.core.dao.exceptions.DaoDisabledException; -import bisq.core.dao.state.model.blockchain.TxType; import bisq.core.offer.Offer; import bisq.core.offer.placeoffer.PlaceOfferModel; -import bisq.core.util.FeeReceiverSelector; +//import bisq.core.util.FeeReceiverSelector; import bisq.common.UserThread; import bisq.common.taskrunner.Task; @@ -65,106 +62,11 @@ public class CreateMakerFeeTx extends Task { Address changeAddress = walletService.getFreshAddressEntry().getAddress(); TradeWalletService tradeWalletService = model.getTradeWalletService(); - - String feeReceiver = FeeReceiverSelector.getAddress(model.getDaoFacade(), model.getFilterManager()); - - if (offer.isCurrencyForMakerFeeBtc()) { - tradeWalletService.createBtcTradingFeeTx( - fundingAddress, - reservedForTradeAddress, - changeAddress, - model.getReservedFundsForOffer(), - model.isUseSavingsWallet(), - offer.getMakerFee(), - offer.getTxFee(), - feeReceiver, - true, - new TxBroadcaster.Callback() { - @Override - public void onSuccess(Transaction transaction) { - // we delay one render frame to be sure we don't get called before the method call has - // returned (tradeFeeTx would be null in that case) - UserThread.execute(() -> { - if (!completed) { - offer.setOfferFeePaymentTxId(transaction.getTxId().toString()); - model.setTransaction(transaction); - walletService.swapTradeEntryToAvailableEntry(id, AddressEntry.Context.OFFER_FUNDING); - - model.getOffer().setState(Offer.State.OFFER_FEE_RESERVED); - - complete(); - } else { - log.warn("We got the onSuccess callback called after the timeout has been triggered a complete()."); - } - }); - } - - @Override - public void onFailure(TxBroadcastException exception) { - if (!completed) { - failed(exception); - } else { - log.warn("We got the onFailure callback called after the timeout has been triggered a complete()."); - } - } - }); - } else { - BsqWalletService bsqWalletService = model.getBsqWalletService(); - Transaction preparedBurnFeeTx = model.getBsqWalletService().getPreparedTradeFeeTx(offer.getMakerFee()); - Transaction txWithBsqFee = tradeWalletService.completeBsqTradingFeeTx(preparedBurnFeeTx, - fundingAddress, - reservedForTradeAddress, - changeAddress, - model.getReservedFundsForOffer(), - model.isUseSavingsWallet(), - offer.getTxFee()); - - Transaction signedTx = model.getBsqWalletService().signTx(txWithBsqFee); - WalletService.checkAllScriptSignaturesForTx(signedTx); - bsqWalletService.commitTx(signedTx, TxType.PAY_TRADE_FEE); - // We need to create another instance, otherwise the tx would trigger an invalid state exception - // if it gets committed 2 times - tradeWalletService.commitTx(tradeWalletService.getClonedTransaction(signedTx)); - - // We use a short timeout as there are issues with BSQ txs. See comment in TxBroadcaster - bsqWalletService.broadcastTx(signedTx, new TxBroadcaster.Callback() { - @Override - public void onSuccess(@Nullable Transaction transaction) { - if (transaction != null) { - offer.setOfferFeePaymentTxId(transaction.getTxId().toString()); - model.setTransaction(transaction); - log.debug("onSuccess, offerId={}, OFFER_FUNDING", id); - walletService.swapTradeEntryToAvailableEntry(id, AddressEntry.Context.OFFER_FUNDING); - - log.debug("Successfully sent tx with id " + transaction.getTxId().toString()); - model.getOffer().setState(Offer.State.OFFER_FEE_RESERVED); - - complete(); - } - } - - @Override - public void onFailure(TxBroadcastException exception) { - log.error(exception.toString()); - exception.printStackTrace(); - offer.setErrorMessage("An error occurred.\n" + - "Error message:\n" - + exception.getMessage()); - failed(exception); - } - }, - 1); - } + throw new RuntimeException("CreateMakerFeeTx not used for XMR"); } catch (Throwable t) { - if (t instanceof DaoDisabledException) { - offer.setErrorMessage("You cannot pay the trade fee in BSQ at the moment because the DAO features have been " + - "disabled due technical problems. Please use the BTC fee option until the issues are resolved. " + - "For more information please visit the Bisq Forum."); - } else { offer.setErrorMessage("An error occurred.\n" + "Error message:\n" + t.getMessage()); - } failed(t); } diff --git a/core/src/main/java/bisq/core/offer/takeoffer/TakeOfferModel.java b/core/src/main/java/bisq/core/offer/takeoffer/TakeOfferModel.java index 8e345b8142..5a45a8ab60 100644 --- a/core/src/main/java/bisq/core/offer/takeoffer/TakeOfferModel.java +++ b/core/src/main/java/bisq/core/offer/takeoffer/TakeOfferModel.java @@ -69,8 +69,6 @@ public class TakeOfferModel implements Model { private AddressEntry addressEntry; @Getter private Coin amount; - @Getter - private boolean isCurrencyForTakerFeeBtc; private Offer offer; private PaymentAccount paymentAccount; @Getter @@ -124,8 +122,7 @@ public class TakeOfferModel implements Model { this.securityDeposit = offer.getDirection() == SELL ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit(); - this.isCurrencyForTakerFeeBtc = offerUtil.isCurrencyForTakerFeeBtc(amount); - this.takerFee = offerUtil.getTakerFee(isCurrencyForTakerFeeBtc, amount); + this.takerFee = offerUtil.getTakerFee(amount); calculateTxFees(); calculateVolume(); @@ -179,9 +176,7 @@ public class TakeOfferModel implements Model { // maker created the offer and reserved his funds, so that would not work well // with dynamic fees. The mining fee for the takeOfferFee tx is deducted from // the createOfferFee and not visible to the trader. - Coin feeAndSecDeposit = getTotalTxFee().add(securityDeposit); - if (isCurrencyForTakerFeeBtc) - feeAndSecDeposit = feeAndSecDeposit.add(takerFee); + Coin feeAndSecDeposit = getTotalTxFee().add(securityDeposit).add(takerFee); totalToPayAsCoin = offer.isBuyOffer() ? feeAndSecDeposit.add(amount) @@ -226,11 +221,7 @@ public class TakeOfferModel implements Model { } public Coin getTotalTxFee() { - Coin totalTxFees = txFeeFromFeeService.add(getTxFeeForDepositTx()).add(getTxFeeForPayoutTx()); - if (isCurrencyForTakerFeeBtc) - return totalTxFees; - else - return totalTxFees.subtract(takerFee); + return txFeeFromFeeService.add(getTxFeeForDepositTx()).add(getTxFeeForPayoutTx()); } @NotNull @@ -274,7 +265,6 @@ public class TakeOfferModel implements Model { this.amount = null; this.balance = null; this.isBtcWalletFunded = false; - this.isCurrencyForTakerFeeBtc = false; this.missingCoin = ZERO; this.offer = null; this.paymentAccount = null; @@ -309,7 +299,6 @@ public class TakeOfferModel implements Model { ", balance=" + balance + "\n" + ", volume=" + volume + "\n" + ", fundsNeededForTrade=" + getFundsNeededForTrade() + "\n" + - ", isCurrencyForTakerFeeBtc=" + isCurrencyForTakerFeeBtc + "\n" + ", isBtcWalletFunded=" + isBtcWalletFunded + "\n" + '}'; } diff --git a/core/src/main/java/bisq/core/payment/TradeLimits.java b/core/src/main/java/bisq/core/payment/TradeLimits.java index 2d3c5c5688..a35778cb27 100644 --- a/core/src/main/java/bisq/core/payment/TradeLimits.java +++ b/core/src/main/java/bisq/core/payment/TradeLimits.java @@ -17,10 +17,6 @@ package bisq.core.payment; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.state.DaoStateService; - import bisq.common.util.MathUtils; import org.bitcoinj.core.Coin; @@ -38,17 +34,14 @@ import javax.annotation.Nullable; @Slf4j @Singleton public class TradeLimits { + private static final Coin MAX_TRADE_LIMIT = Coin.parseCoin("2"); // max trade limit for lowest risk payment method. Others will get derived from that. @Nullable @Getter private static TradeLimits INSTANCE; - private final DaoStateService daoStateService; - private final PeriodService periodService; @Inject - public TradeLimits(DaoStateService daoStateService, PeriodService periodService) { - this.daoStateService = daoStateService; - this.periodService = periodService; + public TradeLimits() { INSTANCE = this; } @@ -60,14 +53,13 @@ public class TradeLimits { /** - * The default trade limits defined as statics in PaymentMethod are only used until the DAO - * is fully synchronized. + * The default trade limits. * * @see bisq.core.payment.payload.PaymentMethod - * @return the maximum trade limit set by the DAO. + * @return the maximum trade limit */ public Coin getMaxTradeLimit() { - return daoStateService.getParamValueAsCoin(Param.MAX_TRADE_LIMIT, periodService.getChainHeight()); + return MAX_TRADE_LIMIT; } // We possibly rounded value for the first month gets multiplied by 4 to get the trade limit after the account diff --git a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java index 8364676112..840dc5dca1 100644 --- a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java +++ b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java @@ -17,10 +17,11 @@ package bisq.core.payment.payload; +import bisq.core.payment.TradeLimits; + import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.locale.TradeCurrency; -import bisq.core.payment.TradeLimits; import bisq.common.proto.persistable.PersistablePayload; @@ -38,8 +39,6 @@ import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; -import static com.google.common.base.Preconditions.checkNotNull; - @EqualsAndHashCode(exclude = {"maxTradePeriod", "maxTradeLimit"}) @ToString @Slf4j @@ -240,7 +239,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable optionalAsset = CurrencyUtil.findAsset(assetRegistry, currencyCode, - Config.baseCurrencyNetwork(), DevEnv.isDaoTradingActivated()); + Config.baseCurrencyNetwork()); if (optionalAsset.isPresent()) { Asset asset = optionalAsset.get(); AddressValidationResult result = asset.validateAddress(input); diff --git a/core/src/main/java/bisq/core/proto/CoreProtoResolver.java b/core/src/main/java/bisq/core/proto/CoreProtoResolver.java index 3841a0e051..1b8f05c6d0 100644 --- a/core/src/main/java/bisq/core/proto/CoreProtoResolver.java +++ b/core/src/main/java/bisq/core/proto/CoreProtoResolver.java @@ -19,8 +19,6 @@ package bisq.core.proto; import bisq.core.account.sign.SignedWitness; import bisq.core.account.witness.AccountAgeWitness; -import bisq.core.dao.governance.blindvote.storage.BlindVotePayload; -import bisq.core.dao.governance.proposal.storage.appendonly.ProposalPayload; import bisq.core.payment.payload.AdvancedCashAccountPayload; import bisq.core.payment.payload.AliPayAccountPayload; import bisq.core.payment.payload.AmazonGiftCardAccountPayload; @@ -185,10 +183,6 @@ public class CoreProtoResolver implements ProtoResolver { return AccountAgeWitness.fromProto(proto.getAccountAgeWitness()); case TRADE_STATISTICS2: return TradeStatistics2.fromProto(proto.getTradeStatistics2()); - case PROPOSAL_PAYLOAD: - return ProposalPayload.fromProto(proto.getProposalPayload()); - case BLIND_VOTE_PAYLOAD: - return BlindVotePayload.fromProto(proto.getBlindVotePayload()); case SIGNED_WITNESS: return SignedWitness.fromProto(proto.getSignedWitness()); case TRADE_STATISTICS3: diff --git a/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java b/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java index e613bd2476..e5fe65a86f 100644 --- a/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java +++ b/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java @@ -19,20 +19,6 @@ package bisq.core.proto.network; import bisq.core.alert.Alert; import bisq.core.alert.PrivateNotificationMessage; -import bisq.core.dao.governance.blindvote.network.messages.RepublishGovernanceDataRequest; -import bisq.core.dao.governance.proposal.storage.temp.TempProposalPayload; -import bisq.core.dao.monitoring.network.messages.GetBlindVoteStateHashesRequest; -import bisq.core.dao.monitoring.network.messages.GetBlindVoteStateHashesResponse; -import bisq.core.dao.monitoring.network.messages.GetDaoStateHashesRequest; -import bisq.core.dao.monitoring.network.messages.GetDaoStateHashesResponse; -import bisq.core.dao.monitoring.network.messages.GetProposalStateHashesRequest; -import bisq.core.dao.monitoring.network.messages.GetProposalStateHashesResponse; -import bisq.core.dao.monitoring.network.messages.NewBlindVoteStateHashMessage; -import bisq.core.dao.monitoring.network.messages.NewDaoStateHashMessage; -import bisq.core.dao.monitoring.network.messages.NewProposalStateHashMessage; -import bisq.core.dao.node.messages.GetBlocksRequest; -import bisq.core.dao.node.messages.GetBlocksResponse; -import bisq.core.dao.node.messages.NewBlockBroadcastMessage; import bisq.core.filter.Filter; import bisq.core.network.p2p.inventory.messages.GetInventoryRequest; import bisq.core.network.p2p.inventory.messages.GetInventoryResponse; @@ -229,39 +215,12 @@ public class CoreNetworkProtoResolver extends CoreProtoResolver implements Netwo case PRIVATE_NOTIFICATION_MESSAGE: return PrivateNotificationMessage.fromProto(proto.getPrivateNotificationMessage(), messageVersion); - case GET_BLOCKS_REQUEST: - return GetBlocksRequest.fromProto(proto.getGetBlocksRequest(), messageVersion); - case GET_BLOCKS_RESPONSE: - return GetBlocksResponse.fromProto(proto.getGetBlocksResponse(), messageVersion); - case NEW_BLOCK_BROADCAST_MESSAGE: - return NewBlockBroadcastMessage.fromProto(proto.getNewBlockBroadcastMessage(), messageVersion); case ADD_PERSISTABLE_NETWORK_PAYLOAD_MESSAGE: return AddPersistableNetworkPayloadMessage.fromProto(proto.getAddPersistableNetworkPayloadMessage(), this, messageVersion); case ACK_MESSAGE: return AckMessage.fromProto(proto.getAckMessage(), messageVersion); - case REPUBLISH_GOVERNANCE_DATA_REQUEST: - return RepublishGovernanceDataRequest.fromProto(proto.getRepublishGovernanceDataRequest(), messageVersion); - case NEW_DAO_STATE_HASH_MESSAGE: - return NewDaoStateHashMessage.fromProto(proto.getNewDaoStateHashMessage(), messageVersion); - case GET_DAO_STATE_HASHES_REQUEST: - return GetDaoStateHashesRequest.fromProto(proto.getGetDaoStateHashesRequest(), messageVersion); - case GET_DAO_STATE_HASHES_RESPONSE: - return GetDaoStateHashesResponse.fromProto(proto.getGetDaoStateHashesResponse(), messageVersion); - case NEW_PROPOSAL_STATE_HASH_MESSAGE: - return NewProposalStateHashMessage.fromProto(proto.getNewProposalStateHashMessage(), messageVersion); - case GET_PROPOSAL_STATE_HASHES_REQUEST: - return GetProposalStateHashesRequest.fromProto(proto.getGetProposalStateHashesRequest(), messageVersion); - case GET_PROPOSAL_STATE_HASHES_RESPONSE: - return GetProposalStateHashesResponse.fromProto(proto.getGetProposalStateHashesResponse(), messageVersion); - - case NEW_BLIND_VOTE_STATE_HASH_MESSAGE: - return NewBlindVoteStateHashMessage.fromProto(proto.getNewBlindVoteStateHashMessage(), messageVersion); - case GET_BLIND_VOTE_STATE_HASHES_REQUEST: - return GetBlindVoteStateHashesRequest.fromProto(proto.getGetBlindVoteStateHashesRequest(), messageVersion); - case GET_BLIND_VOTE_STATE_HASHES_RESPONSE: - return GetBlindVoteStateHashesResponse.fromProto(proto.getGetBlindVoteStateHashesResponse(), messageVersion); case BUNDLE_OF_ENVELOPES: return BundleOfEnvelopes.fromProto(proto.getBundleOfEnvelopes(), this, messageVersion); @@ -317,8 +276,6 @@ public class CoreNetworkProtoResolver extends CoreProtoResolver implements Netwo return MailboxStoragePayload.fromProto(proto.getMailboxStoragePayload()); case OFFER_PAYLOAD: return OfferPayload.fromProto(proto.getOfferPayload()); - case TEMP_PROPOSAL_PAYLOAD: - return TempProposalPayload.fromProto(proto.getTempProposalPayload()); default: throw new ProtobufferRuntimeException("Unknown proto message case (PB.StoragePayload). messageCase=" + proto.getMessageCase() + "; proto raw data=" + proto.toString()); diff --git a/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java b/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java index 297cbe7ff0..d32913ef32 100644 --- a/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java +++ b/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java @@ -23,17 +23,6 @@ import bisq.core.btc.model.AddressEntryList; import bisq.core.btc.model.XmrAddressEntryList; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.XmrWalletService; -import bisq.core.dao.governance.blindvote.MyBlindVoteList; -import bisq.core.dao.governance.blindvote.storage.BlindVoteStore; -import bisq.core.dao.governance.bond.reputation.MyReputationList; -import bisq.core.dao.governance.myvote.MyVoteList; -import bisq.core.dao.governance.proofofburn.MyProofOfBurnList; -import bisq.core.dao.governance.proposal.MyProposalList; -import bisq.core.dao.governance.proposal.storage.appendonly.ProposalStore; -import bisq.core.dao.governance.proposal.storage.temp.TempProposalStore; -import bisq.core.dao.state.model.governance.BallotList; -import bisq.core.dao.state.storage.DaoStateStore; -import bisq.core.dao.state.unconfirmed.UnconfirmedBsqChangeOutputList; import bisq.core.offer.SignedOfferList; import bisq.core.payment.PaymentAccountList; import bisq.core.proto.CoreProtoResolver; @@ -116,28 +105,6 @@ public class CorePersistenceProtoResolver extends CoreProtoResolver implements P return AccountAgeWitnessStore.fromProto(proto.getAccountAgeWitnessStore()); case TRADE_STATISTICS2_STORE: return TradeStatistics2Store.fromProto(proto.getTradeStatistics2Store()); - case BLIND_VOTE_STORE: - return BlindVoteStore.fromProto(proto.getBlindVoteStore()); - case PROPOSAL_STORE: - return ProposalStore.fromProto(proto.getProposalStore()); - case TEMP_PROPOSAL_STORE: - return TempProposalStore.fromProto(proto.getTempProposalStore(), networkProtoResolver); - case MY_PROPOSAL_LIST: - return MyProposalList.fromProto(proto.getMyProposalList()); - case BALLOT_LIST: - return BallotList.fromProto(proto.getBallotList()); - case MY_VOTE_LIST: - return MyVoteList.fromProto(proto.getMyVoteList()); - case MY_BLIND_VOTE_LIST: - return MyBlindVoteList.fromProto(proto.getMyBlindVoteList()); - case DAO_STATE_STORE: - return DaoStateStore.fromProto(proto.getDaoStateStore()); - case MY_REPUTATION_LIST: - return MyReputationList.fromProto(proto.getMyReputationList()); - case MY_PROOF_OF_BURN_LIST: - return MyProofOfBurnList.fromProto(proto.getMyProofOfBurnList()); - case UNCONFIRMED_BSQ_CHANGE_OUTPUT_LIST: - return UnconfirmedBsqChangeOutputList.fromProto(proto.getUnconfirmedBsqChangeOutputList()); case SIGNED_WITNESS_STORE: return SignedWitnessStore.fromProto(proto.getSignedWitnessStore()); case TRADE_STATISTICS3_STORE: diff --git a/core/src/main/java/bisq/core/provider/fee/FeeService.java b/core/src/main/java/bisq/core/provider/fee/FeeService.java index 0c68293565..61ab85ca5c 100644 --- a/core/src/main/java/bisq/core/provider/fee/FeeService.java +++ b/core/src/main/java/bisq/core/provider/fee/FeeService.java @@ -17,15 +17,12 @@ package bisq.core.provider.fee; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.state.DaoStateService; - import bisq.common.UserThread; import bisq.common.config.Config; import bisq.common.handlers.FaultHandler; import bisq.common.util.Tuple2; +import org.bitcoinj.utils.MonetaryFormat; import org.bitcoinj.core.Coin; import com.google.inject.Inject; @@ -43,6 +40,7 @@ import java.time.Instant; import java.util.Map; import java.util.concurrent.TimeUnit; +import bisq.core.util.ParsingUtils; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -64,34 +62,28 @@ public class FeeService { // fee service would not deliver data. private static final long BTC_DEFAULT_TX_FEE = 50; private static final long MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN = 2; - private static DaoStateService daoStateService; - private static PeriodService periodService; + private static final MonetaryFormat btcCoinFormat = Config.baseCurrencyNetworkParameters().getMonetaryFormat(); - private static Coin getFeeFromParamAsCoin(Param parm) { - return daoStateService != null && periodService != null ? daoStateService.getParamValueAsCoin(parm, periodService.getChainHeight()) : Coin.ZERO; + + public static Coin getMakerFeePerBtc() { + return ParsingUtils.parseToCoin("0.001", btcCoinFormat); } - public static Coin getMakerFeePerBtc(boolean currencyForFeeIsBtc) { - return currencyForFeeIsBtc ? getFeeFromParamAsCoin(Param.DEFAULT_MAKER_FEE_BTC) : getFeeFromParamAsCoin(Param.DEFAULT_MAKER_FEE_BSQ); + public static Coin getMinMakerFee() { + return ParsingUtils.parseToCoin("0.00005", btcCoinFormat); } - public static Coin getMinMakerFee(boolean currencyForFeeIsBtc) { - return currencyForFeeIsBtc ? getFeeFromParamAsCoin(Param.MIN_MAKER_FEE_BTC) : getFeeFromParamAsCoin(Param.MIN_MAKER_FEE_BSQ); + public static Coin getTakerFeePerBtc() { + return ParsingUtils.parseToCoin("0.003", btcCoinFormat); } - public static Coin getTakerFeePerBtc(boolean currencyForFeeIsBtc) { - return currencyForFeeIsBtc ? getFeeFromParamAsCoin(Param.DEFAULT_TAKER_FEE_BTC) : getFeeFromParamAsCoin(Param.DEFAULT_TAKER_FEE_BSQ); + public static Coin getMinTakerFee() { + return ParsingUtils.parseToCoin("0.00005", btcCoinFormat); } - public static Coin getMinTakerFee(boolean currencyForFeeIsBtc) { - return currencyForFeeIsBtc ? getFeeFromParamAsCoin(Param.MIN_TAKER_FEE_BTC) : getFeeFromParamAsCoin(Param.MIN_TAKER_FEE_BSQ); - } - - /////////////////////////////////////////////////////////////////////////////////////////// // Class fields /////////////////////////////////////////////////////////////////////////////////////////// - private final FeeProvider feeProvider; private final IntegerProperty feeUpdateCounter = new SimpleIntegerProperty(0); private long txFeePerVbyte = BTC_DEFAULT_TX_FEE; @@ -108,10 +100,8 @@ public class FeeService { /////////////////////////////////////////////////////////////////////////////////////////// @Inject - public FeeService(FeeProvider feeProvider, DaoStateService daoStateService, PeriodService periodService) { + public FeeService(FeeProvider feeProvider) { this.feeProvider = feeProvider; - FeeService.daoStateService = daoStateService; - FeeService.periodService = periodService; } diff --git a/core/src/main/java/bisq/core/provider/mempool/MempoolService.java b/core/src/main/java/bisq/core/provider/mempool/MempoolService.java index 2f35182894..73b1131508 100644 --- a/core/src/main/java/bisq/core/provider/mempool/MempoolService.java +++ b/core/src/main/java/bisq/core/provider/mempool/MempoolService.java @@ -17,8 +17,6 @@ package bisq.core.provider.mempool; -import bisq.core.dao.DaoFacade; -import bisq.core.dao.state.DaoStateService; import bisq.core.filter.FilterManager; import bisq.core.offer.OfferPayload; import bisq.core.trade.Trade; @@ -57,8 +55,6 @@ public class MempoolService { private final Config config; private final Preferences preferences; private final FilterManager filterManager; - private final DaoFacade daoFacade; - private final DaoStateService daoStateService; private final List btcFeeReceivers = new ArrayList<>(); @Getter private int outstandingRequests = 0; @@ -67,15 +63,11 @@ public class MempoolService { public MempoolService(Socks5ProxyProvider socks5ProxyProvider, Config config, Preferences preferences, - FilterManager filterManager, - DaoFacade daoFacade, - DaoStateService daoStateService) { + FilterManager filterManager) { this.socks5ProxyProvider = socks5ProxyProvider; this.config = config; this.preferences = preferences; this.filterManager = filterManager; - this.daoFacade = daoFacade; - this.daoStateService = daoStateService; } public void onAllServicesInitialized() { @@ -88,12 +80,11 @@ public class MempoolService { public boolean canRequestBeMade(OfferPayload offerPayload) { // when validating a new offer, wait 1 block for the tx to propagate - return offerPayload.getBlockHeightAtOfferCreation() < daoStateService.getChainHeight() && canRequestBeMade(); + return canRequestBeMade(); } public void validateOfferMakerTx(OfferPayload offerPayload, Consumer resultHandler) { - validateOfferMakerTx(new TxValidator(daoStateService, offerPayload.getOfferFeePaymentTxId(), Coin.valueOf(offerPayload.getAmount()), - offerPayload.isCurrencyForMakerFeeBtc()), resultHandler); + validateOfferMakerTx(new TxValidator( offerPayload.getOfferFeePaymentTxId(), Coin.valueOf(offerPayload.getAmount())), resultHandler); } public void validateOfferMakerTx(TxValidator txValidator, Consumer resultHandler) { @@ -107,8 +98,7 @@ public class MempoolService { public void validateOfferTakerTx(Trade trade, Consumer resultHandler) { throw new RuntimeException("MempoolService.validateOfferTakerTx needs updated for XMR"); -// validateOfferTakerTx(new TxValidator(daoStateService, trade.getTakerFeeTxId(), trade.getTradeAmount(), -// trade.isCurrencyForTakerFeeBtc()), resultHandler); + //validateOfferTakerTx(new TxValidator( trade.getTakerFeeTxId(), trade.getTradeAmount(),resultHandler)); } public void validateOfferTakerTx(TxValidator txValidator, Consumer resultHandler) { @@ -121,7 +111,7 @@ public class MempoolService { } public void checkTxIsConfirmed(String txId, Consumer resultHandler) { - TxValidator txValidator = new TxValidator(daoStateService, txId); + TxValidator txValidator = new TxValidator(txId); if (!isServiceSupported()) { UserThread.runAfter(() -> resultHandler.accept(txValidator.endResult("mempool request not supported, bypassing", true)), 1); return; @@ -159,7 +149,7 @@ public class MempoolService { public void onSuccess(@Nullable String jsonTxt) { UserThread.execute(() -> { outstandingRequests--; - resultHandler.accept(txValidator.parseJsonValidateMakerFeeTx(jsonTxt, btcFeeReceivers)); + resultHandler.accept(txValidator.endResult("onSuccess", true)); }); } @@ -189,7 +179,7 @@ public class MempoolService { public void onSuccess(@Nullable String jsonTxt) { UserThread.execute(() -> { outstandingRequests--; - resultHandler.accept(txValidator.parseJsonValidateTakerFeeTx(jsonTxt, btcFeeReceivers)); + resultHandler.accept(txValidator.endResult("onSuccess", true)); }); } @@ -252,7 +242,6 @@ public class MempoolService { // If input format is not as expected we ignore entry } }); - btcFeeReceivers.addAll(daoFacade.getAllDonationAddresses()); log.info("Known BTC fee receivers: {}", btcFeeReceivers.toString()); return btcFeeReceivers; diff --git a/core/src/main/java/bisq/core/provider/mempool/TxValidator.java b/core/src/main/java/bisq/core/provider/mempool/TxValidator.java index 7a70f54363..b29c47204e 100644 --- a/core/src/main/java/bisq/core/provider/mempool/TxValidator.java +++ b/core/src/main/java/bisq/core/provider/mempool/TxValidator.java @@ -17,19 +17,8 @@ package bisq.core.provider.mempool; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.state.DaoStateService; - -import bisq.common.util.Tuple2; - import org.bitcoinj.core.Coin; -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonSyntaxException; - import java.util.ArrayList; import java.util.List; @@ -37,337 +26,30 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.jetbrains.annotations.Nullable; - -import static bisq.core.util.coin.CoinUtil.maxCoin; -import static com.google.common.base.Preconditions.checkNotNull; - @Slf4j @Getter public class TxValidator { - private final static double FEE_TOLERANCE = 0.95; // we expect fees to be at least 95% of target - private final static long BLOCK_TOLERANCE = 599999L; // allow really old offers with weird fee addresses - private final DaoStateService daoStateService; private final List errorList; private final String txId; private Coin amount; - @Nullable - private Boolean isFeeCurrencyBtc = null; - @Nullable - private Long chainHeight; @Setter private String jsonTxt; - public TxValidator(DaoStateService daoStateService, String txId, Coin amount, @Nullable Boolean isFeeCurrencyBtc) { - this.daoStateService = daoStateService; + public TxValidator(String txId, Coin amount) { this.txId = txId; this.amount = amount; - this.isFeeCurrencyBtc = isFeeCurrencyBtc; this.errorList = new ArrayList<>(); this.jsonTxt = ""; } - public TxValidator(DaoStateService daoStateService, String txId) { - this.daoStateService = daoStateService; + public TxValidator(String txId) { this.txId = txId; - this.chainHeight = (long) daoStateService.getChainHeight(); this.errorList = new ArrayList<>(); this.jsonTxt = ""; } - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public TxValidator parseJsonValidateMakerFeeTx(String jsonTxt, List btcFeeReceivers) { - this.jsonTxt = jsonTxt; - boolean status = initialSanityChecks(txId, jsonTxt); - try { - if (status) { - if (checkNotNull(isFeeCurrencyBtc)) { - status = checkFeeAddressBTC(jsonTxt, btcFeeReceivers) - && checkFeeAmountBTC(jsonTxt, amount, true, getBlockHeightForFeeCalculation(jsonTxt)); - } else { - status = checkFeeAmountBSQ(jsonTxt, amount, true, getBlockHeightForFeeCalculation(jsonTxt)); - } - } - } catch (JsonSyntaxException e) { - String s = "The maker fee tx JSON validation failed with reason: " + e.toString(); - log.info(s); - errorList.add(s); - status = false; - } - return endResult("Maker tx validation", status); - } - - public TxValidator parseJsonValidateTakerFeeTx(String jsonTxt, List btcFeeReceivers) { - this.jsonTxt = jsonTxt; - boolean status = initialSanityChecks(txId, jsonTxt); - try { - if (status) { - if (isFeeCurrencyBtc == null) { - isFeeCurrencyBtc = checkFeeAddressBTC(jsonTxt, btcFeeReceivers); - } - if (isFeeCurrencyBtc) { - status = checkFeeAddressBTC(jsonTxt, btcFeeReceivers) - && checkFeeAmountBTC(jsonTxt, amount, false, getBlockHeightForFeeCalculation(jsonTxt)); - } else { - status = checkFeeAmountBSQ(jsonTxt, amount, false, getBlockHeightForFeeCalculation(jsonTxt)); - } - } - } catch (JsonSyntaxException e) { - String s = "The taker fee tx JSON validation failed with reason: " + e.toString(); - log.info(s); - errorList.add(s); - status = false; - } - return endResult("Taker tx validation", status); - } - - public long parseJsonValidateTx() { - if (!initialSanityChecks(txId, jsonTxt)) { - return -1; - } - return getTxConfirms(jsonTxt, chainHeight); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - - private boolean checkFeeAddressBTC(String jsonTxt, List btcFeeReceivers) { - try { - JsonArray jsonVout = getVinAndVout(jsonTxt).second; - JsonObject jsonVout0 = jsonVout.get(0).getAsJsonObject(); - JsonElement jsonFeeAddress = jsonVout0.get("scriptpubkey_address"); - log.debug("fee address: {}", jsonFeeAddress.getAsString()); - if (btcFeeReceivers.contains(jsonFeeAddress.getAsString())) { - return true; - } else if (getBlockHeightForFeeCalculation(jsonTxt) < BLOCK_TOLERANCE) { - log.info("Leniency rule, unrecognised fee receiver but its a really old offer so let it pass, {}", jsonFeeAddress.getAsString()); - return true; - } else { - String error = "fee address: " + jsonFeeAddress.getAsString() + " was not a known BTC fee receiver"; - errorList.add(error); - log.info(error); - } - } catch (JsonSyntaxException e) { - errorList.add(e.toString()); - log.warn(e.toString()); - } - return false; - } - - private boolean checkFeeAmountBTC(String jsonTxt, Coin tradeAmount, boolean isMaker, long blockHeight) { - JsonArray jsonVin = getVinAndVout(jsonTxt).first; - JsonArray jsonVout = getVinAndVout(jsonTxt).second; - JsonObject jsonVin0 = jsonVin.get(0).getAsJsonObject(); - JsonObject jsonVout0 = jsonVout.get(0).getAsJsonObject(); - JsonElement jsonVIn0Value = jsonVin0.getAsJsonObject("prevout").get("value"); - JsonElement jsonFeeValue = jsonVout0.get("value"); - if (jsonVIn0Value == null || jsonFeeValue == null) { - throw new JsonSyntaxException("vin/vout missing data"); - } - long feeValue = jsonFeeValue.getAsLong(); - log.debug("BTC fee: {}", feeValue); - Coin expectedFee = getFeeHistorical(tradeAmount, - isMaker ? getMakerFeeRateBtc(blockHeight) : getTakerFeeRateBtc(blockHeight), - isMaker ? Param.MIN_MAKER_FEE_BTC : Param.MIN_TAKER_FEE_BTC); - double leniencyCalc = feeValue / (double) expectedFee.getValue(); - String description = "Expected BTC fee: " + expectedFee.toString() + " sats , actual fee paid: " + Coin.valueOf(feeValue).toString() + " sats"; - if (expectedFee.getValue() == feeValue) { - log.debug("The fee matched what we expected"); - return true; - } else if (expectedFee.getValue() < feeValue) { - log.info("The fee was more than what we expected: " + description); - return true; - } else if (leniencyCalc > FEE_TOLERANCE) { - log.info("Leniency rule: the fee was low, but above {} of what was expected {} {}", FEE_TOLERANCE, leniencyCalc, description); - return true; - } else if (feeExistsUsingDifferentDaoParam(tradeAmount, Coin.valueOf(feeValue), - isMaker ? Param.DEFAULT_MAKER_FEE_BTC : Param.DEFAULT_TAKER_FEE_BTC, - isMaker ? Param.MIN_MAKER_FEE_BTC : Param.MIN_TAKER_FEE_BTC)) { - log.info("Leniency rule: the fee matches a different DAO parameter {}", description); - return true; - } else { - String feeUnderpaidMessage = "UNDERPAID. " + description; - errorList.add(feeUnderpaidMessage); - log.info(feeUnderpaidMessage); - } - return false; - } - - // I think its better to postpone BSQ fee check once the BSQ trade fee tx is confirmed and then use the BSQ explorer to request the - // BSQ fee to check if it is correct. - // Otherwise the requirements here become very complicated and potentially impossible to verify as we don't know - // if inputs and outputs are valid BSQ without the BSQ parser and confirmed transactions. - private boolean checkFeeAmountBSQ(String jsonTxt, Coin tradeAmount, boolean isMaker, long blockHeight) { - JsonArray jsonVin = getVinAndVout(jsonTxt).first; - JsonArray jsonVout = getVinAndVout(jsonTxt).second; - JsonObject jsonVin0 = jsonVin.get(0).getAsJsonObject(); - JsonObject jsonVout0 = jsonVout.get(0).getAsJsonObject(); - JsonElement jsonVIn0Value = jsonVin0.getAsJsonObject("prevout").get("value"); - JsonElement jsonFeeValue = jsonVout0.get("value"); - if (jsonVIn0Value == null || jsonFeeValue == null) { - throw new JsonSyntaxException("vin/vout missing data"); - } - Coin expectedFee = getFeeHistorical(tradeAmount, - isMaker ? getMakerFeeRateBsq(blockHeight) : getTakerFeeRateBsq(blockHeight), - isMaker ? Param.MIN_MAKER_FEE_BSQ : Param.MIN_TAKER_FEE_BSQ); - long feeValue = jsonVIn0Value.getAsLong() - jsonFeeValue.getAsLong(); - // if the first output (BSQ) is greater than the first input (BSQ) include the second input (presumably BSQ) - if (jsonFeeValue.getAsLong() > jsonVIn0Value.getAsLong()) { - // in this case 2 or more UTXOs were spent to pay the fee: - //TODO missing handling of > 2 BSQ inputs - JsonObject jsonVin1 = jsonVin.get(1).getAsJsonObject(); - JsonElement jsonVIn1Value = jsonVin1.getAsJsonObject("prevout").get("value"); - feeValue += jsonVIn1Value.getAsLong(); - } - log.debug("BURNT BSQ maker fee: {} BSQ ({} sats)", (double) feeValue / 100.0, feeValue); - double leniencyCalc = feeValue / (double) expectedFee.getValue(); - String description = String.format("Expected fee: %.2f BSQ, actual fee paid: %.2f BSQ", - (double) expectedFee.getValue() / 100.0, (double) feeValue / 100.0); - if (expectedFee.getValue() == feeValue) { - log.debug("The fee matched what we expected"); - return true; - } else if (expectedFee.getValue() < feeValue) { - log.info("The fee was more than what we expected. " + description); - return true; - } else if (leniencyCalc > FEE_TOLERANCE) { - log.info("Leniency rule: the fee was low, but above {} of what was expected {} {}", FEE_TOLERANCE, leniencyCalc, description); - return true; - } else if (feeExistsUsingDifferentDaoParam(tradeAmount, Coin.valueOf(feeValue), - isMaker ? Param.DEFAULT_MAKER_FEE_BSQ : Param.DEFAULT_TAKER_FEE_BSQ, - isMaker ? Param.MIN_MAKER_FEE_BSQ : Param.MIN_TAKER_FEE_BSQ)) { - log.info("Leniency rule: the fee matches a different DAO parameter {}", description); - return true; - } else { - errorList.add(description); - log.info(description); - } - return false; - } - - private static Tuple2 getVinAndVout(String jsonTxt) throws JsonSyntaxException { - // there should always be "vout" at the top level - // check that there are 2 or 3 vout elements: the fee, the reserved for trade, optional change - JsonObject json = new Gson().fromJson(jsonTxt, JsonObject.class); - if (json.get("vin") == null || json.get("vout") == null) { - throw new JsonSyntaxException("missing vin/vout"); - } - JsonArray jsonVin = json.get("vin").getAsJsonArray(); - JsonArray jsonVout = json.get("vout").getAsJsonArray(); - if (jsonVin == null || jsonVout == null || jsonVin.size() < 1 || jsonVout.size() < 2) { - throw new JsonSyntaxException("not enough vins/vouts"); - } - return new Tuple2<>(jsonVin, jsonVout); - } - - private static boolean initialSanityChecks(String txId, String jsonTxt) { - // there should always be "status" container element at the top level - if (jsonTxt == null || jsonTxt.length() == 0) { - return false; - } - JsonObject json = new Gson().fromJson(jsonTxt, JsonObject.class); - if (json.get("status") == null) { - return false; - } - // there should always be "txid" string element at the top level - if (json.get("txid") == null) { - return false; - } - // txid should match what we requested - if (!txId.equals(json.get("txid").getAsString())) { - return false; - } - JsonObject jsonStatus = json.get("status").getAsJsonObject(); - JsonElement jsonConfirmed = jsonStatus.get("confirmed"); - return (jsonConfirmed != null); - // the json is valid and it contains a "confirmed" field then tx is known to mempool.space - // we don't care if it is confirmed or not, just that it exists. - } - - private static long getTxConfirms(String jsonTxt, long chainHeight) { - long blockHeight = getTxBlockHeight(jsonTxt); - if (blockHeight > 0) { - return (chainHeight - blockHeight) + 1; // if it is in the current block it has 1 conf - } - return 0; // 0 indicates unconfirmed - } - - // we want the block height applicable for calculating the appropriate expected trading fees - // if the tx is not yet confirmed, use current block tip, if tx is confirmed use the block it was confirmed at. - private long getBlockHeightForFeeCalculation(String jsonTxt) { - long txBlockHeight = getTxBlockHeight(jsonTxt); - if (txBlockHeight > 0) { - return txBlockHeight; - } - return daoStateService.getChainHeight(); - } - - // this would be useful for the arbitrator verifying that the delayed payout tx is confirmed - private static long getTxBlockHeight(String jsonTxt) { - // there should always be "status" container element at the top level - JsonObject json = new Gson().fromJson(jsonTxt, JsonObject.class); - if (json.get("status") == null) { - return -1L; - } - JsonObject jsonStatus = json.get("status").getAsJsonObject(); - JsonElement jsonConfirmed = jsonStatus.get("confirmed"); - if (jsonConfirmed == null) { - return -1L; - } - if (jsonConfirmed.getAsBoolean()) { - // it is confirmed, lets get the block height - JsonElement jsonBlockHeight = jsonStatus.get("block_height"); - if (jsonBlockHeight == null) { - return -1L; // block height error - } - return (jsonBlockHeight.getAsLong()); - } - return 0L; // in mempool, not confirmed yet - } - - private Coin getFeeHistorical(Coin amount, Coin feeRatePerBtc, Param minFeeParam) { - double feePerBtcAsDouble = (double) feeRatePerBtc.value; - double amountAsDouble = amount != null ? (double) amount.value : 0; - double btcAsDouble = (double) Coin.COIN.value; - double fact = amountAsDouble / btcAsDouble; - Coin feePerBtc = Coin.valueOf(Math.round(feePerBtcAsDouble * fact)); - Coin minFee = daoStateService.getParamValueAsCoin(minFeeParam, minFeeParam.getDefaultValue()); - return maxCoin(feePerBtc, minFee); - } - - private Coin getMakerFeeRateBsq(long blockHeight) { - return daoStateService.getParamValueAsCoin(Param.DEFAULT_MAKER_FEE_BSQ, (int) blockHeight); - } - - private Coin getTakerFeeRateBsq(long blockHeight) { - return daoStateService.getParamValueAsCoin(Param.DEFAULT_TAKER_FEE_BSQ, (int) blockHeight); - } - - private Coin getMakerFeeRateBtc(long blockHeight) { - return daoStateService.getParamValueAsCoin(Param.DEFAULT_MAKER_FEE_BTC, (int) blockHeight); - } - - private Coin getTakerFeeRateBtc(long blockHeight) { - return daoStateService.getParamValueAsCoin(Param.DEFAULT_TAKER_FEE_BTC, (int) blockHeight); - } - - // implements leniency rule of accepting old DAO rate parameters: https://github.com/bisq-network/bisq/issues/5329#issuecomment-803223859 - // We iterate over all past dao param values and if one of those matches we consider it valid. That covers the non-in-sync cases. - private boolean feeExistsUsingDifferentDaoParam(Coin tradeAmount, Coin actualFeeValue, Param defaultFeeParam, Param minFeeParam) { - for (Coin daoHistoricalRate : daoStateService.getParamChangeList(defaultFeeParam)) { - if (actualFeeValue.equals(getFeeHistorical(tradeAmount, daoHistoricalRate, minFeeParam))) { - return true; - } - } - // finally check the default rate used when we ask for the fee rate at block height 0 (it is hard coded in the Param enum) - Coin defaultRate = daoStateService.getParamValueAsCoin(defaultFeeParam, 0); - return actualFeeValue.equals(getFeeHistorical(tradeAmount, defaultRate, minFeeParam)); - } - public TxValidator endResult(String title, boolean status) { log.info("{} : {}", title, status ? "SUCCESS" : "FAIL"); if (!status) { @@ -388,6 +70,7 @@ public class TxValidator { return errorList.toString().substring(0, Math.min(85, errorList.toString().length())); } + @Override public String toString() { return errorList.toString(); } diff --git a/core/src/main/java/bisq/core/provider/price/PriceFeedService.java b/core/src/main/java/bisq/core/provider/price/PriceFeedService.java index 57e9a5197d..29b42fd7d4 100644 --- a/core/src/main/java/bisq/core/provider/price/PriceFeedService.java +++ b/core/src/main/java/bisq/core/provider/price/PriceFeedService.java @@ -325,16 +325,6 @@ public class PriceFeedService { }); } - public Optional getBsqPrice() { - MarketPrice bsqMarketPrice = getMarketPrice("BSQ"); - if (bsqMarketPrice != null) { - long bsqPriceAsLong = MathUtils.roundDoubleToLong(MathUtils.scaleUpByPowerOf10(bsqMarketPrice.getPrice(), Altcoin.SMALLEST_UNIT_EXPONENT)); - return Optional.of(Price.valueOf("BSQ", bsqPriceAsLong)); - } else { - return Optional.empty(); - } - } - /////////////////////////////////////////////////////////////////////////////////////////// // Private /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java b/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java index 69485612c7..d279bf7ffb 100644 --- a/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java +++ b/core/src/main/java/bisq/core/setup/CoreNetworkCapabilities.java @@ -34,7 +34,6 @@ public class CoreNetworkCapabilities { Capability.ACK_MSG, Capability.PROPOSAL, Capability.BLIND_VOTE, - Capability.DAO_STATE, Capability.BUNDLE_OF_ENVELOPES, Capability.MEDIATION, Capability.SIGNED_ACCOUNT_AGE_WITNESS, @@ -44,23 +43,6 @@ public class CoreNetworkCapabilities { Capability.TRADE_STATISTICS_3 ); - if (config.daoActivated) { - maybeApplyDaoFullMode(config); - } - log.info(Capabilities.app.prettyPrint()); } - - public static void maybeApplyDaoFullMode(Config config) { - // If we set dao full mode at the preferences view we add the capability there. We read the preferences a - // 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) { - 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. - Capabilities.app.addAll(Capability.RECEIVE_BSQ_BLOCK); - } - } } diff --git a/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java b/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java index ab2a162f06..003b98ec6a 100644 --- a/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java +++ b/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java @@ -19,13 +19,6 @@ package bisq.core.setup; import bisq.core.btc.model.AddressEntryList; import bisq.core.btc.model.XmrAddressEntryList; -import bisq.core.dao.governance.ballot.BallotListService; -import bisq.core.dao.governance.blindvote.MyBlindVoteListService; -import bisq.core.dao.governance.bond.reputation.MyReputationListService; -import bisq.core.dao.governance.myvote.MyVoteListService; -import bisq.core.dao.governance.proofofburn.MyProofOfBurnListService; -import bisq.core.dao.governance.proposal.MyProposalListService; -import bisq.core.dao.state.unconfirmed.UnconfirmedBsqChangeOutputListService; import bisq.core.offer.OpenOfferManager; import bisq.core.support.dispute.arbitration.ArbitrationDisputeListService; import bisq.core.support.dispute.mediation.MediationDisputeListService; @@ -75,15 +68,6 @@ public class CorePersistedDataHost { persistedDataHosts.add(injector.getInstance(IgnoredMailboxService.class)); persistedDataHosts.add(injector.getInstance(RemovedPayloadsService.class)); - if (injector.getInstance(Config.class).daoActivated) { - persistedDataHosts.add(injector.getInstance(BallotListService.class)); - persistedDataHosts.add(injector.getInstance(MyBlindVoteListService.class)); - persistedDataHosts.add(injector.getInstance(MyVoteListService.class)); - persistedDataHosts.add(injector.getInstance(MyProposalListService.class)); - persistedDataHosts.add(injector.getInstance(MyReputationListService.class)); - persistedDataHosts.add(injector.getInstance(MyProofOfBurnListService.class)); - persistedDataHosts.add(injector.getInstance(UnconfirmedBsqChangeOutputListService.class)); - } return persistedDataHosts; } } diff --git a/core/src/main/java/bisq/core/support/dispute/DisputeManager.java b/core/src/main/java/bisq/core/support/dispute/DisputeManager.java index 2810cf59b4..990a45295c 100644 --- a/core/src/main/java/bisq/core/support/dispute/DisputeManager.java +++ b/core/src/main/java/bisq/core/support/dispute/DisputeManager.java @@ -21,7 +21,6 @@ import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.Restrictions; import bisq.core.btc.wallet.TradeWalletService; import bisq.core.btc.wallet.XmrWalletService; -import bisq.core.dao.DaoFacade; import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.monetary.Altcoin; @@ -97,7 +96,6 @@ public abstract class DisputeManager> extends Sup protected final DisputeListService disputeListService; private final Config config; private final PriceFeedService priceFeedService; - protected final DaoFacade daoFacade; @Getter protected final ObservableList validationExceptions = @@ -117,7 +115,6 @@ public abstract class DisputeManager> extends Sup TradeManager tradeManager, ClosedTradableManager closedTradableManager, OpenOfferManager openOfferManager, - DaoFacade daoFacade, KeyRing keyRing, DisputeListService disputeListService, Config config, @@ -129,7 +126,6 @@ public abstract class DisputeManager> extends Sup this.tradeManager = tradeManager; this.closedTradableManager = closedTradableManager; this.openOfferManager = openOfferManager; - this.daoFacade = daoFacade; this.pubKeyRing = keyRing.getPubKeyRing(); signatureKeyPair = keyRing.getSignatureKeyPair(); this.disputeListService = disputeListService; @@ -272,7 +268,7 @@ public abstract class DisputeManager> extends Sup List disputes = getDisputeList().getList(); disputes.forEach(dispute -> { try { - TradeDataValidation.validateDonationAddress(dispute, dispute.getDonationAddressOfDelayedPayoutTx(), daoFacade); + TradeDataValidation.validateDonationAddress(dispute, dispute.getDonationAddressOfDelayedPayoutTx()); TradeDataValidation.validateNodeAddress(dispute, dispute.getContract().getBuyerNodeAddress(), config); TradeDataValidation.validateNodeAddress(dispute, dispute.getContract().getSellerNodeAddress(), config); } catch (TradeDataValidation.AddressException | TradeDataValidation.NodeAddressException e) { @@ -366,7 +362,7 @@ public abstract class DisputeManager> extends Sup try { TradeDataValidation.validatePaymentAccountPayloads(dispute); - TradeDataValidation.validateDonationAddress(dispute.getDonationAddressOfDelayedPayoutTx(), daoFacade); + TradeDataValidation.validateDonationAddress(dispute.getDonationAddressOfDelayedPayoutTx()); //TradeDataValidation.testIfDisputeTriesReplay(dispute, disputeList.getList()); // TODO (woodser): disabled for xmr, needed? TradeDataValidation.validateNodeAddress(dispute, dispute.getContract().getBuyerNodeAddress(), config); TradeDataValidation.validateNodeAddress(dispute, dispute.getContract().getSellerNodeAddress(), config); diff --git a/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java b/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java index 90bd8a8ab2..0471d6f05a 100644 --- a/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java +++ b/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java @@ -20,7 +20,6 @@ package bisq.core.support.dispute.arbitration; import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.TradeWalletService; import bisq.core.btc.wallet.XmrWalletService; -import bisq.core.dao.DaoFacade; import bisq.core.locale.Res; import bisq.core.offer.OpenOffer; import bisq.core.offer.OpenOfferManager; @@ -99,13 +98,12 @@ public final class ArbitrationManager extends DisputeManager TradeManager tradeManager, ClosedTradableManager closedTradableManager, OpenOfferManager openOfferManager, - DaoFacade daoFacade, KeyRing keyRing, MediationDisputeListService mediationDisputeListService, Config config, PriceFeedService priceFeedService) { super(p2PService, tradeWalletService, walletService, walletsSetup, tradeManager, closedTradableManager, - openOfferManager, daoFacade, keyRing, mediationDisputeListService, config, priceFeedService); + openOfferManager, keyRing, mediationDisputeListService, config, priceFeedService); } diff --git a/core/src/main/java/bisq/core/support/dispute/refund/RefundManager.java b/core/src/main/java/bisq/core/support/dispute/refund/RefundManager.java index 527c53ab01..cff1645c87 100644 --- a/core/src/main/java/bisq/core/support/dispute/refund/RefundManager.java +++ b/core/src/main/java/bisq/core/support/dispute/refund/RefundManager.java @@ -20,7 +20,6 @@ package bisq.core.support.dispute.refund; import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.TradeWalletService; import bisq.core.btc.wallet.XmrWalletService; -import bisq.core.dao.DaoFacade; import bisq.core.locale.Res; import bisq.core.offer.OpenOffer; import bisq.core.offer.OpenOfferManager; @@ -76,13 +75,13 @@ public final class RefundManager extends DisputeManager { TradeManager tradeManager, ClosedTradableManager closedTradableManager, OpenOfferManager openOfferManager, - DaoFacade daoFacade, // TODO (woodser): remove daoFacade, priceFeedService? + // TODO (woodser): remove priceFeedService? KeyRing keyRing, RefundDisputeListService refundDisputeListService, Config config, PriceFeedService priceFeedService) { super(p2PService, tradeWalletService, walletService, walletsSetup, tradeManager, closedTradableManager, - openOfferManager, daoFacade, keyRing, refundDisputeListService, config, priceFeedService); + openOfferManager, keyRing, refundDisputeListService, config, priceFeedService); } diff --git a/core/src/main/java/bisq/core/trade/TradeDataValidation.java b/core/src/main/java/bisq/core/trade/TradeDataValidation.java index 5d4d9883b8..2ef42d6fe2 100644 --- a/core/src/main/java/bisq/core/trade/TradeDataValidation.java +++ b/core/src/main/java/bisq/core/trade/TradeDataValidation.java @@ -18,7 +18,6 @@ package bisq.core.trade; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.dao.DaoFacade; import bisq.core.offer.Offer; import bisq.core.support.SupportType; import bisq.core.support.dispute.Dispute; @@ -60,9 +59,9 @@ public class TradeDataValidation { if (!Arrays.equals(dispute.getTakerPaymentAccountPayload().getHash(), dispute.getContract().getTakerPaymentAccountPayloadHash())) throw new InvalidPaymentAccountPayloadException(dispute, "Hash of taker's payment account payload does not match contract"); } - public static void validateDonationAddress(String addressAsString, DaoFacade daoFacade) + public static void validateDonationAddress(String addressAsString) throws AddressException { - validateDonationAddress(null, addressAsString, daoFacade); + validateDonationAddress(null, addressAsString); } public static void validateNodeAddress(Dispute dispute, NodeAddress nodeAddress, Config config) @@ -75,22 +74,13 @@ public class TradeDataValidation { } } - public static void validateDonationAddress(@Nullable Dispute dispute, String addressAsString, DaoFacade daoFacade) + public static void validateDonationAddress(@Nullable Dispute dispute, String addressAsString) throws AddressException { if (addressAsString == null) { log.debug("address is null at validateDonationAddress. This is expected in case of an not updated trader."); return; } - - Set allPastParamValues = daoFacade.getAllDonationAddresses(); - if (!allPastParamValues.contains(addressAsString)) { - String errorMsg = "Donation address is not a valid DAO donation address." + - "\nAddress used in the dispute: " + addressAsString + - "\nAll DAO param donation addresses:" + allPastParamValues; - log.error(errorMsg); - throw new AddressException(dispute, errorMsg); - } } public static void testIfAnyDisputeTriedReplay(List disputeList, @@ -213,14 +203,12 @@ public class TradeDataValidation { public static void validateDelayedPayoutTx(Trade trade, Transaction delayedPayoutTx, - DaoFacade daoFacade, BtcWalletService btcWalletService) throws AddressException, MissingTxException, InvalidTxException, InvalidLockTimeException, InvalidAmountException { validateDelayedPayoutTx(trade, delayedPayoutTx, null, - daoFacade, btcWalletService, null); } @@ -228,21 +216,18 @@ public class TradeDataValidation { public static void validateDelayedPayoutTx(Trade trade, Transaction delayedPayoutTx, @Nullable Dispute dispute, - DaoFacade daoFacade, BtcWalletService btcWalletService) throws AddressException, MissingTxException, InvalidTxException, InvalidLockTimeException, InvalidAmountException { validateDelayedPayoutTx(trade, delayedPayoutTx, dispute, - daoFacade, btcWalletService, null); } public static void validateDelayedPayoutTx(Trade trade, Transaction delayedPayoutTx, - DaoFacade daoFacade, BtcWalletService btcWalletService, @Nullable Consumer addressConsumer) throws AddressException, MissingTxException, @@ -250,7 +235,6 @@ public class TradeDataValidation { validateDelayedPayoutTx(trade, delayedPayoutTx, null, - daoFacade, btcWalletService, addressConsumer); } @@ -258,7 +242,6 @@ public class TradeDataValidation { public static void validateDelayedPayoutTx(Trade trade, Transaction delayedPayoutTx, @Nullable Dispute dispute, - DaoFacade daoFacade, BtcWalletService btcWalletService, @Nullable Consumer addressConsumer) throws AddressException, MissingTxException, @@ -332,7 +315,7 @@ public class TradeDataValidation { addressConsumer.accept(addressAsString); } - validateDonationAddress(addressAsString, daoFacade); + validateDonationAddress(addressAsString); if (dispute != null) { // Verify that address in the dispute matches the one in the trade. diff --git a/core/src/main/java/bisq/core/trade/TradeManager.java b/core/src/main/java/bisq/core/trade/TradeManager.java index 7a641b787c..2c4c1a5446 100644 --- a/core/src/main/java/bisq/core/trade/TradeManager.java +++ b/core/src/main/java/bisq/core/trade/TradeManager.java @@ -18,7 +18,6 @@ package bisq.core.trade; import bisq.core.btc.model.XmrAddressEntry; -import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.XmrWalletService; import bisq.core.locale.Res; import bisq.core.offer.Offer; @@ -128,7 +127,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi @Getter private final KeyRing keyRing; private final XmrWalletService xmrWalletService; - private final BsqWalletService bsqWalletService; private final OfferBookService offerBookService; private final OpenOfferManager openOfferManager; private final ClosedTradableManager closedTradableManager; @@ -168,7 +166,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi public TradeManager(User user, KeyRing keyRing, XmrWalletService xmrWalletService, - BsqWalletService bsqWalletService, OfferBookService offerBookService, OpenOfferManager openOfferManager, ClosedTradableManager closedTradableManager, @@ -189,7 +186,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi this.user = user; this.keyRing = keyRing; this.xmrWalletService = xmrWalletService; - this.bsqWalletService = bsqWalletService; this.offerBookService = offerBookService; this.openOfferManager = openOfferManager; this.closedTradableManager = closedTradableManager; @@ -372,7 +368,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi // handle request as arbitrator boolean isArbitrator = request.getArbitratorNodeAddress().equals(p2PService.getNetworkNode().getNodeAddress()); if (isArbitrator) { - + // verify this node is registered arbitrator Mediator thisArbitrator = user.getRegisteredMediator(); NodeAddress thisAddress = p2PService.getNetworkNode().getNodeAddress(); @@ -392,10 +388,10 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi log.warn("Ignoring InitTradeRequest from {} with tradeId {} because no offer is on the books", sender, request.getTradeId()); return; } - + // verify arbitrator is payload signer unless they are offline // TODO (woodser): handle if payload signer differs from current arbitrator (verify signer is offline) - + // verify maker is offer owner // TODO (woodser): maker address might change if they disconnect and reconnect, should allow maker address to differ if pubKeyRing is same ? if (!offer.getOwnerNodeAddress().equals(request.getMakerNodeAddress())) { @@ -407,24 +403,24 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi Optional tradeOptional = getTradeById(offer.getId()); if (tradeOptional.isPresent()) { trade = tradeOptional.get(); - + // verify request is from maker if (!sender.equals(request.getMakerNodeAddress())) { log.warn("Trade is already taken"); // TODO (woodser): need to respond with bad ack return; } } else { - + // verify request is from taker if (!sender.equals(request.getTakerNodeAddress())) { log.warn("Ignoring InitTradeRequest from {} with tradeId {} because request must be from taker when trade is not initialized", sender, request.getTradeId()); return; } - + // compute expected taker fee - Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getTakerFeePerBtc(true), Coin.valueOf(offer.getOfferPayload().getAmount())); - Coin takerFee = CoinUtil.maxCoin(feePerBtc, FeeService.getMinTakerFee(true)); - + Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getTakerFeePerBtc(), Coin.valueOf(offer.getOfferPayload().getAmount())); + Coin takerFee = CoinUtil.maxCoin(feePerBtc, FeeService.getMinTakerFee()); + // create arbitrator trade trade = new ArbitratorTrade(offer, Coin.valueOf(offer.getOfferPayload().getAmount()), @@ -436,7 +432,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi request.getMakerNodeAddress(), request.getTakerNodeAddress(), request.getArbitratorNodeAddress()); - + // set reserve tx hash Optional signedOfferOptional = openOfferManager.getSignedOfferById(request.getTradeId()); if (!signedOfferOptional.isPresent()) return; @@ -469,7 +465,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi Offer offer = openOffer.getOffer(); openOfferManager.reserveOpenOffer(openOffer); // TODO (woodser): reserve offer if arbitrator? - + // verify request is from signing arbitrator when they're online, else from selected arbitrator if (!sender.equals(offer.getOfferPayload().getArbitratorNodeAddress())) { boolean isSignerOnline = true; // TODO (woodser): determine if signer is online and test @@ -545,7 +541,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi } }); } - + private void handleSignContractRequest(SignContractRequest request, NodeAddress peer) { log.info("Received SignContractRequest from {} with tradeId {} and uid {}", peer, request.getTradeId(), request.getUid()); @@ -565,7 +561,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi } }); } - + private void handleSignContractResponse(SignContractResponse request, NodeAddress peer) { log.info("Received SignContractResponse from {} with tradeId {} and uid {}", peer, request.getTradeId(), request.getUid()); @@ -585,7 +581,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi } }); } - + private void handleDepositRequest(DepositRequest request, NodeAddress peer) { log.info("Received DepositRequest from {} with tradeId {} and uid {}", peer, request.getTradeId(), request.getUid()); @@ -605,7 +601,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi } }); } - + private void handleDepositResponse(DepositResponse response, NodeAddress peer) { log.info("Received DepositResponse from {} with tradeId {} and uid {}", peer, response.getTradeId(), response.getUid()); @@ -625,7 +621,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi } }); } - + private void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress peer) { log.info("Received PaymentAccountPayloadRequest from {} with tradeId {} and uid {}", peer, request.getTradeId(), request.getUid()); @@ -690,7 +686,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi ErrorMessageHandler errorMessageHandler) { checkArgument(!wasOfferAlreadyUsedInTrade(offer.getId())); - + OfferAvailabilityModel model = getOfferAvailabilityModel(offer, isTakerApiUser, paymentAccountId); offer.checkOfferAvailability(model, () -> { @@ -719,7 +715,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi P2PService.getMyNodeAddress(), offer.getOfferPayload().getArbitratorNodeAddress()); } - + trade.getProcessModel().setTradeMessage(model.getTradeRequest()); trade.getProcessModel().setMakerSignature(model.getMakerSignature()); trade.getProcessModel().setArbitratorNodeAddress(model.getArbitratorNodeAddress()); diff --git a/core/src/main/java/bisq/core/trade/messages/InputsForDepositTxRequest.java b/core/src/main/java/bisq/core/trade/messages/InputsForDepositTxRequest.java index a67a879e3d..10fa651db1 100644 --- a/core/src/main/java/bisq/core/trade/messages/InputsForDepositTxRequest.java +++ b/core/src/main/java/bisq/core/trade/messages/InputsForDepositTxRequest.java @@ -47,7 +47,6 @@ public final class InputsForDepositTxRequest extends TradeMessage implements Dir private final long tradePrice; private final long txFee; private final long takerFee; - private final boolean isCurrencyForTakerFeeBtc; private final List rawTransactionInputs; private final long changeOutputValue; @Nullable @@ -75,7 +74,6 @@ public final class InputsForDepositTxRequest extends TradeMessage implements Dir long tradePrice, long txFee, long takerFee, - boolean isCurrencyForTakerFeeBtc, List rawTransactionInputs, long changeOutputValue, @Nullable String changeOutputAddress, @@ -101,7 +99,6 @@ public final class InputsForDepositTxRequest extends TradeMessage implements Dir this.tradePrice = tradePrice; this.txFee = txFee; this.takerFee = takerFee; - this.isCurrencyForTakerFeeBtc = isCurrencyForTakerFeeBtc; this.rawTransactionInputs = rawTransactionInputs; this.changeOutputValue = changeOutputValue; this.changeOutputAddress = changeOutputAddress; @@ -135,7 +132,6 @@ public final class InputsForDepositTxRequest extends TradeMessage implements Dir .setTradePrice(tradePrice) .setTxFee(txFee) .setTakerFee(takerFee) - .setIsCurrencyForTakerFeeBtc(isCurrencyForTakerFeeBtc) .addAllRawTransactionInputs(rawTransactionInputs.stream() .map(RawTransactionInput::toProtoMessage).collect(Collectors.toList())) .setChangeOutputValue(changeOutputValue) @@ -182,7 +178,6 @@ public final class InputsForDepositTxRequest extends TradeMessage implements Dir proto.getTradePrice(), proto.getTxFee(), proto.getTakerFee(), - proto.getIsCurrencyForTakerFeeBtc(), rawTransactionInputs, proto.getChangeOutputValue(), ProtoUtil.stringOrNullFromProto(proto.getChangeOutputAddress()), @@ -212,7 +207,6 @@ public final class InputsForDepositTxRequest extends TradeMessage implements Dir ",\n tradePrice=" + tradePrice + ",\n txFee=" + txFee + ",\n takerFee=" + takerFee + - ",\n isCurrencyForTakerFeeBtc=" + isCurrencyForTakerFeeBtc + ",\n rawTransactionInputs=" + rawTransactionInputs + ",\n changeOutputValue=" + changeOutputValue + ",\n changeOutputAddress='" + changeOutputAddress + '\'' + diff --git a/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java b/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java index fecf1e72f3..d7eae6b031 100644 --- a/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java +++ b/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java @@ -19,11 +19,9 @@ package bisq.core.trade.protocol; import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.btc.model.RawTransactionInput; -import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.TradeWalletService; import bisq.core.btc.wallet.XmrWalletService; -import bisq.core.dao.DaoFacade; import bisq.core.filter.FilterManager; import bisq.core.network.MessageState; import bisq.core.offer.Offer; @@ -403,10 +401,6 @@ public class ProcessModel implements Model, PersistablePayload { return provider.getP2PService(); } - public BsqWalletService getBsqWalletService() { - return provider.getBsqWalletService(); - } - public TradeWalletService getTradeWalletService() { return provider.getTradeWalletService(); } @@ -447,10 +441,6 @@ public class ProcessModel implements Model, PersistablePayload { return provider.getKeyRing(); } - public DaoFacade getDaoFacade() { - return provider.getDaoFacade(); - } - public void setBuyerSignedPayoutTx(MoneroTxWallet buyerSignedPayoutTx) { this.buyerSignedPayoutTx = buyerSignedPayoutTx; } diff --git a/core/src/main/java/bisq/core/trade/protocol/ProcessModelServiceProvider.java b/core/src/main/java/bisq/core/trade/protocol/ProcessModelServiceProvider.java index 1b1fe0cc2b..c2c8518104 100644 --- a/core/src/main/java/bisq/core/trade/protocol/ProcessModelServiceProvider.java +++ b/core/src/main/java/bisq/core/trade/protocol/ProcessModelServiceProvider.java @@ -18,11 +18,9 @@ package bisq.core.trade.protocol; import bisq.core.account.witness.AccountAgeWitnessService; -import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.TradeWalletService; import bisq.core.btc.wallet.XmrWalletService; -import bisq.core.dao.DaoFacade; import bisq.core.filter.FilterManager; import bisq.core.offer.OpenOfferManager; import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager; @@ -46,9 +44,7 @@ public class ProcessModelServiceProvider { private final P2PService p2PService; private final BtcWalletService btcWalletService; private final XmrWalletService xmrWalletService; - private final BsqWalletService bsqWalletService; private final TradeWalletService tradeWalletService; - private final DaoFacade daoFacade; private final ReferralIdService referralIdService; private final User user; private final FilterManager filterManager; @@ -64,9 +60,7 @@ public class ProcessModelServiceProvider { P2PService p2PService, BtcWalletService btcWalletService, XmrWalletService xmrWalletService, - BsqWalletService bsqWalletService, TradeWalletService tradeWalletService, - DaoFacade daoFacade, ReferralIdService referralIdService, User user, FilterManager filterManager, @@ -80,9 +74,7 @@ public class ProcessModelServiceProvider { this.p2PService = p2PService; this.btcWalletService = btcWalletService; this.xmrWalletService = xmrWalletService; - this.bsqWalletService = bsqWalletService; this.tradeWalletService = tradeWalletService; - this.daoFacade = daoFacade; this.referralIdService = referralIdService; this.user = user; this.filterManager = filterManager; diff --git a/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java b/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java index 913441b180..b3b5fb46c6 100644 --- a/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java @@ -294,7 +294,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D } protected void sendAckMessage(NodeAddress peer, TradeMessage message, boolean result, @Nullable String errorMessage) { - + // TODO (woodser): remove trade.getTradingPeerNodeAddress() and processModel.getTempTradingPeerNodeAddress() if everything should be maker, taker, or arbitrator // get peer's pub key ring diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerProcessDelayedPayoutTxSignatureRequest.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerProcessDelayedPayoutTxSignatureRequest.java index 3cf0ee338b..42c2bfff4a 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerProcessDelayedPayoutTxSignatureRequest.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerProcessDelayedPayoutTxSignatureRequest.java @@ -49,7 +49,7 @@ public class BuyerProcessDelayedPayoutTxSignatureRequest extends TradeTask { trade.getTradingPeer().setDelayedPayoutTxSignature(checkNotNull(request.getDelayedPayoutTxSellerSignature())); // When we receive that message the taker has published the taker fee, so we apply it to the trade. - // The takerFeeTx was sent in the first message. It should be part of DelayedPayoutTxSignatureRequest + // The takerFeeTx was sent in the first message. It should be part of DelayedPayoutTxSignatureRequest // but that cannot be changed due backward compatibility issues. It is a left over from the old trade protocol. trade.setTakerFeeTxId(processModel.getTakeOfferFeeTxId()); diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesFinalDelayedPayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesFinalDelayedPayoutTx.java index 05efe134fc..1e209e6d3a 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesFinalDelayedPayoutTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesFinalDelayedPayoutTx.java @@ -36,23 +36,6 @@ public class BuyerVerifiesFinalDelayedPayoutTx extends TradeTask { runInterceptHook(); throw new RuntimeException("BuyerVerifiesFinalDelayedPayoutTx not applicable for xmr"); - -// Transaction delayedPayoutTx = trade.getDelayedPayoutTx(); -// checkNotNull(delayedPayoutTx, "trade.getDelayedPayoutTx() must not be null"); -// // Check again tx -// TradeDataValidation.validateDelayedPayoutTx(trade, -// delayedPayoutTx, -// processModel.getDaoFacade(), -// processModel.getBtcWalletService()); -// -// // Now as we know the deposit tx we can also verify the input -// Transaction depositTx = trade.getDepositTx(); -// checkNotNull(depositTx, "trade.getDepositTx() must not be null"); -// TradeDataValidation.validatePayoutTxInput(depositTx, delayedPayoutTx); -// -// complete(); -// } catch (TradeDataValidation.ValidationException e) { -// failed(e.getMessage()); } catch (Throwable t) { failed(t); } diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesPreparedDelayedPayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesPreparedDelayedPayoutTx.java index 96f93c8ca6..b91a6ca390 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesPreparedDelayedPayoutTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerVerifiesPreparedDelayedPayoutTx.java @@ -41,7 +41,6 @@ public class BuyerVerifiesPreparedDelayedPayoutTx extends TradeTask { var preparedDelayedPayoutTx = processModel.getPreparedDelayedPayoutTx(); TradeDataValidation.validateDelayedPayoutTx(trade, preparedDelayedPayoutTx, - processModel.getDaoFacade(), processModel.getBtcWalletService()); // If the deposit tx is non-malleable, we already know its final ID, so should check that now diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerCreatesDelayedPayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerCreatesDelayedPayoutTx.java index 17cfa77f75..17b546763c 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerCreatesDelayedPayoutTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerCreatesDelayedPayoutTx.java @@ -18,7 +18,6 @@ package bisq.core.trade.protocol.tasks.seller; import bisq.core.btc.wallet.TradeWalletService; -import bisq.core.dao.governance.param.Param; import bisq.core.trade.Trade; import bisq.core.trade.TradeDataValidation; import bisq.core.trade.protocol.tasks.TradeTask; @@ -43,28 +42,7 @@ public class SellerCreatesDelayedPayoutTx extends TradeTask { protected void run() { try { runInterceptHook(); - if (true) throw new RuntimeException("SellerCreatesDelayedPayoutTx not implemented for xmr"); - - String donationAddressString = processModel.getDaoFacade().getParamValue(Param.RECIPIENT_BTC_ADDRESS); - Coin minerFee = trade.getTxFee(); - TradeWalletService tradeWalletService = processModel.getTradeWalletService(); - Transaction depositTx = checkNotNull(processModel.getDepositTx()); - - long lockTime = trade.getLockTime(); - Transaction preparedDelayedPayoutTx = tradeWalletService.createDelayedUnsignedPayoutTx(depositTx, - donationAddressString, - minerFee, - lockTime); - TradeDataValidation.validateDelayedPayoutTx(trade, - preparedDelayedPayoutTx, - processModel.getDaoFacade(), - processModel.getBtcWalletService()); - - processModel.setPreparedDelayedPayoutTx(preparedDelayedPayoutTx); - - processModel.getTradeManager().requestPersistence(); - - complete(); + throw new RuntimeException("SellerCreatesDelayedPayoutTx not implemented for xmr"); } catch (Throwable t) { failed(t); } diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerCreateFeeTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerCreateFeeTx.java index 62a5e4ce60..ff497caaa6 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerCreateFeeTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerCreateFeeTx.java @@ -20,7 +20,6 @@ package bisq.core.trade.protocol.tasks.taker; import bisq.core.btc.model.XmrAddressEntry; import bisq.core.btc.wallet.TradeWalletService; import bisq.core.btc.wallet.XmrWalletService; -import bisq.core.dao.exceptions.DaoDisabledException; import bisq.core.trade.Trade; import bisq.core.trade.protocol.tasks.TradeTask; @@ -66,11 +65,7 @@ public class TakerCreateFeeTx extends TradeTask { processModel.setTakeOfferFeeTx(tx); complete(); } catch (Throwable t) { - if (t instanceof DaoDisabledException) { - failed("You cannot pay the trade fee in BSQ at the moment because the DAO features have been " + "disabled due technical problems. Please use the BTC fee option until the issues are resolved. " + "For more information please visit the Bisq Forum."); - } else { failed(t); - } } } } diff --git a/core/src/main/java/bisq/core/user/Preferences.java b/core/src/main/java/bisq/core/user/Preferences.java index adce4e4c6d..b0f2df72f2 100644 --- a/core/src/main/java/bisq/core/user/Preferences.java +++ b/core/src/main/java/bisq/core/user/Preferences.java @@ -30,7 +30,6 @@ import bisq.core.locale.TradeCurrency; import bisq.core.payment.PaymentAccount; import bisq.core.payment.PaymentAccountUtil; import bisq.core.provider.fee.FeeService; -import bisq.core.setup.CoreNetworkCapabilities; import bisq.network.p2p.network.BridgeAddressProvider; @@ -56,12 +55,10 @@ import javafx.collections.ObservableMap; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; -import java.util.Random; import java.util.stream.Collectors; import lombok.Getter; @@ -111,15 +108,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid new BlockChainExplorer("SoChain. Wow.", "https://chain.so/tx/BTCTEST/", "https://chain.so/address/BTCTEST/"), new BlockChainExplorer("Blockchair", "https://blockchair.com/bitcoin/testnet/transaction/", "https://blockchair.com/bitcoin/testnet/address/") )); - private static final ArrayList BTC_DAO_TEST_NET_EXPLORERS = new ArrayList<>(Collections.singletonList( - new BlockChainExplorer("BTC DAO-testnet explorer", "https://bisq.network/explorer/btc/dao_testnet/tx/", "https://bisq.network/explorer/btc/dao_testnet/address/") - )); - - public static final ArrayList BSQ_MAIN_NET_EXPLORERS = new ArrayList<>(Arrays.asList( - new BlockChainExplorer("mempool.space (@wiz)", "https://mempool.space/bisq/tx/", "https://mempool.space/bisq/address/"), - new BlockChainExplorer("mempool.emzy.de (@emzy)", "https://mempool.emzy.de/bisq/tx/", "https://mempool.emzy.de/bisq/address/"), - new BlockChainExplorer("mempool.bisq.services (@devinbileck)", "https://mempool.bisq.services/bisq/tx/", "https://mempool.bisq.services/bisq/address/") - )); private static final ArrayList XMR_MAIN_NET_EXPLORERS = new ArrayList<>(Arrays.asList( new BlockChainExplorer("xmrchain.net", "https://xmrchain.net/tx/", "") )); @@ -174,10 +162,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid private final Config config; private final FeeService feeService; private final LocalBitcoinNode localBitcoinNode; - private final String btcNodesFromOptions, referralIdFromOptions, - rpcUserFromOptions, rpcPwFromOptions; - private final int blockNotifyPortFromOptions; - private final boolean fullDaoNodeFromOptions; + private final String btcNodesFromOptions; @Getter private final BooleanProperty useStandbyModeProperty = new SimpleBooleanProperty(prefPayload.isUseStandbyMode()); @@ -191,23 +176,13 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid Config config, FeeService feeService, LocalBitcoinNode localBitcoinNode, - @Named(Config.BTC_NODES) String btcNodesFromOptions, - @Named(Config.REFERRAL_ID) String referralId, - @Named(Config.FULL_DAO_NODE) boolean fullDaoNode, - @Named(Config.RPC_USER) String rpcUser, - @Named(Config.RPC_PASSWORD) String rpcPassword, - @Named(Config.RPC_BLOCK_NOTIFICATION_PORT) int rpcBlockNotificationPort) { + @Named(Config.BTC_NODES) String btcNodesFromOptions) { this.persistenceManager = persistenceManager; this.config = config; this.feeService = feeService; this.localBitcoinNode = localBitcoinNode; this.btcNodesFromOptions = btcNodesFromOptions; - this.referralIdFromOptions = referralId; - this.fullDaoNodeFromOptions = fullDaoNode; - this.rpcUserFromOptions = rpcUser; - this.rpcPwFromOptions = rpcPassword; - this.blockNotifyPortFromOptions = rpcBlockNotificationPort; useAnimationsProperty.addListener((ov) -> { prefPayload.setUseAnimations(useAnimationsProperty.get()); @@ -263,18 +238,8 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid setPreferredTradeCurrency(preferredTradeCurrency); setFiatCurrencies(prefPayload.getFiatCurrencies()); setCryptoCurrencies(prefPayload.getCryptoCurrencies()); - setBsqBlockChainExplorer(prefPayload.getBsqBlockChainExplorer()); GlobalSettings.setDefaultTradeCurrency(preferredTradeCurrency); - // If a user has updated and the field was not set and get set to 0 by protobuf - // As there is no way to detect that a primitive value field was set we cannot apply - // a "marker" value like -1 to it. We also do not want to wrap the value in a new - // proto message as thats too much for that feature... So we accept that if the user - // sets the value to 0 it will be overwritten by the default at next startup. - if (prefPayload.getBsqAverageTrimThreshold() == 0) { - prefPayload.setBsqAverageTrimThreshold(0.05); - } - setupPreferences(); } @@ -322,26 +287,13 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid useStandbyModeProperty.set(prefPayload.isUseStandbyMode()); cssThemeProperty.set(prefPayload.getCssTheme()); - // a list of previously-used federated explorers - // if user preference references any deprecated explorers we need to select a new valid explorer - String deprecatedExplorers = "(bsq.bisq.cc|bsq.vante.me|bsq.emzy.de|bsq.sqrrm.net|bsq.bisq.services|bsq.ninja).*"; // if no valid Bitcoin block explorer is set, select the 1st valid Bitcoin block explorer ArrayList btcExplorers = getBlockChainExplorers(); if (getBlockChainExplorer() == null || - getBlockChainExplorer().name.length() == 0 || - getBlockChainExplorer().name.matches(deprecatedExplorers)) { + getBlockChainExplorer().name.length() == 0) { setBlockChainExplorer(btcExplorers.get(0)); } - - // if no valid BSQ block explorer is set, randomly select a valid BSQ block explorer - ArrayList bsqExplorers = getBsqBlockChainExplorers(); - if (getBsqBlockChainExplorer() == null || - getBsqBlockChainExplorer().name.length() == 0 || - getBsqBlockChainExplorer().name.matches(deprecatedExplorers)) { - setBsqBlockChainExplorer(bsqExplorers.get((new Random()).nextInt(bsqExplorers.size()))); - } - tradeCurrenciesAsObservable.addAll(prefPayload.getFiatCurrencies()); tradeCurrenciesAsObservable.addAll(prefPayload.getCryptoCurrencies()); dontShowAgainMapAsObservable.putAll(getDontShowAgainMap()); @@ -358,8 +310,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid setBitcoinNodes(btcNodesFromOptions); setBitcoinNodesOptionOrdinal(BtcNodes.BitcoinNodesOption.CUSTOM.ordinal()); } - if (referralIdFromOptions != null && !referralIdFromOptions.isEmpty()) - setReferralId(referralIdFromOptions); if (prefPayload.getIgnoreDustThreshold() < Restrictions.getMinNonDustOutput().value) { setIgnoreDustThreshold(600); @@ -382,10 +332,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid }); } - // We set the capability in CoreNetworkCapabilities if the program argument is set. - // If we have set it in the preferences view we handle it here. - CoreNetworkCapabilities.maybeApplyDaoFullMode(config); - initialReadDone = true; requestPersistence(); } @@ -471,11 +417,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid requestPersistence(); } - public void setBsqAverageTrimThreshold(double bsqAverageTrimThreshold) { - prefPayload.setBsqAverageTrimThreshold(bsqAverageTrimThreshold); - requestPersistence(); - } - public Optional findAutoConfirmSettings(String currencyCode) { return prefPayload.getAutoConfirmSettingsList().stream() .filter(e -> e.getCurrencyCode().equals(currencyCode)) @@ -648,11 +589,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid requestPersistence(); } - public void setPayFeeInBtc(boolean payFeeInBtc) { - prefPayload.setPayFeeInBtc(payFeeInBtc); - requestPersistence(); - } - private void setFiatCurrencies(List currencies) { fiatCurrenciesAsObservable.setAll(currencies.stream() .map(fiatCurrency -> new FiatCurrency(fiatCurrency.getCurrency())) @@ -665,11 +601,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid requestPersistence(); } - public void setBsqBlockChainExplorer(BlockChainExplorer bsqBlockChainExplorer) { - prefPayload.setBsqBlockChainExplorer(bsqBlockChainExplorer); - requestPersistence(); - } - private void setBlockChainExplorerTestNet(BlockChainExplorer blockChainExplorerTestNet) { prefPayload.setBlockChainExplorerTestNet(blockChainExplorerTestNet); requestPersistence(); @@ -756,40 +687,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid prefPayload.setTakeOfferSelectedPaymentAccountId(value); requestPersistence(); } - - public void setDaoFullNode(boolean value) { - // We only persist if we have not set the program argument - if (config.fullDaoNodeOptionSetExplicitly) { - prefPayload.setDaoFullNode(value); - requestPersistence(); - } - } - - public void setRpcUser(String value) { - // We only persist if we have not set the program argument - if (!rpcUserFromOptions.isEmpty()) { - prefPayload.setRpcUser(value); - } - prefPayload.setRpcUser(value); - requestPersistence(); - } - - public void setRpcPw(String value) { - // We only persist if we have not set the program argument - if (rpcPwFromOptions.isEmpty()) { - prefPayload.setRpcPw(value); - requestPersistence(); - } - } - - public void setBlockNotifyPort(int value) { - // We only persist if we have not set the program argument - if (blockNotifyPortFromOptions == Config.UNSPECIFIED_PORT) { - prefPayload.setBlockNotifyPort(value); - requestPersistence(); - } - } - + public void setIgnoreDustThreshold(int value) { prefPayload.setIgnoreDustThreshold(value); requestPersistence(); @@ -843,12 +741,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid case XMR_TESTNET: case XMR_STAGENET: return prefPayload.getBlockChainExplorerTestNet(); - case BTC_DAO_TESTNET: - return BTC_DAO_TEST_NET_EXPLORERS.get(0); - case BTC_DAO_BETANET: - return prefPayload.getBlockChainExplorerMainNet(); - case BTC_DAO_REGTEST: - return BTC_DAO_TEST_NET_EXPLORERS.get(0); default: throw new RuntimeException("BaseCurrencyNetwork not defined. BaseCurrencyNetwork=" + baseCurrencyNetwork); } @@ -862,21 +754,11 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid case XMR_TESTNET: case XMR_STAGENET: return BTC_TEST_NET_EXPLORERS; - case BTC_DAO_TESTNET: - return BTC_DAO_TEST_NET_EXPLORERS; - case BTC_DAO_BETANET: - return BTC_MAIN_NET_EXPLORERS; - case BTC_DAO_REGTEST: - return BTC_DAO_TEST_NET_EXPLORERS; default: throw new RuntimeException("BaseCurrencyNetwork not defined. BaseCurrencyNetwork=" + baseCurrencyNetwork); } } - public ArrayList getBsqBlockChainExplorers() { - return BSQ_MAIN_NET_EXPLORERS; - } - public boolean showAgain(String key) { return !prefPayload.getDontShowAgainMap().containsKey(key) || !prefPayload.getDontShowAgainMap().get(key); } @@ -907,11 +789,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid return value == 0 ? Restrictions.getDefaultBuyerSecurityDepositAsPercent() : value; } - //TODO remove and use isPayFeeInBtc instead - public boolean getPayFeeInBtc() { - return prefPayload.isPayFeeInBtc(); - } - @Override @Nullable public List getBridgeAddresses() { @@ -923,43 +800,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid feeService.getMinFeePerVByte()); } - public boolean isDaoFullNode() { - if (config.fullDaoNodeOptionSetExplicitly) { - return fullDaoNodeFromOptions; - } else { - return prefPayload.isDaoFullNode(); - } - } - - public String getRpcUser() { - if (!rpcUserFromOptions.isEmpty()) { - return rpcUserFromOptions; - } else { - return prefPayload.getRpcUser(); - } - } - - public String getRpcPw() { - if (!rpcPwFromOptions.isEmpty()) { - return rpcPwFromOptions; - } else { - return prefPayload.getRpcPw(); - } - } - - public int getBlockNotifyPort() { - if (blockNotifyPortFromOptions != Config.UNSPECIFIED_PORT) { - try { - return blockNotifyPortFromOptions; - } catch (Throwable ignore) { - return 0; - } - - } else { - return prefPayload.getBlockNotifyPort(); - } - } - public List getDefaultXmrTxProofServices() { if (config.useLocalhostForP2P) { return XMR_TX_PROOF_SERVICES_CLEAR_NET; @@ -1042,8 +882,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid void setSelectedPaymentAccountForCreateOffer(@Nullable PaymentAccount paymentAccount); - void setBsqBlockChainExplorer(BlockChainExplorer bsqBlockChainExplorer); - void setPayFeeInBtc(boolean payFeeInBtc); void setFiatCurrencies(List currencies); @@ -1096,16 +934,12 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid double getBuyerSecurityDepositAsPercent(); - void setDaoFullNode(boolean value); - void setRpcUser(String value); void setRpcPw(String value); void setBlockNotifyPort(int value); - boolean isDaoFullNode(); - String getRpcUser(); String getRpcPw(); @@ -1114,8 +948,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid void setTacAcceptedV120(boolean tacAccepted); - void setBsqAverageTrimThreshold(double bsqAverageTrimThreshold); - void setAutoConfirmSettings(AutoConfirmSettings autoConfirmSettings); void setHideNonAccountPaymentMethods(boolean hideNonAccountPaymentMethods); diff --git a/core/src/main/java/bisq/core/user/PreferencesPayload.java b/core/src/main/java/bisq/core/user/PreferencesPayload.java index 3eb6ecfa7b..6cf2483e2d 100644 --- a/core/src/main/java/bisq/core/user/PreferencesPayload.java +++ b/core/src/main/java/bisq/core/user/PreferencesPayload.java @@ -57,8 +57,6 @@ public final class PreferencesPayload implements PersistableEnvelope { private BlockChainExplorer blockChainExplorerMainNet; private BlockChainExplorer blockChainExplorerTestNet; @Nullable - private BlockChainExplorer bsqBlockChainExplorer; - @Nullable private String backupDirectory; private boolean autoSelectArbitrators = true; private Map dontShowAgainMap = new HashMap<>(); @@ -95,7 +93,6 @@ public final class PreferencesPayload implements PersistableEnvelope { private int cssTheme; @Nullable private PaymentAccount selectedPaymentAccountForCreateOffer; - private boolean payFeeInBtc = true; @Nullable private List bridgeAddresses; private int bridgeOptionOrdinal; @@ -112,7 +109,6 @@ public final class PreferencesPayload implements PersistableEnvelope { private boolean useMarketNotifications = true; private boolean usePriceNotifications = true; private boolean useStandbyMode = false; - private boolean isDaoFullNode = false; @Nullable private String rpcUser; @Nullable @@ -124,7 +120,6 @@ public final class PreferencesPayload implements PersistableEnvelope { private double buyerSecurityDepositAsPercentForCrypto = getDefaultBuyerSecurityDepositAsPercent(); private int blockNotifyPort; private boolean tacAcceptedV120; - private double bsqAverageTrimThreshold = 0.05; // Added at 1.3.8 private List autoConfirmSettingsList = new ArrayList<>(); @@ -179,7 +174,6 @@ public final class PreferencesPayload implements PersistableEnvelope { .setBuyerSecurityDepositAsLong(buyerSecurityDepositAsLong) .setUseAnimations(useAnimations) .setCssTheme(cssTheme) - .setPayFeeInBtc(payFeeInBtc) .setBridgeOptionOrdinal(bridgeOptionOrdinal) .setTorTransportOrdinal(torTransportOrdinal) .setBitcoinNodesOptionOrdinal(bitcoinNodesOptionOrdinal) @@ -188,13 +182,11 @@ public final class PreferencesPayload implements PersistableEnvelope { .setUseMarketNotifications(useMarketNotifications) .setUsePriceNotifications(usePriceNotifications) .setUseStandbyMode(useStandbyMode) - .setIsDaoFullNode(isDaoFullNode) .setBuyerSecurityDepositAsPercent(buyerSecurityDepositAsPercent) .setIgnoreDustThreshold(ignoreDustThreshold) .setBuyerSecurityDepositAsPercentForCrypto(buyerSecurityDepositAsPercentForCrypto) .setBlockNotifyPort(blockNotifyPort) .setTacAcceptedV120(tacAcceptedV120) - .setBsqAverageTrimThreshold(bsqAverageTrimThreshold) .addAllAutoConfirmSettings(autoConfirmSettingsList.stream() .map(autoConfirmSettings -> ((protobuf.AutoConfirmSettings) autoConfirmSettings.toProtoMessage())) .collect(Collectors.toList())) @@ -218,7 +210,6 @@ public final class PreferencesPayload implements PersistableEnvelope { Optional.ofNullable(rpcUser).ifPresent(builder::setRpcUser); Optional.ofNullable(rpcPw).ifPresent(builder::setRpcPw); Optional.ofNullable(takeOfferSelectedPaymentAccountId).ifPresent(builder::setTakeOfferSelectedPaymentAccountId); - Optional.ofNullable(bsqBlockChainExplorer).ifPresent(e -> builder.setBsqBlockChainExplorer((protobuf.BlockChainExplorer) e.toProtoMessage())); return protobuf.PersistableEnvelope.newBuilder().setPreferencesPayload(builder).build(); } @@ -242,7 +233,6 @@ public final class PreferencesPayload implements PersistableEnvelope { .collect(Collectors.toList())), BlockChainExplorer.fromProto(proto.getBlockChainExplorerMainNet()), BlockChainExplorer.fromProto(proto.getBlockChainExplorerTestNet()), - proto.hasBsqBlockChainExplorer() ? BlockChainExplorer.fromProto(proto.getBsqBlockChainExplorer()) : null, ProtoUtil.stringOrNullFromProto(proto.getBackupDirectory()), proto.getAutoSelectArbitrators(), Maps.newHashMap(proto.getDontShowAgainMapMap()), @@ -269,7 +259,6 @@ public final class PreferencesPayload implements PersistableEnvelope { proto.getUseAnimations(), proto.getCssTheme(), paymentAccount, - proto.getPayFeeInBtc(), proto.getBridgeAddressesList().isEmpty() ? null : new ArrayList<>(proto.getBridgeAddressesList()), proto.getBridgeOptionOrdinal(), proto.getTorTransportOrdinal(), @@ -282,7 +271,6 @@ public final class PreferencesPayload implements PersistableEnvelope { proto.getUseMarketNotifications(), proto.getUsePriceNotifications(), proto.getUseStandbyMode(), - proto.getIsDaoFullNode(), proto.getRpcUser().isEmpty() ? null : proto.getRpcUser(), proto.getRpcPw().isEmpty() ? null : proto.getRpcPw(), proto.getTakeOfferSelectedPaymentAccountId().isEmpty() ? null : proto.getTakeOfferSelectedPaymentAccountId(), @@ -291,7 +279,6 @@ public final class PreferencesPayload implements PersistableEnvelope { proto.getBuyerSecurityDepositAsPercentForCrypto(), proto.getBlockNotifyPort(), proto.getTacAcceptedV120(), - proto.getBsqAverageTrimThreshold(), proto.getAutoConfirmSettingsList().isEmpty() ? new ArrayList<>() : new ArrayList<>(proto.getAutoConfirmSettingsList().stream() .map(AutoConfirmSettings::fromProto) diff --git a/core/src/main/java/bisq/core/util/AveragePriceUtil.java b/core/src/main/java/bisq/core/util/AveragePriceUtil.java deleted file mode 100644 index beb320e0e2..0000000000 --- a/core/src/main/java/bisq/core/util/AveragePriceUtil.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.util; - -import bisq.core.monetary.Altcoin; -import bisq.core.monetary.Price; -import bisq.core.trade.statistics.TradeStatistics3; -import bisq.core.trade.statistics.TradeStatisticsManager; -import bisq.core.user.Preferences; - -import bisq.common.util.MathUtils; -import bisq.common.util.Tuple2; - -import org.bitcoinj.utils.Fiat; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Comparator; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.List; -import java.util.stream.Collectors; - -public class AveragePriceUtil { - private static final double HOW_MANY_STD_DEVS_CONSTITUTE_OUTLIER = 10; - - public static Tuple2 getAveragePriceTuple(Preferences preferences, - TradeStatisticsManager tradeStatisticsManager, - int days) { - double percentToTrim = Math.max(0, Math.min(49, preferences.getBsqAverageTrimThreshold() * 100)); - Date pastXDays = getPastDate(days); - List bsqAllTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() - .filter(e -> e.getCurrency().equals("BSQ")) - .filter(e -> e.getDate().after(pastXDays)) - .collect(Collectors.toList()); - List bsqTradePastXDays = percentToTrim > 0 ? - removeOutliers(bsqAllTradePastXDays, percentToTrim) : - bsqAllTradePastXDays; - - List usdAllTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream() - .filter(e -> e.getCurrency().equals("USD")) - .filter(e -> e.getDate().after(pastXDays)) - .collect(Collectors.toList()); - List usdTradePastXDays = percentToTrim > 0 ? - removeOutliers(usdAllTradePastXDays, percentToTrim) : - usdAllTradePastXDays; - - Price usdPrice = Price.valueOf("USD", getUSDAverage(bsqTradePastXDays, usdTradePastXDays)); - Price bsqPrice = Price.valueOf("BSQ", getBTCAverage(bsqTradePastXDays)); - return new Tuple2<>(usdPrice, bsqPrice); - } - - private static List removeOutliers(List list, double percentToTrim) { - List yValues = list.stream() - .filter(TradeStatistics3::isValid) - .map(e -> (double) e.getPrice()) - .collect(Collectors.toList()); - - Tuple2 tuple = InlierUtil.findInlierRange(yValues, percentToTrim, HOW_MANY_STD_DEVS_CONSTITUTE_OUTLIER); - double lowerBound = tuple.first; - double upperBound = tuple.second; - return list.stream() - .filter(e -> e.getPrice() > lowerBound) - .filter(e -> e.getPrice() < upperBound) - .collect(Collectors.toList()); - } - - private static long getBTCAverage(List list) { - long accumulatedVolume = 0; - long accumulatedAmount = 0; - - for (TradeStatistics3 item : list) { - accumulatedVolume += item.getTradeVolume().getValue(); - accumulatedAmount += item.getTradeAmount().getValue(); // Amount of BTC traded - } - long averagePrice; - double accumulatedAmountAsDouble = MathUtils.scaleUpByPowerOf10((double) accumulatedAmount, Altcoin.SMALLEST_UNIT_EXPONENT); - averagePrice = accumulatedVolume > 0 ? MathUtils.roundDoubleToLong(accumulatedAmountAsDouble / (double) accumulatedVolume) : 0; - - return averagePrice; - } - - private static long getUSDAverage(List bsqList, List usdList) { - // Use next USD/BTC print as price to calculate BSQ/USD rate - // Store each trade as amount of USD and amount of BSQ traded - List> usdBsqList = new ArrayList<>(bsqList.size()); - usdList.sort(Comparator.comparing(TradeStatistics3::getDateAsLong)); - var usdBTCPrice = 10000d; // Default to 10000 USD per BTC if there is no USD feed at all - - for (TradeStatistics3 item : bsqList) { - // Find usdprice for trade item - usdBTCPrice = usdList.stream() - .filter(usd -> usd.getDateAsLong() > item.getDateAsLong()) - .map(usd -> MathUtils.scaleDownByPowerOf10((double) usd.getTradePrice().getValue(), - Fiat.SMALLEST_UNIT_EXPONENT)) - .findFirst() - .orElse(usdBTCPrice); - var bsqAmount = MathUtils.scaleDownByPowerOf10((double) item.getTradeVolume().getValue(), - Altcoin.SMALLEST_UNIT_EXPONENT); - var btcAmount = MathUtils.scaleDownByPowerOf10((double) item.getTradeAmount().getValue(), - Altcoin.SMALLEST_UNIT_EXPONENT); - usdBsqList.add(new Tuple2<>(usdBTCPrice * btcAmount, bsqAmount)); - } - long averagePrice; - var usdTraded = usdBsqList.stream() - .mapToDouble(item -> item.first) - .sum(); - var bsqTraded = usdBsqList.stream() - .mapToDouble(item -> item.second) - .sum(); - var averageAsDouble = bsqTraded > 0 ? usdTraded / bsqTraded : 0d; - var averageScaledUp = MathUtils.scaleUpByPowerOf10(averageAsDouble, Fiat.SMALLEST_UNIT_EXPONENT); - averagePrice = bsqTraded > 0 ? MathUtils.roundDoubleToLong(averageScaledUp) : 0; - - return averagePrice; - } - - private static Date getPastDate(int days) { - Calendar cal = new GregorianCalendar(); - cal.setTime(new Date()); - cal.add(Calendar.DAY_OF_MONTH, -1 * days); - return cal.getTime(); - } -} diff --git a/core/src/main/java/bisq/core/util/FeeReceiverSelector.java b/core/src/main/java/bisq/core/util/FeeReceiverSelector.java deleted file mode 100644 index 2854a0e09f..0000000000 --- a/core/src/main/java/bisq/core/util/FeeReceiverSelector.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.util; - -import bisq.core.dao.DaoFacade; -import bisq.core.dao.governance.param.Param; -import bisq.core.filter.FilterManager; - -import org.bitcoinj.core.Coin; - -import com.google.common.annotations.VisibleForTesting; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.Random; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class FeeReceiverSelector { - public static String getAddress(DaoFacade daoFacade, FilterManager filterManager) { - return getAddress(daoFacade, filterManager, new Random()); - } - - @VisibleForTesting - static String getAddress(DaoFacade daoFacade, FilterManager filterManager, Random rnd) { - List feeReceivers = Optional.ofNullable(filterManager.getFilter()) - .flatMap(f -> Optional.ofNullable(f.getBtcFeeReceiverAddresses())) - .orElse(List.of()); - - List amountList = new ArrayList<>(); - List receiverAddressList = new ArrayList<>(); - - feeReceivers.forEach(e -> { - try { - String[] tokens = e.split("#"); - amountList.add(Coin.parseCoin(tokens[1]).longValue()); // total amount the victim should receive - receiverAddressList.add(tokens[0]); // victim's receiver address - } catch (RuntimeException ignore) { - // If input format is not as expected we ignore entry - } - }); - - if (!amountList.isEmpty()) { - return receiverAddressList.get(weightedSelection(amountList, rnd)); - } - - // We keep default value as fallback in case no filter value is available or user has old version. - return daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS); - } - - @VisibleForTesting - static int weightedSelection(List weights, Random rnd) { - long sum = weights.stream().mapToLong(n -> n).sum(); - long target = rnd.longs(0, sum).findFirst().orElseThrow(); - int i; - for (i = 0; i < weights.size() && target >= 0; i++) { - target -= weights.get(i); - } - return i - 1; - } -} diff --git a/core/src/main/java/bisq/core/util/FormattingUtils.java b/core/src/main/java/bisq/core/util/FormattingUtils.java index d6658e7129..3086e692e2 100644 --- a/core/src/main/java/bisq/core/util/FormattingUtils.java +++ b/core/src/main/java/bisq/core/util/FormattingUtils.java @@ -127,10 +127,7 @@ public class FormattingUtils { try { // TODO quick hack... String res; - if (altcoin.getCurrencyCode().equals("BSQ")) - res = altcoinFormat.noCode().minDecimals(2).repeatOptionalDecimals(0, 0).format(altcoin).toString(); - else - res = altcoinFormat.noCode().format(altcoin).toString(); + res = altcoinFormat.noCode().format(altcoin).toString(); if (appendCurrencyCode) return res + " " + altcoin.getCurrencyCode(); else diff --git a/core/src/main/java/bisq/core/util/coin/BsqFormatter.java b/core/src/main/java/bisq/core/util/coin/BsqFormatter.java deleted file mode 100644 index ab476097be..0000000000 --- a/core/src/main/java/bisq/core/util/coin/BsqFormatter.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.util.coin; - -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.locale.GlobalSettings; -import bisq.core.locale.Res; -import bisq.core.monetary.Price; -import bisq.core.util.FormattingUtils; -import bisq.core.util.ParsingUtils; -import bisq.core.util.validation.BtcAddressValidator; -import bisq.core.util.validation.InputValidator; - -import bisq.common.app.DevEnv; -import bisq.common.config.Config; -import bisq.common.util.MathUtils; - -import org.bitcoinj.core.AddressFormatException; -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.LegacyAddress; -import org.bitcoinj.utils.MonetaryFormat; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import java.text.DecimalFormat; -import java.text.NumberFormat; - -import java.util.Locale; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@Singleton -public class BsqFormatter implements CoinFormatter { - private final ImmutableCoinFormatter immutableCoinFormatter; - - // We don't support localized formatting. Format is always using "." as decimal mark and no grouping separator. - // Input of "," as decimal mark (like in German locale) will be replaced with ".". - // Input of a group separator (1,123,45) leads to a validation error. - // Note: BtcFormat was intended to be used, but it leads to many problems (automatic format to mBit, - // no way to remove grouping separator). It seems to be not optimal for user input formatting. - @Getter - private MonetaryFormat monetaryFormat; - - - @SuppressWarnings("PointlessBooleanExpression") - private static final boolean useBsqAddressFormat = true || !DevEnv.isDevMode(); - private final String prefix = "B"; - private DecimalFormat amountFormat; - private DecimalFormat marketCapFormat; - private final MonetaryFormat btcCoinFormat; - - @Inject - public BsqFormatter() { - this.btcCoinFormat = Config.baseCurrencyNetworkParameters().getMonetaryFormat(); - this.monetaryFormat = new MonetaryFormat().shift(6).code(6, "BSQ").minDecimals(2); - this.immutableCoinFormatter = new ImmutableCoinFormatter(monetaryFormat); - - GlobalSettings.localeProperty().addListener((observable, oldValue, newValue) -> switchLocale(newValue)); - switchLocale(GlobalSettings.getLocale()); - - amountFormat.setMinimumFractionDigits(2); - } - - private void switchLocale(Locale locale) { - amountFormat = (DecimalFormat) NumberFormat.getNumberInstance(locale); - amountFormat.setMinimumFractionDigits(2); - amountFormat.setMaximumFractionDigits(2); - - marketCapFormat = (DecimalFormat) NumberFormat.getNumberInstance(locale); - marketCapFormat = new DecimalFormat(); - marketCapFormat.setMaximumFractionDigits(0); - } - - /** - * Returns the base-58 encoded String representation of this - * object, including version and checksum bytes. - */ - public String getBsqAddressStringFromAddress(LegacyAddress address) { - final String addressString = address.toString(); - if (useBsqAddressFormat) - return prefix + addressString; - else - return addressString; - - } - - public LegacyAddress getAddressFromBsqAddress(String encoded) { - String maybeUpdatedEncoded = encoded; - if (useBsqAddressFormat) - maybeUpdatedEncoded = encoded.substring(prefix.length(), encoded.length()); - - try { - return LegacyAddress.fromBase58(Config.baseCurrencyNetworkParameters(), maybeUpdatedEncoded); - } catch (AddressFormatException e) { - throw new RuntimeException(e); - } - } - - public String formatAmountWithGroupSeparatorAndCode(Coin amount) { - return amountFormat.format(MathUtils.scaleDownByPowerOf10(amount.value, 2)) + " BSQ"; - } - - public String formatMarketCap(Price usdBsqPrice, Coin issuedAmount) { - if (usdBsqPrice != null && issuedAmount != null) { - double marketCap = usdBsqPrice.getValue() * (MathUtils.scaleDownByPowerOf10(issuedAmount.value, 6)); - return marketCapFormat.format(MathUtils.doubleToLong(marketCap)) + " USD"; - } else { - return ""; - } - } - - public String formatBSQSatoshis(long satoshi) { - return FormattingUtils.formatCoin(satoshi, monetaryFormat); - } - - public String formatBSQSatoshisWithCode(long satoshi) { - return FormattingUtils.formatCoinWithCode(satoshi, monetaryFormat); - } - - public String formatBTCSatoshis(long satoshi) { - return FormattingUtils.formatCoin(satoshi, btcCoinFormat); - } - - public String formatBTCWithCode(long satoshi) { - return FormattingUtils.formatCoinWithCode(satoshi, btcCoinFormat); - } - - public String formatBTCWithCode(Coin coin) { - return FormattingUtils.formatCoinWithCode(coin, btcCoinFormat); - } - - private String formatBTC(Coin coin) { - return FormattingUtils.formatCoin(coin.value, btcCoinFormat); - } - - public Coin parseToBTC(String input) { - return ParsingUtils.parseToCoin(input, btcCoinFormat); - } - - public String formatParamValue(Param param, String value) { - switch (param.getParamType()) { - case UNDEFINED: - // In case we add a new param old clients will not know that enum and fall back to UNDEFINED. - return Res.get("shared.na"); - case BSQ: - return formatCoinWithCode(ParsingUtils.parseToCoin(value, this)); - case BTC: - return formatBTCWithCode(parseToBTC(value)); - case PERCENT: - return FormattingUtils.formatToPercentWithSymbol(ParsingUtils.parsePercentStringToDouble(value)); - case BLOCK: - return Res.get("dao.param.blocks", Integer.parseInt(value)); - case ADDRESS: - return value; - default: - log.warn("Param type {} not handled in switch case at formatParamValue", param.getParamType()); - return Res.get("shared.na"); - } - } - - public Coin parseParamValueToCoin(Param param, String inputValue) { - switch (param.getParamType()) { - case BSQ: - return ParsingUtils.parseToCoin(inputValue, this); - case BTC: - return parseToBTC(inputValue); - default: - throw new IllegalArgumentException("Unsupported paramType. param: " + param); - } - } - - private int parseParamValueToBlocks(Param param, String inputValue) { - switch (param.getParamType()) { - case BLOCK: - return Integer.parseInt(inputValue); - default: - throw new IllegalArgumentException("Unsupported paramType. param: " + param); - } - } - - public String parseParamValueToString(Param param, String inputValue) throws ProposalValidationException { - switch (param.getParamType()) { - case UNDEFINED: - return Res.get("shared.na"); - case BSQ: - return formatCoin(parseParamValueToCoin(param, inputValue)); - case BTC: - return formatBTC(parseParamValueToCoin(param, inputValue)); - case PERCENT: - return FormattingUtils.formatToPercent(ParsingUtils.parsePercentStringToDouble(inputValue)); - case BLOCK: - return Integer.toString(parseParamValueToBlocks(param, inputValue)); - case ADDRESS: - InputValidator.ValidationResult validationResult = new BtcAddressValidator().validate(inputValue); - if (validationResult.isValid) - return inputValue; - else - throw new ProposalValidationException(validationResult.errorMessage); - default: - log.warn("Param type {} not handled in switch case at parseParamValueToString", param.getParamType()); - return Res.get("shared.na"); - } - } - - public String formatCoin(Coin coin) { - return formatCoin(coin, false); - } - - public String formatCoin(Coin coin, boolean appendCode) { - return appendCode ? - immutableCoinFormatter.formatCoinWithCode(coin) : - immutableCoinFormatter.formatCoin(coin); - } - - public String formatCoin(Coin coin, int decimalPlaces) { - return immutableCoinFormatter.formatCoin(coin, decimalPlaces); - } - - public String formatCoin(Coin coin, - int decimalPlaces, - boolean decimalAligned, - int maxNumberOfDigits) { - return immutableCoinFormatter.formatCoin(coin, decimalPlaces, decimalAligned, maxNumberOfDigits); - } - - public String formatCoinWithCode(Coin coin) { - return formatCoin(coin, true); - } - - public String formatCoinWithCode(long value) { - return immutableCoinFormatter.formatCoinWithCode(value); - } -} diff --git a/core/src/main/java/bisq/core/util/coin/CoinUtil.java b/core/src/main/java/bisq/core/util/coin/CoinUtil.java index 3f8b16bda0..8eb6958dc6 100644 --- a/core/src/main/java/bisq/core/util/coin/CoinUtil.java +++ b/core/src/main/java/bisq/core/util/coin/CoinUtil.java @@ -92,15 +92,14 @@ public class CoinUtil { /** * Calculates the maker fee for the given amount, marketPrice and marketPriceMargin. * - * @param isCurrencyForMakerFeeBtc {@code true} to pay fee in BTC, {@code false} to pay fee in BSQ * @param amount the amount of BTC to trade * @return the maker fee for the given trade amount, or {@code null} if the amount is {@code null} */ @Nullable - public static Coin getMakerFee(boolean isCurrencyForMakerFeeBtc, @Nullable Coin amount) { + public static Coin getMakerFee(@Nullable Coin amount) { if (amount != null) { - Coin feePerBtc = getFeePerBtc(FeeService.getMakerFeePerBtc(isCurrencyForMakerFeeBtc), amount); - return maxCoin(feePerBtc, FeeService.getMinMakerFee(isCurrencyForMakerFeeBtc)); + Coin feePerBtc = getFeePerBtc(FeeService.getMakerFeePerBtc(), amount); + return maxCoin(feePerBtc, FeeService.getMinMakerFee()); } else { return null; } diff --git a/core/src/main/resources/btc_dao_betanet.seednodes b/core/src/main/resources/btc_dao_betanet.seednodes deleted file mode 100644 index c260a87394..0000000000 --- a/core/src/main/resources/btc_dao_betanet.seednodes +++ /dev/null @@ -1,2 +0,0 @@ -# nodeaddress.onion:port [(@owner)] -csmijmjs7ftqfw6v.onion:8004 diff --git a/core/src/main/resources/btc_dao_regtest.seednodes b/core/src/main/resources/btc_dao_regtest.seednodes deleted file mode 100644 index 4c6367383f..0000000000 --- a/core/src/main/resources/btc_dao_regtest.seednodes +++ /dev/null @@ -1,6 +0,0 @@ -# nodeaddress.onion:port [(@owner)] -2bnvhfkdrnx5hrlv.onion:8005 -b3jnw7fyph2jsu6n.onion:8005 - -# omentgpxrxy5lehq.onion:8005 -# r7cucuwouvhdhdgo.onion:8005 diff --git a/core/src/main/resources/btc_dao_testnet.seednodes b/core/src/main/resources/btc_dao_testnet.seednodes deleted file mode 100644 index a57515edb1..0000000000 --- a/core/src/main/resources/btc_dao_testnet.seednodes +++ /dev/null @@ -1,3 +0,0 @@ -# nodeaddress.onion:port [(@owner)] -fjr5w4eckjghqtnu.onion:8003 -74w2sttlo4qk6go3.onion:8003 diff --git a/core/src/main/resources/help/createcryptopaymentacct-help.txt b/core/src/main/resources/help/createcryptopaymentacct-help.txt deleted file mode 100644 index 83fa2e9f63..0000000000 --- a/core/src/main/resources/help/createcryptopaymentacct-help.txt +++ /dev/null @@ -1,47 +0,0 @@ -createcryptopaymentacct - -NAME ----- -createcryptopaymentacct - create a cryptocurrency payment account - -SYNOPSIS --------- -createcryptopaymentacct - --account-name= - --currency-code= - --address= - [--trade-instant=] - -DESCRIPTION ------------ -Create an cryptocurrency (altcoin) payment account. Only BSQ payment accounts are currently supported. - -OPTIONS -------- ---account-name - The name of the cryptocurrency payment account used to create and take altcoin offers. - ---currency-code - The three letter code for the altcoin, e.g., BSQ. - ---address - The altcoin address to be used receive cryptocurrency payment when selling BTC. - ---trade-instant - True for creating an instant cryptocurrency payment account, false otherwise. - Default is false. - -EXAMPLES --------- - -To create a BSQ Altcoin payment account: -$ ./bisq-cli --password=xyz --port=9998 createcryptopaymentacct --account-name="My BSQ Account" \ - --currency-code=bsq \ - --address=Bn3PCQgRwhkrGnaMp1RYwt9tFwL51YELqne \ - --trade-instant=false - -To create a BSQ Instant Altcoin payment account: -$ ./bisq-cli --password=xyz --port=9998 createcryptopaymentacct --account-name="My Instant BSQ Account" \ - --currency-code=bsq \ - --address=Bn3PCQgRwhkrGnaMp1RYwt9tFwL51YELqne \ - --trade-instant=true diff --git a/core/src/main/resources/help/createoffer-help.txt b/core/src/main/resources/help/createoffer-help.txt index 38ec0fd8da..fcd4f3a71e 100644 --- a/core/src/main/resources/help/createoffer-help.txt +++ b/core/src/main/resources/help/createoffer-help.txt @@ -14,7 +14,7 @@ createoffer --amount= --min-amount= --security-deposit= - [--fee-currency=] + [--fee-currency=] DESCRIPTION ----------- @@ -50,7 +50,7 @@ OPTIONS The percentage of the BTC amount being traded for the security deposit, e.g., 60.0 (60%). --fee-currency - The wallet currency used to pay the Bisq trade maker fee (BSQ|BTC). Default is BTC + The wallet currency used to pay the Bisq trade maker fee (BTC). Default is BTC EXAMPLES -------- @@ -59,14 +59,14 @@ To create a BUY 0.125 BTC with EUR offer at the current market price, using a payment account with ID 7413d263-225a-4f1b-837a-1e3094dc0d77, putting up a 30 percent security deposit, - and paying the Bisq maker trading fee in BSQ: + and paying the Bisq maker trading fee in BTC: $ ./bisq-cli --password=xyz --port=9998 createoffer --payment-account=7413d263-225a-4f1b-837a-1e3094dc0d77 \ --direction=buy \ --currency-code=eur \ --amount=0.125 \ --market-price-margin=0.00 \ --security-deposit=30.0 \ - --fee-currency=bsq + --fee-currency=btc To create a SELL 0.006 BTC for USD offer at a fixed price of 40,000 USD, diff --git a/core/src/main/resources/help/getbalance-help.txt b/core/src/main/resources/help/getbalance-help.txt index 9426373be7..4cb9425c91 100644 --- a/core/src/main/resources/help/getbalance-help.txt +++ b/core/src/main/resources/help/getbalance-help.txt @@ -7,23 +7,23 @@ getbalance - get wallet balance(s) SYNOPSIS -------- getbalance - [--currency-code=] + [--currency-code=] DESCRIPTION ----------- -Returns full balance information for Bisq BSQ and/or BTC wallets. +Returns full balance information for Bisq BTC wallets. OPTIONS ------- ---currency-code= +--currency-code= The three letter Bisq wallet crypto currency code. EXAMPLES -------- -Show full BSQ and BTC wallet balance information: +Show full BTC wallet balance information: $ ./bisq-cli --password=xyz --port=9998 getbalance -Show full BSQ wallet balance information: +Show full wallet balance information: $ ./bisq-cli --password=xyz --port=9998 getbalance --currency-code=bsq Show full BTC wallet balance information: diff --git a/core/src/main/resources/help/getunusedbsqaddress-help.txt b/core/src/main/resources/help/getunusedbsqaddress-help.txt deleted file mode 100644 index 308ad01ba7..0000000000 --- a/core/src/main/resources/help/getunusedbsqaddress-help.txt +++ /dev/null @@ -1,17 +0,0 @@ -getunusedbsqaddress - -NAME ----- -getunusedbsqaddress - get BSQ receiving address - -SYNOPSIS --------- -getunusedbsqaddress - -DESCRIPTION ------------ -Returns an unused BSQ receiving address. - -EXAMPLES --------- -$ ./bisq-cli --password=xyz --port=9998 getunusedbsqaddress diff --git a/core/src/main/resources/help/sendbsq-help.txt b/core/src/main/resources/help/sendbsq-help.txt deleted file mode 100644 index 8a3e7d938a..0000000000 --- a/core/src/main/resources/help/sendbsq-help.txt +++ /dev/null @@ -1,38 +0,0 @@ -sendbsq - -NAME ----- -sendbsq - send BSQ to an external wallet - -SYNOPSIS --------- -sendbsq - --address= - --amount= - [--tx-fee-rate=] - -DESCRIPTION ------------ -Send BSQ from your Bisq wallet to an external BSQ address. - -OPTIONS -------- ---address - The destination BSQ address for the send transaction. - ---amount - The amount of BSQ to send. - ---tx-fee-rate - An optional transaction fee rate (sats/byte) for the transaction. The user is - responsible for choosing a fee rate that will be accepted by the network in a - reasonable amount of time, and the fee rate must be greater than 1 (sats/byte). - -EXAMPLES --------- -Send 500 BSQ to address Bn3PCQgRwhkrGnaMp1RYwt9tFwL51YELqne with a default transaction fee rate: -$ ./bisq-cli --password=xyz --port=9998 sendbsq --address=Bn3PCQgRwhkrGnaMp1RYwt9tFwL51YELqne --amount=500.00 - -Send 3000 BSQ to address Bn3PCQgRwhkrGnaMp1RYwt9tFwL51YELqne with transaction fee rate of 40 sats/byte: -$ ./bisq-cli --password=xyz --port=9998 sendbsq --address=Bn3PCQgRwhkrGnaMp1RYwt9tFwL51YELqne --amount=3000.00 \ - --tx-fee-rate=40 diff --git a/core/src/main/resources/help/takeoffer-help.txt b/core/src/main/resources/help/takeoffer-help.txt index 1290a00839..c7e4212f19 100644 --- a/core/src/main/resources/help/takeoffer-help.txt +++ b/core/src/main/resources/help/takeoffer-help.txt @@ -13,7 +13,7 @@ takeoffer DESCRIPTION ----------- -Take an existing offer using a matching payment method. The Bisq trade fee can be paid in BSQ or BTC. +Take an existing offer using a matching payment method. The Bisq trade fee can be paid in BTC. OPTIONS ------- @@ -25,13 +25,13 @@ OPTIONS The payment account's payment method must match that of the offer. --fee-currency - The wallet currency used to pay the Bisq trade taker fee (BSQ|BTC). Default is BTC + The wallet currency used to pay the Bisq trade taker fee (BTC). Default is BTC EXAMPLES -------- To take an offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea using a payment account with ID fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e, - and paying the Bisq trading fee in BSQ: + and paying the Bisq trading fee in BTC: $ ./bisq-cli --password=xyz --port=9998 takeoffer --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \ --payment-account=fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e \ - -fee-currency=bsq + -fee-currency=btc diff --git a/core/src/main/resources/help/verifybsqsenttoaddress-help.txt b/core/src/main/resources/help/verifybsqsenttoaddress-help.txt deleted file mode 100644 index 47cb09d790..0000000000 --- a/core/src/main/resources/help/verifybsqsenttoaddress-help.txt +++ /dev/null @@ -1,39 +0,0 @@ -verifybsqsenttoaddress - -NAME ----- -verifybsqsenttoaddress - verify BSQ sent to wallet address - -SYNOPSIS --------- -verifybsqsenttoaddress - --address= - --amount= - -DESCRIPTION ------------ -Verify an exact amount of BSQ was sent to a specific Bisq wallet's BSQ address. -Receipt of BSQ to a BSQ (altcoin) payment account address should always be verified -before a BSQ seller sends a confirmpaymentreceived message for a BSQ/BTC trade. - -Warning: The verification result should be considered a false positive if a BSQ wallet -address has received the same amount of BSQ in more than one transaction. A way to -avoid this problem is to use different BSQ payment accounts for different trades, so -the payment account receiving address will vary from trade to trade. Another way is to -slightly vary your offer amounts and BSQ prices (if you are the maker), to make sure the -received BSQ amounts vary from trade to trade. Doing all of the above further reduces -the chance of a false positive. Another step is to check your BSQ wallet balance when -you verify BSQ has been received to an address. - -OPTIONS -------- ---address - The receiving BSQ address. - ---amount - The amount of BSQ received. - -EXAMPLES --------- -Verify 500.00 BSQ was sent to address Bn3PCQgRwhkrGnaMp1RYwt9tFwL51YELqne: -$ ./bisq-cli --password=xyz --port=9998 verifybsqsenttoaddress --address=Bn3PCQgRwhkrGnaMp1RYwt9tFwL51YELqne --amount=500.00 diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 2762597f4e..da0cb8bcd2 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -192,7 +192,6 @@ shared.tradeWalletBalance=Trade wallet balance shared.makerTxFee=Maker: {0} shared.takerTxFee=Taker: {0} shared.iConfirm=I confirm -shared.tradingFeeInBsqInfo=≈ {0} shared.openURL=Open {0} shared.fiat=Fiat shared.crypto=Crypto @@ -204,9 +203,6 @@ shared.actions=Actions shared.buyerUpperCase=Buyer shared.sellerUpperCase=Seller shared.new=NEW -shared.blindVoteTxId=Blind vote transaction ID -shared.proposal=Proposal -shared.votes=Votes shared.learnMore=Learn more shared.dismiss=Dismiss shared.selectedArbitrator=Selected arbitrator @@ -240,7 +236,6 @@ mainView.menu.funds=Funds mainView.menu.support=Support mainView.menu.settings=Settings mainView.menu.account=Account -mainView.menu.dao=DAO mainView.marketPriceWithProvider.label=Market price by {0} mainView.marketPrice.bisqInternalPrice=Price of latest Haveno trade @@ -258,13 +253,11 @@ mainView.footer.localhostBitcoinNode=(localhost) mainView.footer.btcInfo={0} {1} mainView.footer.btcFeeRate=/ Fee rate: {0} sat/vB mainView.footer.btcInfo.initializing=Connecting to Monero network -mainView.footer.bsqInfo.synchronizing=/ Synchronizing DAO 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=Connecting to mainView.footer.btcInfo.connectionFailed=Connection failed to mainView.footer.p2pInfo=Monero network peers: {0} / Haveno network peers: {1} -mainView.footer.daoFullNode=DAO full node mainView.bootstrapState.connectionToTorNetwork=(1/4) Connecting to Tor network... mainView.bootstrapState.torNodeCreated=(2/4) Tor node created @@ -448,7 +441,6 @@ createOffer.fundsBox.networkFee=Mining fee createOffer.fundsBox.placeOfferSpinnerInfo=Offer publishing is in progress ... createOffer.fundsBox.paymentLabel=Haveno trade with ID {0} createOffer.fundsBox.fundsStructure=({0} security deposit, {1} trade fee, {2} mining fee) -createOffer.fundsBox.fundsStructure.BSQ=({0} security deposit, {1} mining fee) + {2} trade fee createOffer.success.headline=Your offer has been published createOffer.success.info=You can manage your open offers at \"Portfolio/My open offers\". createOffer.info.sellAtMarketPrice=You will always sell at market price as the price of your offer will be continuously updated. @@ -458,7 +450,7 @@ createOffer.info.buyBelowMarketPrice=You will always pay {0}% less than the curr createOffer.warning.sellBelowMarketPrice=You will always get {0}% less than the current market price as the price of your offer will be continuously updated. createOffer.warning.buyAboveMarketPrice=You will always pay {0}% more than the current market price as the price of your offer will be continuously updated. createOffer.tradeFee.descriptionBTCOnly=Trade fee -createOffer.tradeFee.descriptionBSQEnabled=Select trade fee currency +createOffer.tradeFee.description=Trade fee createOffer.triggerPrice.prompt=Set optional trigger price createOffer.triggerPrice.label=Deactivate offer if market price is {0} @@ -682,7 +674,6 @@ portfolio.pending.step2_buyer.amountToTransfer=Amount to transfer portfolio.pending.step2_buyer.sellersAddress=Seller''s {0} address portfolio.pending.step2_buyer.buyerAccount=Your payment account to be used portfolio.pending.step2_buyer.paymentStarted=Payment started -portfolio.pending.step2_buyer.fillInBsqWallet=Pay from BSQ wallet portfolio.pending.step2_buyer.warn=You still have not done your {0} payment!\nPlease note that the trade has to be completed by {1}. portfolio.pending.step2_buyer.openForDispute=You have not completed your payment!\nThe max. period for the trade has elapsed.\ Please contact the mediator for assistance. @@ -1057,7 +1048,6 @@ funds.locked.locked=Locked in multisig for trade with ID: {0} funds.tx.direction.sentTo=Sent to: funds.tx.direction.receivedWith=Received with: funds.tx.direction.genesisTx=From Genesis tx: -funds.tx.txFeePaymentForBsqTx=Miner fee for BSQ tx funds.tx.createOfferFee=Maker and tx fee: {0} funds.tx.takeOfferFee=Taker and tx fee: {0} funds.tx.multiSigDeposit=Multisig deposit: {0} @@ -1071,15 +1061,11 @@ funds.tx.unknown=Unknown reason: {0} funds.tx.noFundsFromDispute=No refund from dispute funds.tx.receivedFunds=Received funds funds.tx.withdrawnFromWallet=Withdrawn from wallet -funds.tx.withdrawnFromBSQWallet=XMR withdrawn from BSQ wallet funds.tx.memo=Memo funds.tx.noTxAvailable=No transactions available funds.tx.revert=Revert funds.tx.txSent=Transaction successfully sent to a new address in the local Haveno wallet. funds.tx.direction.self=Sent to yourself -funds.tx.daoTxFee=Miner fee for BSQ tx -funds.tx.reimbursementRequestTxFee=Reimbursement request -funds.tx.compensationRequestTxFee=Compensation request funds.tx.dustAttackTx=Received dust funds.tx.dustAttackTx.popup=This transaction is sending a very small XMR amount to your wallet and might be an attempt \ from chain analysis companies to spy on your wallet.\n\n\ @@ -1210,9 +1196,7 @@ settings.tab.about=About setting.preferences.general=General preferences setting.preferences.explorer=Monero Explorer -setting.preferences.explorer.bsq=Haveno Explorer setting.preferences.deviation=Max. deviation from market price -setting.preferences.bsqAverageTrimThreshold=Outlier threshold for BSQ rate setting.preferences.avoidStandbyMode=Avoid standby mode setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled @@ -1244,25 +1228,6 @@ setting.preferences.notifyOnPreRelease=Receive pre-release notifications 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}. -setting.preferences.daoOptions=DAO options -setting.preferences.dao.resyncFromGenesis.label=Rebuild DAO state from genesis tx -setting.preferences.dao.resyncFromResources.label=Rebuild DAO state from resources -setting.preferences.dao.resyncFromResources.popup=After an application restart the Haveno 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\n\ - If you proceed, after an application restart the Haveno 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.resync=Resync from genesis and shutdown -setting.preferences.dao.isDaoFullNode=Run Haveno as DAO full node -setting.preferences.dao.rpcUser=RPC username -setting.preferences.dao.rpcPw=RPC password -setting.preferences.dao.blockNotifyPort=Block notify port -setting.preferences.dao.fullNodeInfo=For running Haveno as DAO full node you need to have Monero Core locally running \ - and RPC enabled. All requirements are documented in ''{0}''.\n\n\ - After changing the mode you need to restart. -setting.preferences.dao.fullNodeInfo.ok=Open docs page -setting.preferences.dao.fullNodeInfo.cancel=No, I stick with lite node mode settings.preferences.editCustomExplorer.headline=Explorer Settings settings.preferences.editCustomExplorer.description=Choose a system defined explorer from the list on the left, and/or \ customize to suit your own preferences. @@ -1315,7 +1280,7 @@ settings.net.needRestart=You need to restart the application to apply that chang settings.net.notKnownYet=Not known yet... 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=Haveno DAO chain height: {0} | Monero Peers chain height: {1} +settings.net.chainHeight=Monero Peers chain height: {1} settings.net.ips=[IP address:port | host name:port | onion address:port] (comma separated). Port can be omitted if default is used (8333). settings.net.seedNode=Seed node settings.net.directPeer=Peer (direct) @@ -1372,14 +1337,10 @@ setting.about.shortcuts.walletDetails=Open wallet details window setting.about.shortcuts.openEmergencyBtcWalletTool=Open emergency wallet tool for XMR wallet -setting.about.shortcuts.openEmergencyBsqWalletTool=Open emergency wallet tool for BSQ wallet - setting.about.shortcuts.showTorLogs=Toggle log level for Tor messages between DEBUG and WARN setting.about.shortcuts.manualPayoutTxWindow=Open window for manual payout from 2of2 Multisig deposit tx -setting.about.shortcuts.reRepublishAllGovernanceData=Republish DAO governance data (proposals, votes) - setting.about.shortcuts.removeStuckTrade=Open popup to move failed trade to open trades tab again setting.about.shortcuts.removeStuckTrade.value=Select failed trade and press: {0} @@ -1699,7 +1660,7 @@ out of your wallet, and when restoring your wallet from seed words. account.seed.backup.title=Backup your wallets seed words account.seed.info=Please write down both wallet seed words and the date! \ You can recover your wallet any time with seed words and the date.\n\ -The same seed words are used for the XMR and BSQ wallet.\n\n\ +The same seed words are used for the XMR wallet.\n\n\ You should write down the seed words on a sheet of paper. Do not save them on your computer.\n\n\ Please note that the seed words are NOT a replacement for a backup.\n\ You need to create a backup of the whole application directory from the \"Account/Backup\" screen to recover application state and data.\n\ @@ -1788,722 +1749,6 @@ account.notifications.noWebCamFound.warning=No webcam found.\n\n\ account.notifications.priceAlert.warning.highPriceTooLow=The higher price must be larger than the lower price. account.notifications.priceAlert.warning.lowerPriceTooHigh=The lower price must be lower than the higher price. - - - -#################################################################### -# DAO -#################################################################### - -dao.tab.factsAndFigures=Facts & Figures -dao.tab.bsqWallet=BSQ wallet -dao.tab.proposals=Governance -dao.tab.bonding=Bonding -dao.tab.proofOfBurn=Asset listing fee/Proof of burn -dao.tab.monitor=Network monitor -dao.tab.news=News - -dao.paidWithBsq=paid with BSQ -dao.availableBsqBalance=Available for spending (verified + unconfirmed change outputs) -dao.verifiedBsqBalance=Balance of all verified UTXOs -dao.unconfirmedChangeBalance=Balance of all unconfirmed change outputs -dao.unverifiedBsqBalance=Balance of all unverified transactions (awaiting block confirmation) -dao.lockedForVoteBalance=Used for voting -dao.lockedInBonds=Locked in bonds -dao.availableNonBsqBalance=Available non-BSQ balance (XMR) -dao.reputationBalance=Merit Value (not spendable) - -dao.tx.published.success=Your transaction has been successfully published. -dao.proposal.menuItem.make=Make proposal -dao.proposal.menuItem.browse=Browse open proposals -dao.proposal.menuItem.vote=Vote on proposals -dao.proposal.menuItem.result=Vote results -dao.cycle.headline=Voting cycle -dao.cycle.overview.headline=Voting cycle overview -dao.cycle.currentPhase=Current phase -dao.cycle.currentBlockHeight=Current block height -dao.cycle.proposal=Proposal phase -dao.cycle.proposal.next=Next proposal phase -dao.cycle.blindVote=Blind vote phase -dao.cycle.voteReveal=Vote reveal phase -dao.cycle.voteResult=Vote result -dao.cycle.phaseDuration={0} blocks (≈{1}); Block {2} - {3} (≈{4} - ≈{5}) -dao.cycle.phaseDurationWithoutBlocks=Block {0} - {1} (≈{2} - ≈{3}) - -dao.voteReveal.txPublished.headLine=Vote reveal transaction published -dao.voteReveal.txPublished=Your vote reveal transaction with transaction ID {0} was successfully published.\n\n\ - This happens automatically by the software if you have participated in the DAO voting. - -dao.results.cycles.header=Cycles -dao.results.cycles.table.header.cycle=Cycle -dao.results.cycles.table.header.numProposals=Proposals -dao.results.cycles.table.header.voteWeight=Vote weight -dao.results.cycles.table.header.issuance=Issuance - -dao.results.results.table.item.cycle=Cycle {0} started: {1} - -dao.results.proposals.header=Proposals of selected cycle -dao.results.proposals.table.header.nameLink=Name/link -dao.results.proposals.table.header.details=Details -dao.results.proposals.table.header.myVote=My vote -dao.results.proposals.table.header.result=Vote result -dao.results.proposals.table.header.threshold=Threshold -dao.results.proposals.table.header.quorum=Quorum - -dao.results.proposals.voting.detail.header=Vote results for selected proposal - -dao.results.exceptions=Vote result exception(s) - -# suppress inspection "UnusedProperty" -dao.param.UNDEFINED=Undefined - -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BSQ=BSQ maker fee -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BSQ=BSQ taker fee -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BSQ=Min. BSQ maker fee -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BSQ=Min. BSQ taker fee -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BTC=XMR maker fee -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BTC=XMR taker fee -# suppress inspection "UnusedProperty" -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BTC=Min. XMR maker fee -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BTC=Min. XMR taker fee -# suppress inspection "UnusedProperty" - -# suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=Proposal fee in BSQ -# suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=Voting fee in BSQ - -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=Compensation request min. BSQ amount -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=Compensation request max. BSQ amount -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MIN_AMOUNT=Reimbursement request min. BSQ amount -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MAX_AMOUNT=Reimbursement request max. BSQ amount - -# suppress inspection "UnusedProperty" -dao.param.QUORUM_GENERIC=Required quorum in BSQ for generic proposal -# suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=Required quorum in BSQ for compensation request -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REIMBURSEMENT=Required quorum in BSQ for reimbursement request -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=Required quorum in BSQ for changing a parameter -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Required quorum in BSQ for removing an asset -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=Required quorum in BSQ for a confiscation request -# suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=Required quorum in BSQ for bonded role requests - -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_GENERIC=Required threshold in % for generic proposal -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=Required threshold in % for compensation request -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REIMBURSEMENT=Required threshold in % for reimbursement request -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=Required threshold in % for changing a parameter -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Required threshold in % for removing an asset -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=Required threshold in % for a confiscation request -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_ROLE=Required threshold in % for bonded role requests - -# suppress inspection "UnusedProperty" -dao.param.RECIPIENT_BTC_ADDRESS=Recipient XMR address - -# suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=Asset listing fee per day -# suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Min. trade volume for assets - -# suppress inspection "UnusedProperty" -dao.param.LOCK_TIME_TRADE_PAYOUT=Lock time for alternative trade payout tx -# suppress inspection "UnusedProperty" -dao.param.ARBITRATOR_FEE=Arbitrator fee in XMR - -# suppress inspection "UnusedProperty" -dao.param.MAX_TRADE_LIMIT=Max. trade limit in XMR - -# suppress inspection "UnusedProperty" -dao.param.BONDED_ROLE_FACTOR=Bonded role unit factor in BSQ -# suppress inspection "UnusedProperty" -dao.param.ISSUANCE_LIMIT=Issuance limit per cycle in BSQ - -dao.param.currentValue=Current value: {0} -dao.param.currentAndPastValue=Current value: {0} (Value when proposal was made: {1}) -dao.param.blocks={0} blocks - -dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was \ - not distributed well in the Haveno network.\n{0} - -# suppress inspection "UnusedProperty" -dao.phase.PHASE_UNDEFINED=Undefined -# suppress inspection "UnusedProperty" -dao.phase.PHASE_PROPOSAL=Proposal phase -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK1=Break 1 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=Blind vote phase -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK2=Break 2 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=Vote reveal phase -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK3=Break 3 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_RESULT=Result phase - -dao.results.votes.table.header.stakeAndMerit=Vote weight -dao.results.votes.table.header.stake=Stake -dao.results.votes.table.header.merit=Earned -dao.results.votes.table.header.vote=Vote - -dao.bond.menuItem.bondedRoles=Bonded roles -dao.bond.menuItem.reputation=Bonded reputation -dao.bond.menuItem.bonds=Bonds - -dao.bond.dashboard.bondsHeadline=Bonded BSQ -dao.bond.dashboard.lockupAmount=Lockup funds -dao.bond.dashboard.unlockingAmount=Unlocking funds (wait until lock time is over) - - -dao.bond.reputation.header=Lockup a bond for reputation -dao.bond.reputation.table.header=My reputation bonds -dao.bond.reputation.amount=Amount of BSQ to lockup -dao.bond.reputation.time=Unlock time in blocks -dao.bond.reputation.salt=Salt -dao.bond.reputation.hash=Hash -dao.bond.reputation.lockupButton=Lockup -dao.bond.reputation.lockup.headline=Confirm lockup transaction -dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\n\ - Mining fee: {3} ({4} Satoshis/vbyte)\nTransaction vsize: {5} Kb\n\nAre you sure you want to proceed? -dao.bond.reputation.unlock.headline=Confirm unlock transaction -dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\n\ - Mining fee: {3} ({4} Satoshis/vbyte)\nTransaction vsize: {5} Kb\n\nAre you sure you want to proceed? - -dao.bond.allBonds.header=All bonds - -dao.bond.bondedReputation=Bonded Reputation -dao.bond.bondedRoles=Bonded roles - -dao.bond.details.header=Role details -dao.bond.details.role=Role -dao.bond.details.requiredBond=Required BSQ bond -dao.bond.details.unlockTime=Unlock time in blocks -dao.bond.details.link=Link to role description -dao.bond.details.isSingleton=Can be taken by multiple role holders -dao.bond.details.blocks={0} blocks - -dao.bond.table.column.name=Name -dao.bond.table.column.link=Link -dao.bond.table.column.bondType=Bond type -dao.bond.table.column.details=Details -dao.bond.table.column.lockupTxId=Lockup Tx ID -dao.bond.table.column.bondState=Bond state -dao.bond.table.column.lockTime=Unlock time -dao.bond.table.column.lockupDate=Lockup date - -dao.bond.table.button.lockup=Lockup -dao.bond.table.button.unlock=Unlock -dao.bond.table.button.revoke=Revoke - -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNDEFINED=Undefined -# suppress inspection "UnusedProperty" -dao.bond.bondState.READY_FOR_LOCKUP=Not bonded yet -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_PENDING=Lockup pending -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_CONFIRMED=Bond locked up -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_PENDING=Unlock pending -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_CONFIRMED=Unlock tx confirmed -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKING=Bond unlocking -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKED=Bond unlocked -# suppress inspection "UnusedProperty" -dao.bond.bondState.CONFISCATED=Bond confiscated - -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.UNDEFINED=Undefined -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.BONDED_ROLE=Bonded role -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.REPUTATION=Bonded reputation - -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.UNDEFINED=Undefined -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=Forum admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=Haveno maintainer -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork maintainer -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer maintainer -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=Website operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=Forum operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Seed node operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Price node operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Monero node operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile notifications relay operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Domain name holder -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DNS_ADMIN=DNS admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=Mediator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=Arbitrator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=XMR donation address owner - -dao.burnBsq.assetFee=Asset listing -dao.burnBsq.menuItem.assetFee=Asset listing fee -dao.burnBsq.menuItem.proofOfBurn=Proof of burn -dao.burnBsq.header=Fee for asset listing -dao.burnBsq.selectAsset=Select Asset -dao.burnBsq.fee=Fee -dao.burnBsq.trialPeriod=Trial period -dao.burnBsq.payFee=Pay fee -dao.burnBsq.allAssets=All assets -dao.burnBsq.assets.nameAndCode=Asset name -dao.burnBsq.assets.state=State -dao.burnBsq.assets.tradeVolume=Trade volume -dao.burnBsq.assets.lookBackPeriod=Verification period -dao.burnBsq.assets.trialFee=Fee for trial period -dao.burnBsq.assets.totalFee=Total fees paid -dao.burnBsq.assets.days={0} days -dao.burnBsq.assets.toFewDays=The asset fee is too low. The min. amount of days for the trial period is {0}. - -# suppress inspection "UnusedProperty" -dao.assetState.UNDEFINED=Undefined -# suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=In trial period -# suppress inspection "UnusedProperty" -dao.assetState.ACTIVELY_TRADED=Actively traded -# suppress inspection "UnusedProperty" -dao.assetState.DE_LISTED=De-listed due to inactivity -# suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=Removed by voting - -dao.proofOfBurn.header=Proof of burn -dao.proofOfBurn.amount=Amount -dao.proofOfBurn.preImage=Pre-image -dao.proofOfBurn.burn=Burn -dao.proofOfBurn.allTxs=All proof of burn transactions -dao.proofOfBurn.myItems=My proof of burn transactions -dao.proofOfBurn.date=Date -dao.proofOfBurn.hash=Hash -dao.proofOfBurn.txs=Transactions -dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Sign a message with key from proof of burn transaction -dao.proofOfBurn.verify.window.title=Verify a message with key from proof of burn transaction -dao.proofOfBurn.copySig=Copy signature to clipboard -dao.proofOfBurn.sign=Sign -dao.proofOfBurn.message=Message -dao.proofOfBurn.sig=Signature -dao.proofOfBurn.verify=Verify -dao.proofOfBurn.verificationResult.ok=Verification succeeded -dao.proofOfBurn.verificationResult.failed=Verification failed - -# suppress inspection "UnusedProperty" -dao.phase.UNDEFINED=Undefined -# suppress inspection "UnusedProperty" -dao.phase.PROPOSAL=Proposal phase -# suppress inspection "UnusedProperty" -dao.phase.BREAK1=Break before blind vote phase -# suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=Blind vote phase -# suppress inspection "UnusedProperty" -dao.phase.BREAK2=Break before vote reveal phase -# suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=Vote reveal phase -# suppress inspection "UnusedProperty" -dao.phase.BREAK3=Break before result phase -# suppress inspection "UnusedProperty" -dao.phase.RESULT=Vote result phase - -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.PROPOSAL=Proposal phase -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=Blind vote -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.VOTE_REVEAL=Vote reveal -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.RESULT=Vote result - -# suppress inspection "UnusedProperty" -dao.proposal.type.UNDEFINED=Undefined -# suppress inspection "UnusedProperty" -dao.proposal.type.COMPENSATION_REQUEST=Compensation request -# suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=Reimbursement request -# suppress inspection "UnusedProperty" -dao.proposal.type.BONDED_ROLE=Proposal for a bonded role -# suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=Proposal for removing an asset -# suppress inspection "UnusedProperty" -dao.proposal.type.CHANGE_PARAM=Proposal for changing a parameter -# suppress inspection "UnusedProperty" -dao.proposal.type.GENERIC=Generic proposal -# suppress inspection "UnusedProperty" -dao.proposal.type.CONFISCATE_BOND=Proposal for confiscating a bond - -# suppress inspection "UnusedProperty" -dao.proposal.type.short.UNDEFINED=Undefined -# suppress inspection "UnusedProperty" -dao.proposal.type.short.COMPENSATION_REQUEST=Compensation request -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=Reimbursement request -# suppress inspection "UnusedProperty" -dao.proposal.type.short.BONDED_ROLE=Bonded role -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CHANGE_PARAM=Changing a parameter -# suppress inspection "UnusedProperty" -dao.proposal.type.short.GENERIC=Generic proposal -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CONFISCATE_BOND=Confiscating a bond - -dao.proposal.details=Proposal details -dao.proposal.selectedProposal=Selected proposal -dao.proposal.active.header=Proposals of current cycle -dao.proposal.active.remove.confirm=Are you sure you want to remove that proposal?\n\ - The already paid proposal fee will be lost. -dao.proposal.active.remove.doRemove=Yes, remove my proposal -dao.proposal.active.remove.failed=Could not remove proposal. -dao.proposal.myVote.title=Voting -dao.proposal.myVote.accept=Accept proposal -dao.proposal.myVote.reject=Reject proposal -dao.proposal.myVote.removeMyVote=Ignore proposal -dao.proposal.myVote.merit=Vote weight from earned BSQ -dao.proposal.myVote.stake=Vote weight from stake -dao.proposal.myVote.revealTxId=Vote reveal transaction ID -dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0} -dao.proposal.votes.header=Set stake for voting and publish your votes -dao.proposal.myVote.button=Publish votes -dao.proposal.myVote.setStake.description=After voting on all proposals you have to set your stake for voting by locking up \ - BSQ. The more BSQ you lock up, the more weight your vote will have. \n\n\ - BSQ locked up for voting will be unlocked again during the vote reveal phase. -dao.proposal.create.selectProposalType=Select proposal type -dao.proposal.create.phase.inactive=Please wait until the next proposal phase -dao.proposal.create.proposalType=Proposal type -dao.proposal.create.new=Make new proposal -dao.proposal.create.button=Make proposal -dao.proposal.create.publish=Publish proposal -dao.proposal.create.publishing=Proposal publishing is in progress ... -dao.proposal=proposal -dao.proposal.display.type=Proposal type -dao.proposal.display.name=Exact GitHub username -dao.proposal.display.link=Link to detailed info -dao.proposal.display.link.prompt=Link to proposal -dao.proposal.display.requestedBsq=Requested amount in BSQ -dao.proposal.display.txId=Proposal transaction ID -dao.proposal.display.proposalFee=Proposal fee -dao.proposal.display.myVote=My vote -dao.proposal.display.voteResult=Vote result summary -dao.proposal.display.bondedRoleComboBox.label=Bonded role type -dao.proposal.display.requiredBondForRole.label=Required bond for role -dao.proposal.display.option=Option - -dao.proposal.table.header.proposalType=Proposal type -dao.proposal.table.header.link=Link -dao.proposal.table.header.myVote=My vote -# suppress inspection "UnusedProperty" -dao.proposal.table.header.remove=Remove -dao.proposal.table.icon.tooltip.removeProposal=Remove my proposal -dao.proposal.table.icon.tooltip.changeVote=Current vote: ''{0}''. Change vote to: ''{1}'' - -dao.proposal.display.myVote.accepted=Accepted -dao.proposal.display.myVote.rejected=Rejected -dao.proposal.display.myVote.ignored=Ignored -dao.proposal.display.myVote.unCounted=Vote was not included in result -dao.proposal.myVote.summary=Voted: {0}; Vote weight: {1} (earned: {2} + stake: {3}) {4} -dao.proposal.myVote.invalid=Vote was invalid - -dao.proposal.voteResult.success=Accepted -dao.proposal.voteResult.failed=Rejected -dao.proposal.voteResult.summary=Result: {0}; Threshold: {1} (required > {2}); Quorum: {3} (required > {4}) - -dao.proposal.display.paramComboBox.label=Select parameter to change -dao.proposal.display.paramValue=Parameter value - -dao.proposal.display.confiscateBondComboBox.label=Choose bond -dao.proposal.display.assetComboBox.label=Asset to remove - -dao.blindVote=blind vote - -dao.blindVote.startPublishing=Publishing blind vote transaction... -dao.blindVote.success=Your blind vote transaction has been successfully published.\n\nPlease note, that you have to be \ - online in the vote reveal phase so that your Haveno application can publish the vote reveal transaction. \ - Without the vote reveal transaction your vote would be invalid! - -dao.wallet.menuItem.send=Send -dao.wallet.menuItem.receive=Receive -dao.wallet.menuItem.transactions=Transactions - -dao.wallet.dashboard.myBalance=My wallet balance - -dao.wallet.receive.fundYourWallet=Your BSQ receive address -dao.wallet.receive.bsqAddress=BSQ wallet address (Fresh unused address) - -dao.wallet.send.sendFunds=Send funds -dao.wallet.send.sendBtcFunds=Send non-BSQ funds (XMR) -dao.wallet.send.amount=Amount in BSQ -dao.wallet.send.btcAmount=Amount in XMR (non-BSQ funds) -dao.wallet.send.setAmount=Set amount to withdraw (min. amount is {0}) -dao.wallet.send.receiverAddress=Receiver's BSQ address -dao.wallet.send.receiverBtcAddress=Receiver's XMR address -dao.wallet.send.setDestinationAddress=Fill in your destination address -dao.wallet.send.send=Send BSQ funds -dao.wallet.send.inputControl=Select inputs -dao.wallet.send.sendBtc=Send XMR funds -dao.wallet.send.sendFunds.headline=Confirm withdrawal request -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=Latest verified block: {0} -dao.wallet.chainHeightSyncing=Awaiting blocks... Verified {0} blocks out of {1} -dao.wallet.tx.type=Type - -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED=Undefined -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED_TX_TYPE=Not recognized -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNVERIFIED=Unverified BSQ transaction -# suppress inspection "UnusedProperty" -dao.tx.type.enum.INVALID=Invalid BSQ transaction -# suppress inspection "UnusedProperty" -dao.tx.type.enum.GENESIS=Genesis transaction -# suppress inspection "UnusedProperty" -dao.tx.type.enum.TRANSFER_BSQ=Transfer BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.received.TRANSFER_BSQ=Received BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.sent.TRANSFER_BSQ=Sent BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PAY_TRADE_FEE=Trading fee -# suppress inspection "UnusedProperty" -dao.tx.type.enum.COMPENSATION_REQUEST=Fee for compensation request -# suppress inspection "UnusedProperty" -dao.tx.type.enum.REIMBURSEMENT_REQUEST=Fee for reimbursement request -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROPOSAL=Fee for proposal -# suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=Fee for blind vote -# suppress inspection "UnusedProperty" -dao.tx.type.enum.VOTE_REVEAL=Vote reveal -# suppress inspection "UnusedProperty" -dao.tx.type.enum.LOCKUP=Lock up bond -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNLOCK=Unlock bond -# suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=Asset listing fee -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=Proof of burn -# suppress inspection "UnusedProperty" -dao.tx.type.enum.IRREGULAR=Irregular - -dao.tx.withdrawnFromWallet=XMR withdrawn from wallet -dao.tx.issuanceFromCompReq=Compensation request/issuance -dao.tx.issuanceFromCompReq.tooltip=Compensation request which led to an issuance of new BSQ.\n\ - Issuance date: {0} -dao.tx.issuanceFromReimbursement=Reimbursement request/issuance -dao.tx.issuanceFromReimbursement.tooltip=Reimbursement request which led to an issuance of new BSQ.\n\ - Issuance date: {0} -dao.proposal.create.missingBsqFunds=You don''t have sufficient BSQ funds for creating the proposal. If you have an \ - unconfirmed BSQ transaction you need to wait for a blockchain confirmation because BSQ is validated only if it is \ - included in a block.\n\ - Missing: {0} - -dao.proposal.create.missingBsqFundsForBond=You don''t have sufficient BSQ funds for this role. You can still \ - publish this proposal, but you''ll need the full BSQ amount required for this role if it gets accepted. \n\ - Missing: {0} - -dao.proposal.create.missingMinerFeeFunds=You don''t have sufficient XMR funds for creating the proposal transaction. \ - All BSQ transactions require a miner fee in XMR.\n\ - Missing: {0} - -dao.proposal.create.missingIssuanceFunds=You don''t have sufficient XMR funds for creating the proposal transaction. \ - All BSQ transactions require a miner fee in XMR, and issuance transactions also require XMR for the requested BSQ \ - amount ({0} Satoshis/BSQ).\n\ - Missing: {1} - -dao.feeTx.confirm=Confirm {0} transaction -dao.feeTx.confirm.details={0} fee: {1}\n\ - Mining fee: {2} ({3} Satoshis/vbyte)\n\ - Transaction vsize: {4} vKb\n\n\ - Are you sure you want to publish the {5} transaction? - -dao.feeTx.issuanceProposal.confirm.details={0} fee: {1}\n\ - XMR needed for BSQ issuance: {2} ({3} Satoshis/BSQ)\n\ - Mining fee: {4} ({5} Satoshis/vbyte)\n\ - Transaction vsize: {6} vKb\n\n\ - If your request is approved, you will receive the amount you requested net of the 2 BSQ proposal fee.\n\n\ - Are you sure you want to publish the {7} transaction? - -dao.news.bisqDAO.title=THE BISQ DAO -dao.news.bisqDAO.description=Just as the Haveno exchange is decentralized and censorship-resistant, so is its \ - governance model - and the Haveno DAO and BSQ token are the tools that make it possible. -dao.news.bisqDAO.readMoreLink=Learn More About the Haveno DAO - -dao.news.pastContribution.title=MADE PAST CONTRIBUTIONS? REQUEST BSQ -dao.news.pastContribution.description=If you have contributed to Haveno please use the BSQ address below and make a \ - request for taking part of the BSQ genesis distribution. -dao.news.pastContribution.yourAddress=Your BSQ Wallet Address -dao.news.pastContribution.requestNow=Request now - -dao.news.DAOOnTestnet.title=RUN THE BISQ DAO ON OUR TESTNET -dao.news.DAOOnTestnet.description=The mainnet Haveno DAO is not launched yet but you can learn about the Haveno DAO \ - by running it on our testnet. -dao.news.DAOOnTestnet.firstSection.title=1. Switch to DAO Testnet Mode -dao.news.DAOOnTestnet.firstSection.content=Switch to DAO Testnet from the Settings screen. -dao.news.DAOOnTestnet.secondSection.title=2. Acquire Some BSQ -dao.news.DAOOnTestnet.secondSection.content=Request BSQ on Slack or Buy BSQ on Haveno. -dao.news.DAOOnTestnet.thirdSection.title=3. Participate in a Voting Cycle -dao.news.DAOOnTestnet.thirdSection.content=Making proposals and voting on proposals to change various aspects of Haveno. -dao.news.DAOOnTestnet.fourthSection.title=4. Explore a BSQ Block Explorer -dao.news.DAOOnTestnet.fourthSection.content=Since BSQ is just bitcoin, you can see BSQ transactions on our bitcoin block explorer. -dao.news.DAOOnTestnet.readMoreLink=Read the full documentation - -dao.monitor.daoState=DAO state -dao.monitor.proposals=Proposals state -dao.monitor.blindVotes=Blind votes state - -dao.monitor.table.peers=Peers -dao.monitor.table.conflicts=Conflicts -dao.monitor.state=Status -dao.monitor.requestAlHashes=Request all hashes -dao.monitor.resync=Resync DAO state -dao.monitor.table.header.cycleBlockHeight=Cycle / block height -dao.monitor.table.cycleBlockHeight=Cycle {0} / block {1} -dao.monitor.table.seedPeers=Seed node: {0} - -dao.monitor.daoState.headline=DAO state -dao.monitor.daoState.table.headline=Chain of DAO state hashes -dao.monitor.daoState.table.blockHeight=Block height -dao.monitor.daoState.table.hash=Hash of DAO state -dao.monitor.daoState.table.prev=Previous hash -dao.monitor.daoState.conflictTable.headline=DAO state hashes from peers in conflict -dao.monitor.daoState.utxoConflicts=UTXO conflicts -dao.monitor.daoState.utxoConflicts.blockHeight=Block height: {0} -dao.monitor.daoState.utxoConflicts.sumUtxo=Sum of all UTXO: {0} BSQ -dao.monitor.daoState.utxoConflicts.sumBsq=Sum of all BSQ: {0} BSQ -dao.monitor.daoState.checkpoint.popup=DAO state is not in sync with the network. \ - After restart the DAO state will resync. - -dao.monitor.proposal.headline=Proposals state -dao.monitor.proposal.table.headline=Chain of proposal state hashes -dao.monitor.proposal.conflictTable.headline=Proposal state hashes from peers in conflict - -dao.monitor.proposal.table.hash=Hash of proposal state -dao.monitor.proposal.table.prev=Previous hash -dao.monitor.proposal.table.numProposals=No. proposals - -dao.monitor.isInConflictWithSeedNode=Your local data is not in consensus with at least one seed node. \ - Please resync the DAO state. -dao.monitor.isInConflictWithNonSeedNode=One of your peers is not in consensus with the network but your node \ - is in sync with the seed nodes. -dao.monitor.daoStateInSync=Your local node is in consensus with the network - -dao.monitor.blindVote.headline=Blind votes state -dao.monitor.blindVote.table.headline=Chain of blind vote state hashes -dao.monitor.blindVote.conflictTable.headline=Blind vote state hashes from peers in conflict -dao.monitor.blindVote.table.hash=Hash of blind vote state -dao.monitor.blindVote.table.prev=Previous hash -dao.monitor.blindVote.table.numBlindVotes=No. blind votes - -dao.factsAndFigures.menuItem.supply=BSQ Supply -dao.factsAndFigures.menuItem.transactions=BSQ Transactions - -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 BSQ/USD price -dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on 30 days average BSQ/USD price) -dao.factsAndFigures.dashboard.availableAmount=Total available BSQ -dao.factsAndFigures.dashboard.volumeUsd=Total trade volume in USD -dao.factsAndFigures.dashboard.volumeBtc=Total trade volume in BTC -dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection=Average BSQ/USD trade price from selected time period in chart -dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection=Average BSQ/BTC trade price from selected time period in chart - -dao.factsAndFigures.supply.issuedVsBurnt=BSQ issued v. BSQ burnt - -dao.factsAndFigures.supply.issued=BSQ issued -dao.factsAndFigures.supply.compReq=Compensation requests -dao.factsAndFigures.supply.reimbursement=Reimbursement requests -dao.factsAndFigures.supply.genesisIssueAmount=BSQ issued at genesis transaction -dao.factsAndFigures.supply.compRequestIssueAmount=BSQ issued for compensation requests -dao.factsAndFigures.supply.reimbursementAmount=BSQ issued for reimbursement requests -dao.factsAndFigures.supply.totalIssued=Total issued BSQ -dao.factsAndFigures.supply.totalBurned=Total burned BSQ -dao.factsAndFigures.supply.chart.tradeFee.toolTip={0}\n{1} -dao.factsAndFigures.supply.burnt=BSQ burnt - -dao.factsAndFigures.supply.priceChat=BSQ price -dao.factsAndFigures.supply.volumeChat=Trade volume -dao.factsAndFigures.supply.tradeVolumeInUsd=Trade volume in USD -dao.factsAndFigures.supply.tradeVolumeInBtc=Trade volume in BTC -dao.factsAndFigures.supply.bsqUsdPrice=BSQ/USD price -dao.factsAndFigures.supply.bsqBtcPrice=BSQ/BTC price -dao.factsAndFigures.supply.btcUsdPrice=BTC/USD price - -dao.factsAndFigures.supply.locked=Global state of locked BSQ -dao.factsAndFigures.supply.totalLockedUpAmount=Locked up in bonds -dao.factsAndFigures.supply.totalUnlockingAmount=Unlocking BSQ from bonds -dao.factsAndFigures.supply.totalUnlockedAmount=Unlocked BSQ from bonds -dao.factsAndFigures.supply.totalConfiscatedAmount=Confiscated BSQ from bonds -dao.factsAndFigures.supply.proofOfBurn=Proof of Burn -dao.factsAndFigures.supply.bsqTradeFee=BSQ Trade fees -dao.factsAndFigures.supply.btcTradeFee=BTC Trade fees - -dao.factsAndFigures.transactions.genesis=Genesis transaction -dao.factsAndFigures.transactions.genesisBlockHeight=Genesis block height -dao.factsAndFigures.transactions.genesisTxId=Genesis transaction ID -dao.factsAndFigures.transactions.txDetails=BSQ transactions statistics -dao.factsAndFigures.transactions.allTx=No. of all BSQ transactions -dao.factsAndFigures.transactions.utxo=No. of all unspent transaction outputs -dao.factsAndFigures.transactions.compensationIssuanceTx=No. of all compensation request issuance transactions -dao.factsAndFigures.transactions.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions -dao.factsAndFigures.transactions.burntTx=No. of all fee payments transactions -dao.factsAndFigures.transactions.invalidTx=No. of all invalid transactions -dao.factsAndFigures.transactions.irregularTx=No. of all irregular transactions - - - #################################################################### # Windows #################################################################### @@ -2629,8 +1874,6 @@ Before you use this tool, please backup your data directory. \ You can do this at \"Account/Backup\".\n\n\ Please report us your problem and file a bug report on GitHub or at the Haveno forum so that we can investigate what was causing the problem. emptyWalletWindow.balance=Your available wallet balance -emptyWalletWindow.bsq.btcBalance=Balance of non-BSQ Satoshis - emptyWalletWindow.address=Your destination address emptyWalletWindow.button=Send all funds emptyWalletWindow.openOffers.warn=You have open offers which will be removed if you empty the wallet.\nAre you sure that you want to empty your wallet? @@ -2655,10 +1898,8 @@ filterWindow.seedNode=Filtered seed nodes (comma sep. onion addresses) filterWindow.priceRelayNode=Filtered price relay nodes (comma sep. onion addresses) filterWindow.btcNode=Filtered Monero nodes (comma sep. addresses + port) filterWindow.preventPublicBtcNetwork=Prevent usage of public Monero network -filterWindow.disableDao=Disable DAO filterWindow.disableAutoConf=Disable auto-confirm filterWindow.autoConfExplorers=Filtered auto-confirm explorers (comma sep. addresses) -filterWindow.disableDaoBelowVersion=Min. version required for DAO filterWindow.disableTradeBelowVersion=Min. version required for trading filterWindow.add=Add filter filterWindow.remove=Remove filter @@ -2738,8 +1979,6 @@ tradeDetailsWindow.detailData=Detail data txDetailsWindow.headline=Transaction Details txDetailsWindow.btc.note=You have sent BTC. -txDetailsWindow.bsq.note=You have sent BSQ funds. \ - BSQ is colored bitcoin, so the transaction will not show in a BSQ explorer until it has been confirmed in a bitcoin block. txDetailsWindow.sentTo=Sent to txDetailsWindow.txId=TxId @@ -2751,9 +1990,6 @@ closedTradesSummaryWindow.totalMinerFee.title=Sum of all miner fees closedTradesSummaryWindow.totalMinerFee.value={0} ({1} of total trade amount) closedTradesSummaryWindow.totalTradeFeeInBtc.title=Sum of all trade fees paid in BTC closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} of total trade amount) -closedTradesSummaryWindow.totalTradeFeeInBsq.title=Sum of all trade fees paid in BSQ -closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} of total trade amount) - walletPasswordWindow.headline=Enter password to unlock torNetworkSettingWindow.header=Tor networks settings @@ -2780,7 +2016,7 @@ torNetworkSettingWindow.bridges.info=If Tor is blocked by your internet provider bridges and pluggable transports. feeOptionWindow.headline=Choose currency for trade fee payment -feeOptionWindow.info=You can choose to pay the trade fee in BSQ or in XMR. If you choose BSQ you appreciate the discounted trade fee. +feeOptionWindow.info=You can choose to pay the trade fee in XMR. feeOptionWindow.optionsLabel=Choose currency for trade fee payment feeOptionWindow.useBTC=Use XMR feeOptionWindow.fee={0} (≈ {1}) @@ -2858,23 +2094,6 @@ popup.warning.tooLargePercentageValue=You cannot set a percentage of 100% or lar popup.warning.examplePercentageValue=Please enter a percentage number like \"5.4\" for 5.4% popup.warning.noPriceFeedAvailable=There is no price feed available for that currency. You cannot use a percent based price.\nPlease select the fixed price. popup.warning.sendMsgFailed=Sending message to your trading partner failed.\nPlease try again and if it continue to fail report a bug. -popup.warning.insufficientBtcFundsForBsqTx=You don''t have sufficient XMR funds for paying the miner fee for that transaction.\n\ -Please fund your XMR wallet.\nMissing funds: {0} -popup.warning.bsqChangeBelowDustException=This transaction creates a BSQ change output which is below dust \ - limit (5.46 BSQ) and would be rejected by the Monero network.\n\n\ - You need to either send a higher amount to avoid the change output (e.g. by adding the dust amount to your \ - sending amount) or add more BSQ funds to your wallet so you avoid to generate a dust output.\n\n\ - The dust output is {0}. -popup.warning.btcChangeBelowDustException=This transaction creates a change output which is below dust \ - limit (546 Satoshi) and would be rejected by the Monero network.\n\n\ - You need to add the dust amount to your sending amount to avoid to generate a dust output.\n\n\ - The dust output is {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 Monero protocol.\n\n\ - You can either buy more BSQ or pay trade fees with BTC.\n\n\ - Missing funds: {0} -popup.warning.noBsqFundsForBtcFeePayment=Your BSQ wallet does not have sufficient funds for paying the trade fee in BSQ. popup.warning.messageTooLong=Your message exceeds the max. allowed size. Please send it in several parts or upload it to a service like https://pastebin.com. popup.warning.lockedUpFunds=You have locked up funds from a failed trade.\n\ Locked up balance: {0} \n\ @@ -2894,11 +2113,6 @@ popup.warning.seed=seed popup.warning.mandatoryUpdate.trading=Please update to the latest Haveno version. \ A mandatory update was released which disables trading for old versions. \ Please check out the Haveno Forum for more information. -popup.warning.mandatoryUpdate.dao=Please update to the latest Haveno version. \ - A mandatory update was released which disables the Haveno DAO and BSQ for old versions. \ - Please check out the Haveno Forum for more information. -popup.warning.disable.dao=The Haveno DAO and BSQ are temporary disabled. \ - Please check out the Haveno Forum for more information. popup.warning.noFilter=We did not receive a filter object from the seed nodes. This is a not expected situation. Please inform the Haveno developers. popup.warning.burnBTC=This transaction is not possible, as the mining fees of {0} would exceed the amount to transfer of {1}. \ Please wait until the mining fees are low again or until you''ve accumulated more XMR to transfer. @@ -2939,7 +2153,6 @@ popup.info.shutDownWithOpenOffers=Haveno is being shut down, but there are open popup.info.qubesOSSetupInfo=It appears you are running Haveno on Qubes OS. \n\n\ Please make sure your Haveno 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 Haveno 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=Important private notification! @@ -3125,7 +2338,6 @@ navigation.settings.preferences=\"Settings/Preferences\" # suppress inspection "UnusedProperty" navigation.funds.transactions=\"Funds/Transactions\" navigation.support=\"Support\" -navigation.dao.wallet.receive=\"DAO/BSQ Wallet/Receive\" #################################################################### @@ -3155,12 +2367,6 @@ XMR_MAINNET=Monero Mainnet XMR_TESTNET=Monero Testnet # suppress inspection "UnusedProperty" XMR_STAGENET=Monero Stagenet -# suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Monero DAO Testnet (deprecated) -# suppress inspection "UnusedProperty" -BTC_DAO_BETANET=Haveno DAO Betanet (Monero Mainnet) -# suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=Monero DAO Regtest time.year=Year time.month=Month @@ -3641,9 +2847,7 @@ validation.accountNrChars=Account number must consist of {0} characters. validation.btc.invalidAddress=The address is not correct. Please check the address format. validation.integerOnly=Please enter integer numbers only. validation.inputError=Your input caused an error:\n{0} -validation.bsq.insufficientBalance=Your available balance is {0}. validation.btc.exceedsMaxTradeLimit=Your trade limit is {0}. -validation.bsq.amountBelowMinAmount=Min. amount is {0} validation.nationalAccountId={0} must consist of {1} numbers. #new @@ -3665,7 +2869,6 @@ validation.bic.invalidLocationCode=BIC contains invalid location code validation.bic.invalidBranchCode=BIC contains invalid branch code validation.bic.sepaRevolutBic=Revolut Sepa accounts are not supported. validation.btc.invalidFormat=Invalid format for a Monero address. -validation.bsq.invalidFormat=Invalid format for a BSQ address. validation.email.invalidAddress=Invalid address validation.iban.invalidCountryCode=Country code invalid validation.iban.checkSumNotNumeric=Checksum must be numeric diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index 46e91be50f..72480e7599 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -192,7 +192,6 @@ shared.tradeWalletBalance=Zůstatek obchodní peněženky shared.makerTxFee=Tvůrce: {0} shared.takerTxFee=Příjemce: {0} shared.iConfirm=Potvrzuji -shared.tradingFeeInBsqInfo=≈ {0} shared.openURL=Otevřené {0} shared.fiat=Fiat shared.crypto=Krypto @@ -204,9 +203,6 @@ shared.actions=Akce shared.buyerUpperCase=Kupující shared.sellerUpperCase=Prodejce shared.new=NOVÝ -shared.blindVoteTxId=ID transakce se slepým hlasováním -shared.proposal=Návrh -shared.votes=Hlasy shared.learnMore=Zjistit více shared.dismiss=Zavřít shared.selectedArbitrator=Zvolený rozhodce @@ -240,7 +236,6 @@ mainView.menu.funds=Finance mainView.menu.support=Podpora mainView.menu.settings=Nastavení mainView.menu.account=Účet -mainView.menu.dao=DAO mainView.marketPriceWithProvider.label=Tržní cena {0} mainView.marketPrice.bisqInternalPrice=Cena posledního Bisq obchodu @@ -257,13 +252,11 @@ mainView.footer.localhostBitcoinNode=(localhost) mainView.footer.btcInfo={0} {1} mainView.footer.btcFeeRate=/ Aktuální poplatek: {0} sat/vB mainView.footer.btcInfo.initializing=Připojování do Bitcoinové sítě -mainView.footer.bsqInfo.synchronizing=/ Synchronizace DAO mainView.footer.btcInfo.synchronizingWith=Synchronizace s {0} v bloku: {1} / {2} mainView.footer.btcInfo.synchronizedWith=Synchronizováno s {0} v bloku {1} mainView.footer.btcInfo.connectingTo=Připojování mainView.footer.btcInfo.connectionFailed=Připojení se nezdařilo mainView.footer.p2pInfo=Bitcoin síťové nody: {0} / Bisq síťové nody: {1} -mainView.footer.daoFullNode=DAO full node mainView.bootstrapState.connectionToTorNetwork=(1/4) Připojování do sítě Tor... mainView.bootstrapState.torNodeCreated=(2/4) Tor node vytvořen @@ -435,7 +428,6 @@ createOffer.fundsBox.networkFee=Poplatek za těžbu createOffer.fundsBox.placeOfferSpinnerInfo=Probíhá publikování nabídky ... createOffer.fundsBox.paymentLabel=Bisq obchod s ID {0} createOffer.fundsBox.fundsStructure=(kauce {0}, obchodní poplatek {1}, poplatek za těžbu {2}) -createOffer.fundsBox.fundsStructure.BSQ=(kauce {0}, poplatek za těžbu {1}) + obchodní poplatek {2} createOffer.success.headline=Vaše nabídka byla publikována createOffer.success.info=Otevřené nabídky můžete spravovat na stránce \"Portfolio/Moje otevřené nabídky\". createOffer.info.sellAtMarketPrice=Vždy budete prodávat za tržní cenu, protože cena vaší nabídky bude průběžně aktualizována. @@ -636,7 +628,6 @@ portfolio.pending.step2_buyer.amountToTransfer=Částka k převodu portfolio.pending.step2_buyer.sellersAddress={0} adresa prodejce portfolio.pending.step2_buyer.buyerAccount=Použijte svůj platební účet portfolio.pending.step2_buyer.paymentStarted=Platba zahájena -portfolio.pending.step2_buyer.fillInBsqWallet=Odeslat z BSQ peněženky portfolio.pending.step2_buyer.warn=Platbu {0} jste ještě neprovedli!\nVezměte prosím na vědomí, že obchod musí být dokončen do {1}. portfolio.pending.step2_buyer.openForDispute=Neukončili jste platbu!\nMax. doba obchodu uplynula. Obraťte se na mediátora a požádejte o pomoc. portfolio.pending.step2_buyer.paperReceipt.headline=Odeslali jste papírový doklad prodejci BTC? @@ -882,7 +873,6 @@ funds.locked.locked=Uzamčeno v multisig adrese pro obchodování s ID: {0} funds.tx.direction.sentTo=Odesláno na: funds.tx.direction.receivedWith=Přijato z: funds.tx.direction.genesisTx=Z Genesis tx: -funds.tx.txFeePaymentForBsqTx=Poplatek za těžbu za BSQ tx funds.tx.createOfferFee=Poplatky tvůrce a tx: {0} funds.tx.takeOfferFee=Poplatky příjemce a tx: {0} funds.tx.multiSigDeposit=Vklad na multisig adresu: {0} @@ -896,15 +886,11 @@ funds.tx.unknown=Neznámý důvod: {0} funds.tx.noFundsFromDispute=Žádná náhrada ze sporu funds.tx.receivedFunds=Přijaté prostředky funds.tx.withdrawnFromWallet=Výběr z peněženky -funds.tx.withdrawnFromBSQWallet=Výběr BTC z BSQ peněženky funds.tx.memo=Poznámka funds.tx.noTxAvailable=Není k dispozici žádná transakce funds.tx.revert=Vrátit funds.tx.txSent=Transakce byla úspěšně odeslána na novou adresu v lokální peněžence Bisq. funds.tx.direction.self=Posláno sobě -funds.tx.daoTxFee=Poplatek za těžbu za BSQ tx -funds.tx.reimbursementRequestTxFee=Žádost o vyrovnání -funds.tx.compensationRequestTxFee=Žádost o odměnu funds.tx.dustAttackTx=Přijaté drobné funds.tx.dustAttackTx.popup=Tato transakce odesílá do vaší peněženky velmi malou částku BTC a může se jednat o pokus společností provádějících analýzu blockchainu o špehování vaší peněženky.\n\nPoužijete-li tento transakční výstup ve výdajové transakci, zjistí, že jste pravděpodobně také vlastníkem jiné adresy (sloučení mincí).\n\nKvůli ochraně vašeho soukromí ignoruje peněženka Bisq takové drobné výstupy pro účely utrácení a na obrazovce zůstatku. Můžete nastavit hodnotu "drobnosti", kdy je výstup považován za drobné, v nastavení. @@ -996,9 +982,7 @@ settings.tab.about=O Bisq setting.preferences.general=Základní nastavení setting.preferences.explorer=Bitcoin Explorer -setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=Max. odchylka od tržní ceny -setting.preferences.bsqAverageTrimThreshold=Mezní hodnota pro kurz BSQ setting.preferences.avoidStandbyMode=Vyhněte se pohotovostnímu režimu setting.preferences.autoConfirmXMR=Automatické potvrzení XMR setting.preferences.autoConfirmEnabled=Povoleno @@ -1032,19 +1016,6 @@ setting.preferences.notifyOnPreRelease=Získávat oznámení o beta verzích setting.preferences.resetAllFlags=Zrušit všechny "Nezobrazovat znovu" settings.preferences.languageChange=Chcete-li použít změnu jazyka na všech obrazovkách, musíte restartovat aplikaci. settings.preferences.supportLanguageWarning=V případě sporu mějte na paměti, že zprostředkování je řešeno v {0} a arbitráž v {1}. -setting.preferences.daoOptions=Možnosti DAO -setting.preferences.dao.resyncFromGenesis.label=Obnovit stav DAO z genesis tx -setting.preferences.dao.resyncFromResources.label=Obnovit stav DAO ze zdrojů -setting.preferences.dao.resyncFromResources.popup=Po restartu aplikace budou data správy sítě Bisq znovu načtena z počátečních uzlů a stav konsensu BSQ bude znovu vytvořen z nejnovějších zdrojů. -setting.preferences.dao.resyncFromGenesis.popup=Resynchronizace z genesis transakce může stát značné množství času a prostředků CPU. Opravdu to chcete udělat? Většinou je resynchronizace z nejnovějších zdrojových souborů dostatečná a mnohem rychlejší.\n\nPokud budete pokračovat, po restartu aplikace budou data správy sítě Bisq znovu načtena z počátečních uzlů a stav konsensu BSQ bude znovu vytvořen z genesis transakce. -setting.preferences.dao.resyncFromGenesis.resync=Resynchronizovat z genesis transakce a vypnout -setting.preferences.dao.isDaoFullNode=Spusťte Bisq jako full node DAO -setting.preferences.dao.rpcUser=Uživatelské jméno RPC -setting.preferences.dao.rpcPw=RPC heslo -setting.preferences.dao.blockNotifyPort=Blokovat oznamovací port -setting.preferences.dao.fullNodeInfo=Pro spuštění Bisq jako DAO full nodu musíte mít lokálně spuštěný Bitcoin Core a povoleno RPC. Všechny požadavky jsou dokumentovány v ''{0}''.\n\nPo změně režimu je třeba restartovat. -setting.preferences.dao.fullNodeInfo.ok=Otevřete stránku dokumentace -setting.preferences.dao.fullNodeInfo.cancel=Ne, zůstanu u režimu lite node settings.preferences.editCustomExplorer.headline=Nastavení Průzkumníku settings.preferences.editCustomExplorer.description=Ze seznamu vlevo vyberte průzkumníka definovaného systémem nebo si jej přizpůsobte podle svých vlastních preferencí. settings.preferences.editCustomExplorer.available=Dostupní průzkumníci @@ -1090,7 +1061,7 @@ settings.net.needRestart=Chcete-li použít tuto změnu, musíte restartovat apl settings.net.notKnownYet=Není dosud známo... settings.net.sentData=Odeslaná data: {0}, {1} zprávy, {2} zprávy/sekundu settings.net.receivedData=Přijatá data: {0}, {1} zprávy, {2} zprávy/sekundu -settings.net.chainHeight=Výška blockchainu - Bisq DAO: {0} | Bitcoin Peers: {1} +settings.net.chainHeight=Bitcoin Peers: {1} settings.net.ips=[IP adresa:port | název hostitele:port | onion adresa:port] (oddělené čárkou). Pokud je použit výchozí port (8333), lze port vynechat. settings.net.seedNode=Seed node settings.net.directPeer=Peer uzel (přímý) @@ -1144,14 +1115,10 @@ setting.about.shortcuts.walletDetails=Otevřít okno s podrobností peněženky setting.about.shortcuts.openEmergencyBtcWalletTool=Otevřít nástroj nouzové peněženky pro BTC peněženku -setting.about.shortcuts.openEmergencyBsqWalletTool=Otevřete nástroj nouzové peněženky pro BSQ peněženku - setting.about.shortcuts.showTorLogs=Přepnout úroveň protokolu pro zprávy Tor mezi DEBUG a WARN setting.about.shortcuts.manualPayoutTxWindow=Otevřít okno pro manuální výběr z vkladu 2z2 Multisig tx -setting.about.shortcuts.reRepublishAllGovernanceData=Zveřejnit data správy DAO (návrhy, hlasy) - setting.about.shortcuts.removeStuckTrade=Otevřít vyskakovací okno pro přesun neúspěšného obchodu zpět na kartu otevřených obchodů setting.about.shortcuts.removeStuckTrade.value=Vyberte neúspěšný obchod a stiskněte: {0} @@ -1337,687 +1304,6 @@ account.notifications.noWebCamFound.warning=Nebyla nalezena žádná webkamera.\ account.notifications.priceAlert.warning.highPriceTooLow=Vyšší cena musí být větší než nižší cena. account.notifications.priceAlert.warning.lowerPriceTooHigh=Nižší cena musí být nižší než vyšší cena. - - - -#################################################################### -# DAO -#################################################################### - -dao.tab.factsAndFigures=Fakta & Čísla -dao.tab.bsqWallet=Peněženka BSQ -dao.tab.proposals=Vláda -dao.tab.bonding=Upisování -dao.tab.proofOfBurn=Poplatek za vedení aktiva/Důkaz spálení -dao.tab.monitor=Sledování sítě -dao.tab.news=Novinky - -dao.paidWithBsq=zaplacen BSQ -dao.availableBsqBalance=K dispozici pro výdaje (ověřené + nepotvrzené drobné výstupy) -dao.verifiedBsqBalance=Zůstatek všech ověřených UTXO -dao.unconfirmedChangeBalance=Zůstatek všech nepotvrzených drobných výstupů -dao.unverifiedBsqBalance=Zůstatek všech neověřených transakcí (čeká se na potvrzení bloku) -dao.lockedForVoteBalance=Použito pro hlasování -dao.lockedInBonds=Uzamčeno v úpisech -dao.availableNonBsqBalance=Dostupný zůstatek mimo BSQ (BTC) -dao.reputationBalance=Body zásluhy (nedají se utratit) - -dao.tx.published.success=Vaše transakce byla úspěšně zveřejněna. -dao.proposal.menuItem.make=Podat návrh -dao.proposal.menuItem.browse=Otevřené návrhy -dao.proposal.menuItem.vote=Hlasování o návrzích -dao.proposal.menuItem.result=Výsledky hlasování -dao.cycle.headline=Hlasovací cyklus -dao.cycle.overview.headline=Přehled hlasovacího cyklu -dao.cycle.currentPhase=Aktuální fáze -dao.cycle.currentBlockHeight=Aktuální výška bloku -dao.cycle.proposal=Fáze návrhu -dao.cycle.proposal.next=Další fáze návrhu -dao.cycle.blindVote=Fáze slepého hlasování -dao.cycle.voteReveal=Fáze odhalení hlasování -dao.cycle.voteResult=Výsledek hlasování -dao.cycle.phaseDuration={0} bloky (≈{1}); Bloky {2} - {3} (≈{4} - ≈{5}) -dao.cycle.phaseDurationWithoutBlocks=Blok {0} - {1} (≈{2} - ≈{3}) - -dao.voteReveal.txPublished.headLine=Transakce odhalující hlasování zveřejněna -dao.voteReveal.txPublished=Vaše transakce odhalující hlasování s ID transakce {0} byla úspěšně zveřejněna.\n\nToto se provádí automaticky, pokud jste se zúčastnili hlasování DAO. - -dao.results.cycles.header=Cykly -dao.results.cycles.table.header.cycle=Cyklus -dao.results.cycles.table.header.numProposals=Návrhy -dao.results.cycles.table.header.voteWeight=Váha hlasování -dao.results.cycles.table.header.issuance=Emise - -dao.results.results.table.item.cycle=Cyklus {0} začal: {1} - -dao.results.proposals.header=Návrhy vybraného cyklu -dao.results.proposals.table.header.nameLink=Jméno/odkaz -dao.results.proposals.table.header.details=Detaily -dao.results.proposals.table.header.myVote=Můj hlas -dao.results.proposals.table.header.result=Výsledek hlasování -dao.results.proposals.table.header.threshold=Práh -dao.results.proposals.table.header.quorum=Kvórum - -dao.results.proposals.voting.detail.header=Výsledky hlasování pro vybraný návrh - -dao.results.exceptions=Výjimky výsledku hlasování - -# suppress inspection "UnusedProperty" -dao.param.UNDEFINED=Nedefinováno - -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BSQ=Poplatek tvůrce BSQ -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BSQ=Poplatek příjemce BSQ -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BSQ=Min. poplatek tvůrce BSQ -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BSQ=Min. poplatek příjemce BSQ -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BTC=Poplatek tvůrce BTC -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BTC=Poplatek příjemce BTC -# suppress inspection "UnusedProperty" -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BTC=Min. poplatek tvůrce BTC -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BTC=Min. poplatek příjemce BTC -# suppress inspection "UnusedProperty" - -# suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=Poplatek za návrh v BSQ -# suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=Hlasovací poplatek v BSQ - -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=Žádost o odměnu - min. částka BSQ -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=Žádost o odměnu - max. částka BSQ -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MIN_AMOUNT=Žádost o vyrovnání min. částka BSQ -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MAX_AMOUNT=Žádost o vyrovnání max. částka BSQ - -# suppress inspection "UnusedProperty" -dao.param.QUORUM_GENERIC=Požadované kvórum v BSQ pro obecný návrh -# suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=Požadované kvórum v BSQ pro žádost o odměnu -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REIMBURSEMENT=Požadované kvórum v BSQ pro žádost o vyrovnání -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=Požadované kvórum v BSQ pro změnu parametru -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Požadované kvórum v BSQ pro odebrání aktiva -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=Požadované kvórum v BSQ pro žádost o konfiskaci -# suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=Požadované kvórum v BSQ pro žádost o upsání - -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_GENERIC=Požadovaná prahová hodnota v % pro obecný návrh -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=Požadovaná prahová hodnota v % pro žádost o odměnu -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REIMBURSEMENT=Požadovaná prahová hodnota v % pro žádost o vyrovnání -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=Požadovaná prahová hodnota v % pro změnu parametru -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Požadovaná prahová hodnota v % pro odebrání aktiva -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=Požadovaná prahová hodnota v % pro žádost o konfiskaci -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_ROLE=Požadovaná prahová hodnota v % pro žádost o úpis - -# suppress inspection "UnusedProperty" -dao.param.RECIPIENT_BTC_ADDRESS=BTC adresa příjemce - -# suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=Poplatek za vedení aktiva za den -# suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Min. objem obchodu s aktivy - -# suppress inspection "UnusedProperty" -dao.param.LOCK_TIME_TRADE_PAYOUT=Doba uzamčení pro alternativní výplaty obchodu tx -# suppress inspection "UnusedProperty" -dao.param.ARBITRATOR_FEE=Poplatek rozhodce v BTC - -# suppress inspection "UnusedProperty" -dao.param.MAX_TRADE_LIMIT=Max. obchodní limit v BTC - -# suppress inspection "UnusedProperty" -dao.param.BONDED_ROLE_FACTOR=Jednotkový faktor úpisu v BSQ -# suppress inspection "UnusedProperty" -dao.param.ISSUANCE_LIMIT=Emisní limit cyklu v BSQ - -dao.param.currentValue=Aktuální hodnota: {0} -dao.param.currentAndPastValue=Aktuální hodnota: {0} (Hodnota v okamžiku vytvoření návrhu: {1}) -dao.param.blocks={0} bloků - -dao.results.invalidVotes=V tomto hlasovacím cyklu jsme měli neplatné hlasy. To se může stát, pokud hlas nebyl v síti Bisq dobře distribuován.\n{0} - -# suppress inspection "UnusedProperty" -dao.phase.PHASE_UNDEFINED=Nedefinováno -# suppress inspection "UnusedProperty" -dao.phase.PHASE_PROPOSAL=Fáze návrhu -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK1=Přestávka 1 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=Fáze slepého hlasování -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK2=Přestávka 2 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=Fáze odhalení hlasování -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK3=Přestávka 3 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_RESULT=Výsledná fáze - -dao.results.votes.table.header.stakeAndMerit=Váha hlasování -dao.results.votes.table.header.stake=Vklad -dao.results.votes.table.header.merit=Vyděláno -dao.results.votes.table.header.vote=Hlas - -dao.bond.menuItem.bondedRoles=Role s úpisy -dao.bond.menuItem.reputation=Vaše úpisy -dao.bond.menuItem.bonds=Všechny úpisy - -dao.bond.dashboard.bondsHeadline=Upsané BSQ -dao.bond.dashboard.lockupAmount=Zamknout prostředky -dao.bond.dashboard.unlockingAmount=Uvolnění prostředků (počkejte, dokud neuplyne doba uzamčení) - - -dao.bond.reputation.header=Zamknout úpis pro reputaci -dao.bond.reputation.table.header=Moje reputační úpisy -dao.bond.reputation.amount=Množství BSQ na uzamčení -dao.bond.reputation.time=Čas odemčení v blocích -dao.bond.reputation.salt=Salt -dao.bond.reputation.hash=Hash -dao.bond.reputation.lockupButton=Zamknout -dao.bond.reputation.lockup.headline=Potvrďte uzamčení transakce -dao.bond.reputation.lockup.details=Uzamčená částka: {0}\nČas odemknutí: {1} blok(ů) (≈ {2})\n\nPoplatek za těžbu: {3} ({4} Satoshis/vbyte)\nTransakční vsize: {5} Kb\n\nOpravdu chcete pokračovat? -dao.bond.reputation.unlock.headline=Potvrďte odemknutí transakce -dao.bond.reputation.unlock.details=Odemknout částku: {0}\nČas odemknutí: {1} blok(ů) (≈ {2})\n\nPoplatek za těžbu: {3} ({4} Satoshis/vbyte)\nTransakční vsize: {5} vKb\n\nOpravdu chcete pokračovat? - -dao.bond.allBonds.header=Všechny úpisy - -dao.bond.bondedReputation=Úpis reputace -dao.bond.bondedRoles=Role s úpisy - -dao.bond.details.header=Podrobnosti role -dao.bond.details.role=Role -dao.bond.details.requiredBond=Požadované BSQ úpisy -dao.bond.details.unlockTime=Čas odemčení v blocích -dao.bond.details.link=Odkaz na popis role -dao.bond.details.isSingleton=Může být přijato více držiteli rolí -dao.bond.details.blocks={0} bloků - -dao.bond.table.column.name=Jméno -dao.bond.table.column.link=Odkaz -dao.bond.table.column.bondType=Typ úpisu -dao.bond.table.column.details=Detaily -dao.bond.table.column.lockupTxId=Tx ID úpisu -dao.bond.table.column.bondState=Stav úpisu -dao.bond.table.column.lockTime=Čas odemknutí -dao.bond.table.column.lockupDate=Datum uzamčení - -dao.bond.table.button.lockup=Zamknout -dao.bond.table.button.unlock=Odemknout -dao.bond.table.button.revoke=Odvolat - -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNDEFINED=Nedefinováno -# suppress inspection "UnusedProperty" -dao.bond.bondState.READY_FOR_LOCKUP=Zatím není úpis -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_PENDING=Uzamčení čeká na vyřízení -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_CONFIRMED=Zamčený úpis -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_PENDING=Odemčení čeká na vyřízení -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_CONFIRMED=Odemknutí tx potvrzeno -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKING=Odblokování úpisů -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKED=Odemčený úpis -# suppress inspection "UnusedProperty" -dao.bond.bondState.CONFISCATED=Úpis konfiskován - -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.UNDEFINED=Nedefinováno -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.BONDED_ROLE=Úpis -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.REPUTATION=Úpis reputace - -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.UNDEFINED=Nedefinováno -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=Forum admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=Bisq správce -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork správce -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer správce -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=Správce webu -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=Operátor fóra -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Operátor seed nodu -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Operátor cenového nodu -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Operátor Bitcoinového nodu -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Operátor trhů -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Provozovatel průzkumníka -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Operátor přenosu mobilních oznámení -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Držitel domény -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DNS_ADMIN=DNS admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=Mediátor -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=Rozhodce -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=Majitel dárcovské adresy BTC - -dao.burnBsq.assetFee=Vedení aktiva -dao.burnBsq.menuItem.assetFee=Poplatek za vedení aktiva -dao.burnBsq.menuItem.proofOfBurn=Důkaz spálení -dao.burnBsq.header=Poplatek za vedení aktiva -dao.burnBsq.selectAsset=Vybrat aktivum -dao.burnBsq.fee=Poplatek -dao.burnBsq.trialPeriod=Zkušební doba -dao.burnBsq.payFee=Zaplatit poplatek -dao.burnBsq.allAssets=Všechna aktiva -dao.burnBsq.assets.nameAndCode=Jméno aktiva -dao.burnBsq.assets.state=Stav -dao.burnBsq.assets.tradeVolume=Objem obchodu -dao.burnBsq.assets.lookBackPeriod=Období ověření -dao.burnBsq.assets.trialFee=Poplatek za zkušební období -dao.burnBsq.assets.totalFee=Celkové zaplacené poplatky -dao.burnBsq.assets.days={0} dní -dao.burnBsq.assets.toFewDays=Poplatek za aktivum je příliš nízký. Min. počet dnů pro zkušební období je {0}. - -# suppress inspection "UnusedProperty" -dao.assetState.UNDEFINED=Nedefinováno -# suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=Ve zkušebním období -# suppress inspection "UnusedProperty" -dao.assetState.ACTIVELY_TRADED=Aktivně obchodováno -# suppress inspection "UnusedProperty" -dao.assetState.DE_LISTED=Odstranění ze seznamu kvůli nečinnosti -# suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=Odebráno hlasováním - -dao.proofOfBurn.header=Důkaz spálení -dao.proofOfBurn.amount=Množství -dao.proofOfBurn.preImage=Předloha -dao.proofOfBurn.burn=Spálit -dao.proofOfBurn.allTxs=Všechny transakce dokazující spálení -dao.proofOfBurn.myItems=Moje důkazy spálení -dao.proofOfBurn.date=Datum -dao.proofOfBurn.hash=Hash -dao.proofOfBurn.txs=Transakce -dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Podepište zprávu klíčem z transakce dokazující spálení -dao.proofOfBurn.verify.window.title=Ověřte zprávu pomocí klíče z transakce dokazující spálení -dao.proofOfBurn.copySig=Zkopírujte podpis do schránky -dao.proofOfBurn.sign=Podepsat -dao.proofOfBurn.message=Zpráva -dao.proofOfBurn.sig=Podpis -dao.proofOfBurn.verify=Ověřit -dao.proofOfBurn.verificationResult.ok=Ověření proběhlo úspěšně -dao.proofOfBurn.verificationResult.failed=Ověření se nezdařilo - -# suppress inspection "UnusedProperty" -dao.phase.UNDEFINED=Nedefinováno -# suppress inspection "UnusedProperty" -dao.phase.PROPOSAL=Fáze návrhu -# suppress inspection "UnusedProperty" -dao.phase.BREAK1=Přestávka před fází slepého hlasování -# suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=Fáze slepého hlasování -# suppress inspection "UnusedProperty" -dao.phase.BREAK2=Přestávka před fází odhalení hlasování -# suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=Fáze odhalení hlasování -# suppress inspection "UnusedProperty" -dao.phase.BREAK3=Přestávka před výslednou fází -# suppress inspection "UnusedProperty" -dao.phase.RESULT=Hlasujte ve výsledné fázi - -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.PROPOSAL=Fáze návrhu -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=Slepé hlasování -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.VOTE_REVEAL=Odhalení hlasování -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.RESULT=Výsledek hlasování - -# suppress inspection "UnusedProperty" -dao.proposal.type.UNDEFINED=Nedefinováno -# suppress inspection "UnusedProperty" -dao.proposal.type.COMPENSATION_REQUEST=Žádost o odměnu -# suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=Žádost o vyrovnání -# suppress inspection "UnusedProperty" -dao.proposal.type.BONDED_ROLE=Žádost o úpis -# suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=Návrh na odstranění aktiva -# suppress inspection "UnusedProperty" -dao.proposal.type.CHANGE_PARAM=Návrh na změnu parametru -# suppress inspection "UnusedProperty" -dao.proposal.type.GENERIC=Obecný návrh -# suppress inspection "UnusedProperty" -dao.proposal.type.CONFISCATE_BOND=Žádost o konfiskaci úpisu - -# suppress inspection "UnusedProperty" -dao.proposal.type.short.UNDEFINED=Nedefinováno -# suppress inspection "UnusedProperty" -dao.proposal.type.short.COMPENSATION_REQUEST=Žádost o odměnu -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=Žádost o vyrovnání -# suppress inspection "UnusedProperty" -dao.proposal.type.short.BONDED_ROLE=Úpis -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ASSET=Odstranění altcoinu -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CHANGE_PARAM=Změna parametru -# suppress inspection "UnusedProperty" -dao.proposal.type.short.GENERIC=Obecný návrh -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CONFISCATE_BOND=Konfiskovat úpis - -dao.proposal.details=Detaily návrhu -dao.proposal.selectedProposal=Vybraný návrh -dao.proposal.active.header=Návrhy současného cyklu -dao.proposal.active.remove.confirm=Opravdu chcete tento návrh odebrat?\nJiž zaplacený poplatek za návrh bude ztracen. -dao.proposal.active.remove.doRemove=Ano, odeberte můj návrh -dao.proposal.active.remove.failed=Návrh nelze odebrat. -dao.proposal.myVote.title=Hlasování -dao.proposal.myVote.accept=Přijmout návrh -dao.proposal.myVote.reject=Odmítnout návrh -dao.proposal.myVote.removeMyVote=Ignorovat návrh -dao.proposal.myVote.merit=Hlasovací váha ze získaného BSQ -dao.proposal.myVote.stake=Hlasovací váha z vkladu -dao.proposal.myVote.revealTxId=Hlasování odhalí ID transakce -dao.proposal.myVote.stake.prompt=Max. dostupný vklad pro hlasování: {0} -dao.proposal.votes.header=Nastavte vklad pro hlasování a zveřejněte své hlasy -dao.proposal.myVote.button=Zveřejnit hlasy -dao.proposal.myVote.setStake.description=Po hlasování o všech návrzích musíte nastavit svůj vklad pro hlasování zamknutím BSQ. Čím více BSQ zamknete, tím větší váhu bude mít váš hlas.\n\nBSQ uzamčené pro hlasování bude znovu odemčeno během fáze odhalení hlasování. -dao.proposal.create.selectProposalType=Vyberte typ nabídky -dao.proposal.create.phase.inactive=Počkejte prosím do další fáze návrhu -dao.proposal.create.proposalType=Typ nabídky -dao.proposal.create.new=Vytvořte nový návrh -dao.proposal.create.button=Navrhněte -dao.proposal.create.publish=Zveřejnit návrh -dao.proposal.create.publishing=Probíhá zveřejnění nabídek... -dao.proposal=návrh -dao.proposal.display.type=Typ nabídky -dao.proposal.display.name=Přesné uživatelské jméno na GitHub -dao.proposal.display.link=Odkaz na podrobné informace -dao.proposal.display.link.prompt=Odkaz na návrh -dao.proposal.display.requestedBsq=Požadovaná částka v BSQ -dao.proposal.display.txId=ID transakce návrhu -dao.proposal.display.proposalFee=Poplatek za návrh -dao.proposal.display.myVote=Můj hlas -dao.proposal.display.voteResult=Souhrn výsledku hlasování -dao.proposal.display.bondedRoleComboBox.label=Typ úpisu -dao.proposal.display.requiredBondForRole.label=Požadovaný úpis -dao.proposal.display.option=Možnost - -dao.proposal.table.header.proposalType=Typ nabídky -dao.proposal.table.header.link=Odkaz -dao.proposal.table.header.myVote=Můj hlas -# suppress inspection "UnusedProperty" -dao.proposal.table.header.remove=Odstranit -dao.proposal.table.icon.tooltip.removeProposal=Odebrat můj návrh -dao.proposal.table.icon.tooltip.changeVote=Aktuální hlas: ''{0}''. Změnit hlas na: ''{1}'' - -dao.proposal.display.myVote.accepted=Přijato -dao.proposal.display.myVote.rejected=Odmítnuto -dao.proposal.display.myVote.ignored=Ignorováno -dao.proposal.display.myVote.unCounted=Hlasování nebylo zahrnuto do výsledku -dao.proposal.myVote.summary=Hlasováno: {0}; Hlasovací váha: {1} (získané: {2} + vklad: {3}) {4} -dao.proposal.myVote.invalid=Hlasování bylo neplatné - -dao.proposal.voteResult.success=Přijato -dao.proposal.voteResult.failed=Odmítnuto -dao.proposal.voteResult.summary=Výsledek: {0}; Prahová hodnota: {1} (požadováno > {2}); Kvórum: {3} (požadováno > {4}) - -dao.proposal.display.paramComboBox.label=Vyberte parametr, který chcete změnit -dao.proposal.display.paramValue=Hodnota parametru - -dao.proposal.display.confiscateBondComboBox.label=Zvolte úpis -dao.proposal.display.assetComboBox.label=Aktivum k odstranění - -dao.blindVote=slepý hlas - -dao.blindVote.startPublishing=Publikování transakce se slepým hlasováním ... -dao.blindVote.success=Vaše transakce se slepým hlasováním byla úspěšně zveřejněna.\n\nVezměte prosím na vědomí, že musíte být online ve fázi odhalení hlasování, aby vaše aplikace Bisq mohla zveřejnit transakci odhalení hlasování. Bez transakce odhalení hlasování by byl váš hlas neplatný! - -dao.wallet.menuItem.send=Odeslat -dao.wallet.menuItem.receive=Přijmout -dao.wallet.menuItem.transactions=Transakce - -dao.wallet.dashboard.myBalance=Můj zůstatek v peněžence - -dao.wallet.receive.fundYourWallet=Vaše přijímací BSQ adresa -dao.wallet.receive.bsqAddress=Adresa peněženky BSQ (čerstvá nepoužitá adresa) - -dao.wallet.send.sendFunds=Poslat finanční prostředky -dao.wallet.send.sendBtcFunds=Odeslat prostředky jiné než BSQ (BTC) -dao.wallet.send.amount=Částka v BSQ -dao.wallet.send.btcAmount=Částka v BTC (jiná než BSQ prostředky) -dao.wallet.send.setAmount=Nastavit částku k výběru (minimální částka je {0}) -dao.wallet.send.receiverAddress=BSQ adresa příjemce -dao.wallet.send.receiverBtcAddress=BTC adresa příjemce -dao.wallet.send.setDestinationAddress=Vyplňte svou cílovou adresu -dao.wallet.send.send=Pošlete BSQ prostředky -dao.wallet.send.inputControl=Vybrat vstupy -dao.wallet.send.sendBtc=Pošlete BTC prostředky -dao.wallet.send.sendFunds.headline=Potvrďte žádost o výběr -dao.wallet.send.sendFunds.details=Odesílání: {0}\nNa adresu pro příjem: {1}.\nPožadovaný poplatek za těžbu je: {2} ({3} satoshi/vbyte)\nVelikost transakce: {4} vKb\n\nPříjemce obdrží: {5}\n\nOpravdu chcete tuto částku vybrat? -dao.wallet.chainHeightSynced=Poslední ověřený blok: {0} -dao.wallet.chainHeightSyncing=Čekání na bloky... Ověřeno {0} bloků z {1} -dao.wallet.tx.type=Typ - -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED=Nedefinováno -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED_TX_TYPE=Nerozpoznáno -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNVERIFIED=Neověřená transakce BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.INVALID=Neplatná transakce BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.GENESIS=Genesis transakce -# suppress inspection "UnusedProperty" -dao.tx.type.enum.TRANSFER_BSQ=Převod BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.received.TRANSFER_BSQ=Přijaté BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.sent.TRANSFER_BSQ=Odeslané BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PAY_TRADE_FEE=Obchodní poplatek -# suppress inspection "UnusedProperty" -dao.tx.type.enum.COMPENSATION_REQUEST=Poplatek za žádost o odměnu -# suppress inspection "UnusedProperty" -dao.tx.type.enum.REIMBURSEMENT_REQUEST=Poplatek za žádost o vyrovnání -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROPOSAL=Poplatek za návrh -# suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=Poplatek za slepé hlasování -# suppress inspection "UnusedProperty" -dao.tx.type.enum.VOTE_REVEAL=Odhalení hlasování -# suppress inspection "UnusedProperty" -dao.tx.type.enum.LOCKUP=Zamknout úpis -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNLOCK=Odemknout úpis -# suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=Poplatek za vedení aktiva -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=Důkaz spálení -# suppress inspection "UnusedProperty" -dao.tx.type.enum.IRREGULAR=Nepravidelný - -dao.tx.withdrawnFromWallet=BTC vybrané z peněženky -dao.tx.issuanceFromCompReq=Vydání odměny -dao.tx.issuanceFromCompReq.tooltip=Žádost o odměnu, která vedla k vydání nového BSQ.\nDatum vydání: {0} -dao.tx.issuanceFromReimbursement=Vydání vyrovnání -dao.tx.issuanceFromReimbursement.tooltip=Žádost o vyrovnání, která vedla k vydání nového BSQ.\nDatum vydání: {0} -dao.proposal.create.missingBsqFunds=Pro vytvoření návrhu nemáte dostatečné prostředky BSQ. Pokud máte nepotvrzenou transakci BSQ, musíte počkat na potvrzení na blockchainu, protože BSQ je validováno, pouze pokud je zahrnuto v bloku.\nChybí: {0} - -dao.proposal.create.missingBsqFundsForBond=Pro tuto roli nemáte dostatečné prostředky BSQ. Tento návrh můžete stále zveřejnit, ale pokud bude přijat, budete potřebovat celou částku BSQ potřebnou pro tuto roli.\nChybí: {0} - -dao.proposal.create.missingMinerFeeFunds=Nemáte dostatečné prostředky BTC pro vytvoření transakce návrhu. Všechny transakce BSQ vyžadují poplatek za těžbu v BTC.\nChybí: {0} - -dao.proposal.create.missingIssuanceFunds=Nemáte dostatečné prostředky BTC pro vytvoření transakce návrhu. Všechny transakce BSQ vyžadují poplatek za těžbu v BTC a emisní transakce také vyžadují BTC pro požadovanou částku BSQ ({0} Satoshi/BSQ).\nChybí: {1} - -dao.feeTx.confirm=Potvrďte {0} transakci -dao.feeTx.confirm.details={0} poplatek: {1}\nPoplatek za těžbu: {2} ({3} Satoshi/vbyte)\nTransakční vsize: {4} vKb\n\nOpravdu chcete publikovat transakci {5}? - -dao.feeTx.issuanceProposal.confirm.details={0} poplatek: {1}\nPro vydání BSQ je potřeba BTC: {2} ({3} Satoshi/BSQ)\nPoplatek za těžbu: {4} ({5} Satoshi/vbyte)\nTransakční vsize: {6} vKb\n\nPokud bude vaše žádost schválena, obdržíte požadovanou částku bez poplatku za návrh 2 BSQ.\n\nOpravdu chcete publikovat transakci {7}? - -dao.news.bisqDAO.title=BISQ DAO -dao.news.bisqDAO.description=Stejně jako je burza Bisq decentralizovaná a odolná vůči cenzuře, tak její model řízení - a Bisq DAO a BSQ token jsou nástroje, které to umožňují. -dao.news.bisqDAO.readMoreLink=Dozvědět se více o Bisq DAO - -dao.news.pastContribution.title=PŘISPĚLI JSTE V MINULOSTI? POŽÁDEJTE O BSQ -dao.news.pastContribution.description=Pokud jste přispěli do projektu Bisq, použijte prosím níže uvedenou adresu BSQ a požádejte o účast na distribuci prvních BSQ. -dao.news.pastContribution.yourAddress=Adresa vaší BSQ peněženky -dao.news.pastContribution.requestNow=Požádat hned - -dao.news.DAOOnTestnet.title=SPUSŤTE BISQ DAO NA NAŠEM TESTNETU -dao.news.DAOOnTestnet.description=Síť Bisq DAO ještě nebyla spuštěn, ale o Bisq DAO se můžete dozvědět jeho spuštěním na našem testnetu. -dao.news.DAOOnTestnet.firstSection.title=1. Přepněte do režimu DAO Testnet -dao.news.DAOOnTestnet.firstSection.content=Na obrazovce Nastavení přepněte na DAO Testnet. -dao.news.DAOOnTestnet.secondSection.title=2. Získejte některé BSQ -dao.news.DAOOnTestnet.secondSection.content=Vyžádejte si BSQ na Slacku nebo kupte BSQ na Bisq. -dao.news.DAOOnTestnet.thirdSection.title=3. Zúčastněte se hlasovacího cyklu -dao.news.DAOOnTestnet.thirdSection.content=Předkládání návrhů a hlasování o návrzích na změnu různých aspektů Bisq. -dao.news.DAOOnTestnet.fourthSection.title=4. Prozkoumejte BSQ Blok Explorer -dao.news.DAOOnTestnet.fourthSection.content=Protože BSQ je jen bitcoin, můžete vidět BSQ transakce na našem bitcoinovém blok exploreru. -dao.news.DAOOnTestnet.readMoreLink=Přečtěte si celou dokumentaci - -dao.monitor.daoState=Stav DAO -dao.monitor.proposals=Stav návrhů -dao.monitor.blindVotes=Stav slepých hlasů - -dao.monitor.table.peers=Peer uzly -dao.monitor.table.conflicts=Konflikty -dao.monitor.state=Stav -dao.monitor.requestAlHashes=Vyžádat si všechny hashe -dao.monitor.resync=Znovu synchronizovat stav DAO -dao.monitor.table.header.cycleBlockHeight=Cyklus / Výška bloku -dao.monitor.table.cycleBlockHeight=Cyklus {0} / blok {1} -dao.monitor.table.seedPeers=Seed node: {0} - -dao.monitor.daoState.headline=Stav DAO -dao.monitor.daoState.table.headline=Řetězec hashů stavu DAO -dao.monitor.daoState.table.blockHeight=Výška bloku -dao.monitor.daoState.table.hash=Hash stavu DAO -dao.monitor.daoState.table.prev=Předchozí hash -dao.monitor.daoState.conflictTable.headline=Hashe stavu DAO od partnerů v konfliktu -dao.monitor.daoState.utxoConflicts=Konflikt UTXO -dao.monitor.daoState.utxoConflicts.blockHeight=Výška bloku: {0} -dao.monitor.daoState.utxoConflicts.sumUtxo=Součet všech UTXO: {0} BSQ -dao.monitor.daoState.utxoConflicts.sumBsq=Součet všech BSQ: {0} BSQ -dao.monitor.daoState.checkpoint.popup=Stav DAO není synchronizován se sítí. Po restartu se stav DAO znovu synchronizuje. - -dao.monitor.proposal.headline=Stav návrhů -dao.monitor.proposal.table.headline=Řetězec hashů stavu návrhu -dao.monitor.proposal.conflictTable.headline=Navrhované stavy hashů od partnerů v konfliktu - -dao.monitor.proposal.table.hash=Hash stavu návrhu -dao.monitor.proposal.table.prev=Předchozí hash -dao.monitor.proposal.table.numProposals=Počet návrhů - -dao.monitor.isInConflictWithSeedNode=Vaše lokální data nesouhlasí s alespoň jedním seed nodem. Synchronizujte znovu stav DAO. -dao.monitor.isInConflictWithNonSeedNode=Jeden z vašich peerů není v konsenzu se sítí, ale váš node je synchronizován se seed nody. -dao.monitor.daoStateInSync=Váš lokální node je v konsenzu se sítí - -dao.monitor.blindVote.headline=Stav slepých hlasů -dao.monitor.blindVote.table.headline=Řetězec hashů stavu slepého hlasování -dao.monitor.blindVote.conflictTable.headline=Hashe stavu slepého hlasování od partnerů v konfliktu -dao.monitor.blindVote.table.hash=Hash stavu slepého hlasování -dao.monitor.blindVote.table.prev=Předchozí hash -dao.monitor.blindVote.table.numBlindVotes=Počet slepých hlasování - -dao.factsAndFigures.menuItem.supply=Nabídka BSQ -dao.factsAndFigures.menuItem.transactions=Transakce BSQ - -dao.factsAndFigures.dashboard.avgPrice90=Průměrná obchodní cena BSQ/BTC za 90 dní -dao.factsAndFigures.dashboard.avgPrice30=Průměrná obchodní cena BSQ/BTC za 30 dní -dao.factsAndFigures.dashboard.avgUSDPrice90=90denní objemově vážená průměrná cena BSQ/USD -dao.factsAndFigures.dashboard.avgUSDPrice30=30denní objemově vážená průměrná cena BSQ/USD -dao.factsAndFigures.dashboard.marketCap=Tržní kapitalizace (na základě průměrné ceny BSQ/USD za posledních 30 dní) -dao.factsAndFigures.dashboard.availableAmount=Celkem k dispozici BSQ -dao.factsAndFigures.dashboard.volumeUsd=Celkový objem obchodů v USD -dao.factsAndFigures.dashboard.volumeBtc=Celkový objem obchodů v BTC -dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection=Průměrný kurz BSQ/USD obchodů uzavřených ve zvoleném časovém intervalu -dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection=Průměrný kurz BSQ/BTC obchodů uzavřených ve zvoleném časovém intervalu - -dao.factsAndFigures.supply.issuedVsBurnt=Vydaných BSQ vs. Spálených BSQ - -dao.factsAndFigures.supply.issued=Vydáno BSQ -dao.factsAndFigures.supply.compReq=Žádosti o odměnu -dao.factsAndFigures.supply.reimbursement=Žádosti o vyrovnání -dao.factsAndFigures.supply.genesisIssueAmount=BSQ vydané při první (genesis) transakci -dao.factsAndFigures.supply.compRequestIssueAmount=BSQ vydáno na žádosti o odměnu -dao.factsAndFigures.supply.reimbursementAmount=BSQ vydáno na žádosti o vyrovnání -dao.factsAndFigures.supply.totalIssued=Celkem vydáno BSQ -dao.factsAndFigures.supply.totalBurned=Celkem spáleno BSQ -dao.factsAndFigures.supply.chart.tradeFee.toolTip={0}\n{1} -dao.factsAndFigures.supply.burnt=Spálených BSQ - -dao.factsAndFigures.supply.priceChat=BSQ cena -dao.factsAndFigures.supply.volumeChat=Objem obchodu -dao.factsAndFigures.supply.tradeVolumeInUsd=Objem obchodů v USD -dao.factsAndFigures.supply.tradeVolumeInBtc=Objem obchodů v BTC -dao.factsAndFigures.supply.bsqUsdPrice=kurz BSQ/USD -dao.factsAndFigures.supply.bsqBtcPrice=kurz BSQ/BTC -dao.factsAndFigures.supply.btcUsdPrice=kurz BTC/USD - -dao.factsAndFigures.supply.locked=Globální stav uzamčených BSQ -dao.factsAndFigures.supply.totalLockedUpAmount=Zamčeno v úpisech -dao.factsAndFigures.supply.totalUnlockingAmount=Odemykání BSQ z úpisů -dao.factsAndFigures.supply.totalUnlockedAmount=Odemčené BSQ z úpisů -dao.factsAndFigures.supply.totalConfiscatedAmount=Konfiskované BSQ z úpisů -dao.factsAndFigures.supply.proofOfBurn=Důkaz spálení -dao.factsAndFigures.supply.bsqTradeFee=BSQ poplatky -dao.factsAndFigures.supply.btcTradeFee=BTC obchodní poplatky - -dao.factsAndFigures.transactions.genesis=Genesis transakce -dao.factsAndFigures.transactions.genesisBlockHeight=Výška počátečního (genesis) bloku -dao.factsAndFigures.transactions.genesisTxId=ID genesis transakce -dao.factsAndFigures.transactions.txDetails=Statistiky transakcí BSQ -dao.factsAndFigures.transactions.allTx=Počet všech transakcí BSQ -dao.factsAndFigures.transactions.utxo=Počet všech nevyčerpaných transakčních výstupů -dao.factsAndFigures.transactions.compensationIssuanceTx=Počet všech transakcí s vydáním odměn -dao.factsAndFigures.transactions.reimbursementIssuanceTx=Počet všech transakcí s vydáním vyrovnání -dao.factsAndFigures.transactions.burntTx=Počet všech poplatků platebních transakcí -dao.factsAndFigures.transactions.invalidTx=Počet všech neplatných transakcí -dao.factsAndFigures.transactions.irregularTx=Počet všech nepravidelných transakcí - - - #################################################################### # Windows #################################################################### @@ -2120,8 +1406,6 @@ disputeSummaryWindow.close.noPayout.text=Chcete zavřít bez výplaty? emptyWalletWindow.headline={0} nouzový nástroj peněženky emptyWalletWindow.info=Použijte jej pouze v naléhavých případech, pokud nemůžete získat přístup k vašim prostředkům z uživatelského rozhraní.\n\nUpozorňujeme, že při použití tohoto nástroje budou všechny otevřené nabídky automaticky uzavřeny.\n\nPřed použitím tohoto nástroje si prosím zálohujte datový adresář. Můžete to udělat na obrazovce \"Účet/Záloha\".\n\nNahlaste nám svůj problém a nahlaste zprávu o chybě na GitHubu nebo na fóru Bisq, abychom mohli prozkoumat, co způsobilo problém. emptyWalletWindow.balance=Váš zůstatek v peněžence -emptyWalletWindow.bsq.btcBalance=Zůstatek satoshi jiných než BSQ - emptyWalletWindow.address=Vaše cílová adresa emptyWalletWindow.button=Pošlete všechny prostředky emptyWalletWindow.openOffers.warn=Máte otevřené nabídky, které budou odstraněny, pokud vyprázdníte peněženku.\nOpravdu chcete vyprázdnit peněženku? @@ -2146,10 +1430,8 @@ filterWindow.seedNode=Filtrované seed nody (onion adresy oddělené čárkami) filterWindow.priceRelayNode=Filtrované cenové relay nody (onion adresy oddělené čárkami) filterWindow.btcNode=Filtrované Bitcoinové nody (adresy+porty oddělené čárkami) filterWindow.preventPublicBtcNetwork=Zabraňte použití veřejné bitcoinové sítě -filterWindow.disableDao=Zakázat DAO filterWindow.disableAutoConf=Zakázat automatické potvrzení filterWindow.autoConfExplorers=Filtrované průzkumníky s automatickým potvrzením (adresy oddělené čárkami) -filterWindow.disableDaoBelowVersion=Min. verze nutná pro DAO filterWindow.disableTradeBelowVersion=Min. verze nutná pro obchodování filterWindow.add=Přidat filtr filterWindow.remove=Zrušit filtr @@ -2223,7 +1505,6 @@ tradeDetailsWindow.detailData=Detailní data txDetailsWindow.headline=Detaily transakce txDetailsWindow.btc.note=Poslali jste BTC. -txDetailsWindow.bsq.note=Poslali jste BSQ. BSQ je tzv. obarvený bitcoin, takže tato transakce bude viditelná v BSQ exploreru až poté, co bude potvrzena zařazením do bitcoin bloku. txDetailsWindow.sentTo=Odesláno na txDetailsWindow.txId=TxId @@ -2235,9 +1516,6 @@ closedTradesSummaryWindow.totalMinerFee.title=Suma poplatků za těžbu closedTradesSummaryWindow.totalMinerFee.value={0} ({1} z celkového objemu obchodů) closedTradesSummaryWindow.totalTradeFeeInBtc.title=Suma obchodních poplatků v BTC closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} z celkového objemu obchodů) -closedTradesSummaryWindow.totalTradeFeeInBsq.title=Suma obchodních poplatků v BSQ -closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} z celkového objemu obchodů) - walletPasswordWindow.headline=Pro odemknutí zadejte heslo torNetworkSettingWindow.header=Nastavení sítě Tor @@ -2317,12 +1595,6 @@ popup.warning.tooLargePercentageValue=Nelze nastavit procento 100% nebo větší popup.warning.examplePercentageValue=Zadejte procento jako číslo \"5.4\" pro 5.4% popup.warning.noPriceFeedAvailable=Pro tuto měnu není k dispozici žádný zdroj cen. Nelze použít procentuální cenu.\nVyberte pevnou cenu. popup.warning.sendMsgFailed=Odeslání zprávy vašemu obchodnímu partnerovi se nezdařilo.\nZkuste to prosím znovu a pokud to i nadále selže, nahlaste chybu. -popup.warning.insufficientBtcFundsForBsqTx=Nemáte dostatečné prostředky BTC k zaplacení poplatku za těžbu za tuto transakci.\nFinancujte prosím svou BTC peněženku.\nChybějící prostředky: {0} -popup.warning.bsqChangeBelowDustException=Tato transakce vytváří výstup BSQ, který je pod limitem drobných (5,46 BSQ) a byl by bitcoinovou sítí odmítnut.\n\nMusíte buď poslat vyšší částku, abyste se vyhnuli drobným (např. přidáním drobné částky do vaší odeslané částky), nebo přidejte do své peněženky další prostředky BSQ, abyste se vyhnuli generování drobných.\n\nVýstup drobných {0}. -popup.warning.btcChangeBelowDustException=Tato transakce vytváří výstup, který je pod limitem drobných (546 satoshi) a byl by bitcoinovou sítí odmítnut.\n\nMusíte přidat vyšší množství drobných k vašemu odesílanému množství, abyste se vyhnuli vytváření drobných.\n\nVýstup drobných je {0}. - -popup.warning.insufficientBsqFundsForBtcFeePayment=K provedení této transakce budete potřebovat více BSQ - posledních 5,46 BSQ ve vaší peněžence nelze použít k placení obchodních poplatků kvůli omezení prachových mincí v bitcoinovém protokolu.\n\nMůžete si buď koupit více BSQ, nebo zaplatit obchodní poplatky pomocí BTC.\n\nChybějící prostředky: {0} -popup.warning.noBsqFundsForBtcFeePayment=Peněženka BSQ nemá dostatečné prostředky na zaplacení obchodního poplatku v BSQ. popup.warning.messageTooLong=Vaše zpráva překračuje max. povolená velikost. Zašlete jej prosím v několika částech nebo ji nahrajte do služby, jako je https://pastebin.com. popup.warning.lockedUpFunds=Zamkli jste finanční prostředky z neúspěšného obchodu.\nUzamčený zůstatek: {0}\nVkladová tx adresa: {1}\nObchodní ID: {2}.\n\nOtevřete prosím úkol pro podporu výběrem obchodu na obrazovce otevřených obchodů a stisknutím \"alt + o\" nebo \"option + o\"." @@ -2336,8 +1608,6 @@ popup.warning.nodeBanned=Jeden z {0} uzlů byl zabanován. popup.warning.priceRelay=cenové relé popup.warning.seed=seed popup.warning.mandatoryUpdate.trading=Aktualizujte prosím na nejnovější verzi Bisq. Byla vydána povinná aktualizace, která zakazuje obchodování se starými verzemi. Další informace naleznete na fóru Bisq. -popup.warning.mandatoryUpdate.dao=Aktualizujte prosím na nejnovější verzi Bisq. Byla vydána povinná aktualizace, která zakazuje Bisq DAO a BSQ pro staré verze. Další informace naleznete na fóru Bisq. -popup.warning.disable.dao=Bisq DAO a BSQ jsou dočasně deaktivovány. Další informace naleznete na fóru Bisq. popup.warning.noFilter="We did not receive a filter object from the seed nodes." Toto je neočekávaná situace. Prosím upozorněte vývojáře Bisq. popup.warning.burnBTC=Tato transakce není možná, protože poplatky za těžbu {0} by přesáhly částku převodu {1}. Počkejte prosím, dokud nebudou poplatky za těžbu opět nízké nebo dokud nenahromadíte více BTC k převodu. @@ -2356,7 +1626,6 @@ popup.info.cashDepositInfo.confirm=Potvrzuji, že mohu provést vklad popup.info.shutDownWithOpenOffers=Bisq se vypíná, ale existují otevřené nabídky.\n\nTyto nabídky nebudou dostupné v síti P2P, pokud bude Bisq vypnutý, ale budou znovu publikovány do sítě P2P při příštím spuštění Bisq.\n\nChcete-li zachovat své nabídky online, udržujte Bisq spuštěný a ujistěte se, že tento počítač zůstává online (tj. Ujistěte se, že nepřejde do pohotovostního režimu...pohotovostní režim monitoru není problém). popup.info.qubesOSSetupInfo=Zdá se, že používáte Bisq na Qubes OS.\n\nUjistěte se, že je vaše Bisq qube nastaveno podle našeho průvodce nastavením na [HYPERLINK:https://bisq.wiki/Running_Bisq_on_Qubes]. popup.warn.downGradePrevention=Downgrade z verze {0} na verzi {1} není podporován. Použijte prosím nejnovější verzi Bisq. -popup.warn.daoRequiresRestart=Došlo k problému při synchronizaci stavu DAO. Pro nápravu prosím restartujte aplikaci. popup.privateNotification.headline=Důležité soukromé oznámení! @@ -2528,7 +1797,6 @@ navigation.settings.preferences=\"Nastavení/Preference\" # suppress inspection "UnusedProperty" navigation.funds.transactions=\"Prostředky/Transakce\" navigation.support=\"Podpora\" -navigation.dao.wallet.receive="DAO/Peněženka BSQ/Přijmout" #################################################################### @@ -2558,12 +1826,6 @@ XMR_MAINNET=Monero Mainnet XMR_TESTNET=Monero Testnet # suppress inspection "UnusedProperty" XMR_STAGENET=Monero Stagenet -# suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet (zastaralé) -# suppress inspection "UnusedProperty" -BTC_DAO_BETANET=Bisq DAO Betanet (Bitcoin Mainnet) -# suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=Bitcoin DAO Regtest time.year=Rok time.month=Měsíc @@ -2910,9 +2172,7 @@ validation.accountNrChars=Číslo účtu musí obsahovat {0} znaků. validation.btc.invalidAddress=Adresa není správná. Zkontrolujte formát adresy. validation.integerOnly=Zadejte pouze celá čísla. validation.inputError=Váš vstup způsobil chybu:\n{0} -validation.bsq.insufficientBalance=Váš dostupný zůstatek je {0}. validation.btc.exceedsMaxTradeLimit=Váš obchodní limit je {0}. -validation.bsq.amountBelowMinAmount=Min. částka je {0} validation.nationalAccountId={0} se musí skládat z {1} čísel. #new @@ -2934,7 +2194,6 @@ validation.bic.invalidLocationCode=BIC obsahuje neplatný location kód validation.bic.invalidBranchCode=BIC obsahuje neplatný kód pobočky validation.bic.sepaRevolutBic=Účty Revolut Sepa nejsou podporovány. validation.btc.invalidFormat=Neplatný formát bitcoinové adresy. -validation.bsq.invalidFormat=Neplatný formát BSQ adresy. validation.email.invalidAddress=Neplatná adresa validation.iban.invalidCountryCode=Kód země je neplatný validation.iban.checkSumNotNumeric=Kontrolní součet musí být číselný diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 8bc8c23df1..eccaf11ec6 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -192,7 +192,6 @@ shared.tradeWalletBalance=Guthaben der Handels-Wallet shared.makerTxFee=Ersteller: {0} shared.takerTxFee=Abnehmer: {0} shared.iConfirm=Ich bestätige -shared.tradingFeeInBsqInfo=≈ {0} shared.openURL=Öffne {0} shared.fiat=Fiat shared.crypto=Crypto @@ -204,9 +203,6 @@ shared.actions=Aktionen shared.buyerUpperCase=Käufer shared.sellerUpperCase=Verkäufer shared.new=NEU -shared.blindVoteTxId=Geheime Wahl-Transaktion ID -shared.proposal=Vorschlag -shared.votes=Stimmen shared.learnMore=Mehr erfahren shared.dismiss=Verwerfen shared.selectedArbitrator=Gewählte Vermittler @@ -240,7 +236,6 @@ mainView.menu.funds=Gelder mainView.menu.support=Support mainView.menu.settings=Einstellungen mainView.menu.account=Konto -mainView.menu.dao=DAO mainView.marketPriceWithProvider.label=Marktpreis von {0} mainView.marketPrice.bisqInternalPrice=Preis des letzten Bisq-Handels @@ -257,13 +252,11 @@ mainView.footer.localhostBitcoinNode=(localhost) 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=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} -mainView.footer.daoFullNode=DAO Full Node mainView.bootstrapState.connectionToTorNetwork=(1/4) Verbinde mit Tor-Netzwerk... mainView.bootstrapState.torNodeCreated=(2/4) Tor-Knoten erstellt @@ -435,7 +428,6 @@ createOffer.fundsBox.networkFee=Mining-Gebühr createOffer.fundsBox.placeOfferSpinnerInfo=Das Angebot wird veröffentlicht ... createOffer.fundsBox.paymentLabel=Bisq-Handel mit der ID {0} createOffer.fundsBox.fundsStructure=({0} Kaution, {1} Handelsgebühr, {2} Mining-Gebühr) -createOffer.fundsBox.fundsStructure.BSQ=({0} Kaution, {1} Mining-Gebühr) + {2} Handelsgebühr createOffer.success.headline=Ihr Angebot wurde veröffentlicht createOffer.success.info=Sie können Ihre offenen Angebote unter \"Portfolio/Meine offenen Angebote\" verwalten. createOffer.info.sellAtMarketPrice=Sie verkaufen immer zum aktuellen Marktpreis, da ihr Angebot ständig aktualisiert wird. @@ -636,7 +628,6 @@ portfolio.pending.step2_buyer.amountToTransfer=Zu überweisender Betrag portfolio.pending.step2_buyer.sellersAddress={0}-Adresse des Verkäufers portfolio.pending.step2_buyer.buyerAccount=Ihr zu verwendendes Zahlungskonto portfolio.pending.step2_buyer.paymentStarted=Zahlung begonnen -portfolio.pending.step2_buyer.fillInBsqWallet=Pay from BSQ wallet portfolio.pending.step2_buyer.warn=Sie haben Ihre {0} Zahlung noch nicht getätigt!\nBeachten Sie bitte, dass der Handel bis {1} abgeschlossen werden muss. portfolio.pending.step2_buyer.openForDispute=Sie haben Ihre Zahlung noch nicht abgeschlossen!\nDie maximale Frist für den Handel ist abgelaufen, bitte wenden Sie sich an den Vermittler, um Hilfe zu erhalten. portfolio.pending.step2_buyer.paperReceipt.headline=Haben Sie die Quittung an den BTC-Verkäufer gesendet? @@ -882,7 +873,6 @@ funds.locked.locked=Für den Handel mit dieser ID in MultiSig eingesperrt: {0} funds.tx.direction.sentTo=Gesendet nach: funds.tx.direction.receivedWith=Erhalten mit: funds.tx.direction.genesisTx=Aus Ursprungs-Tx: -funds.tx.txFeePaymentForBsqTx=Mining-Gebühr für BSQ-Tx funds.tx.createOfferFee=Ersteller- und Tx-Gebühr: {0} funds.tx.takeOfferFee=Abnehmer- und Tx-Gebühr: {0} funds.tx.multiSigDeposit=MultiSig-Kaution: {0} @@ -896,15 +886,11 @@ funds.tx.unknown=Unbekannter Grund: {0} funds.tx.noFundsFromDispute=Keine Rückzahlung vom Konflikt funds.tx.receivedFunds=Gelder erhalten funds.tx.withdrawnFromWallet=Von Wallet abgehoben -funds.tx.withdrawnFromBSQWallet=BTC von BSQ Wallet abgehoben funds.tx.memo=Notiz 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 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. @@ -996,9 +982,7 @@ settings.tab.about=Über setting.preferences.general=Allgemeine Voreinstellungen setting.preferences.explorer=Bitcoin Explorer -setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=Max. Abweichung vom Marktpreis -setting.preferences.bsqAverageTrimThreshold=Auslöser-Schwellenwert für BSQ-Rate setting.preferences.avoidStandbyMode=Standby Modus verhindern setting.preferences.autoConfirmXMR=XMR automatische Bestätigung setting.preferences.autoConfirmEnabled=Aktiviert @@ -1032,19 +1016,6 @@ setting.preferences.notifyOnPreRelease=Pre-Release Benachrichtungen erhalten setting.preferences.resetAllFlags=Alle \"Nicht erneut anzeigen\"-Häkchen zurücksetzen settings.preferences.languageChange=Um den Sprachwechsel auf alle Bildschirme anzuwenden ist ein Neustart nötig. settings.preferences.supportLanguageWarning=Wenn es zu einem Streitfall kommen sollte, beachten Sie bitte, dass die Mediation in {0} und das Vermittlungsverfahren in {1} geregelt wird. -setting.preferences.daoOptions=DAO-Optionen -setting.preferences.dao.resyncFromGenesis.label=DAO-Zustand von der Genesis-Tx wiederherstellen -setting.preferences.dao.resyncFromResources.label=DAO-Zustand aus Ressourcen wiederherstellen -setting.preferences.dao.resyncFromResources.popup=Nach einem Neustart der Anwendung werden die Bisq-Netzwerk-Governance-Daten von den Seed-Nodes neu geladen, und der BSQ-Konsensstatus wird aus den neuesten Ressourcendateien neu aufgebaut. -setting.preferences.dao.resyncFromGenesis.popup=Eine Resync von der Genesis-Transaktion kann erhebliche Zeit und CPU-Ressourcen in Anspruch nehmen. Sind Sie sicher, dass Sie das tun wollen? Meistens ist ein Resync von den neuesten Ressourcendateien ausreichend und viel schneller.\n\nWenn Sie fortfahren, werden nach einem Neustart der Anwendung die Bisq-Netzwerk-Governance-Daten von den Seed-Nodes neu geladen und der BSQ-Konsensstatus wird aus der Genesis-Transaktion neu aufgebaut. -setting.preferences.dao.resyncFromGenesis.resync=Resync von Genesis ausführen und beenden -setting.preferences.dao.isDaoFullNode=Bisq als DAO Full Node betreiben -setting.preferences.dao.rpcUser=RPC Benutzername -setting.preferences.dao.rpcPw=RPC Passwort -setting.preferences.dao.blockNotifyPort=Blockbenachrichtigung-Port -setting.preferences.dao.fullNodeInfo=Um Bisq als DAO Fullnode laufen zu lassen, müssen Sie Bitcoin Core lokal laufen und RPC eingeschaltet haben. Alle Bedingungen sind in "{0}" dokumentiert.\n\nNach dem Ändern des Modus müssen Sie neu starten. -setting.preferences.dao.fullNodeInfo.ok=Dokumentationsseite öffnen -setting.preferences.dao.fullNodeInfo.cancel=Nein, ich möchte weiterhin den Lite Node Modus verwenden settings.preferences.editCustomExplorer.headline=Explorer-Einstellungen settings.preferences.editCustomExplorer.description=Wählen Sie auf der linken Liste einen Explorer des Systems aus, und/oder passen Sie ihn an Ihre Vorlieben an. settings.preferences.editCustomExplorer.available=Verfügbare Explorer @@ -1090,7 +1061,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 DAO chain height: {0} | Bitcoin Peers chain height: {1} +settings.net.chainHeight=Bitcoin Peers chain height: {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) @@ -1144,14 +1115,10 @@ setting.about.shortcuts.walletDetails=Öffnen Sie das Fenster für Wallet-Detail setting.about.shortcuts.openEmergencyBtcWalletTool=Öffnen Sie das Notfallwerkzeug für die BTC-Wallet -setting.about.shortcuts.openEmergencyBsqWalletTool=Öffnen Sie das Notfallwerkzeug für die BSQ-Wallet - setting.about.shortcuts.showTorLogs=Umschalten des Log-Levels für Tor-Meldungen zwischen DEBUG und WARN setting.about.shortcuts.manualPayoutTxWindow=Fenster öffnen für manuelle Auszahlung einer 2von2 Multisig Einzahlung tx -setting.about.shortcuts.reRepublishAllGovernanceData=Neu veröffentlichen von DAO Governance-Daten (Vorschläge, Abstimmungen) - setting.about.shortcuts.removeStuckTrade=Popup öffnen um fehlgeschlagenen Trade wieder zu den offenen Trades zu verschieben setting.about.shortcuts.removeStuckTrade.value=Fehlgeschlagenen Trade auswählen und drücken: {0} @@ -1337,687 +1304,6 @@ account.notifications.noWebCamFound.warning=Keine Webcam gefunden.\n\nBitte nutz account.notifications.priceAlert.warning.highPriceTooLow=Der hohe Preis muss größer als der tiefe Preis sein. account.notifications.priceAlert.warning.lowerPriceTooHigh=Der tiefe Preis muss kleiner als der hohe Preis sein. - - - -#################################################################### -# DAO -#################################################################### - -dao.tab.factsAndFigures=Zahlen, Daten & Fakten -dao.tab.bsqWallet=BSQ-Wallet -dao.tab.proposals=Führung der DAO -dao.tab.bonding=Kopplung -dao.tab.proofOfBurn=Asset-Listungsgebühr/Nachweis der Verbrennung -dao.tab.monitor=Netzwerkmonitor -dao.tab.news=Neuigkeiten - -dao.paidWithBsq=bezahlt mit BSQ -dao.availableBsqBalance=Zum Ausgeben verfügbar (bestätigte und unbestätigte Restbeträge) -dao.verifiedBsqBalance=Guthaben aller verifizierter UTXOs -dao.unconfirmedChangeBalance=Guthaben aller unbestätigter Restbeträge -dao.unverifiedBsqBalance=Guthaben aller unbestätigten Transaktionen (warte auf Block-Bestätigung) -dao.lockedForVoteBalance=Zum wählen genutzt -dao.lockedInBonds=In Kopplungen gesperrt -dao.availableNonBsqBalance=Verfügbares nicht-BSQ-Guthaben (BTC) -dao.reputationBalance=Merit-Wert (nicht ausgabefähig) - -dao.tx.published.success=Ihre Transaktion wurde erfolgreich veröffentlicht. -dao.proposal.menuItem.make=Antrag erstellen -dao.proposal.menuItem.browse=Offene Vorschläge -dao.proposal.menuItem.vote=Für Vorschläge stimmen -dao.proposal.menuItem.result=Wahlergebnisse -dao.cycle.headline=Wahlzyklus -dao.cycle.overview.headline=Wahlzyklus-Übersicht -dao.cycle.currentPhase=Aktuelle Phase -dao.cycle.currentBlockHeight=Momentane Blockhöhe -dao.cycle.proposal=Vorschlag-Phase -dao.cycle.proposal.next=Nächste Vorschlag-Phase -dao.cycle.blindVote=Geheime Wahl-Phase -dao.cycle.voteReveal=Wahloffenbarung-Phase -dao.cycle.voteResult=Wahlergebnis -dao.cycle.phaseDuration={0} Blöcke (≈{1}); Block {2} - {3} (≈{4} - ≈{5}) -dao.cycle.phaseDurationWithoutBlocks=Block {0} - {1} (≈{2} - ≈{3}) - -dao.voteReveal.txPublished.headLine=Wahloffenbarung Transaktion veröffentlicht -dao.voteReveal.txPublished=Ihre Wahloffenbarung-Transaktion mit ID {0} wurde erfolgreich veröffentlicht.\n\nDies geschieht automatisch durch die Software, wenn Sie an einer DAO Wahl teilgenommen haben. - -dao.results.cycles.header=Zyklen -dao.results.cycles.table.header.cycle=Zyklus -dao.results.cycles.table.header.numProposals=Vorschläge -dao.results.cycles.table.header.voteWeight=Stimm-Gewicht -dao.results.cycles.table.header.issuance=Ausgabe - -dao.results.results.table.item.cycle=Zyklus {0} gestartet: {1} - -dao.results.proposals.header=Vorschläge des gewählten Zyklus -dao.results.proposals.table.header.nameLink=Name/Link -dao.results.proposals.table.header.details=Details -dao.results.proposals.table.header.myVote=Meine Stimme -dao.results.proposals.table.header.result=Wahlergebnis -dao.results.proposals.table.header.threshold=Schwellenwert -dao.results.proposals.table.header.quorum=Quorum - -dao.results.proposals.voting.detail.header=Wahlergebnisse für gewählten Vorschlag - -dao.results.exceptions=Wahlergebnisseausnahme(n) - -# suppress inspection "UnusedProperty" -dao.param.UNDEFINED=Undefiniert - -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BSQ=BSQ Erstellungsgebühr -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BSQ=BSQ Abnehmergebühr -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BSQ=Min. BSQ Erstellergebühr -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BSQ=Min. BSQ Abnehmergebühr -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BTC=BTC Erstellungsgebühr -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BTC=BTC Abnehmergebühr -# suppress inspection "UnusedProperty" -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BTC=Min. BTC Erstellergebühr -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BTC=Min. BTC Abnehmergebühr -# suppress inspection "UnusedProperty" - -# suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=Gebühr für Antrag -# suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=Gebühr für Wahl in BSQ - -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=Min. BSQ-Betrag für Entlohnungsantrag -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=Max. BSQ-Betrag für Entlohnungsantrag -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MIN_AMOUNT=Min. BSQ Betrag für Rückerstattungsantrag -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MAX_AMOUNT=Max. BSQ Betrag für Rückerstattungsantrag - -# suppress inspection "UnusedProperty" -dao.param.QUORUM_GENERIC=Benötigtes Quorum in BSQ für allgemeinen Antrag -# suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=Benötigtes Quorum in BSQ für Entlohnungsantrag -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REIMBURSEMENT=Benötigtes Quorum für Rückerstattungsantrag -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=Benötigtes Quorum in BSQ um einen Parameter zu ändern -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Benötigtes Quorum in BSQ um ein Gut zu entfernen -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=Benötigtes Quorum für Konfiszierungsantrag -# suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=Benötigtes Quorum in BSQ für Antrag einer Rolle mit Pfand - -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_GENERIC=Benötigter Schwellwert in % für allgemeinen Antrag -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=Benötigter Schwellwert in % für Entlohnungsantrag -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REIMBURSEMENT=Benötigter Schwellwert in % für Entschädigungsantrag -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=Benötigter Schwellwert in % um einen Parameter zu ändern -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Benötigter Schwellwert in % um ein Gut zu entfernen -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=Benötigter Schwellwert in % für Konfiszierungsantrag -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_ROLE=Benötigter Schwellwert in % für Antrag einer Rolle mit Pfand - -# suppress inspection "UnusedProperty" -dao.param.RECIPIENT_BTC_ADDRESS=Adresse des BTC Empfängers - -# suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=Gut Listungsgebühr pro Tag -# suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Min. Handelsvolumen für Güter - -# suppress inspection "UnusedProperty" -dao.param.LOCK_TIME_TRADE_PAYOUT=Sperrzeit für alternative Handelsauszahlung Tx -# suppress inspection "UnusedProperty" -dao.param.ARBITRATOR_FEE=Vermittlergebühr in BTC - -# suppress inspection "UnusedProperty" -dao.param.MAX_TRADE_LIMIT=Max. Handels-Limit in BTC - -# suppress inspection "UnusedProperty" -dao.param.BONDED_ROLE_FACTOR=Gebundene Rolle Einheitsfaktor in BSQ -# suppress inspection "UnusedProperty" -dao.param.ISSUANCE_LIMIT=Ausgabelimit pro Zyklus in BSQ - -dao.param.currentValue=Aktueller Wert: {0} -dao.param.currentAndPastValue=Aktueller Wert: {0} (Wert, als der Vorschlag gemacht wurde: {1}) -dao.param.blocks={0} Blöcke - -dao.results.invalidVotes=Wir hatten in diesem Abstimmungszyklus ungültige Stimmen. Das kann passieren, wenn eine Abstimmung im Bisq-Netzwerk nicht gut verteilt wurde.\n{0} - -# suppress inspection "UnusedProperty" -dao.phase.PHASE_UNDEFINED=Undefiniert -# suppress inspection "UnusedProperty" -dao.phase.PHASE_PROPOSAL=Vorschlag-Phase -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK1=Pause 1 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=Geheime Wahl-Phase -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK2=Pause 2 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=Wahloffenbarung-Phase -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK3=Pause 3 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_RESULT=Ergebnis-Phase - -dao.results.votes.table.header.stakeAndMerit=Wahl-Gewicht -dao.results.votes.table.header.stake=Einsatz -dao.results.votes.table.header.merit=Verdient -dao.results.votes.table.header.vote=Wahl - -dao.bond.menuItem.bondedRoles=Gekoppelte Rollen -dao.bond.menuItem.reputation=Gekoppeltes Ansehen -dao.bond.menuItem.bonds=Pfänder - -dao.bond.dashboard.bondsHeadline=Gekoppelte BSQ -dao.bond.dashboard.lockupAmount=Gesperrte Gelder -dao.bond.dashboard.unlockingAmount=Entsperre Gelder (Warten Sie bis die Sperrzeit vorbei ist) - - -dao.bond.reputation.header=Ein Pfand für Reputation hinterlegen -dao.bond.reputation.table.header=Meine Pfänder für Reputation -dao.bond.reputation.amount=Betrag von BSQ zu sperren -dao.bond.reputation.time=Entsperrzeit in Blöcken -dao.bond.reputation.salt=Salt -dao.bond.reputation.hash=Hash -dao.bond.reputation.lockupButton=Sperren -dao.bond.reputation.lockup.headline=Sperrung-Transaktion bestätigen -dao.bond.reputation.lockup.details=Gesperrter Betrag: {0}\nEntsperrzeit: {1} Block(Blöcke) (≈{2})\n\nMining-Gebühr: {3} ({4} satoshis/vbyte)\nTransaktionsgröße (vsize): {5} Kb\n\nSind Sie sicher, dass Sie fortfahren möchten?? -dao.bond.reputation.unlock.headline=Entsperrung-Transaktion bestätigen -dao.bond.reputation.unlock.details=Entsperrter Betrag: {0}\nEntsperrzeit: {1} Block(Blöcke) (≈{2})\n\nMining-Gebühr: {3} ({4} satoshis/vbyte)\nTransaktionsgröße (vsize): {5} Kb\n\nSind Sie sicher, dass Sie fortfahren möchten? - -dao.bond.allBonds.header=Alle Pfänder - -dao.bond.bondedReputation=Bepfändete Reputation -dao.bond.bondedRoles=Gekoppelte Rollen - -dao.bond.details.header=Rollendetails -dao.bond.details.role=Rolle -dao.bond.details.requiredBond=Benötigt BSQ Kopplung -dao.bond.details.unlockTime=Entsperrzeit in Blöcken -dao.bond.details.link=Link zur Rollenbeschreibung -dao.bond.details.isSingleton=Kann von mehreren Rollenhalter genommen werden -dao.bond.details.blocks={0} Blöcke - -dao.bond.table.column.name=Name -dao.bond.table.column.link=Link -dao.bond.table.column.bondType=Pfand-Typ -dao.bond.table.column.details=Details -dao.bond.table.column.lockupTxId=Sperrung Tx ID -dao.bond.table.column.bondState=Kopplungsstatus -dao.bond.table.column.lockTime=Entsperrzeit -dao.bond.table.column.lockupDate=Zeitpunkt der Sperrung - -dao.bond.table.button.lockup=Sperren -dao.bond.table.button.unlock=Entsperren -dao.bond.table.button.revoke=Widerrufen - -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNDEFINED=Undefiniert -# suppress inspection "UnusedProperty" -dao.bond.bondState.READY_FOR_LOCKUP=Noch nicht gekoppelt -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_PENDING=Sperrung ausstehend -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_CONFIRMED=Kopplung gesperrt -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_PENDING=Entsperrung ausstehend -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_CONFIRMED=Entsperrungs-Tx bestätigt -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKING=Entsperre Kopplung -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKED=Kopplung entsperrt -# suppress inspection "UnusedProperty" -dao.bond.bondState.CONFISCATED=Pfand konfisziert - -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.UNDEFINED=Undefiniert -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.BONDED_ROLE=Gekoppelte Rolle -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.REPUTATION=Gekoppeltes Ansehen - -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.UNDEFINED=Undefiniert -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub Administrator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=Forum Administrator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter Administrator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase Admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube Administrator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=Bisq Betreuer -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-Fork Betreuer -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer Betreuer -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=Betreiber der Webseite -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=Betreiber des Forums -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Seed-Knoten Betreiber -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Preis-Node Betreiber -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Bitcoin-Node Betreiber -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Betreiber der Handelsstatistik Webseite -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer Operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile Benachrichtigung Weiterleitung Betreiber -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Domainnamen Inhaber -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DNS_ADMIN=DNS Administrator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=Mediator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=Vermittler -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC Spendenadresse Besitzer - -dao.burnBsq.assetFee=Gut Listung -dao.burnBsq.menuItem.assetFee=Gut Listungsgebühr -dao.burnBsq.menuItem.proofOfBurn=Nachweis der Verbrennung -dao.burnBsq.header=Asset-Listungsgebühr -dao.burnBsq.selectAsset=Gut auswählen -dao.burnBsq.fee=Gebühr -dao.burnBsq.trialPeriod=Probezeit -dao.burnBsq.payFee=Gebühr bezahlen -dao.burnBsq.allAssets=Alle Güter -dao.burnBsq.assets.nameAndCode=Gutsname -dao.burnBsq.assets.state=Status -dao.burnBsq.assets.tradeVolume=Handelsvolumen -dao.burnBsq.assets.lookBackPeriod=Überprüfungsphase -dao.burnBsq.assets.trialFee=Gebühr für Probezeit -dao.burnBsq.assets.totalFee=Insgesamt gezahlte Gebühren -dao.burnBsq.assets.days={0} Tage -dao.burnBsq.assets.toFewDays=Die Gutsgebühr ist zu niedrig. Die min. Anzahl Tage für die Probezeit sind {0} Tage. - -# suppress inspection "UnusedProperty" -dao.assetState.UNDEFINED=Undefiniert -# suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=Probezeit läuft -# suppress inspection "UnusedProperty" -dao.assetState.ACTIVELY_TRADED=Aktiv gehandelt -# suppress inspection "UnusedProperty" -dao.assetState.DE_LISTED=Aufgrund von Inaktivität aus der Liste entfernt -# suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=Per Wahl entfernt - -dao.proofOfBurn.header=Nachweis der Verbrennung -dao.proofOfBurn.amount=Betrag -dao.proofOfBurn.preImage=Vorabbild -dao.proofOfBurn.burn=Verbrennen -dao.proofOfBurn.allTxs=Alle Transaktionen zum Nachweis der Verbrennung -dao.proofOfBurn.myItems=Meine Transaktionen zum Nachweis der Verbrennung -dao.proofOfBurn.date=Datum -dao.proofOfBurn.hash=Hash -dao.proofOfBurn.txs=Transaktionen -dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Unterzeichnen einer Nachricht mit Schlüssel vom Nachweis der Verbrennung -dao.proofOfBurn.verify.window.title=Verifizieren einer Nachricht mit Schlüssel vom Nachweis der Verbrennung -dao.proofOfBurn.copySig=Signatur in Zwischenablage kopieren -dao.proofOfBurn.sign=Unterzeichnen -dao.proofOfBurn.message=Nachricht -dao.proofOfBurn.sig=Signatur -dao.proofOfBurn.verify=Bestätigen -dao.proofOfBurn.verificationResult.ok=Überprüfung erfolgreich -dao.proofOfBurn.verificationResult.failed=Überprüfung fehlgeschlagen - -# suppress inspection "UnusedProperty" -dao.phase.UNDEFINED=Undefiniert -# suppress inspection "UnusedProperty" -dao.phase.PROPOSAL=Vorschlag-Phase -# suppress inspection "UnusedProperty" -dao.phase.BREAK1=Pause vor geheime Wahl-Phase -# suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=Geheime Wahl-Phase -# suppress inspection "UnusedProperty" -dao.phase.BREAK2=Pause vor Wahloffenbarung-Phase -# suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=Wahloffenbarung-Phase -# suppress inspection "UnusedProperty" -dao.phase.BREAK3=Pause vor Ergebnis-Phase -# suppress inspection "UnusedProperty" -dao.phase.RESULT=Wahlergebnisse-Phase - -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.PROPOSAL=Vorschlag-Phase -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=Geheime Wahl -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.VOTE_REVEAL=Wahl offenbaren -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.RESULT=Wahlergebnis - -# suppress inspection "UnusedProperty" -dao.proposal.type.UNDEFINED=Undefiniert -# suppress inspection "UnusedProperty" -dao.proposal.type.COMPENSATION_REQUEST=Entlohnungsanfrage -# suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=Rückerstattungsantrag -# suppress inspection "UnusedProperty" -dao.proposal.type.BONDED_ROLE=Vorschlag für Gekoppelte Rolle -# suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=Vorschlag ein Gut zu entfernen -# suppress inspection "UnusedProperty" -dao.proposal.type.CHANGE_PARAM=Vorschlag einen Parameter zu ändern -# suppress inspection "UnusedProperty" -dao.proposal.type.GENERIC=Allgemeiner Vorschlag -# suppress inspection "UnusedProperty" -dao.proposal.type.CONFISCATE_BOND=Vorschlag eine Kopplung zu konfiszieren - -# suppress inspection "UnusedProperty" -dao.proposal.type.short.UNDEFINED=Undefiniert -# suppress inspection "UnusedProperty" -dao.proposal.type.short.COMPENSATION_REQUEST=Entlohnungsanfrage -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=Rückerstattungsantrag -# suppress inspection "UnusedProperty" -dao.proposal.type.short.BONDED_ROLE=Gekoppelte Rolle -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ASSET=Einen Altcoin entfernen -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CHANGE_PARAM=Einen Parameter ändern -# suppress inspection "UnusedProperty" -dao.proposal.type.short.GENERIC=Allgemeiner Vorschlag -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CONFISCATE_BOND=Konfisziere eine Kopplung - -dao.proposal.details=Vorschlagdetails -dao.proposal.selectedProposal=Ausgewählte Anträge -dao.proposal.active.header=Vorschläge des momentanen Zyklus -dao.proposal.active.remove.confirm=Sind Sie sicher, dass Sie diesen Antrag entfernen wollen?\nDie Erstellergebühr geht verloren, wenn Sie den Antrag entfernen. -dao.proposal.active.remove.doRemove=Ja, Antrag entfernen -dao.proposal.active.remove.failed=Konnte Vorschlag nicht entfernen -dao.proposal.myVote.title=Wahl -dao.proposal.myVote.accept=Vorschlag annehmen -dao.proposal.myVote.reject=Vorschlag ablehnen -dao.proposal.myVote.removeMyVote=Vorschlag ignorieren -dao.proposal.myVote.merit=Wahlgewicht durch verdiente BSQ -dao.proposal.myVote.stake=Wahlgewicht vom Einsatz -dao.proposal.myVote.revealTxId=Wahloffenbarung Transaktion ID -dao.proposal.myVote.stake.prompt=Max. verfügbarer Einsatz zum wählen: {0} -dao.proposal.votes.header=Einsatz für Wahl festlegen und Ihre Wahlen veröffentlichen -dao.proposal.myVote.button=Wahlen veröffentlichen -dao.proposal.myVote.setStake.description=Nach dem Sie für alle Vorschläge gewählt haben, müssen Sie Ihren Einsatz festlegen, indem Sie BSQ sperren. Desto mehr BSQ Sie sperren, desto mehr Gewicht hat Ihre Wahl.\n\nGesperrte BSQ wird während der Wahl Veröffentlichungsphase entsperrt. -dao.proposal.create.selectProposalType=Vorschlagtyp auswählen -dao.proposal.create.phase.inactive=Bitte warten sie auf die nächste Vorschlags-Phase -dao.proposal.create.proposalType=Vorschlagtype -dao.proposal.create.new=Neuen Antrag erstellen -dao.proposal.create.button=Antrag erstellen -dao.proposal.create.publish=Vorschlag veröffentlichen -dao.proposal.create.publishing=Vorschlag wird veröffentlicht ... -dao.proposal=Vorschlag -dao.proposal.display.type=Vorschlagtyp -dao.proposal.display.name=Exakter GitHub-Benutzername  -dao.proposal.display.link=Link zu detaillierten Infos -dao.proposal.display.link.prompt=Link zu Vorschlag -dao.proposal.display.requestedBsq=Gelder in BSQ anfordern -dao.proposal.display.txId=Antrag Transaktion-ID: -dao.proposal.display.proposalFee=Vorschlag-Gebühr -dao.proposal.display.myVote=Meine Wahl -dao.proposal.display.voteResult=Zusammenfassung des Wahlergebnisses -dao.proposal.display.bondedRoleComboBox.label=Typ der Rolle mit Pfand -dao.proposal.display.requiredBondForRole.label=Benötigter Pfand für Rolle -dao.proposal.display.option=Option - -dao.proposal.table.header.proposalType=Vorschlagtyp -dao.proposal.table.header.link=Link -dao.proposal.table.header.myVote=Meine Wahl -# suppress inspection "UnusedProperty" -dao.proposal.table.header.remove=Entfernen -dao.proposal.table.icon.tooltip.removeProposal=Antrag entfernen -dao.proposal.table.icon.tooltip.changeVote=Aktuelles Votum: ''{0}''. Votum abändern zu: ''{1}'' - -dao.proposal.display.myVote.accepted=Angenommen -dao.proposal.display.myVote.rejected=Abgelehnt -dao.proposal.display.myVote.ignored=Ignoriert -dao.proposal.display.myVote.unCounted=Stimme wurde nicht in das Ergebnis einbezogen -dao.proposal.myVote.summary=Gewählt: {0}; Stimmengewicht: {1} (verdient: {2} + Einsatz: {3}) {4} -dao.proposal.myVote.invalid=Wahl war ungültig - -dao.proposal.voteResult.success=Angenommen -dao.proposal.voteResult.failed=Abgelehnt -dao.proposal.voteResult.summary=Ergebnis: {0}; Schwellwert: {1} (benötigt > {2}); Quorum: {3} (benötigt > {4}) - -dao.proposal.display.paramComboBox.label=Zu ändernden Parameter auswählen -dao.proposal.display.paramValue=Wert des Parameter - -dao.proposal.display.confiscateBondComboBox.label=Kopplung wählen -dao.proposal.display.assetComboBox.label=Zu entfernendes Gut - -dao.blindVote=Geheime Wahl - -dao.blindVote.startPublishing=Veröffentliche geheimen Wahl Transaktion... -dao.blindVote.success=Die Transaktion ihrer geheimen Wahl wurde erfolgreich veröffentlicht.\n\nDamit ihre Stimme auch gezählt wird, müssen sie im Zeitraum der Stimmoffenbahrungs-Phase in Bisq online sein. Wenn die Stimmoffenbahrungs-Transaktion nicht veröffentlicht wird, ist ihre Stimme nicht gültig! - -dao.wallet.menuItem.send=Senden -dao.wallet.menuItem.receive=Empfangen -dao.wallet.menuItem.transactions=Transaktionen - -dao.wallet.dashboard.myBalance=Mein Guthaben der Handels-Wallet - -dao.wallet.receive.fundYourWallet=Ihre BSQ Empfangs-Adresse -dao.wallet.receive.bsqAddress=Adresse der BSQ-Wallet (Neue ungebrauchte Adresse) - -dao.wallet.send.sendFunds=Gelder senden -dao.wallet.send.sendBtcFunds=Sende nicht-BSQ-Gelder (BTC) -dao.wallet.send.amount=Betrag in BSQ -dao.wallet.send.btcAmount=Betrag in BTC (nicht-BSQ-Gelder) -dao.wallet.send.setAmount=Betrag zum Abheben festlegen (Min­dest­be­trag ist {0}) -dao.wallet.send.receiverAddress=Adresse des BSQ Empfängers -dao.wallet.send.receiverBtcAddress=Adresse des BTC Empfängers -dao.wallet.send.setDestinationAddress=Tragen Sie Ihre Zieladresse ein -dao.wallet.send.send=BSQ-Gelder senden -dao.wallet.send.inputControl=Inputs auswählen -dao.wallet.send.sendBtc=BTC-Gelder senden -dao.wallet.send.sendFunds.headline=Abhebeanfrage bestätigen -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 - -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED=Undefiniert -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED_TX_TYPE=Nicht erkannt -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNVERIFIED=Unbestätigte BSQ-Transaktion -# suppress inspection "UnusedProperty" -dao.tx.type.enum.INVALID=Ungültige BSQ-Transaktion -# suppress inspection "UnusedProperty" -dao.tx.type.enum.GENESIS=Ursprungstransaktion -# suppress inspection "UnusedProperty" -dao.tx.type.enum.TRANSFER_BSQ=BSQ überweisen -# suppress inspection "UnusedProperty" -dao.tx.type.enum.received.TRANSFER_BSQ=BSQ erhalten -# suppress inspection "UnusedProperty" -dao.tx.type.enum.sent.TRANSFER_BSQ=BSQ senden -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PAY_TRADE_FEE=Handelsgebühr -# suppress inspection "UnusedProperty" -dao.tx.type.enum.COMPENSATION_REQUEST=Gebühr für Entlohnungsanfrage -# suppress inspection "UnusedProperty" -dao.tx.type.enum.REIMBURSEMENT_REQUEST=Gebühr für Rückerstattungsantrag -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROPOSAL=Gebühr für Vorschlag -# suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=Gebühr für geheime Wahl -# suppress inspection "UnusedProperty" -dao.tx.type.enum.VOTE_REVEAL=Wahl offenbaren -# suppress inspection "UnusedProperty" -dao.tx.type.enum.LOCKUP=Sperre Kopplung -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNLOCK=Entsperre Kopplung -# suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=Listungsgebühr für Gut -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=Nachweis der Verbrennung -# suppress inspection "UnusedProperty" -dao.tx.type.enum.IRREGULAR=Irregulär - -dao.tx.withdrawnFromWallet=BTC von Wallet abgehoben -dao.tx.issuanceFromCompReq=Entlohnungsanfrage/ausgabe -dao.tx.issuanceFromCompReq.tooltip=Entlohnungsanfrage, die zur Ausgabe neuere BSQ führte.\nAusgabedatum: {0} -dao.tx.issuanceFromReimbursement=Rückerstattungsantrag/Ausgabe -dao.tx.issuanceFromReimbursement.tooltip=Rückerstattungsanfrage, die zur Ausgabe neuer BSQ führte.\nAusgabedatum: {0} -dao.proposal.create.missingBsqFunds=Sie haben nicht ausreichend BSQ um diesen Vorschlag zu erstellen. Falls sie nicht bestätigte BSQ-Transaktionen offen haben, müssen sie auf die Blockchain-Bestätigung warten. BSQ kann nur validiert werden wenn es in einen Block aufgenommen wurde.\nEs fehlen: {0} - -dao.proposal.create.missingBsqFundsForBond=Du hast nicht ausreichend BSQ für diese Rolle. Du kannst den Vorschlag zwar veröffentlichen, benötigst jedoch den vollen BSQ Betrag für diese Rolle, falls dein Vorschlag akzeptiert wird.\nEs fehlen: {0} - -dao.proposal.create.missingMinerFeeFunds=Du hast nicht ausreichend BTC, um die Vorschlags-Transaktion zu erstellen. Jede BSQ-Transaktion benötigt eine Mining-Gebühr in BTC.\nEs fehlen: {0} - -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} 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} 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. -dao.news.bisqDAO.readMoreLink=Erfahren Sie mehr über den Bisq DAO - -dao.news.pastContribution.title=BEREITS ZU BISQ BEIGETRAGEN? BSQ ANFRAGEN -dao.news.pastContribution.description=Falls sie in der Vergangenheit zu Bisq beigetragen haben, verwenden sie die darunterstehende BSQ Adresse um einen Antrag zur Aufnahme in die BSQ Genesis Transaktion zu erstellen. -dao.news.pastContribution.yourAddress=Ihre BSQ-Wallets-Adresse -dao.news.pastContribution.requestNow=Jetzt anfordern - -dao.news.DAOOnTestnet.title=DEN BISQ DAO AUF UNSEREM TESTNETZWERK LAUFEN LASSEN -dao.news.DAOOnTestnet.description=Die Bisq DAO wurde auf Mainnet noch nicht veröffentlicht, jedoch können sie die Bisq DAO jetzt schon in unserem Testnet ausprobieren. -dao.news.DAOOnTestnet.firstSection.title=1. Nach DAO-Testnetzmodus wechseln -dao.news.DAOOnTestnet.firstSection.content=Vom Einstellungsmenü ins DAO-Testnetzwerk wechseln. -dao.news.DAOOnTestnet.secondSection.title=2. Einige BSQ erwerben -dao.news.DAOOnTestnet.secondSection.content=Fragen sie einfach auf Slack nach BSQ oder kaufen sie direkt BSQ in Bisq. -dao.news.DAOOnTestnet.thirdSection.title=3. Beim Wahl-Zyklus teilhaben -dao.news.DAOOnTestnet.thirdSection.content=Erstellen sie Vorschläge/Anträge und stimmen sie über bestehende ab, um unterschiedliche Aspekte von Bisq zu verändern. -dao.news.DAOOnTestnet.fourthSection.title=4. Einen BSQ-Block-Forscher erkunden -dao.news.DAOOnTestnet.fourthSection.content=Da es sich bei BSQ um Bitcoin handelt, können sie die BSQ-Transaktionen in ihrem Bitcoin Block Explorer einsehen. -dao.news.DAOOnTestnet.readMoreLink=Die volle Dokumentation lesen. - -dao.monitor.daoState=DAO Status -dao.monitor.proposals=Vorschlag Status -dao.monitor.blindVotes=Geheime Wahl-Status - -dao.monitor.table.peers=Peers -dao.monitor.table.conflicts=Konflikte -dao.monitor.state=Status -dao.monitor.requestAlHashes=Alle Hashs anfragen -dao.monitor.resync=DAO Status neu syncronisieren -dao.monitor.table.header.cycleBlockHeight=Zyklus / Blockhöhe -dao.monitor.table.cycleBlockHeight=Zyklus {0} / Block {1} -dao.monitor.table.seedPeers=Seed-Knoten: {0} - -dao.monitor.daoState.headline=DAO Status -dao.monitor.daoState.table.headline=Kette von DAO Status Hashs -dao.monitor.daoState.table.blockHeight=Blockhöhe -dao.monitor.daoState.table.hash=Hash des DAO Status -dao.monitor.daoState.table.prev=Vorheriger Hash -dao.monitor.daoState.conflictTable.headline=DAO Status Hashs der Peers im Konflikt -dao.monitor.daoState.utxoConflicts=UTXO Konflikte -dao.monitor.daoState.utxoConflicts.blockHeight=Blockhöhe: {0} -dao.monitor.daoState.utxoConflicts.sumUtxo=Summe aller UTXO: {0} BSQ -dao.monitor.daoState.utxoConflicts.sumBsq=Summe aller BSQ: {0} BSQ -dao.monitor.daoState.checkpoint.popup=Der DAO Status ist nicht mit dem Netzwerk synchronisiert. Nach dem Neustart wird der DAO Status neu synchronisiert. - -dao.monitor.proposal.headline=Vorschlag Status -dao.monitor.proposal.table.headline=Kette von Vorschlag Status Hashs -dao.monitor.proposal.conflictTable.headline=Vorschlag Status Hashs der Peers im Konflikt - -dao.monitor.proposal.table.hash=Hash des Vorschlag Status -dao.monitor.proposal.table.prev=Vorheriger Hash -dao.monitor.proposal.table.numProposals=Keine Vorschläge - -dao.monitor.isInConflictWithSeedNode=Deine lokalen Daten stimmen mit mindestens einem Seed-Knoten nicht überein. Bitte synchronisiere den DAO-Status neu. -dao.monitor.isInConflictWithNonSeedNode=Einer deiner Peers is nicht im Konsens mit dem Netzwerk, aber dein Knoten ist synchron mit den Seed-Knoten. -dao.monitor.daoStateInSync=Dein lokaler Knoten ist in Konsens mit dem Netzwerk - -dao.monitor.blindVote.headline=Geheime Wahl-Status -dao.monitor.blindVote.table.headline=Kette von geheime Wahl Status Hashs -dao.monitor.blindVote.conflictTable.headline=Geheime Wahl Status Hashs der Peers im Konflikt -dao.monitor.blindVote.table.hash=Hash des geheimen Wahl Status -dao.monitor.blindVote.table.prev=Vorheriger Hash -dao.monitor.blindVote.table.numBlindVotes=Anzahl geheimer Wahlen - -dao.factsAndFigures.menuItem.supply=Angebot an BSQ -dao.factsAndFigures.menuItem.transactions=BSQ Transaktionen - -dao.factsAndFigures.dashboard.avgPrice90=90 Tage durchschnittlicher BSQ/BTC-Handelspreis -dao.factsAndFigures.dashboard.avgPrice30=30 Tage durchschnittlicher BSQ/BTC-Handelspreis -dao.factsAndFigures.dashboard.avgUSDPrice90=90 Tage volumengewichteter durchschnittlicher BSQ/USD Preis -dao.factsAndFigures.dashboard.avgUSDPrice30=30 Tage volumengewichteter durchschnittlicher BSQ/USD Preis -dao.factsAndFigures.dashboard.marketCap=Marktkapitalisierung (basierend auf 30-tägigem Durchschnittspreis von BSQ/USD) -dao.factsAndFigures.dashboard.availableAmount=Insgesamt verfügbare BSQ -dao.factsAndFigures.dashboard.volumeUsd=Gesamtes Handelsvolumen in USD -dao.factsAndFigures.dashboard.volumeBtc=Gesamtes Handelsvolumen in BTC -dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection=Durchschnittlicher BSQ/USD Handelspreis einer ausgewählten Zeitperiode im Chart -dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection=Durchschnittlicher BSQ/BTC Handelspreis einer ausgewählten Zeitperiode im Chart - -dao.factsAndFigures.supply.issuedVsBurnt=Ausgestellte BSQ v. verbrannte BSQ - -dao.factsAndFigures.supply.issued=Ausgestellte BSQ -dao.factsAndFigures.supply.compReq=Entlohnungsanfragen -dao.factsAndFigures.supply.reimbursement=Rückerstattungsantrag -dao.factsAndFigures.supply.genesisIssueAmount=Ausgestellte BSQ in Genesis-Transaktion -dao.factsAndFigures.supply.compRequestIssueAmount=Ausgestellte BSQ für Entlohnungsanträge -dao.factsAndFigures.supply.reimbursementAmount=Ausgestellte BSQ für Rückerstattungsanträge -dao.factsAndFigures.supply.totalIssued=Gesamt ausgegebene BSQ -dao.factsAndFigures.supply.totalBurned=Gesamt verbrannte BSQ -dao.factsAndFigures.supply.chart.tradeFee.toolTip={0}\n{1} -dao.factsAndFigures.supply.burnt=Verbrannte BSQ (Gebühren und Proof-of-Burn) - -dao.factsAndFigures.supply.priceChat=BSQ Preis -dao.factsAndFigures.supply.volumeChat=Handelsvolumen -dao.factsAndFigures.supply.tradeVolumeInUsd=Handelsvolumen in USD -dao.factsAndFigures.supply.tradeVolumeInBtc=Handelsvolumen in BTC -dao.factsAndFigures.supply.bsqUsdPrice=BSQ/USD Preis -dao.factsAndFigures.supply.bsqBtcPrice=BSQ/BTC Preis -dao.factsAndFigures.supply.btcUsdPrice=BTC/USD Preis - -dao.factsAndFigures.supply.locked=Globaler Zustand der gesperrten BSQ -dao.factsAndFigures.supply.totalLockedUpAmount=In Pfänden gesperrt -dao.factsAndFigures.supply.totalUnlockingAmount=BSQ von Pfand auslösen -dao.factsAndFigures.supply.totalUnlockedAmount=BSQ von Pfand ausgelöst -dao.factsAndFigures.supply.totalConfiscatedAmount=Konfiszierte BSQ von Pfänden -dao.factsAndFigures.supply.proofOfBurn=Nachweis der Verbrennung -dao.factsAndFigures.supply.bsqTradeFee=BSQ Handelsgebühren -dao.factsAndFigures.supply.btcTradeFee=BTC Handelsgebühren - -dao.factsAndFigures.transactions.genesis=Ursprungstransaktion -dao.factsAndFigures.transactions.genesisBlockHeight=Genesisblock-Höhe -dao.factsAndFigures.transactions.genesisTxId=Genesis-Transaktions-ID -dao.factsAndFigures.transactions.txDetails=BSQ Transationsstatistiken -dao.factsAndFigures.transactions.allTx=Anzahl aller BSQ-Transaktionen -dao.factsAndFigures.transactions.utxo=Anzahl aller nicht ausgegebenen Transaktionsausgängen -dao.factsAndFigures.transactions.compensationIssuanceTx=Anzahl aller Transaktionen von Entlohnungsanfragen -dao.factsAndFigures.transactions.reimbursementIssuanceTx=Anzahl aller Transaktionen von Entschädigungsanfragen -dao.factsAndFigures.transactions.burntTx=Anzahl aller Transaktionen von bezahlten Gebühren -dao.factsAndFigures.transactions.invalidTx=Anzahl aller ungültigen Transaktionen -dao.factsAndFigures.transactions.irregularTx=Anzahl aller irregulären Transaktionen - - - #################################################################### # Windows #################################################################### @@ -2120,8 +1406,6 @@ disputeSummaryWindow.close.noPayout.text=Wollen Sie schließen ohne eine Auszahl emptyWalletWindow.headline={0} Notfall-Wallets-Werkzeug emptyWalletWindow.info=Bitte nur in Notfällen nutzen, wenn Sie vom UI aus nicht auf Ihre Gelder zugreifen können.\n\nBeachten Sie bitte, dass alle offenen Angebote geschlossen werden, wenn Sie dieses Werkzeug verwenden.\n\nErstellen Sie ein Backup Ihres Dateiverzeichnisses, bevor Sie dieses Werkzeug verwenden. Dies können Sie unter \"Konto/Backup\" tun.\n\nBitte melden Sie uns das Problem und erstellen Sie einen Fehlerbericht auf GitHub oder im Bisq-Forum, damit wir feststellen können, was das Problem verursacht hat. emptyWalletWindow.balance=Ihr verfügbares Wallets-Guthaben -emptyWalletWindow.bsq.btcBalance=Guthaben von nicht-BSQ Satoshis - emptyWalletWindow.address=Ihre Zieladresse emptyWalletWindow.button=Alle Gelder senden emptyWalletWindow.openOffers.warn=Sie haben offene Angebote, die entfernt werden, wenn Sie die Wallet leeren.\nSind Sie sicher, dass Sie Ihre Wallet leeren wollen? @@ -2146,10 +1430,8 @@ filterWindow.seedNode=Gefilterte Seed-Knoten (Komma getr. Onion-Adressen) filterWindow.priceRelayNode=Gefilterte Preisrelais Knoten (Komma getr. Onion-Adressen) 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=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 filterWindow.remove=Filter entfernen @@ -2223,7 +1505,6 @@ tradeDetailsWindow.detailData=Detaillierte Daten txDetailsWindow.headline=Transaktionsdetails txDetailsWindow.btc.note=Sie haben BTC gesendet. -txDetailsWindow.bsq.note=Sie haben BSQ-Gelder gesendet. BSQ sind "Colored Bitcoin", daher wird die Transaktion nicht in einem BSQ-Explorer angezeigt, bis sie in einem Bitcoin-Block bestätigt wurde. txDetailsWindow.sentTo=Gesendet an txDetailsWindow.txId=TxId @@ -2235,9 +1516,6 @@ closedTradesSummaryWindow.totalMinerFee.title=Sum of all miner fees closedTradesSummaryWindow.totalMinerFee.value={0} ({1} of total trade amount) closedTradesSummaryWindow.totalTradeFeeInBtc.title=Sum of all trade fees paid in BTC closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} of total trade amount) -closedTradesSummaryWindow.totalTradeFeeInBsq.title=Sum of all trade fees paid in BSQ -closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} of total trade amount) - walletPasswordWindow.headline=Passwort zum Entsperren eingeben torNetworkSettingWindow.header=Tor-Netzwerkeinstellungen @@ -2317,12 +1595,6 @@ popup.warning.tooLargePercentageValue=Es kann kein Prozentsatz von 100% oder meh popup.warning.examplePercentageValue=Bitte geben sei einen Prozentsatz wie folgt ein \"5.4\" für 5.4% popup.warning.noPriceFeedAvailable=Es ist kein Marktpreis für diese Währung verfügbar. Sie können keinen auf Prozent basierenden Preis verwenden.\nBitte wählen Sie den Festpreis. popup.warning.sendMsgFailed=Das Senden der Nachricht an Ihren Handelspartner ist fehlgeschlagen.\nVersuchen Sie es bitte erneut und falls es weiter fehlschlägt, erstellen Sie bitte einen Fehlerbericht. -popup.warning.insufficientBtcFundsForBsqTx=Sie haben nicht genügend BTC-Gelder, um die Mining-Gebühr für diese Transaktion zu bezahlen.\nBitte finanzieren Sie Ihre BTC-Wallet.\nFehlende Gelder: {0} -popup.warning.bsqChangeBelowDustException=Diese Transaktion erzeugt eine BSQ-Wechselgeld-Ausgabe, die unter dem Dust-Limit (5.46 BSQ) liegt und vom Bitcoin-Netzwerk abgelehnt werden würde.\n\nSie müssen entweder einen höheren Betrag senden, um die Wechselgeld-Ausgabe zu vermeiden (z.B. indem Sie den Dust-Betrag zu Ihrem Sende-Betrag hinzufügen) oder mehr BSQ-Guthaben zu Ihrer Wallet hinzufügen, damit Sie vermeiden, eine Dust-Ausgabe zu generieren.\n\nDie Dust-Ausgabe ist {0}. -popup.warning.btcChangeBelowDustException=Diese Transaktion erzeugt eine Wechselgeld-Ausgabe, die unter dem Dust-Limit (546 Satoshi) liegt und vom Bitcoin-Netzwerk abgelehnt würde.\n\nSie müssen den Dust-Betrag zu Ihrem Sende-Betrag hinzufügen, um zu vermeiden, dass eine Dust-Ausgabe generiert wird.\n\nDie Dust-Ausgabe ist {0}. - -popup.warning.insufficientBsqFundsForBtcFeePayment=Sie benötigen mehr BSQ um diese Transaktion durchzuführen - die letzten 5.46 BSQ in Ihrer Wallet können aufgrund der Dust-Limits im Bitcoin Protokoll nicht für die Trading-Gebühren verwendet werden.\n\nSie können entweder mehr BSQ kaufen oder die Trading-Gebühren in BTC bezahlen.\n\nFehlende Funds: {0} -popup.warning.noBsqFundsForBtcFeePayment=Ihre BSQ-Wallet hat keine ausreichenden Gelder, um die Handels-Gebühr in BSQ zu bezahlen. popup.warning.messageTooLong=Ihre Nachricht überschreitet die maximal erlaubte Größe. Sende Sie diese in mehreren Teilen oder laden Sie sie in einen Dienst wie https://pastebin.com hoch. popup.warning.lockedUpFunds=Sie haben gesperrtes Guthaben aus einem gescheiterten Trade.\nGesperrtes Guthaben: {0} \nEinzahlungs-Tx-Adresse: {1}\nTrade ID: {2}.\n\nBitte öffnen Sie ein Support-Ticket, indem Sie den Trade im Bildschirm "Offene Trades" auswählen und auf \"alt + o\" oder \"option + o\" drücken. @@ -2336,8 +1608,6 @@ popup.warning.nodeBanned=Einer der {0} Nodes wurde gebannt. popup.warning.priceRelay=Preisrelais popup.warning.seed=Seed popup.warning.mandatoryUpdate.trading=Bitte aktualisieren Sie auf die neueste Bisq-Version. Es wurde ein obligatorisches Update veröffentlicht, das den Handel mit alten Versionen deaktiviert. Bitte besuchen Sie das Bisq-Forum für weitere Informationen. -popup.warning.mandatoryUpdate.dao=Bitte aktualisieren Sie auf die neueste Bisq-Version. Ein obligatorisches Update wurde veröffentlicht, das die Bisq DAO und BSQ für alte Versionen deaktiviert. Bitte besuchen Sie das Bisq-Forum für weitere Informationen. -popup.warning.disable.dao=Der Bisq DAO und BSQ sind temporär deaktiviert. Bitte besuchen sie das Bisq-Forum für weitere Informationen. popup.warning.noFilter=Wir haben kein Filterobjekt von den Seed Nodes erhalten. Diese Situation ist unerwartet. Bitte informieren Sie die Bisq Entwickler. popup.warning.burnBTC=Die Transaktion ist nicht möglich, da die Mininggebühren von {0} den übertragenen Betrag von {1} überschreiten würden. Bitte warten Sie, bis die Gebühren wieder niedrig sind, oder Sie mehr BTC zum übertragen angesammelt haben. @@ -2356,7 +1626,6 @@ popup.info.cashDepositInfo.confirm=Ich bestätige, dass ich die Kaution zahlen k 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=Es gab ein Problem mit der Synchronisierung des DAO-Zustands. Sie müssen die Anwendung neu starten, um das Problem zu beheben. popup.privateNotification.headline=Wichtige private Benachrichtigung! @@ -2528,7 +1797,6 @@ navigation.settings.preferences=\"Einstellungen/Voreinstellungen\" # suppress inspection "UnusedProperty" navigation.funds.transactions=\"Gelder/Transaktionen\" navigation.support=\"Support\" -navigation.dao.wallet.receive=\"DAO/BSQ-Wallet/Erhalten\" #################################################################### @@ -2558,12 +1826,6 @@ XMR_MAINNET=Bitcoin-Hauptnetzwerk XMR_TESTNET=Bitcoin-Testnetzwerk # suppress inspection "UnusedProperty" XMR_STAGENET=Bitcoin-Regtest -# suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin-DAO-Testnetzwerk (veraltet) -# suppress inspection "UnusedProperty" -BTC_DAO_BETANET=Bisq DAO Betanet (Bitcoin Mainnet) -# suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=Bitcoin DAO Regtest time.year=Jahr time.month=Monat @@ -2910,9 +2172,7 @@ validation.accountNrChars=Die Kontonummer muss aus {0} Zeichen bestehen. validation.btc.invalidAddress=Die Adresse ist nicht korrekt. Bitte überprüfen Sie das Adressformat. validation.integerOnly=Bitte nur ganze Zahlen eingeben. validation.inputError=Ihre Eingabe hat einen Fehler verursacht:\n{0} -validation.bsq.insufficientBalance=Ihr verfügbares Guthaben ist {0}. validation.btc.exceedsMaxTradeLimit=Ihr Handelslimit ist {0}. -validation.bsq.amountBelowMinAmount=Min. Betrag ist {0} validation.nationalAccountId={0} muss aus {1} Zahlen bestehen. #new @@ -2934,7 +2194,6 @@ validation.bic.invalidLocationCode=Der BIC enthält einen ungültigen Standort-C validation.bic.invalidBranchCode=Der BIC enthält eine ungültige Filialennummer validation.bic.sepaRevolutBic=Revolut SEPA Konten werden nicht unterstüzt. validation.btc.invalidFormat=Ungültiges Bitcoin Adressformat. -validation.bsq.invalidFormat=Ungültiges BSQ Adressformat. validation.email.invalidAddress=Ungültige Adresse validation.iban.invalidCountryCode=Der Ländercode ist ungültig validation.iban.checkSumNotNumeric=Die Prüfsumme muss numerisch sein diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index d32a9f11b3..bcae3b8d94 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -192,7 +192,6 @@ shared.tradeWalletBalance=Saldo de la cartera de intercambio shared.makerTxFee=Creador: {0} shared.takerTxFee=Tomador: {0} shared.iConfirm=Confirmo -shared.tradingFeeInBsqInfo=≈ {0} shared.openURL=Abrir {0} shared.fiat=Fiat shared.crypto=Cripto @@ -204,9 +203,6 @@ shared.actions=Acciones shared.buyerUpperCase=Comprador shared.sellerUpperCase=Vendedor shared.new=NUEVO -shared.blindVoteTxId=ID de la transacción de voto secreto -shared.proposal=Propuesta -shared.votes=Votos shared.learnMore=Aprender más shared.dismiss=Descartar shared.selectedArbitrator=Árbitro seleccionado @@ -240,7 +236,6 @@ mainView.menu.funds=Fondos mainView.menu.support=Soporte mainView.menu.settings=Configuración mainView.menu.account=Cuenta -mainView.menu.dao=DAO mainView.marketPriceWithProvider.label=Precio de mercado por {0} mainView.marketPrice.bisqInternalPrice=Precio del último intercambio en Bisq @@ -257,13 +252,11 @@ mainView.footer.localhostBitcoinNode=(localhost) 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 {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} -mainView.footer.daoFullNode=Nodo completo DAO mainView.bootstrapState.connectionToTorNetwork=(1/4) Conectando a la red Tor... mainView.bootstrapState.torNodeCreated=(2/4) Nodo Tor creado @@ -435,7 +428,6 @@ createOffer.fundsBox.networkFee=Comisión de minado createOffer.fundsBox.placeOfferSpinnerInfo=Publicación de oferta en curso... createOffer.fundsBox.paymentLabel=Intercambio Bisq con ID {0} createOffer.fundsBox.fundsStructure=({0} depósito de seguridad, {1} comisión de transacción, {2} comisión de minado) -createOffer.fundsBox.fundsStructure.BSQ=({0} depósito seguridad, {1} comisión de minado) + {2} comisión de intercambio createOffer.success.headline=Su oferta ha sido publicada. createOffer.success.info=Puede gestionar sus ofertas abiertas en \"Portafolio/Mis ofertas abiertas\". createOffer.info.sellAtMarketPrice=Siempre venderá a precio de mercado ya que el precio de su oferta será actualizado continuamente. @@ -636,7 +628,6 @@ portfolio.pending.step2_buyer.amountToTransfer=Cantidad a transferir portfolio.pending.step2_buyer.sellersAddress=Dirección {0} del vendedor portfolio.pending.step2_buyer.buyerAccount=Su cuenta de pago para ser usada portfolio.pending.step2_buyer.paymentStarted=Pago iniciado -portfolio.pending.step2_buyer.fillInBsqWallet=Pagar desde el monedero BSQ portfolio.pending.step2_buyer.warn=¡Todavía no ha realizado su pago {0}!\nPor favor, tenga en cuenta que el pago tiene que completarse antes de {1}. portfolio.pending.step2_buyer.openForDispute=¡No ha completado su pago!\nEl periodo máximo para el intercambio ha concluido. Por favor, contacte con el mediador para abrir una disputa. portfolio.pending.step2_buyer.paperReceipt.headline=¿Ha enviado el recibo a el vendedor de BTC? @@ -882,7 +873,6 @@ funds.locked.locked=Bloqueado en Multifirma para el intercambio con ID: {0} funds.tx.direction.sentTo=Enviado a: funds.tx.direction.receivedWith=Recibido con: funds.tx.direction.genesisTx=Desde la transacción Génesis: -funds.tx.txFeePaymentForBsqTx=Comisión de minería para la tx BSQ funds.tx.createOfferFee=Creador y comisión de transacción: {0} funds.tx.takeOfferFee=Tomador y comisión de transacción: {0} funds.tx.multiSigDeposit=Depósito Multifirma: {0} @@ -896,15 +886,11 @@ funds.tx.unknown=Razón desconocida: {0} funds.tx.noFundsFromDispute=Sin devolución de disputa funds.tx.receivedFunds=Fondos recibidos funds.tx.withdrawnFromWallet=Retirar desde el monedero -funds.tx.withdrawnFromBSQWallet=BTC retirados desde el monedero BSQ funds.tx.memo=Nota 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 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. @@ -996,10 +982,8 @@ settings.tab.about=Acerca de setting.preferences.general=Preferencias generales setting.preferences.explorer=Explorador Bitcoin -setting.preferences.explorer.bsq=Explorador Bisq setting.preferences.deviation=Desviación máxima del precio de mercado -setting.preferences.bsqAverageTrimThreshold=Umbral de valores atípicos de la tasa de BSQ -setting.preferences.avoidStandbyMode=Evitar modo en espera +setting.preferences.setting.preferences.avoidStandbyMode=Evitar modo en espera setting.preferences.autoConfirmXMR=Autoconfirmación XMR setting.preferences.autoConfirmEnabled=Habilitado setting.preferences.autoConfirmRequiredConfirmations=Confirmaciones requeridas @@ -1032,19 +1016,6 @@ setting.preferences.notifyOnPreRelease=Recibir notificaciones de pre-lanzamiento setting.preferences.resetAllFlags=Restablecer todas las casillas \"No mostrar de nuevo\" settings.preferences.languageChange=Para aplicar un cambio de idioma en todas las pantallas, se precisa reiniciar. settings.preferences.supportLanguageWarning=En caso de disputa, tenga en cuenta que la mediación se maneja en {0} y el arbitraje en {1}. -setting.preferences.daoOptions=Opciones de DAO -setting.preferences.dao.resyncFromGenesis.label=Reconstruir estado de la DAO desde la tx génesis -setting.preferences.dao.resyncFromResources.label=Reconstruir el estado de la DAO desde recursos -setting.preferences.dao.resyncFromResources.popup=Después de un reinicio de la aplicación, los datos de gobernanza en la red Bisq se volverán a cargar desde los nodos semilla y el estado de consenso en BSQ se reconstruirá a partir de los últimos archivos de recursos. -setting.preferences.dao.resyncFromGenesis.popup=Una resincronización desde la transacción génesis puede llevar mucho tiempo y recursos de CPU. ¿Está seguro de que quiere hacer eso? Generalmente una resincronización de los últimos archivos de recursos es suficiente y mucho más rápida\n\nSi continúa, después de reiniciar la aplicación, los datos de gobernanza de la red Bisq se volverán a cargar desde los nodos semilla y el estado de consenso BSQ se reconstruirá a partir de la transacción génesis. -setting.preferences.dao.resyncFromGenesis.resync=Resincronizar desde la transacción génesis y cerrar la aplicación -setting.preferences.dao.isDaoFullNode=Ejecutar Bisq como nodo completo de la DAO -setting.preferences.dao.rpcUser=nombre de usuario RPC -setting.preferences.dao.rpcPw=contraseña RPC -setting.preferences.dao.blockNotifyPort=Puerto de notificación de bloque -setting.preferences.dao.fullNodeInfo=Para ejecutar Bisq como un nodo completo de la DAO necesita estar ejecutando localmente Bitcoin Core con RPC activado. Todos los requisitos están documentados en ''{0}''.\n\nDespués de cambiar el modo, necesita reiniciar. -setting.preferences.dao.fullNodeInfo.ok=Abrir página de documentos -setting.preferences.dao.fullNodeInfo.cancel=No, me quedo con el modo ligero settings.preferences.editCustomExplorer.headline=Configuraciones de explorador settings.preferences.editCustomExplorer.description=Elija un explorador definido por el sistema de la lista de la izquierda, y/o personalícelo para ajustarse a sus preferencias. settings.preferences.editCustomExplorer.available=Exploradores disponibles @@ -1090,7 +1061,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=Altura de cadena de la DAO Bisq: {0} | Altura de la cadena de pares Bitcoin: {1} +settings.net.chainHeight=Altura de la cadena de pares Bitcoin: {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) @@ -1144,14 +1115,10 @@ setting.about.shortcuts.walletDetails=Abrir ventana de detalles de monedero setting.about.shortcuts.openEmergencyBtcWalletTool=Abrir herramienta de monedero de emergencia para el monedero BTC -setting.about.shortcuts.openEmergencyBsqWalletTool=Abrir herramienta de monedero de emergencia para el monedero BSQ - setting.about.shortcuts.showTorLogs=Cambiar nivel de registro para mensajes Tor entre DEBUG y WARN setting.about.shortcuts.manualPayoutTxWindow=Abrir ventana para pago manual desde la transacción de depósito multifirma 2de2 -setting.about.shortcuts.reRepublishAllGovernanceData=Republicar datos de gobernanza DAO (propuestas, votos) - setting.about.shortcuts.removeStuckTrade=Abrir ventana emergente para moverel intercambio a la pestaña de intercambios abiertos de nuevo setting.about.shortcuts.removeStuckTrade.value=Seleccionar intercambio fallido y presione: {0} @@ -1337,687 +1304,6 @@ account.notifications.noWebCamFound.warning=No se ha encontrado una webcam.\n\nP account.notifications.priceAlert.warning.highPriceTooLow=El precio superior debe ser mayor que el precio inferior. account.notifications.priceAlert.warning.lowerPriceTooHigh=El precio inferior debe ser más bajo que el precio superior. - - - -#################################################################### -# DAO -#################################################################### - -dao.tab.factsAndFigures=Hechos y gráficos -dao.tab.bsqWallet=Monedero BSQ -dao.tab.proposals=Gobernanza -dao.tab.bonding=Garantías -dao.tab.proofOfBurn=Comisión de listado de activos/Prueba de quemado -dao.tab.monitor=Monitor de red -dao.tab.news=Noticias - -dao.paidWithBsq=pagado con BSQ -dao.availableBsqBalance=Disponible para gastar (verificados + outputs de cambio no confirmados) -dao.verifiedBsqBalance=Balance de todas las UTXOs verificadas -dao.unconfirmedChangeBalance=Balance de todos los outputs de cambio no confirmados -dao.unverifiedBsqBalance=Balance de todas las transacciones no verificadas (esperando una confirmación en bloque) -dao.lockedForVoteBalance=Usado para votar -dao.lockedInBonds=Bloqueado en garantías -dao.availableNonBsqBalance=Saldo no-BSQ disponible (BTC) -dao.reputationBalance=Valor de mérito (no se puede gastar) - -dao.tx.published.success=Su transacción ha sido publicada satisfactoriamente. -dao.proposal.menuItem.make=Hacer propuesta -dao.proposal.menuItem.browse=Consultar propuestas abiertas -dao.proposal.menuItem.vote=Votar propuestas -dao.proposal.menuItem.result=Resultados de votaciones -dao.cycle.headline=Ciclo de votación -dao.cycle.overview.headline=Resumen del ciclo de votación -dao.cycle.currentPhase=Fase actual -dao.cycle.currentBlockHeight=Altura de bloque actual -dao.cycle.proposal=Fase de propuesta -dao.cycle.proposal.next=Siguiente fase de propuesta -dao.cycle.blindVote=Fase de votación secreta -dao.cycle.voteReveal=Fase de revelado de voto -dao.cycle.voteResult=Resultado de votación -dao.cycle.phaseDuration={0} bloques (≈{1}); Bloque {2} - {3} (≈{4} - ≈{5}) -dao.cycle.phaseDurationWithoutBlocks=Bloque {0} - {1} (≈{2} - ≈{3}) - -dao.voteReveal.txPublished.headLine=Transacción de revelado de voto publicada -dao.voteReveal.txPublished=Su transacción de revelado de voto con ID de transacción {0} se publicó satisfactoriamente.\n\nEsto ocurre automáticamente por el software si ha participado en la votación DAO. - -dao.results.cycles.header=Ciclos -dao.results.cycles.table.header.cycle=Ciclo -dao.results.cycles.table.header.numProposals=Propuestas -dao.results.cycles.table.header.voteWeight=Peso de voto -dao.results.cycles.table.header.issuance=Emisión - -dao.results.results.table.item.cycle=Ciclo {0} comenzó: {1} - -dao.results.proposals.header=Propuestas del ciclo seleccionado -dao.results.proposals.table.header.nameLink=Nombre/enlace -dao.results.proposals.table.header.details=Detalles -dao.results.proposals.table.header.myVote=Mi voto -dao.results.proposals.table.header.result=Resultado de votación -dao.results.proposals.table.header.threshold=Umbral -dao.results.proposals.table.header.quorum=Quorum - -dao.results.proposals.voting.detail.header=Resultados de votación para la propuesta seleccionada - -dao.results.exceptions=Excepción(es) del resultado de votación - -# suppress inspection "UnusedProperty" -dao.param.UNDEFINED=Indefinido - -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BSQ=Comisión de creador BSQ -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BSQ=Comisión de tomador BSQ -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BSQ=Comisión de creador BSQ mínima -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BSQ=Comisión de tomador BSQ mínima -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BTC=Comisión de creador BTC -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BTC=Comisión de tomador BTC -# suppress inspection "UnusedProperty" -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BTC=Comisión de creador BTC mínima -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BTC=Comisión de tomador mínima BTC -# suppress inspection "UnusedProperty" - -# suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=Comisión de propuesta en BSQ -# suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=Comisión de votación en BSQ - -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=Cantidad mínima BSQ para solicitud de compensación -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=Cantidad máxima BSQ para solicitud de compensación -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MIN_AMOUNT=Cantidad mínima BSQ para petición de reembolso -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MAX_AMOUNT=Cantidad máxima BSQ para petición de reembolso - -# suppress inspection "UnusedProperty" -dao.param.QUORUM_GENERIC=Quorum requerido en BSQ para propuesta genérica -# suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=Quorum requerido en BSQ para solicitud de compensación -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REIMBURSEMENT=Quorum requerido en BSQ para solicitud de reembolso -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=Quorum requerido en BSQ para cambiar un parámetro -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Quorum requerido en BSQ para eliminar un activo -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=Quorum requerido en BSQ para una petición de confiscación -# suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=Quorum requerido en BSQ para peticiones de rol en garantía - -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_GENERIC=Umbral requerido en % para una propuesta genérica -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=Umbral requerido en % para una solicitud de compensación -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REIMBURSEMENT=Umbral requerido en % para una solicitud de reembolso -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=Umbral requerido en % para cambiar un parámetro -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Umbral requerido en % para eliminar un activo -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=Umbral requerido en % para una solicitud de confiscación -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_ROLE=Umbral requerido en % para solicitudes de rol en garantía - -# suppress inspection "UnusedProperty" -dao.param.RECIPIENT_BTC_ADDRESS=Dirección BTC del receptor - -# suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=Tasa de listado de activo por día -# suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Volumen mínimo de intercambio para activos - -# suppress inspection "UnusedProperty" -dao.param.LOCK_TIME_TRADE_PAYOUT=Tiempo límite para un pago de transacción alternativo -# suppress inspection "UnusedProperty" -dao.param.ARBITRATOR_FEE=Comisión de arbitraje en BTC - -# suppress inspection "UnusedProperty" -dao.param.MAX_TRADE_LIMIT=Límite máximo de intercambio en BTC - -# suppress inspection "UnusedProperty" -dao.param.BONDED_ROLE_FACTOR=Factor de unidad de rol en garantía en BSQ -# suppress inspection "UnusedProperty" -dao.param.ISSUANCE_LIMIT=Límite de emisión por ciclo en BSQ - -dao.param.currentValue=Valor actual: {0} -dao.param.currentAndPastValue=Valor actual: {0} (Valor cuando la propuesta fue hecha: {1}) -dao.param.blocks={0} bloques - -dao.results.invalidVotes=Hubo votos inválidos en ese ciclo. Eso pueda ocurrir si un voto no fue bien distribuido en la red Bisq.\n{0} - -# suppress inspection "UnusedProperty" -dao.phase.PHASE_UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.phase.PHASE_PROPOSAL=Fase de propuesta -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK1=Pausa 1 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=Fase de votación secreta -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK2=Pausa 2 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=Fase de revelado de voto -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK3=Pausa 3 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_RESULT=Fase de resultado - -dao.results.votes.table.header.stakeAndMerit=Peso de voto -dao.results.votes.table.header.stake=Cantidad -dao.results.votes.table.header.merit=Conseguido -dao.results.votes.table.header.vote=Votar - -dao.bond.menuItem.bondedRoles=Roles en garantía -dao.bond.menuItem.reputation=Reputación con garantía -dao.bond.menuItem.bonds=Garantías - -dao.bond.dashboard.bondsHeadline=BSQ en garantías -dao.bond.dashboard.lockupAmount=Fondos bloqueados: -dao.bond.dashboard.unlockingAmount=Desbloqueando fondos (espere hasta que el tiempo de bloqueo finalice) - - -dao.bond.reputation.header=Bloquear una garantía para reputación -dao.bond.reputation.table.header=Mis garantías de reputación -dao.bond.reputation.amount=Cantidad de BSQ a bloquear -dao.bond.reputation.time=Tiempo de desbloqueo en bloques -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=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=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 - -dao.bond.bondedReputation=Reputación en garantía -dao.bond.bondedRoles=Roles en garantía - -dao.bond.details.header=Detalles del rol -dao.bond.details.role=Rol -dao.bond.details.requiredBond=Garantía BSQ requerida -dao.bond.details.unlockTime=Tiempo de desbloqueo en bloques -dao.bond.details.link=Enlace a la descripción del rol -dao.bond.details.isSingleton=Puede tomarse por múltiples titulares -dao.bond.details.blocks={0} bloques - -dao.bond.table.column.name=Nombre -dao.bond.table.column.link=Link -dao.bond.table.column.bondType=Tipo de garantía -dao.bond.table.column.details=Detalles -dao.bond.table.column.lockupTxId=Tx ID de bloqueo -dao.bond.table.column.bondState=Estado de garantía -dao.bond.table.column.lockTime=Tiempo de desbloqueo -dao.bond.table.column.lockupDate=Fecha de bloqueo - -dao.bond.table.button.lockup=Bloquear -dao.bond.table.button.unlock=Desbloquear -dao.bond.table.button.revoke=Revocar - -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.bond.bondState.READY_FOR_LOCKUP=Aún no en garantía -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_PENDING=Bloqueo pendiente -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_CONFIRMED=Garantía bloqueada -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_PENDING=Desbloqueo pendiente -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_CONFIRMED=Desbloqueo de tx confirmada -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKING=Desbloqueando garantía -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKED=Garantía desbloqueada -# suppress inspection "UnusedProperty" -dao.bond.bondState.CONFISCATED=Garantía confiscada - -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.BONDED_ROLE=Rol en garantía -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.REPUTATION=Reputación en garantía - -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Admin Github -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=Admin foro -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=Admin Twitter -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Administrador de Keybase -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Admin Youtube -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=Mantenedor Bisq -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=Mantenedor BitcoinJ-fork -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Mantenedor Netlayer -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=Operador de la web -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=Operador Foro -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Operador de nodo semilla -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Operador nodo de precio -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Operador de nodo Bitcoin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Operador de mercados -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Operador de explorador -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Operador transmisión de notificaciones móvil -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Titular del nombre de dominio -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DNS_ADMIN=Admin DNS -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=Mediador -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=Árbitro -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=Propietario de dirección de donaciones BTC - -dao.burnBsq.assetFee=Listado de activos -dao.burnBsq.menuItem.assetFee=Listado de comisiones de activo -dao.burnBsq.menuItem.proofOfBurn=Prueba de quemado -dao.burnBsq.header=Comisión por listar de activo -dao.burnBsq.selectAsset=Seleccionar activo -dao.burnBsq.fee=Comisión -dao.burnBsq.trialPeriod=Periodo de prueba -dao.burnBsq.payFee=Pagar comisión -dao.burnBsq.allAssets=Todos los activos -dao.burnBsq.assets.nameAndCode=Nombre del activo -dao.burnBsq.assets.state=Estado -dao.burnBsq.assets.tradeVolume=Volumen de intercambio -dao.burnBsq.assets.lookBackPeriod=Periodo de verificación -dao.burnBsq.assets.trialFee=Tasa de periodo de prueba -dao.burnBsq.assets.totalFee=Comisiones totales pagadas -dao.burnBsq.assets.days={0} días -dao.burnBsq.assets.toFewDays=La comisión de activo es demasiado baja. La cantidad mínima de días para el periodo de prueba es {0}. - -# suppress inspection "UnusedProperty" -dao.assetState.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=En periodo de prueba -# suppress inspection "UnusedProperty" -dao.assetState.ACTIVELY_TRADED=Intercambiado activamente -# suppress inspection "UnusedProperty" -dao.assetState.DE_LISTED=Delistados debido a inactividad -# suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=Eliminado por votación - -dao.proofOfBurn.header=Prueba de quemado -dao.proofOfBurn.amount=Cantidad -dao.proofOfBurn.preImage=Pre-imagen -dao.proofOfBurn.burn=Quemar -dao.proofOfBurn.allTxs=Todas las transacciones de prueba de quemado -dao.proofOfBurn.myItems=Mis transacciones de prueba de quemado -dao.proofOfBurn.date=Fecha -dao.proofOfBurn.hash=Hash -dao.proofOfBurn.txs=Transacciones -dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Firmar un mensaje con clave de transacción de quemado -dao.proofOfBurn.verify.window.title=Verificar un mensaje con clave de transacción de quemado. -dao.proofOfBurn.copySig=Copiar firma al portapapeles -dao.proofOfBurn.sign=Firma -dao.proofOfBurn.message=Mensaje -dao.proofOfBurn.sig=Firma -dao.proofOfBurn.verify=Verificar -dao.proofOfBurn.verificationResult.ok=Verificación exitosa -dao.proofOfBurn.verificationResult.failed=Verificación fallida - -# suppress inspection "UnusedProperty" -dao.phase.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.phase.PROPOSAL=Fase de propuesta -# suppress inspection "UnusedProperty" -dao.phase.BREAK1=Pausa antes de la fase de votación secreta -# suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=Fase de votación secreta -# suppress inspection "UnusedProperty" -dao.phase.BREAK2=Pausa antes de la fase de revelado de vot -# suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=Fase de revelado de voto -# suppress inspection "UnusedProperty" -dao.phase.BREAK3=Pausa antes de la fase de resultados -# suppress inspection "UnusedProperty" -dao.phase.RESULT=Fase de resultado de votación - -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.PROPOSAL=Fase de propuesta -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=Votación secreta -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.VOTE_REVEAL=Revelar voto -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.RESULT=Resultado de la votación - -# suppress inspection "UnusedProperty" -dao.proposal.type.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.proposal.type.COMPENSATION_REQUEST=Petición de compensación -# suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=Solicitud de reembolso -# suppress inspection "UnusedProperty" -dao.proposal.type.BONDED_ROLE=Propuesta para un rol en garantía -# suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=Propuesta para eliminar un activo -# suppress inspection "UnusedProperty" -dao.proposal.type.CHANGE_PARAM=Propuesta para cambiar un parámetro -# suppress inspection "UnusedProperty" -dao.proposal.type.GENERIC=Propuesta genérica -# suppress inspection "UnusedProperty" -dao.proposal.type.CONFISCATE_BOND=Propuesta para confiscar una garantía - -# suppress inspection "UnusedProperty" -dao.proposal.type.short.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.proposal.type.short.COMPENSATION_REQUEST=Petición de compensación -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=Solicitud de reembolso -# suppress inspection "UnusedProperty" -dao.proposal.type.short.BONDED_ROLE=Rol en garantía -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ASSET=Eliminar una altcoin -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CHANGE_PARAM=Cambiar un parámetro -# suppress inspection "UnusedProperty" -dao.proposal.type.short.GENERIC=Propuesta genérica -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CONFISCATE_BOND=Confiscar una garantía - -dao.proposal.details=Detalles de propuesta -dao.proposal.selectedProposal=Propuesta seleccionada -dao.proposal.active.header=Propuestas del ciclo actual -dao.proposal.active.remove.confirm=¿Está seguro que quiere eliminar esta propuesta?\nLas comisiones de propuesta pagadas se perderán. -dao.proposal.active.remove.doRemove=Sí, eliminar mi propuesta -dao.proposal.active.remove.failed=No se pudo eliminar la propuesta -dao.proposal.myVote.title=Votando -dao.proposal.myVote.accept=Aceptar propuesta -dao.proposal.myVote.reject=Rechazar propuesta -dao.proposal.myVote.removeMyVote=Ignorar propuesta -dao.proposal.myVote.merit=Peso de voto por BSQ ganados -dao.proposal.myVote.stake=Peso de voto desde stake -dao.proposal.myVote.revealTxId=ID de transacción de revelado de voto -dao.proposal.myVote.stake.prompt=Cantidad máxima disponible para votaciones: {0} -dao.proposal.votes.header=Establecer cantidad para votaciones y publicar sus votos -dao.proposal.myVote.button=Publicar votos -dao.proposal.myVote.setStake.description=Después de votar en todas las propuestas tiene que establecer su cantidad para votaciones bloqueando BSQ. Cuantos más BSQ bloquee, más peso tendrá su voto.\n\nLos BSQ bloqueados para votaciones serán desbloqueados de nuevo durante la fase de revelado de votación. -dao.proposal.create.selectProposalType=Seleccionar tipo de propuesta -dao.proposal.create.phase.inactive=Por favor espere a la próxima fase de propuesta -dao.proposal.create.proposalType=Tipo de propuesta -dao.proposal.create.new=Hacer una nueva propuesta -dao.proposal.create.button=Hacer propuesta -dao.proposal.create.publish=Publicar propuesta -dao.proposal.create.publishing=Publicación de propuesta en progreso... -dao.proposal=propuesta -dao.proposal.display.type=Tipo de propuesta -dao.proposal.display.name=Nombre de usuario exacto en GitHub -dao.proposal.display.link=Enlace a información detallada -dao.proposal.display.link.prompt=Enlace a la propuesta -dao.proposal.display.requestedBsq=Cantidad solicitada en BSQ -dao.proposal.display.txId=ID de transacción de la propuesta -dao.proposal.display.proposalFee=Comisión de propuesta -dao.proposal.display.myVote=Mi voto -dao.proposal.display.voteResult=Resumen del resultado de la votación -dao.proposal.display.bondedRoleComboBox.label=Tipo de rol en garantía -dao.proposal.display.requiredBondForRole.label=Garantía requerida para el rol -dao.proposal.display.option=Opción - -dao.proposal.table.header.proposalType=Tipo de propuesta -dao.proposal.table.header.link=Enlace -dao.proposal.table.header.myVote=Mi voto -# suppress inspection "UnusedProperty" -dao.proposal.table.header.remove=Eliminar -dao.proposal.table.icon.tooltip.removeProposal=Eliminar mi propuesta -dao.proposal.table.icon.tooltip.changeVote=Voto actual: ''{0}''. Cambiar voto a: ''{1}'' - -dao.proposal.display.myVote.accepted=Aceptado -dao.proposal.display.myVote.rejected=Rechazado -dao.proposal.display.myVote.ignored=Ignorado -dao.proposal.display.myVote.unCounted=Voto no incluido en el resultado -dao.proposal.myVote.summary=Votos: {0}; Peso de voto: {1} (ganado: {2} + cantidad en juego: {3}) {4} -dao.proposal.myVote.invalid=Votación inválida - -dao.proposal.voteResult.success=Aceptado -dao.proposal.voteResult.failed=Rechazado -dao.proposal.voteResult.summary=Resultado: {0}; Umbral: {1} (requerido > {2}); Quorum: {3} (requerido > {4}) - -dao.proposal.display.paramComboBox.label=Seleccionar parámetro a cambiar -dao.proposal.display.paramValue=Valor de parámetro - -dao.proposal.display.confiscateBondComboBox.label=Elegir garantía -dao.proposal.display.assetComboBox.label=Activo a eliminar - -dao.blindVote=votación secreta - -dao.blindVote.startPublishing=Publicando transacción de voto secreto.... -dao.blindVote.success=Su voto secreto ha sido publicado satisfactoriamente.\n\nPor favor tenga en cuenta que tiene que estar en línea en la fase de revelado de votos para que la aplicación Bisq pueda publicar la transacción de revelado de voto. Sin la transacción de revelado de voto, su voto será inválido! - -dao.wallet.menuItem.send=Enviar -dao.wallet.menuItem.receive=Recibir -dao.wallet.menuItem.transactions=Transacciones - -dao.wallet.dashboard.myBalance=Balance de mi cartera - -dao.wallet.receive.fundYourWallet=Su dirección para recibir BSQ -dao.wallet.receive.bsqAddress=Dirección monedero BSQ (Dirección fresca, sin usar) - -dao.wallet.send.sendFunds=Enviar fondos -dao.wallet.send.sendBtcFunds=Enviar fondos no-BSQ (BTC) -dao.wallet.send.amount=Cantidad en BSQ -dao.wallet.send.btcAmount=Cantidad en BTC (fondos no-BSQ) -dao.wallet.send.setAmount=Indicar cantidad a retirar (la cantidad mínima es {0}) -dao.wallet.send.receiverAddress=Dirección BSQ del receptor -dao.wallet.send.receiverBtcAddress=Dirección BTC del receptor -dao.wallet.send.setDestinationAddress=Introduzca su dirección de destino -dao.wallet.send.send=Enviar fondos BSQ -dao.wallet.send.inputControl=Seleccionar entradas -dao.wallet.send.sendBtc=Enviar fondos BTC -dao.wallet.send.sendFunds.headline=Confirme la petición de retiro -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 - -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED_TX_TYPE=No reconocido -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNVERIFIED=Transacción BSQ no verificada -# suppress inspection "UnusedProperty" -dao.tx.type.enum.INVALID=Transacción BSQ inválida -# suppress inspection "UnusedProperty" -dao.tx.type.enum.GENESIS=Transacción génesis -# suppress inspection "UnusedProperty" -dao.tx.type.enum.TRANSFER_BSQ=Transferir BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.received.TRANSFER_BSQ=BSQ recibidos -# suppress inspection "UnusedProperty" -dao.tx.type.enum.sent.TRANSFER_BSQ=BSQ enviados -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PAY_TRADE_FEE=Comisión de intercambio -# suppress inspection "UnusedProperty" -dao.tx.type.enum.COMPENSATION_REQUEST=Comisión de solicitud de compensación -# suppress inspection "UnusedProperty" -dao.tx.type.enum.REIMBURSEMENT_REQUEST=Comisión para solicitud de reembolso -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROPOSAL=Comisión para propuesta -# suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=Comisión para voto secreto -# suppress inspection "UnusedProperty" -dao.tx.type.enum.VOTE_REVEAL=Revelar voto -# suppress inspection "UnusedProperty" -dao.tx.type.enum.LOCKUP=Bloquear garantía -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNLOCK=Desbloquear garantía -# suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=Comisión de listado de activo -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=Prueba de quemado -# suppress inspection "UnusedProperty" -dao.tx.type.enum.IRREGULAR=Irregular - -dao.tx.withdrawnFromWallet=BTC retirados desde el monedero -dao.tx.issuanceFromCompReq=Solicitud/emisión de compensación -dao.tx.issuanceFromCompReq.tooltip=Solicitud de compensación que lleva a emitir nuevos BSQ.\nFecha de emisión: {0} -dao.tx.issuanceFromReimbursement=Solicitud de reembolso/emisión -dao.tx.issuanceFromReimbursement.tooltip=Solicitud de reembolso que lleva a una emisión de nuevos BSQ.\nFecha de emisión: {0} -dao.proposal.create.missingBsqFunds=No tiene suficientes fondos para crear la propuesta. Si tiene una transacción BSQ no confirmada necesita esperar a una confirmación porque los BSQ se validan solo si están incluidos en un bloque.\nSe necesitan: {0} - -dao.proposal.create.missingBsqFundsForBond=No tiene suficientes fondos BSQ para este rol. Aún puede publicar esta propuesta, pero necesitará la cantidad BSQ requerida para este rol si es aceptada.\nSe necesitan: {0} - -dao.proposal.create.missingMinerFeeFunds=No tiene suficientes fondos BTC para crear la transacción propuesta. Todas las transacciones BSQ requieren una comisión de minado en BTC.\nNecesarios: {0} - -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} 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} 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. -dao.news.bisqDAO.readMoreLink=Aprender más acerca de la DAO Bisq. - -dao.news.pastContribution.title=¿HIZO CONTRIBUCIONES EN EL PASADO? SOLICITE BSQ -dao.news.pastContribution.description=Si ha contribuido a Bisq por favor use la dirección BSQ de abajo y haga una solicitud por haber participado en la distribución de BSQ génesis. -dao.news.pastContribution.yourAddress=Su dirección de monedero BSQ -dao.news.pastContribution.requestNow=Solicitar ahora - -dao.news.DAOOnTestnet.title=CORRER LA DAO BISQ EN TESTNET -dao.news.DAOOnTestnet.description=La red principal de la DAO Bisq aún no se ha lanzado pero puede aprender acerca de la DAO ejecutándola en la testnet. -dao.news.DAOOnTestnet.firstSection.title=1. Cambiar a Modo Testnet -dao.news.DAOOnTestnet.firstSection.content=Cambiar a la testnet desde la pantalla de Configuración -dao.news.DAOOnTestnet.secondSection.title=2. Adquirir algunos BSQ -dao.news.DAOOnTestnet.secondSection.content=Solicitar BSQ en Slack o comprar BSQ en Bisq -dao.news.DAOOnTestnet.thirdSection.title=3. Participar en un ciclo de votación -dao.news.DAOOnTestnet.thirdSection.content=Realizar propuestas y votar propuestas para cambiar diversos aspectos de Bisq -dao.news.DAOOnTestnet.fourthSection.title=4. Explorar el explorador de bloques BSQ -dao.news.DAOOnTestnet.fourthSection.content=Como BSQ es simplemente bitcoin, puede ver las transacciones BSQ en cualquier explorador de bloques de bitcoin -dao.news.DAOOnTestnet.readMoreLink=Leer toda la documentación - -dao.monitor.daoState=Estado DAO -dao.monitor.proposals=Estado de propuestas -dao.monitor.blindVotes=Estado de votaciones secretas - -dao.monitor.table.peers=Pares -dao.monitor.table.conflicts=Conflictos -dao.monitor.state=Estado -dao.monitor.requestAlHashes=Solicitar todos los hashes -dao.monitor.resync=Resincronizar estado DAO -dao.monitor.table.header.cycleBlockHeight=Ciclo / altura de bloque -dao.monitor.table.cycleBlockHeight=Ciclo {0} / bloque {1} -dao.monitor.table.seedPeers=Nodo semilla: {0} - -dao.monitor.daoState.headline=Estado DAO -dao.monitor.daoState.table.headline=Cadena de hashes de estado de DAO -dao.monitor.daoState.table.blockHeight=Altura de bloque -dao.monitor.daoState.table.hash=Estado de hashes de DAO -dao.monitor.daoState.table.prev=Hash previo -dao.monitor.daoState.conflictTable.headline=Estado de hashes DAO desde pares en conflicto -dao.monitor.daoState.utxoConflicts=conflictos UTXO -dao.monitor.daoState.utxoConflicts.blockHeight=Altura de bloque: {0} -dao.monitor.daoState.utxoConflicts.sumUtxo=Suma de todas las UTXO: {0} BSQ -dao.monitor.daoState.utxoConflicts.sumBsq=Suma de todas las BSQ: {0} BSQ -dao.monitor.daoState.checkpoint.popup=El estado de DAO no está sincronizado con la red. Después de reiniciar el estado DAO se resincronizará. - -dao.monitor.proposal.headline=Estado de propuestas -dao.monitor.proposal.table.headline=Estado de hashes de cadena de propuesta -dao.monitor.proposal.conflictTable.headline=Hashes de estado de propuesta desde pares en conflicto - -dao.monitor.proposal.table.hash=Estado de hash de propuesta -dao.monitor.proposal.table.prev=Hash previo -dao.monitor.proposal.table.numProposals=No. de propuestas - -dao.monitor.isInConflictWithSeedNode=Sus datos locales no están en consenso con al menos un nodo semilla. Por favor resincronice el estado DAO -dao.monitor.isInConflictWithNonSeedNode=Uno o más de sus pares no está en consenso con la red pero su nodo está en sincronía con la los nodos semilla. -dao.monitor.daoStateInSync=Su nodo local está en consenso con la red. - -dao.monitor.blindVote.headline=Estado de votaciones secretas -dao.monitor.blindVote.table.headline=Cadena de estado de hashes de votación secreta -dao.monitor.blindVote.conflictTable.headline=Estado de hashes de votación secreta desde pares en conflicto -dao.monitor.blindVote.table.hash=Hash de estado de votación secreta -dao.monitor.blindVote.table.prev=Hash previo -dao.monitor.blindVote.table.numBlindVotes=No. de votos secretos - -dao.factsAndFigures.menuItem.supply=Oferta BSQ -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=Precio medio de BSQ/USD a 90 días ponderado por volumen -dao.factsAndFigures.dashboard.avgUSDPrice30=Precio medio de BSQ/USD a 30 días ponderado por volumen -dao.factsAndFigures.dashboard.marketCap=Capitalización de mercado (basada en precio medio de BSQ/USD a 30 días) -dao.factsAndFigures.dashboard.availableAmount=BSQ totales disponibles -dao.factsAndFigures.dashboard.volumeUsd=Volumen de intercambio total en USD -dao.factsAndFigures.dashboard.volumeBtc=Volumen de intercambio total en BTC -dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection=Precio BSQ/USD medio de intercambio desde en el periodo de tiempo seleccionado en el gráfico -dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection=Precio BSQ/BTC medio de intercambio desde en el periodo de tiempo seleccionado en el gráfico - -dao.factsAndFigures.supply.issuedVsBurnt=BSQ emitidos v. BSQ quemados - -dao.factsAndFigures.supply.issued=BSQ emitidos -dao.factsAndFigures.supply.compReq=Solicitudes de compensación -dao.factsAndFigures.supply.reimbursement=Solicitudes de reembolso -dao.factsAndFigures.supply.genesisIssueAmount=BSQ emitidos en la transacción génesis -dao.factsAndFigures.supply.compRequestIssueAmount=BSQ emitidos para solicitudes de compensación -dao.factsAndFigures.supply.reimbursementAmount=BSQ emitidos para solicitudes de reembolso -dao.factsAndFigures.supply.totalIssued=BSQ totales emitidos -dao.factsAndFigures.supply.totalBurned=BSQ totales quemados -dao.factsAndFigures.supply.chart.tradeFee.toolTip={0}\n{1} -dao.factsAndFigures.supply.burnt=BSQ quemados - -dao.factsAndFigures.supply.priceChat=Precio BSQ -dao.factsAndFigures.supply.volumeChat=Volumen de intercambio -dao.factsAndFigures.supply.tradeVolumeInUsd=Volumen de intercambio en USD -dao.factsAndFigures.supply.tradeVolumeInBtc=Volumen de intercambio en BTC -dao.factsAndFigures.supply.bsqUsdPrice=Precio BSQ/USD -dao.factsAndFigures.supply.bsqBtcPrice=precio BSQ/BTC -dao.factsAndFigures.supply.btcUsdPrice=Precio BTC/USD - -dao.factsAndFigures.supply.locked=Estado global de BSQ bloqueados -dao.factsAndFigures.supply.totalLockedUpAmount=Bloqueados en garantías -dao.factsAndFigures.supply.totalUnlockingAmount=Desbloqueando BSQ de garantías -dao.factsAndFigures.supply.totalUnlockedAmount=BSQ desbloqueados de garantías -dao.factsAndFigures.supply.totalConfiscatedAmount=BSQ confiscados de garantías -dao.factsAndFigures.supply.proofOfBurn=Prueba de quemado -dao.factsAndFigures.supply.bsqTradeFee=Tasas de intercambio en BSQ -dao.factsAndFigures.supply.btcTradeFee=Tasas de intercambio en BTC - -dao.factsAndFigures.transactions.genesis=Transacción génesis -dao.factsAndFigures.transactions.genesisBlockHeight=Altura de bloque génesis -dao.factsAndFigures.transactions.genesisTxId=ID transacción génesis -dao.factsAndFigures.transactions.txDetails=Estadísticas de transacción BSQ -dao.factsAndFigures.transactions.allTx=No. de todas las transacciones BSQ -dao.factsAndFigures.transactions.utxo=No. de todos los outputs de transacciones no gastadas -dao.factsAndFigures.transactions.compensationIssuanceTx=No. de todas las transacciones emitidas de solicitudes de compensación -dao.factsAndFigures.transactions.reimbursementIssuanceTx=No. de todas las transacciones emitidas de solicitud de reembolso -dao.factsAndFigures.transactions.burntTx=No. de todas las transacciones de tasa de pago -dao.factsAndFigures.transactions.invalidTx=No. de todas las transacciones inválidas -dao.factsAndFigures.transactions.irregularTx=No. de todas las transacciones irregulares - - - #################################################################### # Windows #################################################################### @@ -2120,8 +1406,6 @@ disputeSummaryWindow.close.noPayout.text=¿Quiere cerrar sin realizar algún pag emptyWalletWindow.headline=Herramienta de monedero {0} de emergencia emptyWalletWindow.info=Por favor usar sólo en caso de emergencia si no puede acceder a sus fondos desde la Interfaz de Usuario (UI).\n\nPor favor, tenga en cuenta que todas las ofertas abiertas se cerrarán automáticamente al usar esta herramienta.\n\nAntes de usar esta herramienta, por favor realice una copia de seguridad del directorio de datos. Puede hacerlo en \"Cuenta/Copia de Seguridad\".\n\nPor favor repórtenos su problema y envíe un reporte de fallos en Github en el foro de Bisq para que podamos investigar qué causa el problema. emptyWalletWindow.balance=Su balance disponible en cartera -emptyWalletWindow.bsq.btcBalance=Balance de Satoshis no-BSQ - emptyWalletWindow.address=Su dirección de destino emptyWalletWindow.button=Enviar todos los fondos emptyWalletWindow.openOffers.warn=Tiene ofertas abiertas que se eliminarán si vacía el monedero.\n¿Está seguro de que quiere vaciar su monedero? @@ -2146,10 +1430,8 @@ filterWindow.seedNode=Nodos semilla filtrados (direcciones onion separadas por c filterWindow.priceRelayNode=nodos de retransmisión de precio filtrados (direcciones onion separadas por coma) filterWindow.btcNode=Nodos Bitcoin filtrados (direcciones + puerto separadas por coma) filterWindow.preventPublicBtcNetwork=Prevenir uso de la red Bitcoin pública -filterWindow.disableDao=Deshabilitar DAO filterWindow.disableAutoConf=Deshabilitar autoconfirmación filterWindow.autoConfExplorers=Exploradores de autoconfirmación filtrados (direcciones separadas por coma) -filterWindow.disableDaoBelowVersion=Versión mínima requerida para DAO filterWindow.disableTradeBelowVersion=Versión mínima requerida para intercambios. filterWindow.add=Añadir filtro filterWindow.remove=Eliminar filtro @@ -2223,7 +1505,6 @@ tradeDetailsWindow.detailData=Detalle de datos txDetailsWindow.headline=Detalles de transacción txDetailsWindow.btc.note=Ha enviado BTC -txDetailsWindow.bsq.note=Ha enviado fondos BSQ. BSQ son bitcoin coloreados, con lo que la transacción no se mostrará en el explorador BSQ hasta que sea confirmada en un bloque BTC. txDetailsWindow.sentTo=Enviado a txDetailsWindow.txId=TxId @@ -2235,9 +1516,6 @@ closedTradesSummaryWindow.totalMinerFee.title=Suma de todas las trasas de minado closedTradesSummaryWindow.totalMinerFee.value={0} ({1} de la cantidad total intercambiada) closedTradesSummaryWindow.totalTradeFeeInBtc.title=Suma de todas las tasas de intercambio pagadas en BTC closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} de la cantidad total intercambiada) -closedTradesSummaryWindow.totalTradeFeeInBsq.title=Suma de todas las tasas de intercambio pagadas en BSQ -closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} de la cantidad total intercambiada) - walletPasswordWindow.headline=Introducir contraseña para desbloquear torNetworkSettingWindow.header=Confirmación de red Tor @@ -2317,12 +1595,6 @@ popup.warning.tooLargePercentageValue=No puede establecer un porcentaje del 100% popup.warning.examplePercentageValue=Por favor, introduzca un número de porcentaje como \"5.4\" para 5.4% popup.warning.noPriceFeedAvailable=No hay una fuente de precios disponible para esta moneda. No puede utilizar un precio basado en porcentaje.\nPor favor, seleccione un precio fijo. popup.warning.sendMsgFailed=El envío de mensaje a su compañero de intercambio falló.\nPor favor, pruebe de nuevo y si continúa fallando, reporte el fallo. -popup.warning.insufficientBtcFundsForBsqTx=No tiene suficientes fondos BTC para pagar la comisión de minado para esta transacción.\nPor favor ingrese fondos en su monedero BTC.\nFondos faltantes: {0} -popup.warning.bsqChangeBelowDustException=Esta transacción crea un output BSQ de cambio que está por debajo del límite dust (5.46 BSQ) y sería rechazado por la red Bitcoin.\n\nTiene que enviar una cantidad mayor para evitar el output de cambio (Ej. agregando la cantidad de dust a su cantidad de envío) o añadir más fondos BSQ a su cartera para evitar generar un output de dust.\n\nEl output dust es {0}. -popup.warning.btcChangeBelowDustException=Esta transacción crea un output de cambio que está por debajo del límite de dust (546 Satoshi) y sería rechazada por la red Bitcoin.\n\nDebe agregar la cantidad de dust a su cantidad de envío para evitar generar un output de dust.\n\nEl output de dust es {0}. - -popup.warning.insufficientBsqFundsForBtcFeePayment=Necesitará más BSQ para hacer esta transacción -los últimos 5.46BSQ en su monedero no pueden usarse para pagar tasas de intercambio debido a límites dust en el protocolo Bitcoin.\n\nPuede comprar más BSQ o pagar las tasas de intercambio con BTC.\n\nFondos necesarios: {0} -popup.warning.noBsqFundsForBtcFeePayment=Su monedero BSQ no tiene suficientes fondos para pagar la comisión de intercambio en BSQ. popup.warning.messageTooLong=Su mensaje excede el tamaño máximo permitido. Por favor, envíelo por partes o súbalo a un servicio como https://pastebin.com popup.warning.lockedUpFunds=Ha bloqueado fondos de un intercambio fallido.\nBalance bloqueado: {0}\nDirección de depósito TX: {1}\nID de intercambio: {2}.\n\nPor favor, abra un ticket de soporte seleccionando el intercambio en la pantalla de intercambios pendientes y haciendo clic en \"alt + o\" o \"option + o\"." @@ -2336,8 +1608,6 @@ popup.warning.nodeBanned=Uno de los nodos {0} ha sido baneado. popup.warning.priceRelay=retransmisión de precio popup.warning.seed=semilla popup.warning.mandatoryUpdate.trading=Por favor, actualice a la última versión de Bisq. Se lanzó una actualización obligatoria que inhabilita intercambios con versiones anteriores. Por favor, lea el Foro de Bisq para más información\n -popup.warning.mandatoryUpdate.dao=Por favor, actualice a la última versión de Bisq. Se lanzó una actualización obligatoria que inhabilita la DAO Bisq y BSQ para las versiones anteriores. Por favor, visite el Foro de Bisq para más información -popup.warning.disable.dao=El DAO Bisq y BSQ estén temporalmente deshabilitados. Por favor revise el foro de Bisq para más información. popup.warning.noFilter=No hemos recibido un objeto de filtro desde los nodos semilla. Esta situación no se esperaba. Por favor, informe a los desarrolladores Bisq. popup.warning.burnBTC=Esta transacción no es posible, ya que las comisiones de minado de {0} excederían la cantidad a transferir de {1}. Por favor, espere a que las comisiones de minado bajen o hasta que haya acumulado más BTC para transferir. @@ -2356,7 +1626,6 @@ 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! @@ -2528,7 +1797,6 @@ navigation.settings.preferences=\"Ajustes/Preferencias\" # suppress inspection "UnusedProperty" navigation.funds.transactions=\"Fondos/Transacciones\" navigation.support=\"Soporte\" -navigation.dao.wallet.receive=\"DAO/Monedero BSQ/Recibir\" #################################################################### @@ -2558,12 +1826,6 @@ XMR_MAINNET=Red principal de Monero XMR_TESTNET=Red de prueba de Monero # suppress inspection "UnusedProperty" XMR_STAGENET=Stagenet Monero -# suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Testnet de Bitcoin DAO (depreciada) -# suppress inspection "UnusedProperty" -BTC_DAO_BETANET=DAO Bisq Betanet (red principal Bitcoin) -# suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=Regtest de Bitcoin DAO time.year=Año time.month=Mes @@ -2910,9 +2172,7 @@ validation.accountNrChars=El número de cuenta debe consistir en {0} caracteres. validation.btc.invalidAddress=La dirección no es correcta. Por favor compruebe el formato de la dirección. validation.integerOnly=Por favor, introduzca sólo números enteros. validation.inputError=Su entrada causó un error:\n{0} -validation.bsq.insufficientBalance=Su saldo disponible es {0} validation.btc.exceedsMaxTradeLimit=Su límite de intercambio es {0}. -validation.bsq.amountBelowMinAmount=La cantidad mínima es {0} validation.nationalAccountId={0} debe consistir de {1} número(s). #new @@ -2934,7 +2194,6 @@ validation.bic.invalidLocationCode=BIC contiene un código de localización inv validation.bic.invalidBranchCode=BIC contiene una sucursal inválida validation.bic.sepaRevolutBic=Cuentas Revolut Sepa no soportadas. validation.btc.invalidFormat=Formato inválido para una dirección Bitcoin. -validation.bsq.invalidFormat=Formato inválido para una dirección BSQ. validation.email.invalidAddress=Dirección inválida validation.iban.invalidCountryCode=Código de país inválido validation.iban.checkSumNotNumeric=El checksum debe ser numérico diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 2f74da5a53..707ec0b4ca 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -192,7 +192,6 @@ shared.tradeWalletBalance=موجودی کیف‌پول معاملات shared.makerTxFee=سفارش گذار: {0} shared.takerTxFee=پذیرنده سفارش: {0} shared.iConfirm=تایید می‌کنم -shared.tradingFeeInBsqInfo=≈ {0} shared.openURL=باز {0} shared.fiat=فیات shared.crypto=کریپتو @@ -204,9 +203,6 @@ shared.actions=عملیات shared.buyerUpperCase=خریدار shared.sellerUpperCase=فروشنده shared.new=جدید -shared.blindVoteTxId=شناسه تراکنش رای ناشناس -shared.proposal=پیشنهاد -shared.votes=آرا shared.learnMore=بیشتر بدانید shared.dismiss=رد کردن shared.selectedArbitrator=داور انتخاب شده @@ -240,7 +236,6 @@ mainView.menu.funds=وجوه mainView.menu.support=پشتیبانی mainView.menu.settings=تنظیمات mainView.menu.account=حساب -mainView.menu.dao=DAO (موسسه خودمختار غیرمتمرکز) mainView.marketPriceWithProvider.label=قیمت بازار بر اساس {0} mainView.marketPrice.bisqInternalPrice=قیمت آخرین معامله‌ی Bisq @@ -257,13 +252,11 @@ mainView.footer.localhostBitcoinNode=(لوکال هاست) 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=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} -mainView.footer.daoFullNode=گره کامل DAO mainView.bootstrapState.connectionToTorNetwork=(1/4) در حال ارتباط با شبکه Tor ... mainView.bootstrapState.torNodeCreated=(2/4) گره Tor ایجاد شد @@ -435,7 +428,6 @@ createOffer.fundsBox.networkFee=کارمزد استخراج createOffer.fundsBox.placeOfferSpinnerInfo=انتشار پیشنهاد در حال انجام است ... createOffer.fundsBox.paymentLabel=معامله Bisq با شناسه‌ی {0} createOffer.fundsBox.fundsStructure=({0} سپرده‌ی اطمینان، {1} کارمزد معامله، {2} کارمزد تراکنش) -createOffer.fundsBox.fundsStructure.BSQ=({0} سپرده‌ی اطمینان، {1} کارمزد تراکنش) + {2} کارمزد معامله createOffer.success.headline=پیشنهاد شما، منتشر شد. createOffer.success.info=شما می توانید پیشنهادهای باز خود را در \"سبد سهام/پیشنهادهای باز من\" مدیریت نمایید. createOffer.info.sellAtMarketPrice=شما همیشه به نرخ روز بازار خواهید فروخت، زیرا قیمت پیشنهادتان به طور مداوم به روزرسانی خواهد شد. @@ -636,7 +628,6 @@ portfolio.pending.step2_buyer.amountToTransfer=مبلغ انتقال portfolio.pending.step2_buyer.sellersAddress=آدرس {0} فروشنده portfolio.pending.step2_buyer.buyerAccount=حساب پرداخت مورد استفاده portfolio.pending.step2_buyer.paymentStarted=پرداخت آغاز شد -portfolio.pending.step2_buyer.fillInBsqWallet=Pay from BSQ wallet portfolio.pending.step2_buyer.warn=You still have not done your {0} payment!\nPlease note that the trade has to be completed by {1}. portfolio.pending.step2_buyer.openForDispute=You have not completed your payment!\nThe max. period for the trade has elapsed.Please contact the mediator for assistance. portfolio.pending.step2_buyer.paperReceipt.headline=آیا کاغذ رسید را برای فروشنده‌ی بیتکوین فرستادید؟ @@ -882,7 +873,6 @@ funds.locked.locked=قفل شده به صورت چند امضایی برای م funds.tx.direction.sentTo=ارسال به: funds.tx.direction.receivedWith=دریافت با: funds.tx.direction.genesisTx=از تراکنش پیدایش: -funds.tx.txFeePaymentForBsqTx=کارمزد استخراج برای تراکنش BSQ funds.tx.createOfferFee=سفارش‌گذار و هزینه تراکنش: {0} funds.tx.takeOfferFee=پذیرنده و هزینه تراکنش: {0} funds.tx.multiSigDeposit=سپرده چند امضایی: {0} @@ -896,15 +886,11 @@ funds.tx.unknown=دلیل ناشناخته: {0} funds.tx.noFundsFromDispute=عدم بازپرداخت از مناقشه funds.tx.receivedFunds=وجوه دریافت شده funds.tx.withdrawnFromWallet=برداشت شده از کیف پول -funds.tx.withdrawnFromBSQWallet=مقدار BTC برداشت شده از کیف پول BSQ funds.tx.memo=Memo funds.tx.noTxAvailable=هیچ تراکنشی موجود نیست funds.tx.revert=عودت funds.tx.txSent=تراکنش به طور موفقیت آمیز به یک آدرس جدید در کیف پول محلی Bisq ارسال شد. funds.tx.direction.self=ارسال شده به خودتان -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. @@ -996,9 +982,7 @@ settings.tab.about=درباره setting.preferences.general=اولویت‌های عمومی setting.preferences.explorer=Bitcoin Explorer -setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=حداکثر تفاوت از قیمت روز بازار -setting.preferences.bsqAverageTrimThreshold=Outlier threshold for BSQ rate setting.preferences.avoidStandbyMode=حالت «آماده باش» را نادیده بگیر setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled @@ -1032,19 +1016,6 @@ setting.preferences.notifyOnPreRelease=Receive pre-release notifications setting.preferences.resetAllFlags=تنظیم مجدد تمام پرچم‌های \"دوباره نشان نده\" settings.preferences.languageChange=اعمال تغییر زبان به تمام صفحات مستلزم یک راه‌اندازی مجدد است. settings.preferences.supportLanguageWarning=In case of a dispute, please note that mediation is handled in {0} and arbitration in {1}. -setting.preferences.daoOptions=گزینه‌های DAO -setting.preferences.dao.resyncFromGenesis.label=بازسازی وضعیت DAO از تراکنش پیدایش -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.resync=Resync from genesis and shutdown -setting.preferences.dao.isDaoFullNode=Bisq را به عنوان یک گره کامل DAO اجرا کن -setting.preferences.dao.rpcUser=نام کاربری RPC -setting.preferences.dao.rpcPw=رمزعبور RPC -setting.preferences.dao.blockNotifyPort=Block notify port -setting.preferences.dao.fullNodeInfo=For running Bisq as DAO full node you need to have Bitcoin Core locally running and RPC enabled. All requirements are documented in ''{0}''.\n\nAfter changing the mode you need to restart. -setting.preferences.dao.fullNodeInfo.ok=باز کردن صفحه مستندات -setting.preferences.dao.fullNodeInfo.cancel=خیر، من با حالت «گره سبک» ادامه می‌دهم settings.preferences.editCustomExplorer.headline=Explorer Settings settings.preferences.editCustomExplorer.description=Choose a system defined explorer from the list on the left, and/or customize to suit your own preferences. settings.preferences.editCustomExplorer.available=Available explorers @@ -1090,7 +1061,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 DAO chain height: {0} | Bitcoin Peers chain height: {1} +settings.net.chainHeight=Bitcoin Peers chain height: {1} settings.net.ips=[آدرس آی پی: پورت | نام میزبان: پورت | آدرس Onion : پورت] (جدا شده با ویرگول). اگر از پیش فرض (8333) استفاده می شود، پورت می تواند حذف شود. settings.net.seedNode=گره ی اصلی settings.net.directPeer=همتا (مستقیم) @@ -1144,14 +1115,10 @@ setting.about.shortcuts.walletDetails=Open wallet details window setting.about.shortcuts.openEmergencyBtcWalletTool=Open emergency wallet tool for BTC wallet -setting.about.shortcuts.openEmergencyBsqWalletTool=Open emergency wallet tool for BSQ wallet - setting.about.shortcuts.showTorLogs=Toggle log level for Tor messages between DEBUG and WARN setting.about.shortcuts.manualPayoutTxWindow=Open window for manual payout from 2of2 Multisig deposit tx -setting.about.shortcuts.reRepublishAllGovernanceData=Republish DAO governance data (proposals, votes) - setting.about.shortcuts.removeStuckTrade=Open popup to move failed trade to open trades tab again setting.about.shortcuts.removeStuckTrade.value=Select failed trade and press: {0} @@ -1337,687 +1304,6 @@ account.notifications.noWebCamFound.warning=دوبین پیدا نشد.\n\nلط account.notifications.priceAlert.warning.highPriceTooLow=قیمت بالاتر باید از قیمت پایین‌تر بزرگتر باشد. account.notifications.priceAlert.warning.lowerPriceTooHigh=قیمت پایین‌تر باید از قیمت بالاتر کوچکتر باشد. - - - -#################################################################### -# DAO -#################################################################### - -dao.tab.factsAndFigures=واقعیت ها و شکل ها -dao.tab.bsqWallet=کیف پول BSQ  -dao.tab.proposals=حکمرانی -dao.tab.bonding=ضمانت -dao.tab.proofOfBurn=کارمزد ثبت دارایی/اثبات امحا -dao.tab.monitor=مانیتور شبکه -dao.tab.news=اخبار - -dao.paidWithBsq=پرداخت شده با BSQ -dao.availableBsqBalance=آمادگی برای ارسال (تغییر خروجی های تائید شده + تائید نشده) -dao.verifiedBsqBalance=متعادل کردن همه UTXO های تائید شده -dao.unconfirmedChangeBalance=متعادل کردن همه تغییر خروجی های تائید نشده -dao.unverifiedBsqBalance=متعادل کردن همه تراکنش های تائید نشده (تائید بلاک های در انتظار) -dao.lockedForVoteBalance=در حال استفاده برای رای دادن -dao.lockedInBonds=قفل شده در ضمانت -dao.availableNonBsqBalance=موجودی غیر BSQ در دسترس (BTC) -dao.reputationBalance=Merit Value (not spendable) - -dao.tx.published.success=تراکنش شما به طور موفقیت آمیز منتشر شد. -dao.proposal.menuItem.make=ارائه ی پیشنهاد -dao.proposal.menuItem.browse=مرور طرح‌های پیشنهادی باز -dao.proposal.menuItem.vote=رای دادن به طرح‌های پیشنهادی -dao.proposal.menuItem.result=تنایج رای‌گیری -dao.cycle.headline=دوره رای‌گیری -dao.cycle.overview.headline=مرور دوره رای‌گیری -dao.cycle.currentPhase=مرحله فعلی -dao.cycle.currentBlockHeight=طول بلاک فعلی -dao.cycle.proposal=مرحله طرح پیشنهادی -dao.cycle.proposal.next=مرحله بعدی طرح پیشنهادی -dao.cycle.blindVote=مرحله رای‌گیری ناشناس -dao.cycle.voteReveal=مرحله آشکار کردن رای -dao.cycle.voteResult=تنیجه را‌ی‌گیری -dao.cycle.phaseDuration={0} بلاک (≈{1})؛ بلاک {2} - {3} (≈{4} - ≈{5}) -dao.cycle.phaseDurationWithoutBlocks=بلاک {0} - {1} (≈{2} - ≈{3}) - -dao.voteReveal.txPublished.headLine=تراکنش انتشار رای، منتشر شد -dao.voteReveal.txPublished=رای شما نشان داد که تراکنش با شناسه تراکنش {0} با موفقیت منتشر شده بود.\n\nاگر شما در رای گیری DAO شرکت داشته باشید، این امر به صورت خودکار توسط نرم افزار اتفاق می افتد. - -dao.results.cycles.header=دوره‌ها -dao.results.cycles.table.header.cycle=دوره -dao.results.cycles.table.header.numProposals=طرح‌های پیشنهادی -dao.results.cycles.table.header.voteWeight=وزن رای -dao.results.cycles.table.header.issuance=صدور - -dao.results.results.table.item.cycle=دوره {0} شروع شد: {1} - -dao.results.proposals.header=طرح‌های پیشنهادی دوره انتخاب شده -dao.results.proposals.table.header.nameLink=Name/link -dao.results.proposals.table.header.details=جزئیات -dao.results.proposals.table.header.myVote=رای من -dao.results.proposals.table.header.result=تنیجه را‌ی‌گیری -dao.results.proposals.table.header.threshold=Threshold -dao.results.proposals.table.header.quorum=Quorum - -dao.results.proposals.voting.detail.header=نتایج رای‌گیری برای طرح پیشنهادی انتخاب شده - -dao.results.exceptions=استثنائات نتایج رای گیری - -# suppress inspection "UnusedProperty" -dao.param.UNDEFINED=تعریف نشده - -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BSQ=کارمزد BSQ سفارش‌گذار -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BSQ=کارمزد BSQ پذیرنده -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BSQ=حداقل کارمزد BSQ سفارش‌گذار -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BSQ=حداقل کارمزد BSQ پذیرنده -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BTC=کارمزد BTC سفارش‌گذار -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BTC=کارمزد BTC پذیرنده -# suppress inspection "UnusedProperty" -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BTC=حداقل کارمزد BTC سفارش‌گذار -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BTC=حداقل کارمزد BTC پذیرنده -# suppress inspection "UnusedProperty" - -# suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=کارمزد طرح پیشنهادی به BSQ -# suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=کارمزد رای دادن به BSQ - -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=حداقل مقدار BSQ برای درخواست خسارت -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=حداکثر مقدار BSQ برای درخواست خسارت -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MIN_AMOUNT=حداقل مقدار BSQ برای درخواست بازپرداخت -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MAX_AMOUNT=حداکثر مقدار BSQ برای درخواست بازپرداخت - -# suppress inspection "UnusedProperty" -dao.param.QUORUM_GENERIC=حدنصاب مورد نیاز به BSQ برای طرح پیشنهادی کلی -# suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=حدنصاب مورد نیاز به BSQ برای درخواست خسارت -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REIMBURSEMENT=حدنصاب مورد نیاز به BSQ برای درخواست بازپرداخت -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=حدنصاب مورد نیاز به BSQ برای تغییر یک پارامتر -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=حدنصاب موردنیاز به BSQ برای حذف کردن یک دارایی -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=حدنصاب مورد نیاز به BSQ برای درخواست یک مصادره -# suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=حدنصاب مورد نیاز به BSQ برای درخواست‌های نقش ضامن - -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_GENERIC=آستانه مورد نیاز به % برای طرح پیشنهادی کلی -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=آستانه مورد نیاز به % برای درخوسات خسارت -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REIMBURSEMENT=آستانه مورد نیاز به % برای درخواست بازپرداخت -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=آستانه مورد نیاز به % برای تغییر یک پارامتر -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=آستانه مورد نیاز به % برای حذف یک دارایی -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=آستانه مورد نیاز به % برای درخواست یک مصادره -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_ROLE=آستانه مورد نیاز به % برای درخواست‌های نقش ضامن - -# suppress inspection "UnusedProperty" -dao.param.RECIPIENT_BTC_ADDRESS=آدرس BTC گیرنده - -# suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=کارمزد ثبت دارایی در روز -# suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=حداقل حجم معامله برای دارایی‌ها - -# suppress inspection "UnusedProperty" -dao.param.LOCK_TIME_TRADE_PAYOUT=زمان قفل کردن برای پرداخت معامله جایگزین tx  -# suppress inspection "UnusedProperty" -dao.param.ARBITRATOR_FEE=دستمزد داور در BTC - -# suppress inspection "UnusedProperty" -dao.param.MAX_TRADE_LIMIT=حداکثر محدودیت معامله در BTC - -# suppress inspection "UnusedProperty" -dao.param.BONDED_ROLE_FACTOR=ویژگی واحد نقش تضمین شده در BSQ -# suppress inspection "UnusedProperty" -dao.param.ISSUANCE_LIMIT=محدودیت صدور در چرخه در BSQ - -dao.param.currentValue=مقدار فعلی: {0} -dao.param.currentAndPastValue=Current value: {0} (Value when proposal was made: {1}) -dao.param.blocks={0} بلاک - -dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was not distributed well in the Bisq network.\n{0} - -# suppress inspection "UnusedProperty" -dao.phase.PHASE_UNDEFINED=تعریف نشده -# suppress inspection "UnusedProperty" -dao.phase.PHASE_PROPOSAL=مرحله طرح پیشنهادی -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK1=وقفه 1 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=مرحله رای‌گیری ناشناس -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK2=وقفه 2 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=مرحله آشکار کردن رای -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK3=وقفه 3 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_RESULT=نتیجه مرحله - -dao.results.votes.table.header.stakeAndMerit=وزن رای -dao.results.votes.table.header.stake=سهام -dao.results.votes.table.header.merit=کسب شده -dao.results.votes.table.header.vote=رأی - -dao.bond.menuItem.bondedRoles=نقش‌های ضمانتی -dao.bond.menuItem.reputation=اعتبار ضمانتی -dao.bond.menuItem.bonds=ضمانت‌ها - -dao.bond.dashboard.bondsHeadline=BSQ ضمانت شده -dao.bond.dashboard.lockupAmount=وجوه قفل شده -dao.bond.dashboard.unlockingAmount=رها کردن وجوه (صبر کن تا زمان قفل بودن به پایان برسد) - - -dao.bond.reputation.header=ضمانتی را برای اعتبار قفل کن -dao.bond.reputation.table.header=ضمانت‌های اعتبار من -dao.bond.reputation.amount=مقدار BSQ برای قفل کردن -dao.bond.reputation.time=زمان رها شدن به بلاک -dao.bond.reputation.salt=داده تصادفی -dao.bond.reputation.hash=تابع درهم ساز (هش) -dao.bond.reputation.lockupButton=قفل کردن -dao.bond.reputation.lockup.headline=تایید تراکنش قفل کردن وجه -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.unlock.headline=تایید تراکنش رها کردن وجه -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.allBonds.header=همه ضمانت‌ها - -dao.bond.bondedReputation=اعتبار ضمانت شده -dao.bond.bondedRoles=نقش‌های ضمانت شده - -dao.bond.details.header=جزئیات نقش -dao.bond.details.role=نقش -dao.bond.details.requiredBond=ضمانت BSQ مورد نیاز -dao.bond.details.unlockTime=زمان رها شدن به بلاک -dao.bond.details.link=پیوند اینترنتی به توضیحات نقش -dao.bond.details.isSingleton=می‌تواند توسط چند نقش آفرین گرفته شود -dao.bond.details.blocks={0} بلاک - -dao.bond.table.column.name=نام -dao.bond.table.column.link=پیوند -dao.bond.table.column.bondType=نوع ضمانت -dao.bond.table.column.details=جزئیات -dao.bond.table.column.lockupTxId=شناسه تراکنش قفل وجوه -dao.bond.table.column.bondState=وضعیت ضمانت -dao.bond.table.column.lockTime=Unlock time -dao.bond.table.column.lockupDate=تاریخ قفل کردن وجه - -dao.bond.table.button.lockup=قفل کردن -dao.bond.table.button.unlock=باز کردن -dao.bond.table.button.revoke=ابطال - -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNDEFINED=تعریف نشده -# suppress inspection "UnusedProperty" -dao.bond.bondState.READY_FOR_LOCKUP=هنوز ضمانت نشده -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_PENDING=در انتظار قفل کردن -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_CONFIRMED=ضمانت قفل شده -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_PENDING=در انتظار رها شدن -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_CONFIRMED=تراکنش رها شدن وجه تایید شده -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKING=رها کردن وجه ضمانتی -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKED=وجه ضمانت شده رها شده -# suppress inspection "UnusedProperty" -dao.bond.bondState.CONFISCATED=ضمانت مصادره شده - -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.UNDEFINED=تعریف نشده -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.BONDED_ROLE=نقش ضمانت شده -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.REPUTATION=اعتبار ضمانت شده - -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.UNDEFINED=تعریف نشده -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=مدیر Github -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=مدیر تالار -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=مدیر توئیتر -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=مدیر یوتیوب -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=نگهدارنده Bisq -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=نگهدارنده BitcoinJ-fork -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.NETLAYER_MAINTAINER=نگهدارنده لایه شبکه -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=گرداننده سایت -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=گرداننده تالار -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.SEED_NODE_OPERATOR=گرداننده گره seed -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=گرداننده گره قیمت -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Bitcoin node operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=گرداننده بازارها -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=اپراتور بازپخش اعلان های موبایل -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=صاحب نام دامنه -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DNS_ADMIN=مدیر DNS -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=واسط -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=داور -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=مالک آدرس کمک مالی BTC - -dao.burnBsq.assetFee=ثبت دارایی -dao.burnBsq.menuItem.assetFee=کارمزد ثبت دارایی -dao.burnBsq.menuItem.proofOfBurn=اثبات امحا -dao.burnBsq.header=کارمزد ثبت دارایی -dao.burnBsq.selectAsset=انتخاب دارایی -dao.burnBsq.fee=کارمزد -dao.burnBsq.trialPeriod=دوره زمانی امتحانی -dao.burnBsq.payFee=کارمزد پرداخت -dao.burnBsq.allAssets=همه دارایی‌ها -dao.burnBsq.assets.nameAndCode=نام دارایی -dao.burnBsq.assets.state=حالت -dao.burnBsq.assets.tradeVolume=حجم معامله -dao.burnBsq.assets.lookBackPeriod=دوره زمانی تایید -dao.burnBsq.assets.trialFee=کارمزد دوره زمانی امتحانی -dao.burnBsq.assets.totalFee=مجموع کارمزدهای پرداختی -dao.burnBsq.assets.days={0} روز -dao.burnBsq.assets.toFewDays=کارمزد دارایی کافی نیست. حداقل مقدار برای دوره زمانی امتحانی {0} است. - -# suppress inspection "UnusedProperty" -dao.assetState.UNDEFINED=تعریف نشده -# suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=در دوره زمانی امتحانی -# suppress inspection "UnusedProperty" -dao.assetState.ACTIVELY_TRADED=فعالانه در حال معامله -# suppress inspection "UnusedProperty" -dao.assetState.DE_LISTED=حذف شده به دلیل عدم فعالیت -# suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=حذف شده به واسطه رای گیری - -dao.proofOfBurn.header=اثبات امحا -dao.proofOfBurn.amount=مقدار -dao.proofOfBurn.preImage=پیش نسخه -dao.proofOfBurn.burn=امحا -dao.proofOfBurn.allTxs=تمام تراکنش‌های اثبات امحا -dao.proofOfBurn.myItems=تراکنش‌های اثبات امحا من -dao.proofOfBurn.date=تاریخ -dao.proofOfBurn.hash=هش -dao.proofOfBurn.txs=تراکنش‌ها -dao.proofOfBurn.pubKey=کلید عمومی -dao.proofOfBurn.signature.window.title=امضاء یک پیغام با کلیدی از اثبات سوختن تراکنش -dao.proofOfBurn.verify.window.title=تائید یک پیغام با کلیدی از اثبات سوختن تراکنش -dao.proofOfBurn.copySig=کپی کردن امضا به حافظه موقت -dao.proofOfBurn.sign=امضا کردن -dao.proofOfBurn.message=پیام -dao.proofOfBurn.sig=امضا -dao.proofOfBurn.verify=تایید کردن -dao.proofOfBurn.verificationResult.ok=تایید با موفقیت انجام شد -dao.proofOfBurn.verificationResult.failed=تایید نا موفق بود - -# suppress inspection "UnusedProperty" -dao.phase.UNDEFINED=تعریف نشده -# suppress inspection "UnusedProperty" -dao.phase.PROPOSAL=مرحله طرح پیشنهادی -# suppress inspection "UnusedProperty" -dao.phase.BREAK1=وقفه قبل از مرحله رای‌گیری ناشناس -# suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=مرحله رای‌گیری ناشناس -# suppress inspection "UnusedProperty" -dao.phase.BREAK2=وقفه قبل از مرحله آشکارسازی رای -# suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=مرحله آشکار کردن رای -# suppress inspection "UnusedProperty" -dao.phase.BREAK3=وقفه قبل از مرحله اعلام نتایج -# suppress inspection "UnusedProperty" -dao.phase.RESULT=مرحله نتایج رای‌گیری - -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.PROPOSAL=مرحله طرح پیشنهادی -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=رای ناشناس -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.VOTE_REVEAL=آشکارسازی رأی -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.RESULT=تنیجه را‌ی‌گیری - -# suppress inspection "UnusedProperty" -dao.proposal.type.UNDEFINED=تعریف نشده -# suppress inspection "UnusedProperty" -dao.proposal.type.COMPENSATION_REQUEST=درخواست خسارت -# suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=درخواست بازپرداخت -# suppress inspection "UnusedProperty" -dao.proposal.type.BONDED_ROLE=طرح پیشنهادی برای یک نقش ضمانتی -# suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=طرح پیشنهادی برای حذف کردن یک دارایی -# suppress inspection "UnusedProperty" -dao.proposal.type.CHANGE_PARAM=طرح پیشنهادی برای تغییر یک پارامتر -# suppress inspection "UnusedProperty" -dao.proposal.type.GENERIC=پیشنهاد عمومی -# suppress inspection "UnusedProperty" -dao.proposal.type.CONFISCATE_BOND=طرح پیشنهادی برای مصادره کردن یک ضمانت - -# suppress inspection "UnusedProperty" -dao.proposal.type.short.UNDEFINED=تعریف نشده -# suppress inspection "UnusedProperty" -dao.proposal.type.short.COMPENSATION_REQUEST=درخواست خسارت -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=درخواست بازپرداخت -# suppress inspection "UnusedProperty" -dao.proposal.type.short.BONDED_ROLE=نقش ضمانت شده -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ASSET=حذف یک آلت‌کوین -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CHANGE_PARAM=تغییر یک پارامتر -# suppress inspection "UnusedProperty" -dao.proposal.type.short.GENERIC=پیشنهاد کلی -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CONFISCATE_BOND=مصادره کردن یک ضمانت - -dao.proposal.details=جزئیات پیشنهاد -dao.proposal.selectedProposal=پیشنهادهای انتخاب شده -dao.proposal.active.header=طرح‌های پیشنهادی برای دوره فعلی -dao.proposal.active.remove.confirm=آیا از حذف کردن آن طرح پیشنهادی مطمئنید؟\nکارمزد طرح پیشنهادی که قبلا پرداخت شده است از بین خواهد رفت. -dao.proposal.active.remove.doRemove=بله، طرح پیشنهادی من را حذف کن -dao.proposal.active.remove.failed=نمی توان پیشنهاد را حذف کرد. -dao.proposal.myVote.title=رأی گیری -dao.proposal.myVote.accept=قبول پیشنهاد -dao.proposal.myVote.reject=رد پیشنهاد -dao.proposal.myVote.removeMyVote=نادیده گرفتن طرح پیشنهادی -dao.proposal.myVote.merit=وزن رای به سبب مقدار BSQ بدست آورده -dao.proposal.myVote.stake=وزن رای به سبب سهام -dao.proposal.myVote.revealTxId=شناسه تراکنش آشکاری سازی رای -dao.proposal.myVote.stake.prompt=حداکثر استک آماده برای رای گیری: {0} -dao.proposal.votes.header=تنظیم استک برای رای گیری و انتشار رای های شما -dao.proposal.myVote.button=انتشار رای‌ها -dao.proposal.myVote.setStake.description=پس از رأی دادن به همه پیشنهادها، شما باید از طریق قفل کردن BSQ، استک خود را برای رای دادن تنظیم کنید. هر چه BSQ بیشتری را قفل کنید، رای شما ارزش بیشتری خواهد داشت.\n\nBSQ قفل شده برای رای گیری، مجددا در طی رای گیری فاز انتشار باز خواهد شد. -dao.proposal.create.selectProposalType=انتخاب نوع پیشنهاد -dao.proposal.create.phase.inactive=لطفا منتظر مرحله بعدی طرح پیشنهادی باشید -dao.proposal.create.proposalType=نوع پیشنهاد -dao.proposal.create.new=ارائه ی پیشنهاد جدید -dao.proposal.create.button=ارائه ی پیشنهاد -dao.proposal.create.publish=انتشار طرح پیشنهادی -dao.proposal.create.publishing=انتشار طرح پیشنهادی در حال انجام است ... -dao.proposal=طرح پیشنهادی -dao.proposal.display.type=نوع طرح پیشنهادی -dao.proposal.display.name=Exact GitHub username -dao.proposal.display.link=پیوند اینترنتی به اطلاعات جزئی -dao.proposal.display.link.prompt=پیوند اینترنتی به طرح پیشنهادی -dao.proposal.display.requestedBsq=مبلغ درخواستی به BSQ -dao.proposal.display.txId=شناسه تراکنش طرح پیشنهادی -dao.proposal.display.proposalFee=کارمزد طرح پیشنهادی -dao.proposal.display.myVote=رای من -dao.proposal.display.voteResult=خلاصه نتایج رای‌گیری -dao.proposal.display.bondedRoleComboBox.label=نوع نقش ضمانت شده -dao.proposal.display.requiredBondForRole.label=ضمانت مورد نیاز برای نقش -dao.proposal.display.option=گزینه - -dao.proposal.table.header.proposalType=نوع طرح پیشنهادی -dao.proposal.table.header.link=پیوند -dao.proposal.table.header.myVote=رای من -# suppress inspection "UnusedProperty" -dao.proposal.table.header.remove=حذف -dao.proposal.table.icon.tooltip.removeProposal=طرح پیشنهادی من را حذف کن -dao.proposal.table.icon.tooltip.changeVote=رای فعلی: ''{0}''. تغییر رای به: ''{1}'' - -dao.proposal.display.myVote.accepted=قبول شده -dao.proposal.display.myVote.rejected=رد شده -dao.proposal.display.myVote.ignored=نادیده گرفته شده -dao.proposal.display.myVote.unCounted=Vote was not included in result -dao.proposal.myVote.summary=Voted: {0}; Vote weight: {1} (earned: {2} + stake: {3}) {4} -dao.proposal.myVote.invalid=رای نامعتبر بود - -dao.proposal.voteResult.success=قبول شده -dao.proposal.voteResult.failed=رد شده -dao.proposal.voteResult.summary=نتیجه: {0}؛ آستانه: {1} (مورد نیاز > {2})؛ حدنصاب: {3} (مورد نیاز > {4}) - -dao.proposal.display.paramComboBox.label=پارامتری برای تغییر انتخاب کنید -dao.proposal.display.paramValue=مقدار پارامتر - -dao.proposal.display.confiscateBondComboBox.label=انتخاب ضمانت -dao.proposal.display.assetComboBox.label=دارایی مورد حذف - -dao.blindVote=رای ناشناس - -dao.blindVote.startPublishing=در حال انتشار تراکنش رای ناشناس... -dao.blindVote.success=تراکنش رأی کور شما با موفقیت منتشر شده است.\n\nلطفا توجه داشته باشید که باید در فاز انتشار رای آنلاین باشید تا برنامه کیف پول Bisq  شما بتواند تراکنش انتشار رای را منتشر کند. بدون تراکنش انتشار رای، رای شما غیر معتبر خواهد بود! - -dao.wallet.menuItem.send=ارسال -dao.wallet.menuItem.receive=دریافت -dao.wallet.menuItem.transactions=تراکنش ها - -dao.wallet.dashboard.myBalance=موجودی کیف‌پول من - -dao.wallet.receive.fundYourWallet=آدرس دریافت BSQ شما: -dao.wallet.receive.bsqAddress=آدرس کیف پول BSQ (آدرس استفاده نشده جدید) - -dao.wallet.send.sendFunds=ارسال وجوه -dao.wallet.send.sendBtcFunds=ارسال وجوه غیر BSQ (به BTC) -dao.wallet.send.amount=مقدار به BSQ -dao.wallet.send.btcAmount=مقدار به BTC (وجوه غیر BSQ) -dao.wallet.send.setAmount=تعیین مبلغ به منظور برداشت (حداقل مبلغ {0} است) -dao.wallet.send.receiverAddress=آدرس BSQ گیرنده -dao.wallet.send.receiverBtcAddress=آدرس BTC گیرنده -dao.wallet.send.setDestinationAddress=آدرس مقصد خود را پر کنید -dao.wallet.send.send=ارسال وجوه BSQ  -dao.wallet.send.inputControl=Select inputs -dao.wallet.send.sendBtc=ارسال وجوه BTC -dao.wallet.send.sendFunds.headline=تأیید درخواست برداشت -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=نوع - -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED=تعریف نشده -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED_TX_TYPE=شناسایی نشده -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNVERIFIED=تراکنش BSQ تأیید نشده -# suppress inspection "UnusedProperty" -dao.tx.type.enum.INVALID=تراکنش BSQ نامعتبر -# suppress inspection "UnusedProperty" -dao.tx.type.enum.GENESIS=تراکنش جنسیس -# suppress inspection "UnusedProperty" -dao.tx.type.enum.TRANSFER_BSQ=انتقال BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.received.TRANSFER_BSQ=BSQ دریافت شده -# suppress inspection "UnusedProperty" -dao.tx.type.enum.sent.TRANSFER_BSQ=BSQ ارسال شده -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PAY_TRADE_FEE=هزینه ی معامله -# suppress inspection "UnusedProperty" -dao.tx.type.enum.COMPENSATION_REQUEST=هزینه برای درخواست خسارت -# suppress inspection "UnusedProperty" -dao.tx.type.enum.REIMBURSEMENT_REQUEST=کارمزد درخواست بازپرداخت -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROPOSAL=هزینه برای پیشنهاد -# suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=کارمزد برای رای ناشناس -# suppress inspection "UnusedProperty" -dao.tx.type.enum.VOTE_REVEAL=آشکارسازی رأی -# suppress inspection "UnusedProperty" -dao.tx.type.enum.LOCKUP=قفل کردن ضمانت -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNLOCK=رها سازی ضمانت -# suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=کارمزد ثبت دارایی -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=اثبات امحا -# suppress inspection "UnusedProperty" -dao.tx.type.enum.IRREGULAR=نامنظم - -dao.tx.withdrawnFromWallet=خروج BTC از کیف پول -dao.tx.issuanceFromCompReq=درخواست/صدور خسارت -dao.tx.issuanceFromCompReq.tooltip=درخواست خسارت که منجر به صدور BSQ جدید می‌شود.\nتاریخ صدور: {0} -dao.tx.issuanceFromReimbursement=درخواست/صدور بازپرداخت -dao.tx.issuanceFromReimbursement.tooltip=درخواست بازپرداختی که منجر به صدور BSQ جدید می‌شود.\nتاریخ صدور: {0} -dao.proposal.create.missingBsqFunds=شما سرمایه BSQ مناسبی برای ایجاد پروپوزال ندارید. اگر یک تراکنش BSQ تائید نشده دارید، لازم است که برای یک تائیدیه بلاک چین منتظر بمانید، زیرا BSQ در صورتی تائید می شود در یک بلاک قرار داشته باشد. از دست رفته: {0} - -dao.proposal.create.missingBsqFundsForBond=شما سرمایه BSQ مناسبی برای این نقش ندارید. شما هنوز قادرید که این پروپوزال را انتشار دهید. اما در صورت پذیرش، نیاز به مقداری کافی از BSQ برای این نقش خواهید داشت.\nاز دست رفته: {0} - -dao.proposal.create.missingMinerFeeFunds=شما سرمایه BSQ مناسبی برای ایجاد تراکنش پروپوزال ندارید. همه تراکنش های BSQ نیازمند دستمزد استخراج کننده در BTC هستند.\nاز دست رفته: {0} - -dao.proposal.create.missingIssuanceFunds=شما سرمایه BTC مناسبی برای ایجاد تراکنش پروپوزال ندارید. همه تراکنش های BSQ نیازمند دستمزد استخراج کننده در BTC هستند و تراکنش های صدور نیز به BTC برای میزان BSQ درخواستی نیاز دارند (BSQ/ساتوشی ها {0}).\nاز دست رفته: {1} - -dao.feeTx.confirm=تایید {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.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.news.bisqDAO.title=سازمان مستقل غیر متمرکز (Bisq (DAO -dao.news.bisqDAO.description=درست همانطور که مبادله کیف پول Bisq، غیر متمرکز و مقاوم در برابر سانسور است، مدل حاکمیت آن نیز وجود دارد و توکن های Bisq DAO و BSQ ابزارهایی هستند که آنرا محقق می سازند. -dao.news.bisqDAO.readMoreLink=درباره Bisq DAO بیشتر بدانید - -dao.news.pastContribution.title=آیا مشارکت قبلی داشته اید؟ برای BSQ درخواست دهید. -dao.news.pastContribution.description=اگر در رابطه با کیف پول Bisq مشارکت داشته اید، لطفا از آدرس BSQ زیر استفاده کنید و برای دریافت بخشی از توزیع جنسیس BSQ درخواست دهید. -dao.news.pastContribution.yourAddress=آدرس کیف‌پول BSQ شما -dao.news.pastContribution.requestNow=حالا درخواست دهید. - -dao.news.DAOOnTestnet.title=BISQ DAO را روی شبکه تستی، اجرا کنید. -dao.news.DAOOnTestnet.description=کیف پول Bisq DAO شبکه اصلی هنوز راه اندازی نشده است، اما شما می توانید با اجرای کیف پول Bisq DAO روی شبکه تستی، در مورد آن چیزهایی را یاد بگیرید. -dao.news.DAOOnTestnet.firstSection.title=1. به حالت شبکه تستی DAO تغییر وضعیت دهید. -dao.news.DAOOnTestnet.firstSection.content=از صفحه تنظیمات، به شبکه تستی DAO بروید. -dao.news.DAOOnTestnet.secondSection.title=2. چند BSQ را خریداری نمایید. -dao.news.DAOOnTestnet.secondSection.content=روی اسلک برای BSQ درخواست دهید و یا روی کیف پول Bisq ، BSQ را خریداری کنید. -dao.news.DAOOnTestnet.thirdSection.title=3. در یک چرخه رای گیری شرکت کنید. -dao.news.DAOOnTestnet.thirdSection.content=پروپوزال هایی تهیه کنید و برای تغییر جنبه های مختلف کیف پول Bisq، روی آنها رای گیری نمایید. -dao.news.DAOOnTestnet.fourthSection.title=4. Explore a BSQ Block Explorer -dao.news.DAOOnTestnet.fourthSection.content=از آنجا که BSQ تنها بیت کوین است، می توانید تراکنش های BSQ را روی مرورگر بلاک بیت کوین مشاهده کنید. -dao.news.DAOOnTestnet.readMoreLink=اسناد را به طور کامل مطالعه کنید. - -dao.monitor.daoState=وضعیت DAO -dao.monitor.proposals=وضعیت طرح‌های پیشنهادی -dao.monitor.blindVotes=وضعیت رای‌های ناشناس - -dao.monitor.table.peers=جفت ها -dao.monitor.table.conflicts=تعارضات -dao.monitor.state=وضعیت -dao.monitor.requestAlHashes=درخواست همه هش ها -dao.monitor.resync=همگام سازی مجدد وضعیت DAO -dao.monitor.table.header.cycleBlockHeight=ارتفاع بلاک / چرخه -dao.monitor.table.cycleBlockHeight={1} بلاک / {0} چرخه -dao.monitor.table.seedPeers={0} گره Seed: - -dao.monitor.daoState.headline=وضعیت DAO -dao.monitor.daoState.table.headline=زنجیره هش های وضعیت DAO -dao.monitor.daoState.table.blockHeight=ارتفاع بلاک -dao.monitor.daoState.table.hash=هش وضعیت DAO -dao.monitor.daoState.table.prev=هش قبلی -dao.monitor.daoState.conflictTable.headline=هش های وضعیت DAO از جفت هایی که با هم در تعارض هستند. -dao.monitor.daoState.utxoConflicts=تناقضات UTXO -dao.monitor.daoState.utxoConflicts.blockHeight=ارتفاع بلاک: {0} -dao.monitor.daoState.utxoConflicts.sumUtxo=مجموع همه UTXO ها:{0} BSQ -dao.monitor.daoState.utxoConflicts.sumBsq=مجموع همه BSQ ها:{BSQ {0 -dao.monitor.daoState.checkpoint.popup=DAO state is not in sync with the network. After restart the DAO state will resync. - -dao.monitor.proposal.headline=وضعیت طرح‌های پیشنهادی -dao.monitor.proposal.table.headline=زنجیره هش های وضعیت پروپوزال -dao.monitor.proposal.conflictTable.headline=هش های وضعیت پروپوزال از جفت هایی که با هم در تعارض هستند. - -dao.monitor.proposal.table.hash=هش وضعیت پروپوزال -dao.monitor.proposal.table.prev=هش قبلی -dao.monitor.proposal.table.numProposals=تعداد پروپوزال ها - -dao.monitor.isInConflictWithSeedNode=داده های محلی شما حداقل با یک گره Seed در اشتراک نیستند. لطفا  مجددا وضعیت DAO را همگام سازی کنید. -dao.monitor.isInConflictWithNonSeedNode=یکی از همتایان شما با شبکه در اشتراک نیست، اما گره شما با گره های Seed همگام است. -dao.monitor.daoStateInSync=گره محلی شما با شبکه در اشتراک است. - -dao.monitor.blindVote.headline=وضعیت رای‌های ناشناس -dao.monitor.blindVote.table.headline=زنجیره هش های وضعیت رای کور -dao.monitor.blindVote.conflictTable.headline=هش های وضعیت رای کور، از جفت هایی که با هم در تعارض هستند. -dao.monitor.blindVote.table.hash=هش وضعیت رای کور -dao.monitor.blindVote.table.prev=هش قبلی -dao.monitor.blindVote.table.numBlindVotes=No. blind votes - -dao.factsAndFigures.menuItem.supply=BSQ Supply -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 BSQ/USD price -dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on 30 days average BSQ/USD price) -dao.factsAndFigures.dashboard.availableAmount=مجموع BSQ در دسترس -dao.factsAndFigures.dashboard.volumeUsd=Total trade volume in USD -dao.factsAndFigures.dashboard.volumeBtc=Total trade volume in BTC -dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection=Average BSQ/USD trade price from selected time period in chart -dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection=Average BSQ/BTC trade price from selected time period in chart - -dao.factsAndFigures.supply.issuedVsBurnt=BSQ issued v. BSQ burnt - -dao.factsAndFigures.supply.issued=BSQ issued -dao.factsAndFigures.supply.compReq=درخواست های خسارت -dao.factsAndFigures.supply.reimbursement=Reimbursement requests -dao.factsAndFigures.supply.genesisIssueAmount=BSQ صادر شده در تراکنش پیدایش -dao.factsAndFigures.supply.compRequestIssueAmount=BSQ صادر شده برای درخواست‌های مصادره -dao.factsAndFigures.supply.reimbursementAmount=BSQ صادر شده برای درخواست‌های بازپرداخت -dao.factsAndFigures.supply.totalIssued=Total issued BSQ -dao.factsAndFigures.supply.totalBurned=Total burned BSQ -dao.factsAndFigures.supply.chart.tradeFee.toolTip={0}\n{1} -dao.factsAndFigures.supply.burnt=BSQ burnt - -dao.factsAndFigures.supply.priceChat=BSQ price -dao.factsAndFigures.supply.volumeChat=حجم معامله -dao.factsAndFigures.supply.tradeVolumeInUsd=Trade volume in USD -dao.factsAndFigures.supply.tradeVolumeInBtc=Trade volume in BTC -dao.factsAndFigures.supply.bsqUsdPrice=BSQ/USD price -dao.factsAndFigures.supply.bsqBtcPrice=BSQ/BTC price -dao.factsAndFigures.supply.btcUsdPrice=BTC/USD price - -dao.factsAndFigures.supply.locked=وضعیت جهانی BSQ های قفل شده -dao.factsAndFigures.supply.totalLockedUpAmount=قفل شده در ضمانت‌ها -dao.factsAndFigures.supply.totalUnlockingAmount=رها کردن BSQ از ضمانت‌ها -dao.factsAndFigures.supply.totalUnlockedAmount=BSQ رها شده از ضمانت‌ها -dao.factsAndFigures.supply.totalConfiscatedAmount=BSQ مصادره شده از ضمانت‌ها -dao.factsAndFigures.supply.proofOfBurn=Proof of Burn -dao.factsAndFigures.supply.bsqTradeFee=BSQ Trade fees -dao.factsAndFigures.supply.btcTradeFee=BTC Trade fees - -dao.factsAndFigures.transactions.genesis=تراکنش پیدایش -dao.factsAndFigures.transactions.genesisBlockHeight=طول بلاک پیدایش -dao.factsAndFigures.transactions.genesisTxId=شناسه تراکنش پیدایش -dao.factsAndFigures.transactions.txDetails=آمار تراکنش‌های BSQ -dao.factsAndFigures.transactions.allTx=تعداد تمام تراکنش‌های BSQ -dao.factsAndFigures.transactions.utxo=تعداد تمام خروجی تراکنش‌های خرج نشده -dao.factsAndFigures.transactions.compensationIssuanceTx=تعداد تمام تراکنش‌های صدور درخواست مصادره -dao.factsAndFigures.transactions.reimbursementIssuanceTx=تعداد تمام تراکنش‌های صدور درخواست بازپرداخت -dao.factsAndFigures.transactions.burntTx=تعداد تمام تراکنش‌های کارمزد پرداخت -dao.factsAndFigures.transactions.invalidTx=No. of all invalid transactions -dao.factsAndFigures.transactions.irregularTx=No. of all irregular transactions - - - #################################################################### # Windows #################################################################### @@ -2120,8 +1406,6 @@ disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any emptyWalletWindow.headline=ابزار اضطراری کیف پول {0} emptyWalletWindow.info=لطفاً تنها در مورد اضطراری از آن استفاده کنید اگر نمی توانید به وجه خود از UI دسترسی داشته باشید.\n\nلطفاً توجه داشته باشید که تمام معاملات باز به طور خودکار در هنگام استفاده از این ابزار، بسته خواهد شد.\n\nقبل از به کار گیری این ابزار، از راهنمای داده ی خود پشتیبان بگیرید. می توانید این کار را در \"حساب/پشتیبان\" انجام دهید.\n\nلطفاً مشکل خود را به ما گزارش کنید و گزارش مشکل را در GitHub یا تالار گفتگوی Bisq بایگانی کنید تا ما بتوانیم منشأ مشکل را بررسی نماییم. emptyWalletWindow.balance=موجودی در دسترس کیف‌پول شما -emptyWalletWindow.bsq.btcBalance=موجودی غیر BSQ بر اساس ساتوشی - emptyWalletWindow.address=آدرس مقصد شما emptyWalletWindow.button=ارسال تمام وجوه emptyWalletWindow.openOffers.warn=شما معاملات بازی دارید که اگر کیف پول را خالی کنید، حذف خواهند شد.\nآیا شما مطمئن هستید که می خواهید کیف پول را خالی کنید؟ @@ -2146,10 +1430,8 @@ filterWindow.seedNode=گره های seed فیلتر شده (آدرس های Onio filterWindow.priceRelayNode=گره های رله قیمت فیلترشده (آدرس های Onion جدا شده با ویرگول) filterWindow.btcNode=گره‌های بیت‌کوین فیلترشده (آدرس + پورت جدا شده با ویرگول) filterWindow.preventPublicBtcNetwork=جلوگیری از استفاده ازشبکه عمومی بیت‌کوین -filterWindow.disableDao=Disable DAO filterWindow.disableAutoConf=Disable auto-confirm filterWindow.autoConfExplorers=Filtered auto-confirm explorers (comma sep. addresses) -filterWindow.disableDaoBelowVersion=Min. version required for DAO filterWindow.disableTradeBelowVersion=Min. version required for trading filterWindow.add=افزودن فیلتر filterWindow.remove=حذف فیلتر @@ -2223,7 +1505,6 @@ tradeDetailsWindow.detailData=Detail data txDetailsWindow.headline=Transaction Details txDetailsWindow.btc.note=You have sent BTC. -txDetailsWindow.bsq.note=You have sent BSQ funds. BSQ is colored bitcoin, so the transaction will not show in a BSQ explorer until it has been confirmed in a bitcoin block. txDetailsWindow.sentTo=Sent to txDetailsWindow.txId=TxId @@ -2235,9 +1516,6 @@ closedTradesSummaryWindow.totalMinerFee.title=Sum of all miner fees closedTradesSummaryWindow.totalMinerFee.value={0} ({1} of total trade amount) closedTradesSummaryWindow.totalTradeFeeInBtc.title=Sum of all trade fees paid in BTC closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} of total trade amount) -closedTradesSummaryWindow.totalTradeFeeInBsq.title=Sum of all trade fees paid in BSQ -closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} of total trade amount) - walletPasswordWindow.headline=وارد کردن رمز عبور به منظور باز کردن torNetworkSettingWindow.header=تنظیمات شبکه Tor  @@ -2317,12 +1595,6 @@ popup.warning.tooLargePercentageValue=شما نمیتوانید درصد 100٪ popup.warning.examplePercentageValue=لطفا یک عدد درصد مانند \"5.4\" برای 5.4% وارد کنید popup.warning.noPriceFeedAvailable=برای این ارز هیچ خوراک قیمتی وجود ندارد. شما نمیتوانید از یک درصد بر اساس قیمت استفاده کنید. \nلطفا قیمت مقطوع را انتخاب کنید. popup.warning.sendMsgFailed=ارسال پیام به شریک معاملاتی شما ناموفق بود. \nلطفا دوباره امتحان کنید و اگر همچنان ناموفق بود، گزارش یک اشکال را ارسال کنید. -popup.warning.insufficientBtcFundsForBsqTx=شما BTC کافی برای پرداخت کارمزد استخراج آن تراکنش BSQ را ندارید.\nلطفاً کیف پول BTC خود را شارژ نموده تا قادر به انتقال BSQ باشید.\nBTC موردنیاز: {0} -popup.warning.bsqChangeBelowDustException=This transaction creates a BSQ change output which is below dust limit (5.46 BSQ) and would be rejected by the Bitcoin network.\n\nYou need to either send a higher amount to avoid the change output (e.g. by adding the dust amount to your sending amount) or add more BSQ funds to your wallet so you avoid to generate a dust output.\n\nThe dust output is {0}. -popup.warning.btcChangeBelowDustException=This transaction creates a change output which is below dust limit (546 Satoshi) and would be rejected by the Bitcoin network.\n\nYou need to add the dust amount to your sending amount to avoid to generate a dust output.\n\nThe dust output is {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.noBsqFundsForBtcFeePayment=کیف‌پول BSQ شما BSQ کافی برای پرداخت کارمزد معامله به BSQ را ندارد. popup.warning.messageTooLong=پیام شما بیش از حداکثر اندازه مجاز است. لطفا آن را در چند بخش ارسال کنید یا آن را در یک سرویس مانند https://pastebin.com آپلود کنید. popup.warning.lockedUpFunds=You have locked up funds from a failed trade.\nLocked up balance: {0} \nDeposit tx address: {1}\nTrade ID: {2}.\n\nPlease open a support ticket by selecting the trade in the open trades screen and pressing \"alt + o\" or \"option + o\"." @@ -2336,8 +1608,6 @@ popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=رله قیمت popup.warning.seed=دانه popup.warning.mandatoryUpdate.trading=Please update to the latest Bisq version. A mandatory update was released which disables trading for old versions. Please check out the Bisq Forum for more information. -popup.warning.mandatoryUpdate.dao=Please update to the latest Bisq version. A mandatory update was released which disables the Bisq DAO and BSQ for old versions. Please check out the Bisq Forum for more information. -popup.warning.disable.dao=The Bisq DAO and BSQ are temporary disabled. Please check out the Bisq Forum for more information. popup.warning.noFilter=We did not receive a filter object from the seed nodes. This is a not expected situation. Please inform the Bisq developers. popup.warning.burnBTC=This transaction is not possible, as the mining fees of {0} would exceed the amount to transfer of {1}. Please wait until the mining fees are low again or until you''ve accumulated more BTC to transfer. @@ -2356,7 +1626,6 @@ 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=اعلان خصوصی مهم! @@ -2528,7 +1797,6 @@ navigation.settings.preferences=\"تنظیمات/اولویت ها\" # suppress inspection "UnusedProperty" navigation.funds.transactions=\"وجوه/تراکنش ها\" navigation.support=\"پشتیبانی\" -navigation.dao.wallet.receive=\"DAO/کیف پول BSQ/دریافت\" #################################################################### @@ -2558,12 +1826,6 @@ XMR_MAINNET=Monero Mainnet XMR_TESTNET=Monero Testnet # suppress inspection "UnusedProperty" XMR_STAGENET=Monero Stagenet -# suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet (deprecated) -# suppress inspection "UnusedProperty" -BTC_DAO_BETANET=Bisq DAO Betanet (Bitcoin Mainnet) -# suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=Bitcoin DAO Regtest time.year=سال time.month=ماه @@ -2910,9 +2172,7 @@ validation.accountNrChars=عدد حساب باید متشکل از {0} کارا validation.btc.invalidAddress=آدرس درست نیست. لطفا فرمت آدرس را بررسی کنید validation.integerOnly=لطفا فقط اعداد صحیح را وارد کنید. validation.inputError=ورودی شما یک خطا ایجاد کرد: {0} -validation.bsq.insufficientBalance=موجودی در دسترس شما {0} است. validation.btc.exceedsMaxTradeLimit=حدمعامله شما {0} است. -validation.bsq.amountBelowMinAmount=مقدار حداقل {0} است validation.nationalAccountId={0} باید شامل {1} عدد باشد. #new @@ -2934,7 +2194,6 @@ validation.bic.invalidLocationCode=BIC حاوی کد مکان نامعتبر ا validation.bic.invalidBranchCode=BIC حاوی کد شعبه نامعتبر است validation.bic.sepaRevolutBic=حساب های Revolut Sepa پشتیبانی نمی شود. validation.btc.invalidFormat=Invalid format for a Bitcoin address. -validation.bsq.invalidFormat=Invalid format for a BSQ address. validation.email.invalidAddress=آدرس نامعتبر است validation.iban.invalidCountryCode=کد کشور نامعتبر است validation.iban.checkSumNotNumeric=سرجمع باید عددی باشد diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index 4529ddd301..13be1b20ee 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -192,7 +192,6 @@ shared.tradeWalletBalance=Solde du portefeuille de trading shared.makerTxFee=Maker: {0} shared.takerTxFee=Taker: {0} shared.iConfirm=Je confirme -shared.tradingFeeInBsqInfo=environ {0} shared.openURL=Ouvert {0} shared.fiat=Fiat shared.crypto=Crypto @@ -204,9 +203,6 @@ shared.actions=Actions shared.buyerUpperCase=Acheteur shared.sellerUpperCase=Vendeur shared.new=NOUVEAU -shared.blindVoteTxId=ID de la transaction du vote caché -shared.proposal=Proposition -shared.votes=Votes shared.learnMore=En savoir plus shared.dismiss=Rejeter shared.selectedArbitrator=Arbitre sélectionné @@ -240,7 +236,6 @@ mainView.menu.funds=Fonds mainView.menu.support=Assistance mainView.menu.settings=Paramètres mainView.menu.account=Compte -mainView.menu.dao=DAO mainView.marketPriceWithProvider.label=Prix du marché par {0} mainView.marketPrice.bisqInternalPrice=Cours de la dernière transaction Bisq @@ -257,13 +252,11 @@ mainView.footer.localhostBitcoinNode=(localhost) mainView.footer.btcInfo={0} {1} mainView.footer.btcFeeRate=/ Taux des frais: {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 {0} au block: {1}/ {2} mainView.footer.btcInfo.synchronizedWith=Synchronisé avec {0} au block {1} mainView.footer.btcInfo.connectingTo=Se connecte à mainView.footer.btcInfo.connectionFailed=Échec de la connexion à mainView.footer.p2pInfo=Pairs du réseau bitcoin: {0} / pairs du réseau Bisq: {1} -mainView.footer.daoFullNode=DAO full node mainView.bootstrapState.connectionToTorNetwork=(1/4) Connection au réseau Tor... mainView.bootstrapState.torNodeCreated=(2/4) Noeud Tor créé @@ -435,7 +428,6 @@ createOffer.fundsBox.networkFee=Frais de minage createOffer.fundsBox.placeOfferSpinnerInfo=Publication de l'ordre en cours ... createOffer.fundsBox.paymentLabel=Transaction Bisq avec l''ID {0} createOffer.fundsBox.fundsStructure=({0} dépôt de garantie, {1} frais de transaction, {2} frais de minage) -createOffer.fundsBox.fundsStructure.BSQ=({0} dépôt de garantie, {1} frais de minage) + {2} frais de transaction createOffer.success.headline=Votre ordre a été publiée createOffer.success.info=Vous pouvez gérer vos ordres en cours dans \"Portfolio/Mes ordres\". createOffer.info.sellAtMarketPrice=Vous vendrez toujours au prix du marché car le prix de votre ordre sera continuellement mis à jour. @@ -882,7 +874,6 @@ funds.locked.locked=Vérouillé en multisig pour le trade avec l''ID: {0} funds.tx.direction.sentTo=Envoyer à: funds.tx.direction.receivedWith=Reçu depuis: funds.tx.direction.genesisTx=Depuis le tx Genesis: -funds.tx.txFeePaymentForBsqTx=Frais de minage du tx BSQ funds.tx.createOfferFee=Frais du maker et du tx: {0} funds.tx.takeOfferFee=Frais du taker et du tx: {0} funds.tx.multiSigDeposit=Dépôt multisig: {0} @@ -896,15 +887,11 @@ funds.tx.unknown=Raison inconnue: {0} funds.tx.noFundsFromDispute=Aucun remboursement en cas de litige funds.tx.receivedFunds=Fonds reçus funds.tx.withdrawnFromWallet=Retiré depuis le portefeuille -funds.tx.withdrawnFromBSQWallet=BTC retiré depuis le portefeuille BSQ funds.tx.memo=Résumé 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 mineur de la 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. @@ -996,9 +983,7 @@ settings.tab.about=À propos setting.preferences.general=Préférences générales setting.preferences.explorer=Exploreur Bitcoin -setting.preferences.explorer.bsq=Exploreur Bisq setting.preferences.deviation=Ecart maximal par rapport au prix du marché -setting.preferences.bsqAverageTrimThreshold=Seuil de valeur trop élévé pour le BSQ setting.preferences.avoidStandbyMode=Éviter le mode veille setting.preferences.autoConfirmXMR=Auto-confirmation XMR setting.preferences.autoConfirmEnabled=Activé @@ -1032,19 +1017,6 @@ setting.preferences.notifyOnPreRelease=Recevoir les notifications de pré-sortie setting.preferences.resetAllFlags=Réinitialiser toutes les balises de notification \"Don't show again\" settings.preferences.languageChange=Un redémarrage est nécessaire pour appliquer le changement de langue à tous les écrans. settings.preferences.supportLanguageWarning=En cas de litige, veuillez noter que la médiation est traitée en {0} et l'arbitrage en {1}. -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=Reconstruire l'état du DAO à partir des ressources -setting.preferences.dao.resyncFromResources.popup=Après un redémarrage de l'application les données de gouvernance du réseau Bisq seront rechargées à partir des noeuds sources et l'état du consensus BSQ sera reconstruit à partir des derniers fichiers de ressources. -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=Resynchroniser depuis Genesis et fermer -setting.preferences.dao.isDaoFullNode=Exécuter la DAO de Bisq en tant que full node -setting.preferences.dao.rpcUser=Nom d'utilisateur RPC -setting.preferences.dao.rpcPw=Mot de passe RPC -setting.preferences.dao.blockNotifyPort=Bloquer le port de notification -setting.preferences.dao.fullNodeInfo=Pour exécuter la DAO de Bisq en tant que full node, vous devez avoir Bitcoin Core en exécution locale et avec le RPC activé. Toutes les recommandations sont indiquées dans ''{0}''.\n\nAprès avoir changé de mode, vous serez contraint de redémarrer.. -setting.preferences.dao.fullNodeInfo.ok=Ouvrir la page des docs -setting.preferences.dao.fullNodeInfo.cancel=Non, je m'en tiens au mode lite node settings.preferences.editCustomExplorer.headline=Paramètres de l'explorateur settings.preferences.editCustomExplorer.description=Choisissez un explorateur défini par le système depuis la liste à gauche, et/où customisez-le pour satisfaire vos préférences. settings.preferences.editCustomExplorer.available=Explorateurs disponibles @@ -1090,7 +1062,7 @@ settings.net.needRestart=Vous devez redémarrer l'application pour appliquer cet settings.net.notKnownYet=Pas encore connu... settings.net.sentData=Données envoyées: {0}, {1} messages, {2} messages/seconde settings.net.receivedData=Données reçues: {0}, {1} messages, {2} messages/seconde -settings.net.chainHeight=Hauteur de la chaîne DAO de Bisq: {0} | Hauteur de la chaîne des pairs Bitcoin: {1} +settings.net.chainHeight=Hauteur de la chaîne des pairs Bitcoin: {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) @@ -1144,14 +1116,10 @@ setting.about.shortcuts.walletDetails=Ouvrir la fenêtre avec les détails sur l setting.about.shortcuts.openEmergencyBtcWalletTool=Ouvrir l'outil de portefeuille d'urgence pour BTC -setting.about.shortcuts.openEmergencyBsqWalletTool=Ouvrir l'outil de portefeuille d'urgence pour BSQ - setting.about.shortcuts.showTorLogs=Basculer le niveau de log pour les messages Tor entre DEBUG et WARN setting.about.shortcuts.manualPayoutTxWindow=Ouvrir la fenêtre pour le paiement manuel à partir du tx de dépôt Multisig 2of2 -setting.about.shortcuts.reRepublishAllGovernanceData=Publier à nouveau les données sur la gouvernance de la DAO (propositions, votes) - setting.about.shortcuts.removeStuckTrade=Ouvrez la popup pour déplacer ce trade échoué vers l'onglet des trades ouverts. setting.about.shortcuts.removeStuckTrade.value=Sélectionnez l'échange échoué et appuyez sur: {0} @@ -1337,687 +1305,6 @@ account.notifications.noWebCamFound.warning=Aucune webcam n'a été trouvée.\n\ account.notifications.priceAlert.warning.highPriceTooLow=Le prix le plus élevé doit être supérieur au prix le plus bas. account.notifications.priceAlert.warning.lowerPriceTooHigh=Le prix le plus bas doit être inférieur au prix le plus élevé. - - - -#################################################################### -# DAO -#################################################################### - -dao.tab.factsAndFigures=Faits et chiffres -dao.tab.bsqWallet=Portefeuille BSQ -dao.tab.proposals=Gouvernance -dao.tab.bonding=Bonding -dao.tab.proofOfBurn=Frais d'inscription des actifs/Preuve du burn -dao.tab.monitor=Contrôleur réseau -dao.tab.news=Actualités - -dao.paidWithBsq=payé en BSQ -dao.availableBsqBalance=Disponible à dépenser (vérifiées + sorties non confirmées) -dao.verifiedBsqBalance=Balance de toutes les UTXOs vérifiées -dao.unconfirmedChangeBalance=Solde de toute les transactions de sorties non confirmées -dao.unverifiedBsqBalance=Solde de toutes les transactions non vérifiées (en attente de confirmation du bloc) -dao.lockedForVoteBalance=Utilisé pour le vote -dao.lockedInBonds=Verrouillé en bonds -dao.availableNonBsqBalance=Solde disponible non-BSQ (BTC) -dao.reputationBalance=Score de mérite (non dépensable) - -dao.tx.published.success=Votre transaction a été publiée avec succès. -dao.proposal.menuItem.make=Faire une proposition -dao.proposal.menuItem.browse=Parcourir les demandes en cours -dao.proposal.menuItem.vote=Vote pour les propositions -dao.proposal.menuItem.result=Résultats des votes -dao.cycle.headline=Cycle de vote -dao.cycle.overview.headline=Aperçu du cycle de vote -dao.cycle.currentPhase=Phase actuelle -dao.cycle.currentBlockHeight=Hauteur actuelle de bloc -dao.cycle.proposal=Phase de proposition -dao.cycle.proposal.next=Prochaine étape de proposition -dao.cycle.blindVote=Phase de vote caché -dao.cycle.voteReveal=Phase de dévoilement du vote -dao.cycle.voteResult=Résultat du vote -dao.cycle.phaseDuration={0} blocs (≈{1}); Blocs {2} - {3} (≈{4} - ≈{5}) -dao.cycle.phaseDurationWithoutBlocks=Bloc {0} - {1} (≈{2} - ≈{3}) - -dao.voteReveal.txPublished.headLine=Transaction du dévoilement du vote publiée. -dao.voteReveal.txPublished=Votre transaction de dévoilement du vote avec l''ID de transaction {0} a été publiée avec succès.\n\nCeci se produit automatiquement avec le logiciel si vous avez participé au processus de vote de la DAO. - -dao.results.cycles.header=Cycles -dao.results.cycles.table.header.cycle=Cycle -dao.results.cycles.table.header.numProposals=Propositions -dao.results.cycles.table.header.voteWeight=Poids du vote -dao.results.cycles.table.header.issuance=Émission - -dao.results.results.table.item.cycle=Cycle {0} commencé: {1} - -dao.results.proposals.header=Proposition relative au cycle sélectionné -dao.results.proposals.table.header.nameLink=Nom/lien -dao.results.proposals.table.header.details=Détails -dao.results.proposals.table.header.myVote=Mon vote -dao.results.proposals.table.header.result=Résultat du vote -dao.results.proposals.table.header.threshold=Seuil -dao.results.proposals.table.header.quorum=Quorum - -dao.results.proposals.voting.detail.header=Résultats des votes pour les propositions sélectionnées - -dao.results.exceptions=Exception(s) au résultat du vote - -# suppress inspection "UnusedProperty" -dao.param.UNDEFINED=Indéfini - -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BSQ=BSQ maker fee -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BSQ=BSQ taker fee -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BSQ=Min. BSQ maker fee -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BSQ=Min. BSQ taker fee -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BTC=BTC maker fee -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BTC=BTC taker fee -# suppress inspection "UnusedProperty" -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BTC=Min. BTC maker fee -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BTC=Min. BTC taker fee -# suppress inspection "UnusedProperty" - -# suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=Frais de l'ordre en BSQ -# suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=Frais de vote en BSQ - -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=Demande de compensation min. Montant BSQ -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=Demande de compensation max. Montant BSQ -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MIN_AMOUNT=Demande de remboursement min. Montant BSQ -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MAX_AMOUNT=Demande de remboursement max. Montant BSQ - -# suppress inspection "UnusedProperty" -dao.param.QUORUM_GENERIC=Quorum requis en BSQ pour une proposition standard -# suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=Quorum requis dans BSQ pour une demande d'indemnisation -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REIMBURSEMENT=Quorum requis dans BSQ pour une demande de remboursement -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=Quorum requis dans BSQ pour modifier un paramètre -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Quorum requis dans BSQ pour retirer un actif -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=Quorum requis dans BSQ pour une demande de confiscation -# suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=Quorum requis dans BSQ pour les demandes de rôle en bond - -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_GENERIC=Seuil requis en % pour une proposition standard -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=Seuil requis en % pour une demande d'indemnisation -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REIMBURSEMENT=Seuil requis en % pour une demande de remboursement -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=Seuil requis en % pour une modification de paramètre -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Seuil requis en % pour une suppression d'un actif -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=Seuil requis en % pour une demande de confiscation -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_ROLE=Seuil requis en % pour les demandes de rôle en bond - -# suppress inspection "UnusedProperty" -dao.param.RECIPIENT_BTC_ADDRESS=Adresse de réception BTC - -# suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=Coût journalier du listing des actifs -# suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Volume minimal d'échanges pour les actifs - -# suppress inspection "UnusedProperty" -dao.param.LOCK_TIME_TRADE_PAYOUT=Temps de verrouillage du tx de versement alternative du trade -# suppress inspection "UnusedProperty" -dao.param.ARBITRATOR_FEE=Frais d'arbitrage en BTC - -# suppress inspection "UnusedProperty" -dao.param.MAX_TRADE_LIMIT=Montant d'échange max. en BTC - -# suppress inspection "UnusedProperty" -dao.param.BONDED_ROLE_FACTOR=Bonded role unit factor en BSQ -# suppress inspection "UnusedProperty" -dao.param.ISSUANCE_LIMIT=Limite d'émission par cycle en BSQ - -dao.param.currentValue=Valeur actuelle: {0} -dao.param.currentAndPastValue=Valeur actuelle: {0} (Valeur au moment de l''offre: {1}) -dao.param.blocks={0} blocs - -dao.results.invalidVotes=Il est fait état de votes invalides au cours de ce cycle de vote. Cela peut arriver si un vote n''a pas été bien distribué sur le réseau Bisq.\n{0} - -# suppress inspection "UnusedProperty" -dao.phase.PHASE_UNDEFINED=Indéfini -# suppress inspection "UnusedProperty" -dao.phase.PHASE_PROPOSAL=Phase de proposition -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK1=Interruption 1 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=Phase de vote caché -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK2=Interruption 2 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=Phase de dévoilement du vote -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK3=Interruption 3 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_RESULT=Phase du résultat - -dao.results.votes.table.header.stakeAndMerit=Poids du vote -dao.results.votes.table.header.stake=Mise -dao.results.votes.table.header.merit=Gagné -dao.results.votes.table.header.vote=Vote - -dao.bond.menuItem.bondedRoles=Bonded roles -dao.bond.menuItem.reputation=Bonded reputation -dao.bond.menuItem.bonds=Bonds - -dao.bond.dashboard.bondsHeadline=Bonded BSQ -dao.bond.dashboard.lockupAmount=Immobiliser les fonds -dao.bond.dashboard.unlockingAmount=Déverrouiller des fonds (veuillez attendre que la période de verrouillage soit terminée). - - -dao.bond.reputation.header=Verrouiller un bond pour la réputation -dao.bond.reputation.table.header=Mes bonds de réputation -dao.bond.reputation.amount=Quantité de BSQ à bloquer -dao.bond.reputation.time=Délai de déverrouillage en blocs -dao.bond.reputation.salt=Salage -dao.bond.reputation.hash=Hash -dao.bond.reputation.lockupButton=Vérrouillage -dao.bond.reputation.lockup.headline=Confirmer la transaction de verrouillage. -dao.bond.reputation.lockup.details=Montant verrouillé : {0}\nTemps de déverrouillage: {1} block(s) (environ {2})\n\nFrais de minage: {3} ({4} Satoshis/byte)\nTaille virtuelle de la transaction: {5} vKb\n\nÊtes-vous certain de vouloir procéder? -dao.bond.reputation.unlock.headline=Confirmer le déblocage de la transaction -dao.bond.reputation.unlock.details=Montant du déverrouillage: {0}\nTemps de déverrouillage: {1} block(s) (environ {2})\n\nFrais de minage : {3} ({4} Satoshis/vbyte)\nTaille virtuelle de la transaction: {5} vKb\n\nÊtes-vous certain de vouloir procéder ? - -dao.bond.allBonds.header=Tous les bonds - -dao.bond.bondedReputation=Bonded Reputation -dao.bond.bondedRoles=Bonded roles - -dao.bond.details.header=Détails du rôle -dao.bond.details.role=Rôle -dao.bond.details.requiredBond=BSQ requis pour le bond -dao.bond.details.unlockTime=Délai de déverrouillage en blocs -dao.bond.details.link=Lien vers la description des rôles -dao.bond.details.isSingleton=Peut être utilisé par détenteurs de plusieurs rôles -dao.bond.details.blocks={0} blocs - -dao.bond.table.column.name=Nom -dao.bond.table.column.link=Lien -dao.bond.table.column.bondType=Type de Bond -dao.bond.table.column.details=Détails -dao.bond.table.column.lockupTxId=Verrouiller le Tx de l'ID -dao.bond.table.column.bondState=État du bond -dao.bond.table.column.lockTime=Temps de déverrouillage -dao.bond.table.column.lockupDate=Date de verrouillage - -dao.bond.table.button.lockup=Vérrouillage -dao.bond.table.button.unlock=Déverrouiller -dao.bond.table.button.revoke=Révoquer - -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNDEFINED=Indéfini -# suppress inspection "UnusedProperty" -dao.bond.bondState.READY_FOR_LOCKUP=Pas encore bonded -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_PENDING=Vérrouillage en attente -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_CONFIRMED=Bond verrouillé -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_PENDING=En attente de dévérrouillage -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_CONFIRMED=Déverrouiller le tx confirmé -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKING=Déblocage du Bond -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKED=Bond déverrouillé -# suppress inspection "UnusedProperty" -dao.bond.bondState.CONFISCATED=Bond confisqué - -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.UNDEFINED=Indéfini -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.BONDED_ROLE=Bonded role -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.REPUTATION=Bonded reputation - -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.UNDEFINED=Indéfini -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Admin GitHub -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=Admin du Forum -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=Admin Twitter -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Administrateur de Keybase -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Admin YouTube -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=Mainteneur Bisq -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=Maintainer BitcoinJ-fork -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Mainteneur Netlayer -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=Opérateur du site Web -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=Opérateur du Forum -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Opérateur du nœud de la seed -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Opérateur du prix du noeud -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Opérateur du noeud Bitcoin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Opérateur de marchés -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Opérateur de l'explorateur -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Opérateur relais pour les notifications mobile -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Titulaire du nom de domaine -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DNS_ADMIN=DNS admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=Médiateur -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=Arbitre -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=Propriétaire de l'adresse BTC de donation - -dao.burnBsq.assetFee=Listing des actifs -dao.burnBsq.menuItem.assetFee=Frais d'inscription des actifs -dao.burnBsq.menuItem.proofOfBurn=Preuve du burn -dao.burnBsq.header=Frais pour l'inscription des actifs -dao.burnBsq.selectAsset=Sélectionner un actif -dao.burnBsq.fee=Frais -dao.burnBsq.trialPeriod=Période d'essai -dao.burnBsq.payFee=Payer les frais -dao.burnBsq.allAssets=Tous les actifs -dao.burnBsq.assets.nameAndCode=Nom de l'actif -dao.burnBsq.assets.state=État -dao.burnBsq.assets.tradeVolume=Volume d'échange -dao.burnBsq.assets.lookBackPeriod=Durée de la vérification -dao.burnBsq.assets.trialFee=Frais pour la période d'essai -dao.burnBsq.assets.totalFee=Total des frais payés -dao.burnBsq.assets.days={0} jours -dao.burnBsq.assets.toFewDays=Les frais de l''actif sont trop bas. Le nombre minimum de jours pour la période d''essai est {0}. - -# suppress inspection "UnusedProperty" -dao.assetState.UNDEFINED=Indéterminé -# suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=En période d'essai -# suppress inspection "UnusedProperty" -dao.assetState.ACTIVELY_TRADED=Activement tradé -# suppress inspection "UnusedProperty" -dao.assetState.DE_LISTED=Déréférencé pour cause d'inactivité -# suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=Supprimée par un vote - -dao.proofOfBurn.header=Preuve du burn -dao.proofOfBurn.amount=Montant -dao.proofOfBurn.preImage=Pre-image -dao.proofOfBurn.burn=Burn -dao.proofOfBurn.allTxs=Toutes les preuves des transactions de burn -dao.proofOfBurn.myItems=Ma preuve des transactions de burn -dao.proofOfBurn.date=Date -dao.proofOfBurn.hash=Hash -dao.proofOfBurn.txs=Transactions -dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Signer un message avec la clé provenant de la transaction de burn -dao.proofOfBurn.verify.window.title=Vérifier un message avec la clé provenant de la preuve de la transaction de burn -dao.proofOfBurn.copySig=Copier la signature dans le presse-papiers -dao.proofOfBurn.sign=Signer -dao.proofOfBurn.message=Message -dao.proofOfBurn.sig=Signature -dao.proofOfBurn.verify=Vérifier -dao.proofOfBurn.verificationResult.ok=Vérification réussie -dao.proofOfBurn.verificationResult.failed=Échec de la vérification - -# suppress inspection "UnusedProperty" -dao.phase.UNDEFINED=Indéfini -# suppress inspection "UnusedProperty" -dao.phase.PROPOSAL=Phase de proposition -# suppress inspection "UnusedProperty" -dao.phase.BREAK1=Interrompre avant la période de vote caché -# suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=Phase de vote caché -# suppress inspection "UnusedProperty" -dao.phase.BREAK2=Interrompre avant la phase de dévoilement du vote -# suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=Phase de dévoilement du vote -# suppress inspection "UnusedProperty" -dao.phase.BREAK3=Interrompre avant la phase de résultat -# suppress inspection "UnusedProperty" -dao.phase.RESULT=Période de résultat du vote - -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.PROPOSAL=Phase de proposition -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=Cacher le vote -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.VOTE_REVEAL=Dévoilement du vote -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.RESULT=Résultat du vote - -# suppress inspection "UnusedProperty" -dao.proposal.type.UNDEFINED=Indéfini -# suppress inspection "UnusedProperty" -dao.proposal.type.COMPENSATION_REQUEST=Demande de compensation -# suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=Demande de remboursement -# suppress inspection "UnusedProperty" -dao.proposal.type.BONDED_ROLE=Demande de bonded role -# suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=Demande de retrait d'un actif -# suppress inspection "UnusedProperty" -dao.proposal.type.CHANGE_PARAM=Demande pour modifier un paramètre -# suppress inspection "UnusedProperty" -dao.proposal.type.GENERIC=Demande standard -# suppress inspection "UnusedProperty" -dao.proposal.type.CONFISCATE_BOND=Demande de confiscation d'un bond - -# suppress inspection "UnusedProperty" -dao.proposal.type.short.UNDEFINED=Indéfini -# suppress inspection "UnusedProperty" -dao.proposal.type.short.COMPENSATION_REQUEST=Demande de compensation -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=Demande de remboursement -# suppress inspection "UnusedProperty" -dao.proposal.type.short.BONDED_ROLE=Bonded role -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ASSET=Retrait d'un altcoin -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CHANGE_PARAM=Modifier un paramètre -# suppress inspection "UnusedProperty" -dao.proposal.type.short.GENERIC=Demande standard -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CONFISCATE_BOND=Confiscation d'un bond - -dao.proposal.details=Détails relatifs à la proposition -dao.proposal.selectedProposal=Proposition sélectionnée -dao.proposal.active.header=Propositions relatives au cycle actuel -dao.proposal.active.remove.confirm=Êtes-vous certain de vouloir retirer cette proposition?\nLes frais de traitement de la proposition déjà payés seront perdus. -dao.proposal.active.remove.doRemove=Oui, retirer ma proposition -dao.proposal.active.remove.failed=Impossible de retirer la proposition -dao.proposal.myVote.title=Vote -dao.proposal.myVote.accept=Accepter la proposition -dao.proposal.myVote.reject=Rejeter la proposition -dao.proposal.myVote.removeMyVote=Ignorer la proposition -dao.proposal.myVote.merit=Poids du vote résultant des BSQ obtenus -dao.proposal.myVote.stake=Poids du vote en fonction de la mise -dao.proposal.myVote.revealTxId=ID de transaction du vote de dévoilement -dao.proposal.myVote.stake.prompt=Solde maximum pour la mise disponible pour le vote: {0} -dao.proposal.votes.header=Fixez une mise pour le vote et publiez vos votes -dao.proposal.myVote.button=Publier les votes -dao.proposal.myVote.setStake.description=Après avoir voté sur toutes les propositions, vous devez fixer votre mise pour le vote en bloquant des BSQ. Plus vous verrouillerez des BSQ, plus votre vote aura de poids. \n\nLes BSQ verrouillés pour le vote seront déverrouillés à nouveau pendant la phase de dévoilement du vote. -dao.proposal.create.selectProposalType=Sélectionner le type de proposition -dao.proposal.create.phase.inactive=Veuillez patienter jusqu'à la prochaine phase de proposition -dao.proposal.create.proposalType=Type de proposition -dao.proposal.create.new=Faire une nouvelle demande -dao.proposal.create.button=Faire une demande -dao.proposal.create.publish=Publier la demande -dao.proposal.create.publishing=La publication de la demande est en cours... -dao.proposal=Demande -dao.proposal.display.type=Type de demande -dao.proposal.display.name=Nom d'utilisateur GitHub exact -dao.proposal.display.link=Lien vers les informations détaillées -dao.proposal.display.link.prompt=Lien vers la proposition -dao.proposal.display.requestedBsq=Montant démandé en BSQ -dao.proposal.display.txId=ID de transaction de la proposition -dao.proposal.display.proposalFee=Frais de la demande -dao.proposal.display.myVote=Mon vote -dao.proposal.display.voteResult=Synthèse des résultats du vote -dao.proposal.display.bondedRoleComboBox.label=Type de rôle Bonded -dao.proposal.display.requiredBondForRole.label=Bond requis pour le rôle -dao.proposal.display.option=Option - -dao.proposal.table.header.proposalType=Type de demande -dao.proposal.table.header.link=Lien -dao.proposal.table.header.myVote=Mon vote -# suppress inspection "UnusedProperty" -dao.proposal.table.header.remove=Enlever -dao.proposal.table.icon.tooltip.removeProposal=Retirer ma demande -dao.proposal.table.icon.tooltip.changeVote=Vote actuel: ''{0}'''. Modifier le vote pour: ''{1}'' - -dao.proposal.display.myVote.accepted=Accepté -dao.proposal.display.myVote.rejected=Rejeté -dao.proposal.display.myVote.ignored=Ignoré -dao.proposal.display.myVote.unCounted=Le vote n'a pas été inclus dans le résultat -dao.proposal.myVote.summary=Voté: {0}; Poids du vote: {1} (gagné: {2} + stake: {3}) {4} -dao.proposal.myVote.invalid=Le vote n'est pas valide - -dao.proposal.voteResult.success=Accepté -dao.proposal.voteResult.failed=Rejeté -dao.proposal.voteResult.summary=Résultat : {0}; Seuil : {1} (requis> {2}); Quorum: {3} (requis> {4}) - -dao.proposal.display.paramComboBox.label=Sélectionner le paramètre à modifier -dao.proposal.display.paramValue=Valeur du paramètre - -dao.proposal.display.confiscateBondComboBox.label=Choisir le bond -dao.proposal.display.assetComboBox.label=Actif à enlever - -dao.blindVote=Masquer le vote - -dao.blindVote.startPublishing=Publier la transaction du vote caché... -dao.blindVote.success=Votre transaction de vote caché a été publiée avec succès.\n\nVeuillez noter que vous devez être en ligne pendant la phase de dévoilement du vote pour que votre application Bisq puisse publier la transaction de dévoilement du vote. Sans le dévoilement du vote, votre vote serait invalide ! - -dao.wallet.menuItem.send=Envoyer -dao.wallet.menuItem.receive=Recevoir -dao.wallet.menuItem.transactions=Transactions - -dao.wallet.dashboard.myBalance=Mon solde de portefeuille - -dao.wallet.receive.fundYourWallet=Votre adresse de réception BSQ -dao.wallet.receive.bsqAddress=Adresse du portefeuille BSQ (nouvelle adresse non utilisée) - -dao.wallet.send.sendFunds=Envoyer des fonds -dao.wallet.send.sendBtcFunds=Envoyer des fonds non-BSQ (BTC) -dao.wallet.send.amount=Montant en BSQ -dao.wallet.send.btcAmount=Montant en BTC (fonds non-BSQ) -dao.wallet.send.setAmount=Définir le montant à retirer (le montant minimum est {0}) -dao.wallet.send.receiverAddress=Adresse BSQ du destinataire -dao.wallet.send.receiverBtcAddress=Adresse BTC du destinataire -dao.wallet.send.setDestinationAddress=Remplissez votre adresse de destination -dao.wallet.send.send=Envoyer des fonds en BSQ -dao.wallet.send.inputControl=Sélectionner les entrées -dao.wallet.send.sendBtc=Envoyer des fonds en BTC -dao.wallet.send.sendFunds.headline=Confirmer la demande de retrait -dao.wallet.send.sendFunds.details=Envoi: {0}\nVers l'adresse de réception: {1}.\nLes frais de minage requis sont de: {2} ({3} satoshis/byte)\nTaille virtuelle de la transaction: {4} vKb\n\nLe destinataire recevra: {5}\n\nÊtes-vous certain de vouloir retirer ce montant ? -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 - -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED=Indéfini -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED_TX_TYPE=Non reconnu -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNVERIFIED=Transaction BSQ non vérifiée -# suppress inspection "UnusedProperty" -dao.tx.type.enum.INVALID=Transaction BSQ invalide -# suppress inspection "UnusedProperty" -dao.tx.type.enum.GENESIS=Transaction genesis -# suppress inspection "UnusedProperty" -dao.tx.type.enum.TRANSFER_BSQ=Transférer des BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.received.TRANSFER_BSQ=BSQ reçu -# suppress inspection "UnusedProperty" -dao.tx.type.enum.sent.TRANSFER_BSQ=BSQ énvoyé -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PAY_TRADE_FEE=Frais de transaction -# suppress inspection "UnusedProperty" -dao.tx.type.enum.COMPENSATION_REQUEST=Frais de demande de compensation -# suppress inspection "UnusedProperty" -dao.tx.type.enum.REIMBURSEMENT_REQUEST=Frais de demande de remboursement -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROPOSAL=Frais de la demande -# suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=Frais du vote caché -# suppress inspection "UnusedProperty" -dao.tx.type.enum.VOTE_REVEAL=Dévoilement du vote -# suppress inspection "UnusedProperty" -dao.tx.type.enum.LOCKUP=Verrouiller le bond -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNLOCK=Déverrouiller le bond -# suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=Frais d'inscription des actifs -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=Preuve du burn -# suppress inspection "UnusedProperty" -dao.tx.type.enum.IRREGULAR=Irrégulier - -dao.tx.withdrawnFromWallet=BTC prélevé sur le portefeuille -dao.tx.issuanceFromCompReq=Demande de compensation/émission -dao.tx.issuanceFromCompReq.tooltip=La demande de compensation a donné lieu à l''émission de nouveaux BSQ.\nDate d''émission: {0} -dao.tx.issuanceFromReimbursement=Demande de remboursement/émission -dao.tx.issuanceFromReimbursement.tooltip=Demande de remboursement ayant donné lieu à l''émission de nouveaux BSQ.\nDate d''émission : {0}. -dao.proposal.create.missingBsqFunds=Vous ne disposez pas de suffisamment de fonds en BSQ pour créer cette demande. Si vous avez une transaction BSQ non confirmée, vous devez attendre la confirmation de la blockchain car les BSQ ne seront validés que si elle est incluse dans un bloc.\nManquant: {0} - -dao.proposal.create.missingBsqFundsForBond=Vous ne disposez pas d''assez de fonds en BSQ pour ce rôle. Vous pouvez toujours publier cette demande, mais vous aurez besoin du montant total de BSQ requis pour que ce rôle puisse être accepté.\nManquant: {0} - -dao.proposal.create.missingMinerFeeFunds=Vous ne disposez pas des fonds en BTC suffisants pour créer cette demande de transaction. Toutes les transactions BSQ requièrent le paiement des frais BTC pour le minage.\nManquant: {0} - -dao.proposal.create.missingIssuanceFunds=Vous ne disposez pas de fonds BTC suffisants pour créer cette demande de transaction. Toutes les transactions BSQ exigent des frais pour le mineur en BTC, et la création d''une transaction exige également des frais en BTC d''un montant de ({0} Satoshis/BSQ).\nManquant: {1} - -dao.feeTx.confirm=Confirmer {0} transaction -dao.feeTx.confirm.details={0} frais: {1}\nFrais de minage: {2} ({3} Satoshis/byte)\nTaille virtuelle de la transaction: {4} vKb\n\nÊtes-vous certain de vouloir publier la transaction {5}? - -dao.feeTx.issuanceProposal.confirm.details={0}frais: {1}\nBTC nécessaire pour l'émission des BSQ: {2} ({3} Satoshis/BSQ)\nFrais de minage: {4} ({5} Satoshis/byte)\nTaille virtuelle de la transaction: {6} vKb\n\nSi votre demande est approuvée, vous allez reçevoir le montant que vous avez requis, ôté des frais de la demande d'un montant de 2 BSQ.\n\nÊtes-vous sûr de vouloir publier la transaction {7}? - -dao.news.bisqDAO.title=La DAO de BISQ -dao.news.bisqDAO.description=Tout comme la plateforme d'échange Bisq est décentralisée et résistante à la censure, son modèle de gouvernance l'est aussi - ainsi que les jetons de la DAO de Bisq et BSQ sont les outils qui rendent cela possible. -dao.news.bisqDAO.readMoreLink=En savoir plus sur la DAO de Bisq - -dao.news.pastContribution.title=VOUS AVEZ PARTICIPÉ ANTÉRIEUREMENT ? DEMANDEZ DES BSQ -dao.news.pastContribution.description=Si vous avez participé à Bisq, veuillez utiliser l'adresse BSQ ci-dessous et faire une demande pour prendre part à la distribution genesis de BSQ. -dao.news.pastContribution.yourAddress=Adresse de votre portefeuille BSQ -dao.news.pastContribution.requestNow=Demander maintenant - -dao.news.DAOOnTestnet.title=LANCEZ LA DAO DE BISQ SUR NOTRE TESTNET -dao.news.DAOOnTestnet.description=Le mainnet de la DAO de Bisq n'est pas encore lancé mais vous pouvez en savoir plus sur la DAO de Bisq en l'exécutant sur notre testnet. -dao.news.DAOOnTestnet.firstSection.title=1. Passer sur le mode Testnet de la DAO -dao.news.DAOOnTestnet.firstSection.content=Passez au Testnet de la DAO à partir de l'écran des paramètres. -dao.news.DAOOnTestnet.secondSection.title=2. Acquérir des BSQ -dao.news.DAOOnTestnet.secondSection.content=Demander des BSQ sur Slack ou acheter des BSQ sur Bisq. -dao.news.DAOOnTestnet.thirdSection.title=3. Participer à un cycle de vote -dao.news.DAOOnTestnet.thirdSection.content=Faire des demandes et voter pour des propositions visant à modifier divers aspects de Bisq. -dao.news.DAOOnTestnet.fourthSection.title=4. Explorez un explorateur de blocs BSQ -dao.news.DAOOnTestnet.fourthSection.content=Dans la mesure où BSQ est comme Bitcoin, vous pouvez voir les transactions en BSQ sur notre explorateur de blocs Bitcoin. -dao.news.DAOOnTestnet.readMoreLink=Lire la documentation complète - -dao.monitor.daoState=Etat de la DAO -dao.monitor.proposals=État des propositions -dao.monitor.blindVotes=État des votes cachés - -dao.monitor.table.peers=Pairs -dao.monitor.table.conflicts=Conflits -dao.monitor.state=Statut -dao.monitor.requestAlHashes=Demander tous les hashes -dao.monitor.resync=Etat de resync de la DAO -dao.monitor.table.header.cycleBlockHeight=Cycle / Hauteur de bloc -dao.monitor.table.cycleBlockHeight=Cycle {0} / bloc {1} -dao.monitor.table.seedPeers=Nœud de la seed: {0} - -dao.monitor.daoState.headline=État de la DAO -dao.monitor.daoState.table.headline=État des hashes de la chaîne DAO -dao.monitor.daoState.table.blockHeight=Hauteur de bloc -dao.monitor.daoState.table.hash=État du hash de la DAO -dao.monitor.daoState.table.prev=Hash précédent -dao.monitor.daoState.conflictTable.headline=État des hashes des pairs de la DAO en situation de conflit -dao.monitor.daoState.utxoConflicts=conflits UTXO -dao.monitor.daoState.utxoConflicts.blockHeight=Hauteur de bloc: {0} -dao.monitor.daoState.utxoConflicts.sumUtxo=Somme de tous les UTXO: {0} BSQ -dao.monitor.daoState.utxoConflicts.sumBsq=Somme de tous les BSQ: {0} BSQ -dao.monitor.daoState.checkpoint.popup=La DAO n'est pas en état de synchronisation avec le réseau. Après redémarrage, la DAO sera resynchronisé. - -dao.monitor.proposal.headline=État des propositions -dao.monitor.proposal.table.headline=Etat du hachage de la chaîne de proposition -dao.monitor.proposal.conflictTable.headline=Etat de la proposition de hachage des pairs en conflit - -dao.monitor.proposal.table.hash=État du hash de la proposition -dao.monitor.proposal.table.prev=Hash précédent -dao.monitor.proposal.table.numProposals=Nombre de propositions - -dao.monitor.isInConflictWithSeedNode=Vos données locales ne font pas consensus avec au moins un nœud de la seed . Veuillez resynchroniser la DAO. -dao.monitor.isInConflictWithNonSeedNode=L'un de vos pairs n'est pas en consensus avec le réseau, mais votre nœud est synchronisé avec les nœuds de la seed. -dao.monitor.daoStateInSync=Votre nœud local est en consensus avec le réseau - -dao.monitor.blindVote.headline=État des votes cachés -dao.monitor.blindVote.table.headline=État du hachage de la chaîne du vote caché -dao.monitor.blindVote.conflictTable.headline=Vote caché de l'état du hash des pairs en conflit -dao.monitor.blindVote.table.hash=État du Hash du vote caché -dao.monitor.blindVote.table.prev=Hash précédent -dao.monitor.blindVote.table.numBlindVotes=Nombre de votes cachés - -dao.factsAndFigures.menuItem.supply=Quantité existante de bsq -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=Moyenne sur 90 jours coefficientée du prix BSQ/USD -dao.factsAndFigures.dashboard.avgUSDPrice30=Moyenne sur 30 jours coefficientée du prix d'échange BSQ/USD -dao.factsAndFigures.dashboard.marketCap=Capitalisation du marché (basée sur la moyenne sur 30 jours du prix d'échange BSQ/USD) -dao.factsAndFigures.dashboard.availableAmount=BSQ disponible au total -dao.factsAndFigures.dashboard.volumeUsd=Volume total du trade en USD -dao.factsAndFigures.dashboard.volumeBtc=Volume total du trade en BTC -dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection=Moyenne du prix de trade BSQ/USD sur la période de temps sélectionnée dans le tableau -dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection=Moyenne du prix de trade BSQ/BTC sur la période de temps sélectionnée dans le tableau - -dao.factsAndFigures.supply.issuedVsBurnt=BSQ émis v. BSQ brûlé - -dao.factsAndFigures.supply.issued=BSQ émis -dao.factsAndFigures.supply.compReq=Requêtes de compensation -dao.factsAndFigures.supply.reimbursement=Demandes de remboursement -dao.factsAndFigures.supply.genesisIssueAmount=BSQ émis lors de la transaction genesis -dao.factsAndFigures.supply.compRequestIssueAmount=BSQ émis pour les demandes de compensation -dao.factsAndFigures.supply.reimbursementAmount=BSQ émis pour les demandes de remboursement -dao.factsAndFigures.supply.totalIssued=BSQ produit au total -dao.factsAndFigures.supply.totalBurned=Total de BSQ brûlé -dao.factsAndFigures.supply.chart.tradeFee.toolTip={0}\n{1} -dao.factsAndFigures.supply.burnt=BSQ précédemment burn - -dao.factsAndFigures.supply.priceChat=Prix du BSQ -dao.factsAndFigures.supply.volumeChat=Volume d'échange -dao.factsAndFigures.supply.tradeVolumeInUsd=Volume du trade en USD -dao.factsAndFigures.supply.tradeVolumeInBtc=Volume du trade en BTC -dao.factsAndFigures.supply.bsqUsdPrice=Prix BSQ/USD -dao.factsAndFigures.supply.bsqBtcPrice=Prix BSQ/BTC -dao.factsAndFigures.supply.btcUsdPrice=Prix BTC/USD - -dao.factsAndFigures.supply.locked=État global des BSQ verrouillés -dao.factsAndFigures.supply.totalLockedUpAmount=Verrouillé dans les bonds -dao.factsAndFigures.supply.totalUnlockingAmount=Déverrouillage des BSQ en bonds -dao.factsAndFigures.supply.totalUnlockedAmount=BSQ déverrouillés des bonds -dao.factsAndFigures.supply.totalConfiscatedAmount=BSQ confisqués en bonds -dao.factsAndFigures.supply.proofOfBurn=Preuve de la destruction -dao.factsAndFigures.supply.bsqTradeFee=Frais de trade du BSQ -dao.factsAndFigures.supply.btcTradeFee=Frais de trade du BTC - -dao.factsAndFigures.transactions.genesis=Transaction genesis -dao.factsAndFigures.transactions.genesisBlockHeight=Hauteur de bloc du bloc genesis -dao.factsAndFigures.transactions.genesisTxId=ID de la transaction genesis -dao.factsAndFigures.transactions.txDetails=Statistiques des transactions en BSQ -dao.factsAndFigures.transactions.allTx=Nombre de transactions en BSQ -dao.factsAndFigures.transactions.utxo=Nombre de transactions de sorties non dépensées -dao.factsAndFigures.transactions.compensationIssuanceTx=Nombre de transactions émises en demande de compensation -dao.factsAndFigures.transactions.reimbursementIssuanceTx=Nombre des transactions émises en demande de remboursement -dao.factsAndFigures.transactions.burntTx=Nombre de transactions ayant occasionné le paiement de frais -dao.factsAndFigures.transactions.invalidTx=Nombre de transactions invalides -dao.factsAndFigures.transactions.irregularTx=Nombre des transactions irrégulières - - - #################################################################### # Windows #################################################################### @@ -2120,8 +1407,6 @@ 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. emptyWalletWindow.balance=Votre solde disponible sur le portefeuille -emptyWalletWindow.bsq.btcBalance=Solde en Satoshis non-BSQ - emptyWalletWindow.address=Votre adresse de destination emptyWalletWindow.button=Envoyer tous les fonds emptyWalletWindow.openOffers.warn=Vous avez des ordres en cours qui seront supprimées si vous videz votre portefeuille.\nVous êtes certain de vouloir vider votre portefeuille ? @@ -2146,10 +1431,8 @@ filterWindow.seedNode=Nœuds de seed filtrés (adresses onion séparées par une filterWindow.priceRelayNode=Nœuds relais avec prix filtrés (adresses onion séparées par une virgule) 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=Désactiver la confirmation automatique filterWindow.autoConfExplorers=Explorateur d'auto-confirmations filtrés (addresses à virgule de séparation) -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 @@ -2223,7 +1506,6 @@ tradeDetailsWindow.detailData=Données détaillées txDetailsWindow.headline=Détails de la transaction txDetailsWindow.btc.note=Vous avez envoyé du BTC. -txDetailsWindow.bsq.note=Vous avez encoyé des fonds en BSQ. Le BSQ est du bitcoin coloré, donc la transaction ne s'affichera pas dans un explorateur BSQ tant qu'elle n'est pas confirmée dans un block bitcoin. txDetailsWindow.sentTo=Envoyé à txDetailsWindow.txId=ID de transaction @@ -2235,9 +1517,6 @@ closedTradesSummaryWindow.totalMinerFee.title=Somme de tous les frais de mineur closedTradesSummaryWindow.totalMinerFee.value={0} ({1} du montant total du trade) closedTradesSummaryWindow.totalTradeFeeInBtc.title=Somme de tous les frais de trade payés en BTC closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} du montant total du trade) -closedTradesSummaryWindow.totalTradeFeeInBsq.title=Somme de tous les frais de trade payés en BSQ -closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} du montant total du trade) - walletPasswordWindow.headline=Entrer le mot de passe pour déverouiller torNetworkSettingWindow.header=Paramètres du réseau Tor @@ -2317,12 +1596,6 @@ popup.warning.tooLargePercentageValue=Vous ne pouvez pas définir un pourcentage popup.warning.examplePercentageValue=Merci de saisir un nombre sous la forme d'un pourcentage tel que \"5.4\" pour 5.4% popup.warning.noPriceFeedAvailable=Il n'y a pas de flux pour le prix de disponible pour cette devise. Vous ne pouvez pas utiliser un prix basé sur un pourcentage.\nVeuillez sélectionner le prix fixé. popup.warning.sendMsgFailed=L'envoi du message à votre partenaire d'échange a échoué.\nMerci d'essayer de nouveau et si l'échec persiste merci de reporter le bug. -popup.warning.insufficientBtcFundsForBsqTx=Vous ne disposez pas de suffisamment de fonds BTC pour payer les frais du minage de cette transaction.\nVeuillez approvisionner votre portefeuille BTC.\nFonds manquants: {0} -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=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\". @@ -2336,8 +1609,6 @@ popup.warning.nodeBanned=Un des noeuds {0} a été banni. popup.warning.priceRelay=Relais de prix popup.warning.seed=seed popup.warning.mandatoryUpdate.trading=Veuillez faire une mise à jour vers la dernière version de Bisq. Une mise à jour obligatoire a été publiée, laquelle désactive le trading sur les anciennes versions. Veuillez consulter le Forum Bisq pour obtenir plus d'informations. -popup.warning.mandatoryUpdate.dao=Veuillez faire une mise à jour vers la dernière version de Bisq. Une mise à jour obligatoire a été publiée, laquelle désactive la DAO de Bisq et BSQ sur les anciennes versions. Veuillez consulter le Forum Bisq pour obtenir plus d'informations. -popup.warning.disable.dao=La DAO de Bisq et BSQ sont désactivés temporairement. Veuillez consulter le Forum Bisq pour obtenir plus d'informations. popup.warning.noFilter=Nous n'avons pas reçu d'object de filtre de la part des noeuds source. Ceci n'est pas une situation attendue. Veuillez informer les développeurs de Bisq popup.warning.burnBTC=Cette transaction n''est pas possible, car les frais de minage de {0} dépasseraient le montant à transférer de {1}. Veuillez patienter jusqu''à ce que les frais de minage soient de nouveau bas ou jusqu''à ce que vous ayez accumulé plus de BTC à transférer. @@ -2356,7 +1627,6 @@ 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=Il semble que vous exécutez Bisq sous Qubes OS.\n\nVeuillez vous assurer que votre Bisq qube est mis en place de la manière expliquée dans notre guide [LIEN:https://bisq.wiki/Running_Bisq_on_Qubes]. popup.warn.downGradePrevention=La rétrogradation depuis la version {0} vers la version {1} n'est pas supportée. Veuillez utiliser la dernière version de Bisq. -popup.warn.daoRequiresRestart=Il y'a eu un problème lors de la synchronisation de l'état du DAO. Vous devez redémarrer l'application pour pallier à ce problème. popup.privateNotification.headline=Notification privée importante! @@ -2528,7 +1798,6 @@ navigation.settings.preferences=\"Paramètres/Préférences\" # suppress inspection "UnusedProperty" navigation.funds.transactions=\"Fonds/Transactions\" navigation.support=\"Assistance\" -navigation.dao.wallet.receive=\"DAO/BSQ Portefeuille/Recevoir\" #################################################################### @@ -2558,12 +1827,6 @@ XMR_MAINNET=Monero Mainnet XMR_TESTNET=Monero Testnet # suppress inspection "UnusedProperty" XMR_STAGENET=Monero Stagenet -# suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet (obsolète) -# suppress inspection "UnusedProperty" -BTC_DAO_BETANET=Bisq DAO Betanet (Bitcoin Mainnet) -# suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=Bitcoin DAO Regtest time.year=Année time.month=Mois @@ -2910,9 +2173,7 @@ validation.accountNrChars=Le numéro du compte doit comporter {0} caractères. validation.btc.invalidAddress=L''adresse n''est pas correcte. Veuillez vérifier le format de l''adresse. validation.integerOnly=Veuillez seulement entrer des nombres entiers. validation.inputError=Votre saisie a causé une erreur:\n{0} -validation.bsq.insufficientBalance=Votre solde disponible est {0}. validation.btc.exceedsMaxTradeLimit=Votre seuil maximum d''échange est {0}. -validation.bsq.amountBelowMinAmount=Le montant minimal est {0} validation.nationalAccountId={0} doit être composé de {1} nombres. #new @@ -2934,7 +2195,6 @@ validation.bic.invalidLocationCode=Le BIC contient un code de localisation inval validation.bic.invalidBranchCode=Le BIC contient un branch code invalide. validation.bic.sepaRevolutBic=Les comptes Sepa de Revolut ne sont pas pris en charge. validation.btc.invalidFormat=Format invalide pour une addresse Bitcoin. -validation.bsq.invalidFormat=Format invalide pour une addresse BSQ. validation.email.invalidAddress=Adresse invalide validation.iban.invalidCountryCode=Code du pays invalide validation.iban.checkSumNotNumeric=La checksum doit être numérique diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index 06fbd92051..02b4beddd2 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -192,7 +192,6 @@ shared.tradeWalletBalance=Saldo del portafogli per gli scambi shared.makerTxFee=Maker: {0} shared.takerTxFee=Taker: {0} shared.iConfirm=Confermo -shared.tradingFeeInBsqInfo=≈ {0} shared.openURL=Aperti {0} shared.fiat=Fiat shared.crypto=Crypto @@ -204,9 +203,6 @@ shared.actions=Azioni shared.buyerUpperCase=Acquirente shared.sellerUpperCase=Venditore shared.new=NUOVO -shared.blindVoteTxId=ID transazione voto cieco -shared.proposal=Proposta -shared.votes=Voti shared.learnMore=Leggi di più shared.dismiss=Chiudi shared.selectedArbitrator=Arbitro selezionato @@ -240,7 +236,6 @@ mainView.menu.funds=Fondi mainView.menu.support=Supporto mainView.menu.settings=Impostazioni mainView.menu.account=Account -mainView.menu.dao=DAO mainView.marketPriceWithProvider.label=Prezzo di mercato per {0} mainView.marketPrice.bisqInternalPrice=Prezzo dell'ultimo scambio su Bisq @@ -257,13 +252,11 @@ mainView.footer.localhostBitcoinNode=(localhost) 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=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} -mainView.footer.daoFullNode=Nodo completo DAO mainView.bootstrapState.connectionToTorNetwork=(1/4) Connessione alla rete Tor... mainView.bootstrapState.torNodeCreated=(2/4) Nodo Tor creato @@ -435,7 +428,6 @@ createOffer.fundsBox.networkFee=Commissione di mining createOffer.fundsBox.placeOfferSpinnerInfo=Pubblicazione dell'offerta in corso ... createOffer.fundsBox.paymentLabel=Scambio Bisq con ID {0} createOffer.fundsBox.fundsStructure=({0} deposito cauzionale, {1} commissione di scambio, {2} commissione di mining) -createOffer.fundsBox.fundsStructure.BSQ=({0} deposito cauzionale, {1} commissione di mining) + {2} commissione di scambio createOffer.success.headline=La tua offerta è stata pubblicata createOffer.success.info=Puoi gestire le tue offerte aperte su \"Portafoglio/Le mie offerte aperte\". createOffer.info.sellAtMarketPrice=Venderai sempre al prezzo di mercato poiché il prezzo della tua offerta verrà continuamente aggiornato. @@ -636,7 +628,6 @@ portfolio.pending.step2_buyer.amountToTransfer=Importo da trasferire portfolio.pending.step2_buyer.sellersAddress=Indirizzo {0} del venditore portfolio.pending.step2_buyer.buyerAccount=Il tuo conto di pagamento da utilizzare portfolio.pending.step2_buyer.paymentStarted=Il pagamento è iniziato -portfolio.pending.step2_buyer.fillInBsqWallet=Pay from BSQ wallet portfolio.pending.step2_buyer.warn=Non hai ancora effettuato il tuo pagamento {0}!\nSi prega di notare che lo scambio è stato completato da {1}. portfolio.pending.step2_buyer.openForDispute=Non hai completato il pagamento!\nÈ trascorso il massimo periodo di scambio. Si prega di contattare il mediatore per assistenza. portfolio.pending.step2_buyer.paperReceipt.headline=Hai inviato la ricevuta cartacea al venditore BTC? @@ -882,7 +873,6 @@ funds.locked.locked=Bloccato in multisig per lo scambio con ID: {0} funds.tx.direction.sentTo=Inviato a: funds.tx.direction.receivedWith=Ricevuto con: funds.tx.direction.genesisTx=Da tx Genesi: -funds.tx.txFeePaymentForBsqTx=Commissione di mining per transazioni BSQ funds.tx.createOfferFee=Commissione per maker e tx: {0} funds.tx.takeOfferFee=Commissione per taker e tx: {0} funds.tx.multiSigDeposit=Deposito multisig: {0} @@ -896,15 +886,11 @@ funds.tx.unknown=Motivo sconosciuto: {0} funds.tx.noFundsFromDispute=Nessun rimborso dalla controversia funds.tx.receivedFunds=Fondi ricevuti funds.tx.withdrawnFromWallet=Prelevato dal portafoglio -funds.tx.withdrawnFromBSQWallet=BTC prelevati dal portafoglio BSQ funds.tx.memo=Memo 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 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  @@ -996,9 +982,7 @@ settings.tab.about=Circa setting.preferences.general=Preferenze generali setting.preferences.explorer=Bitcoin Explorer -setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=Deviazione massima del prezzo di mercato -setting.preferences.bsqAverageTrimThreshold=Outlier threshold for BSQ rate setting.preferences.avoidStandbyMode=Evita modalità standby setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled @@ -1032,19 +1016,6 @@ setting.preferences.notifyOnPreRelease=Receive pre-release notifications setting.preferences.resetAllFlags=Ripristina tutti i flag \"Non mostrare più\" settings.preferences.languageChange=Per applicare la modifica della lingua a tutte le schermate è necessario riavviare. settings.preferences.supportLanguageWarning=In caso di controversia, tenere presente che la mediazione è gestita in {0} e l'arbitrato in {1}. -setting.preferences.daoOptions=Opzioni DAO -setting.preferences.dao.resyncFromGenesis.label=Ricostruisci lo stato della DAO dalla transazione di genesi -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.resync=Resync from genesis and shutdown -setting.preferences.dao.isDaoFullNode=Lancia Bisq come full node DAO -setting.preferences.dao.rpcUser=Username RPC -setting.preferences.dao.rpcPw=Password RPC -setting.preferences.dao.blockNotifyPort=Blocca porta di notifica -setting.preferences.dao.fullNodeInfo=Per eseguire Bisq come nodo DAO completo devi avere Bitcoin Core in esecuzione localmente e RPC abilitato. Tutti i requisiti sono documentati in ''{0}''.\n\nDopo aver modificato la modalità, è necessario riavviare. -setting.preferences.dao.fullNodeInfo.ok=Apri la pagina dei documenti -setting.preferences.dao.fullNodeInfo.cancel=No, continuo ad utilizzare il nodo leggero settings.preferences.editCustomExplorer.headline=Explorer Settings settings.preferences.editCustomExplorer.description=Choose a system defined explorer from the list on the left, and/or customize to suit your own preferences. settings.preferences.editCustomExplorer.available=Available explorers @@ -1090,7 +1061,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 DAO chain height: {0} | Bitcoin Peers chain height: {1} +settings.net.chainHeight=Bitcoin Peers chain height: {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) @@ -1144,14 +1115,10 @@ setting.about.shortcuts.walletDetails=Apri la finestra dei dettagli del portafog setting.about.shortcuts.openEmergencyBtcWalletTool=Apri lo strumento portafoglio di emergenza per il portafoglio BTC -setting.about.shortcuts.openEmergencyBsqWalletTool=Apri lo strumento portafoglio di emergenza per il portafoglio BSQ - setting.about.shortcuts.showTorLogs=Attiva / disattiva il livello di registro per i messaggi Tor tra DEBUG e WARN setting.about.shortcuts.manualPayoutTxWindow=Apri la finestra per il pagamento manuale da una transazione di deposito Multisig 2di2 -setting.about.shortcuts.reRepublishAllGovernanceData=Ripubblicare i dati di governance DAO (proposte, voti) - setting.about.shortcuts.removeStuckTrade=Open popup to move failed trade to open trades tab again setting.about.shortcuts.removeStuckTrade.value=Select failed trade and press: {0} @@ -1337,687 +1304,6 @@ account.notifications.noWebCamFound.warning=Nessuna webcam trovata.\n\nUtilizzar account.notifications.priceAlert.warning.highPriceTooLow=Il prezzo più alto deve essere maggiore del prezzo più basso. account.notifications.priceAlert.warning.lowerPriceTooHigh=Il prezzo più basso deve essere inferiore al prezzo più alto. - - - -#################################################################### -# DAO -#################################################################### - -dao.tab.factsAndFigures=Fatti e cifre -dao.tab.bsqWallet=Portafoglio BSQ -dao.tab.proposals=Governance -dao.tab.bonding=Bonding -dao.tab.proofOfBurn=Commissione di quotazione delle attività/Proof of burn -dao.tab.monitor=Monitor di rete -dao.tab.news=Notizie - -dao.paidWithBsq=pagato con BSQ -dao.availableBsqBalance=Disponibile per la spesa (verificati + output di resti non confermati) -dao.verifiedBsqBalance=Saldo di tutte le UTXO verificate -dao.unconfirmedChangeBalance=Saldo di tutte gli output di resto non confermati -dao.unverifiedBsqBalance=Saldo di tutte le transazioni non verificate (in attesa di conferma del blocco) -dao.lockedForVoteBalance=Utilizzato per il voto -dao.lockedInBonds=Bloccati in bond -dao.availableNonBsqBalance=Saldo disponibile non-BSQ (BTC) -dao.reputationBalance=Valore di merito (non spendibile) - -dao.tx.published.success=La tua transazione è stata pubblicata con successo. -dao.proposal.menuItem.make=Fai una proposta -dao.proposal.menuItem.browse=Sfoglia le proposte aperte -dao.proposal.menuItem.vote=Vota le proposte -dao.proposal.menuItem.result=Risultati del voto -dao.cycle.headline=Ciclo di votazione -dao.cycle.overview.headline=Panoramica del ciclo di votazione -dao.cycle.currentPhase=Fase attuale -dao.cycle.currentBlockHeight=Altezza attuale del blocco -dao.cycle.proposal=Fase della proposta -dao.cycle.proposal.next=Prossima fase della proposta -dao.cycle.blindVote=Fase di voto alla cieca -dao.cycle.voteReveal=Fase di rivelazione dei voti -dao.cycle.voteResult=Risultato del voto -dao.cycle.phaseDuration={0} blocchi (≈{1}); Blocco {2} - {3} (≈{4} - ≈{5}) -dao.cycle.phaseDurationWithoutBlocks=Blocco {0} - {1} (≈{2} - ≈{3}) - -dao.voteReveal.txPublished.headLine=Transazione del voto di rivelazione pubblicata -dao.voteReveal.txPublished=Il tuo voto ha rivelato che la transazione con identificativo transazione {0} è stata pubblicata correttamente.\n\nCiò accade automaticamente, attraverso il software, se hai partecipato al voto DAO. - -dao.results.cycles.header=Cicli -dao.results.cycles.table.header.cycle=Ciclo -dao.results.cycles.table.header.numProposals=Proposte -dao.results.cycles.table.header.voteWeight=Peso del voto -dao.results.cycles.table.header.issuance=Emissione - -dao.results.results.table.item.cycle=Ciclo {0} avviato: {1} - -dao.results.proposals.header=Proposte del ciclo selezionato -dao.results.proposals.table.header.nameLink=Nome/link -dao.results.proposals.table.header.details=Dettagli -dao.results.proposals.table.header.myVote=Il mio voto -dao.results.proposals.table.header.result=Risultato del voto -dao.results.proposals.table.header.threshold=Soglia -dao.results.proposals.table.header.quorum=Quorum - -dao.results.proposals.voting.detail.header=Risultati del voto per la proposta selezionata - -dao.results.exceptions=Eccezioni del risultato del voto - -# suppress inspection "UnusedProperty" -dao.param.UNDEFINED=Non definito - -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BSQ=Commissione maker BSQ -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BSQ=Commissione taker BSQ -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BSQ=Commissione in BSQ minima per il maker -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BSQ=Commissione in BSQ minima per il taker -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BTC=Commissione maker BTC -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BTC=Commissione taker BTC -# suppress inspection "UnusedProperty" -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BTC=Commissione in BTC minima per il maker -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BTC=Commissione in BSQ minima per il taker -# suppress inspection "UnusedProperty" - -# suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=Commissione di proposta in BSQ -# suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=Commissione di voto in BSQ - -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=Richiesta di compenso min. importo BSQ -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=Richiesta di compenso max. importo BSQ -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MIN_AMOUNT=Richiesta di rimborso min. importo BSQ -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MAX_AMOUNT=Richiesta di rimborso max. importo BSQ - -# suppress inspection "UnusedProperty" -dao.param.QUORUM_GENERIC=Quorum richiesto in BSQ per proposta generica -# suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=Quorum richiesto in BSQ per la richiesta di compenso -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REIMBURSEMENT=Quorum richiesto in BSQ per la richiesta di rimborso -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=Quorum richiesto in BSQ per la modifica di un parametro -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Quorum richiesto in BSQ per la rimozione di un asset -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=Quorum richiesto in BSQ per una richiesta di confisca -# suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=Quorum richiesto in BSQ per le proposte di ruoli vincolati - -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_GENERIC=Soglia richiesta in% per la proposta generica -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=Soglia richiesta in% per la richiesta di compenso -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REIMBURSEMENT=Soglia richiesta in% per la richiesta di rimborso -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=Soglia richiesta in% per modificare un parametro -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Soglia obbligatoria in% per la rimozione di un asset -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=Soglia richiesta in% per una richiesta di confisca -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_ROLE=Soglia richiesta in % per le proposte di ruolo vincolati - -# suppress inspection "UnusedProperty" -dao.param.RECIPIENT_BTC_ADDRESS=Indirizzo BTC del destinatario - -# suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=Commissione di quotazione giornaliera degli asset -# suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Volume di scambi minimo degli asset - -# suppress inspection "UnusedProperty" -dao.param.LOCK_TIME_TRADE_PAYOUT=Tempo di blocco per pagamento alternativo di scambio tx -# suppress inspection "UnusedProperty" -dao.param.ARBITRATOR_FEE=Commissione arbitrale in BTC - -# suppress inspection "UnusedProperty" -dao.param.MAX_TRADE_LIMIT=Limite di scambio massimo in BTC - -# suppress inspection "UnusedProperty" -dao.param.BONDED_ROLE_FACTOR=Ruolo legato all’unità di fattore in BSQ -# suppress inspection "UnusedProperty" -dao.param.ISSUANCE_LIMIT=Limite di emissione per ciclo in BSQ - -dao.param.currentValue=Valore corrente: {0} -dao.param.currentAndPastValue=Valore corrente: {0} (valore al momento della proposta: {1}) -dao.param.blocks={0} blocchi - -dao.results.invalidVotes=Abbiamo avuto voti non validi in quel ciclo di votazione. Ciò può accadere se un voto non è stato distribuito bene nella rete Bisq.\n{0} - -# suppress inspection "UnusedProperty" -dao.phase.PHASE_UNDEFINED=Non definito -# suppress inspection "UnusedProperty" -dao.phase.PHASE_PROPOSAL=Fase della proposta -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK1=Pausa 1 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=Fase di voto alla cieca -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK2=Pausa 2 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=Fase di rivelazione dei voti -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK3=Pausa 3 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_RESULT=Fase del risultato - -dao.results.votes.table.header.stakeAndMerit=Peso del voto -dao.results.votes.table.header.stake=Stake -dao.results.votes.table.header.merit=Guadagnato -dao.results.votes.table.header.vote=Votazione - -dao.bond.menuItem.bondedRoles=Ruoli vincolati -dao.bond.menuItem.reputation=Reputazione vincolata -dao.bond.menuItem.bonds=Bond - -dao.bond.dashboard.bondsHeadline=BSQ vincolati -dao.bond.dashboard.lockupAmount=Fondi bloccati -dao.bond.dashboard.unlockingAmount=Sblocco fondi (attendere fino al termine del tempo di blocco) - - -dao.bond.reputation.header=Blocca un deposito per la reputazione -dao.bond.reputation.table.header=I miei vincoli di reputazione -dao.bond.reputation.amount=Quantità di BSQ da bloccare -dao.bond.reputation.time=Tempo di sblocco in blocchi -dao.bond.reputation.salt=Sale -dao.bond.reputation.hash=Hash -dao.bond.reputation.lockupButton=Blocco -dao.bond.reputation.lockup.headline=Conferma transazione di blocco -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.unlock.headline=Conferma transazione di sblocco -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.allBonds.header=Tutti bond - -dao.bond.bondedReputation=Reputazione Vincolata -dao.bond.bondedRoles=Ruoli vincolati - -dao.bond.details.header=Dettagli ruolo -dao.bond.details.role=Ruolo -dao.bond.details.requiredBond=Bond in BSQ richiesto -dao.bond.details.unlockTime=Tempo di sblocco in blocchi -dao.bond.details.link=Link alla descrizione del ruolo -dao.bond.details.isSingleton=Può essere assunto da più detentori di ruoli -dao.bond.details.blocks={0} blocchi - -dao.bond.table.column.name=Nome -dao.bond.table.column.link=Link -dao.bond.table.column.bondType=Tipo di bond -dao.bond.table.column.details=Dettagli -dao.bond.table.column.lockupTxId=ID Tx di blocco -dao.bond.table.column.bondState=Stato bond -dao.bond.table.column.lockTime=Tempo di sblocco -dao.bond.table.column.lockupDate=Data di blocco - -dao.bond.table.button.lockup=Blocco -dao.bond.table.button.unlock=Sbocca -dao.bond.table.button.revoke=Revoca - -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNDEFINED=Non definito -# suppress inspection "UnusedProperty" -dao.bond.bondState.READY_FOR_LOCKUP=Non ancora legato -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_PENDING=Blocco in sospeso -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_CONFIRMED=Bond bloccato -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_PENDING=Sblocco in sospeso -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_CONFIRMED=Tx di sblocco confermata -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKING=Sbloccando il bond -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKED=Bond bloccato -# suppress inspection "UnusedProperty" -dao.bond.bondState.CONFISCATED=Bond confiscato - -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.UNDEFINED=Non definito -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.BONDED_ROLE=Ruolo vincolato -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.REPUTATION=Reputazione vincolata - -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.UNDEFINED=Non definito -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Admin GitHub -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=Admin forum -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=Admin Twitter -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Admin YouTube -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=Maintainer Bisq -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=Maintainer BitcoinJ-fork -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Maintainer Netlayer -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=Operatore sito web -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=Operatore forum -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Operatore nodo seme -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Operatore nodo prezzi -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Operatore nodo Bitcoin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Operatore mercati -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Operatore di inoltro delle notifiche mobile -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Titolare del nome di dominio -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DNS_ADMIN=Admin DNS -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=Mediatore -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=Arbitro -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=Proprietario dell'indirizzo di donazione BTC - -dao.burnBsq.assetFee=Listaggio asset -dao.burnBsq.menuItem.assetFee=Commissione listing dell'asset -dao.burnBsq.menuItem.proofOfBurn=Proof of burn -dao.burnBsq.header=Commissione per listaggio asset -dao.burnBsq.selectAsset=Seleziona Asset -dao.burnBsq.fee=Commissione -dao.burnBsq.trialPeriod=Periodo di prova -dao.burnBsq.payFee=Paga commissione -dao.burnBsq.allAssets=Tutti gli asset -dao.burnBsq.assets.nameAndCode=Nome asset -dao.burnBsq.assets.state=Stato -dao.burnBsq.assets.tradeVolume=Volume di scambio -dao.burnBsq.assets.lookBackPeriod=Periodo di verifica -dao.burnBsq.assets.trialFee=Commissione per il periodo di prova -dao.burnBsq.assets.totalFee=Commissioni totali pagate -dao.burnBsq.assets.days={0} giorni -dao.burnBsq.assets.toFewDays=La commissione è troppo bassa. Il numero minimo di giorni per il periodo di prova è {0}. - -# suppress inspection "UnusedProperty" -dao.assetState.UNDEFINED=Non definito -# suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=Nel periodo di prova -# suppress inspection "UnusedProperty" -dao.assetState.ACTIVELY_TRADED=Scambiato attivamente -# suppress inspection "UnusedProperty" -dao.assetState.DE_LISTED=De-listato per inattività -# suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=Rimosso votando - -dao.proofOfBurn.header=Proof of burn -dao.proofOfBurn.amount=Importo -dao.proofOfBurn.preImage=Pre-immagine -dao.proofOfBurn.burn=Burn -dao.proofOfBurn.allTxs=Tutte le transazioni proof of burn -dao.proofOfBurn.myItems=Le mie transazioni proof of burn -dao.proofOfBurn.date=Data -dao.proofOfBurn.hash=Hash -dao.proofOfBurn.txs=Transazioni -dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Firma un messaggio con la chiave dalla transazione proof of burn -dao.proofOfBurn.verify.window.title=Verifica un messaggio con la chiave dalla transazione proof of burn -dao.proofOfBurn.copySig=Copia la firma negli appunti -dao.proofOfBurn.sign=Firma -dao.proofOfBurn.message=Messaggio -dao.proofOfBurn.sig=Firma -dao.proofOfBurn.verify=Verifica -dao.proofOfBurn.verificationResult.ok=Verifica riuscita -dao.proofOfBurn.verificationResult.failed=Verifica fallita - -# suppress inspection "UnusedProperty" -dao.phase.UNDEFINED=Non definito -# suppress inspection "UnusedProperty" -dao.phase.PROPOSAL=Fase della proposta -# suppress inspection "UnusedProperty" -dao.phase.BREAK1=Pausa prima della fase di voto alla cieca -# suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=Fase di voto alla cieca -# suppress inspection "UnusedProperty" -dao.phase.BREAK2=Pausa prima della fase di rivelazione dei voti -# suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=Fase di rivelazione dei voti -# suppress inspection "UnusedProperty" -dao.phase.BREAK3=Pausa prima della fase del risultato -# suppress inspection "UnusedProperty" -dao.phase.RESULT=Fase del risultato della votazione - -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.PROPOSAL=Fase della proposta -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=Voto cieco -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.VOTE_REVEAL=Rivelazione voto -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.RESULT=Risultato del voto - -# suppress inspection "UnusedProperty" -dao.proposal.type.UNDEFINED=Non definito -# suppress inspection "UnusedProperty" -dao.proposal.type.COMPENSATION_REQUEST=Richiesta di compenso -# suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=Richiesta di rimborso -# suppress inspection "UnusedProperty" -dao.proposal.type.BONDED_ROLE=Proposta per un ruolo vincolato -# suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=Proposta per la rimozione di un asset -# suppress inspection "UnusedProperty" -dao.proposal.type.CHANGE_PARAM=Proposta di modifica di un parametro -# suppress inspection "UnusedProperty" -dao.proposal.type.GENERIC=Proposta generica -# suppress inspection "UnusedProperty" -dao.proposal.type.CONFISCATE_BOND=Proposta di confisca di un bond - -# suppress inspection "UnusedProperty" -dao.proposal.type.short.UNDEFINED=Non definito -# suppress inspection "UnusedProperty" -dao.proposal.type.short.COMPENSATION_REQUEST=Richiesta di compenso -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=Richiesta di rimborso -# suppress inspection "UnusedProperty" -dao.proposal.type.short.BONDED_ROLE=Ruolo bloccato -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ASSET=Rimuovere un altcoin -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CHANGE_PARAM=Cambiare un parametro -# suppress inspection "UnusedProperty" -dao.proposal.type.short.GENERIC=Proposta generica -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CONFISCATE_BOND=Confiscare un bond - -dao.proposal.details=Dettagli della proposta -dao.proposal.selectedProposal=Proposta selezionata -dao.proposal.active.header=Proposte del ciclo attuale -dao.proposal.active.remove.confirm=Sei sicuro di voler rimuovere questa proposta?\nLa commissione di proposta già pagata verrà persa. -dao.proposal.active.remove.doRemove=Sì, rimuovi la mia proposta -dao.proposal.active.remove.failed=Impossibile rimuovere la proposta. -dao.proposal.myVote.title=Voto -dao.proposal.myVote.accept=Accetta la proposta -dao.proposal.myVote.reject=Rifiuta la proposta -dao.proposal.myVote.removeMyVote=Ignora la proposta -dao.proposal.myVote.merit=Peso del voto dai BSQ guadagnati -dao.proposal.myVote.stake=Peso del voto dallo stake -dao.proposal.myVote.revealTxId=ID transazione di rivelazione del voto -dao.proposal.myVote.stake.prompt=Valore massimo disponibile per la votazione: {0} -dao.proposal.votes.header=Imposta il valore per la votazione e pubblica i tuoi voti -dao.proposal.myVote.button=Pubblica voti -dao.proposal.myVote.setStake.description=Dopo aver votato su tutte le proposte devi impostare your stake per il voto bloccando BSQ. Più BSQ bloccati, più peso avrà il tuo voto.\n\nI BSQ bloccati per il voto verranno nuovamente sbloccati durante la fase di rivelazione del voto. -dao.proposal.create.selectProposalType=Seleziona il tipo di proposta -dao.proposal.create.phase.inactive=Attendere fino alla fase della proposta successiva -dao.proposal.create.proposalType=Tipo di proposta -dao.proposal.create.new=Fai una nuova proposta -dao.proposal.create.button=Fai una proposta -dao.proposal.create.publish=Pubblica proposta -dao.proposal.create.publishing=La pubblicazione della proposta è in corso ... -dao.proposal=proposta -dao.proposal.display.type=Tipo di proposta -dao.proposal.display.name=Nome utente GitHub esatto -dao.proposal.display.link=Link a informazioni dettagliate -dao.proposal.display.link.prompt=Link alla proposta -dao.proposal.display.requestedBsq=Importo richiesto in BSQ -dao.proposal.display.txId=ID transazione proposta -dao.proposal.display.proposalFee=Commissione proposta -dao.proposal.display.myVote=Il mio voto -dao.proposal.display.voteResult=Riepilogo dei risultati della votazione -dao.proposal.display.bondedRoleComboBox.label=Tipo di ruolo vincolato -dao.proposal.display.requiredBondForRole.label=Obbligazione richiesta per ruolo -dao.proposal.display.option=Opzione - -dao.proposal.table.header.proposalType=Tipo di proposta -dao.proposal.table.header.link=Link -dao.proposal.table.header.myVote=Il mio voto -# suppress inspection "UnusedProperty" -dao.proposal.table.header.remove=Rimuovi -dao.proposal.table.icon.tooltip.removeProposal=Rimuovi la mia proposta -dao.proposal.table.icon.tooltip.changeVote=Voto attuale: ''{0}''. Cambia voto in: ''{1}'' - -dao.proposal.display.myVote.accepted=Accettato -dao.proposal.display.myVote.rejected=Respinto -dao.proposal.display.myVote.ignored=Ignorato -dao.proposal.display.myVote.unCounted=Il voto non è stato incluso nel risultato -dao.proposal.myVote.summary=Votato: {0}; Peso del voto: {1} (guadagnato: {2} + stake: {3}) {4} -dao.proposal.myVote.invalid=Il voto non è valido - -dao.proposal.voteResult.success=Accettato -dao.proposal.voteResult.failed=Respinto -dao.proposal.voteResult.summary=Risultato: {0}; Soglia: {1} (richiesto> {2}); Quorum: {3} (richiesto> {4}) - -dao.proposal.display.paramComboBox.label=Seleziona il parametro da modificare -dao.proposal.display.paramValue=Valore del parametro - -dao.proposal.display.confiscateBondComboBox.label=Scegli bond -dao.proposal.display.assetComboBox.label=Asset da rimuovere - -dao.blindVote=voto cieco - -dao.blindVote.startPublishing=Pubblicando la transazione di voto alla cieca... -dao.blindVote.success=La tua transazione di voto alla cieca è stata pubblicata con successo.\n\nSi noti che è necessario essere online nella fase di rivelazione dei voti in modo che la propria applicazione Bisq possa pubblicare la transazione di rivelazione del voto. Senza la transazione di rivelazione del voto, il tuo voto non sarebbe valido! - -dao.wallet.menuItem.send=Invia -dao.wallet.menuItem.receive=Ricevi -dao.wallet.menuItem.transactions=Transazioni - -dao.wallet.dashboard.myBalance=Saldo del mio portafoglio - -dao.wallet.receive.fundYourWallet=Il tuo indirizzo di ricezione BSQ -dao.wallet.receive.bsqAddress=Indirizzo del portafoglio BSQ (Indirizzo non ancora utilizzato) - -dao.wallet.send.sendFunds=Invia fondi -dao.wallet.send.sendBtcFunds=Invia fondi non BSQ (BTC) -dao.wallet.send.amount=Importo in BSQ -dao.wallet.send.btcAmount=Importo in BTC (fondi non BSQ) -dao.wallet.send.setAmount=Imposta l'importo da prelevare (l'importo minimo è {0}) -dao.wallet.send.receiverAddress=Indirizzo BSQ del destinatario -dao.wallet.send.receiverBtcAddress=Indirizzo BTC del destinatario -dao.wallet.send.setDestinationAddress=Inserisci il tuo indirizzo di destinazione -dao.wallet.send.send=Invia fondi BSQ -dao.wallet.send.inputControl=Select inputs -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 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 - -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED=Non definito -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED_TX_TYPE=Non riconosciuto -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNVERIFIED=Transazione BSQ non verificata -# suppress inspection "UnusedProperty" -dao.tx.type.enum.INVALID=Transazione BSQ non valida -# suppress inspection "UnusedProperty" -dao.tx.type.enum.GENESIS=Transazione genesi -# suppress inspection "UnusedProperty" -dao.tx.type.enum.TRANSFER_BSQ=Trasferisci BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.received.TRANSFER_BSQ=BSQ ricevuti -# suppress inspection "UnusedProperty" -dao.tx.type.enum.sent.TRANSFER_BSQ=BSQ inviati -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PAY_TRADE_FEE=Commissione di scambio -# suppress inspection "UnusedProperty" -dao.tx.type.enum.COMPENSATION_REQUEST=Commissione per richiesta di compenso -# suppress inspection "UnusedProperty" -dao.tx.type.enum.REIMBURSEMENT_REQUEST=Commissione per richiesta di rimborso -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROPOSAL=Commissione per la proposta -# suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=Commissione per voto alla cieca -# suppress inspection "UnusedProperty" -dao.tx.type.enum.VOTE_REVEAL=Rivelazione voto -# suppress inspection "UnusedProperty" -dao.tx.type.enum.LOCKUP=Blocca bond -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNLOCK=Sblocca bond -# suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=Commissione listing dell'asset -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=Proof of burn -# suppress inspection "UnusedProperty" -dao.tx.type.enum.IRREGULAR=Irregolare - -dao.tx.withdrawnFromWallet=BTC prelevati dal portafoglio -dao.tx.issuanceFromCompReq=Richiesta di compensazione -dao.tx.issuanceFromCompReq.tooltip=Richiesta di compenso che ha portato all'emissione di nuovi BSQ.\nData di emissione: {0} -dao.tx.issuanceFromReimbursement=Richiesta di rimborso/emissione -dao.tx.issuanceFromReimbursement.tooltip=Richiesta di rimborso che ha portato all'emissione di nuovi BSQ.\nData di emissione: {0} -dao.proposal.create.missingBsqFunds=Non hai fondi BSQ sufficienti per creare la proposta. Se hai una transazione BSQ non confermata, devi attendere una conferma sulla blockchain perché BSQ è validato solo se è incluso in un blocco.\nMancante: {0} - -dao.proposal.create.missingBsqFundsForBond=Non hai fondi BSQ sufficienti per questo ruolo. Puoi comunque pubblicare questa proposta, ma avrai bisogno dell'intero importo BSQ richiesto per questo ruolo se viene accettato.\nMancante: {0} - -dao.proposal.create.missingMinerFeeFunds=Non hai fondi BTC sufficienti per creare la transazione di proposta. Tutte le transazioni BSQ richiedono una commissione di mining in BTC.\nMancante: {0} - -dao.proposal.create.missingIssuanceFunds=Non hai fondi BTC sufficienti per creare la transazione di proposta. Tutte le transazioni BSQ richiedono una commissione di mining in BTC e anche le transazioni di emissione richiedono BTC per l'importo BSQ richiesto ({0} Satoshi/BSQ).\nMancante: {1} - -dao.feeTx.confirm=Conferma transazione {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.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.news.bisqDAO.title=LA DAO BISQ -dao.news.bisqDAO.description=Proprio come lo scambio di Bisq è decentralizzato e resistente alla censura, così è il suo modello di governance - e il token Bisq DAO e BSQ sono gli strumenti che lo rendono possibile. -dao.news.bisqDAO.readMoreLink=Ulteriori informazioni sulla Bisq DAO - -dao.news.pastContribution.title=HAI EFFETTUATO CONTRIBUTI IN PASSATO? RICHIEDI BSQ -dao.news.pastContribution.description=Se hai contribuito a Bisq, utilizza l'indirizzo BSQ qui sotto e fai una richiesta per prendere parte alla distribuzione genesi BSQ. -dao.news.pastContribution.yourAddress=L'indirizzo del tuo wallet BSQ -dao.news.pastContribution.requestNow=Richiedi ora - -dao.news.DAOOnTestnet.title=ESEGUI LA BISQ DAO NEL NOSTRO TESTNET -dao.news.DAOOnTestnet.description=La mainnet Bisq DAO non è ancora stata lanciata, ma puoi conoscere Bisq DAO eseguendolo sulla nostra testnet. -dao.news.DAOOnTestnet.firstSection.title=1. Passa alla modalità Testnet DAO -dao.news.DAOOnTestnet.firstSection.content=Passa a DAO Testnet dalla schermata Impostazioni. -dao.news.DAOOnTestnet.secondSection.title=2. Acquista un po' di BSQ -dao.news.DAOOnTestnet.secondSection.content=Richiedi BSQ su Slack o Acquista BSQ su Bisq. -dao.news.DAOOnTestnet.thirdSection.title=3. Partecipa a un Ciclo di Votazione -dao.news.DAOOnTestnet.thirdSection.content=Fare proposte e votare proposte per cambiare vari aspetti di Bisq. -dao.news.DAOOnTestnet.fourthSection.title=4. Esplora un BSQ Block Explorer -dao.news.DAOOnTestnet.fourthSection.content=Poiché BSQ è solo bitcoin, puoi vedere le transazioni BSQ sul nostro explorer di blocchi bitcoin. -dao.news.DAOOnTestnet.readMoreLink=Leggi la documentazione completa - -dao.monitor.daoState=Stato DAO -dao.monitor.proposals=Stato delle proposte -dao.monitor.blindVotes=Stato dei voti ciechi - -dao.monitor.table.peers=Peers -dao.monitor.table.conflicts=Conflitti -dao.monitor.state=Stato -dao.monitor.requestAlHashes=Richiedi tutti gli hash -dao.monitor.resync=Risincronizza lo stato della DAO -dao.monitor.table.header.cycleBlockHeight=Altezza ciclo / blocco -dao.monitor.table.cycleBlockHeight=Ciclo {0} / blocco {1} -dao.monitor.table.seedPeers=Nodo seme: {0} - -dao.monitor.daoState.headline=Stato DAO -dao.monitor.daoState.table.headline=Catena di hash di stato DAO -dao.monitor.daoState.table.blockHeight=Altezza del blocco -dao.monitor.daoState.table.hash=Hash dello stato DAO -dao.monitor.daoState.table.prev=Hash precedente -dao.monitor.daoState.conflictTable.headline=Hash di stato DAO da peer in conflitto -dao.monitor.daoState.utxoConflicts=Conflitti UTXO -dao.monitor.daoState.utxoConflicts.blockHeight=Altezza blocco: {0} -dao.monitor.daoState.utxoConflicts.sumUtxo=Somma di tutti gli UTXO: {0} BSQ -dao.monitor.daoState.utxoConflicts.sumBsq=Somma di tutti i BSQ: {0} BSQ -dao.monitor.daoState.checkpoint.popup=Lo stato DAO non è sincronizzato con la rete. Dopo il riavvio, lo stato DAO verrà risincronizzato. - -dao.monitor.proposal.headline=Stato delle proposte -dao.monitor.proposal.table.headline=Catena di hash dello stato della proposta -dao.monitor.proposal.conflictTable.headline=Hash di stato della proposta da peer in conflitto - -dao.monitor.proposal.table.hash=Hash dello stato della proposta -dao.monitor.proposal.table.prev=Hash precedente -dao.monitor.proposal.table.numProposals=N. di proposte - -dao.monitor.isInConflictWithSeedNode=I dati locali non sono in accordo con almeno un nodo seme. Risincronizzare nuovamente lo stato DAO. -dao.monitor.isInConflictWithNonSeedNode=Uno dei tuoi peer non è d'accordo con la rete ma il tuo nodo è sincronizzato con i nodi seed. -dao.monitor.daoStateInSync=Il nodo locale è in accordo con la rete - -dao.monitor.blindVote.headline=Stato dei voti ciechi -dao.monitor.blindVote.table.headline=Catena di hash dello stato del voto cieco -dao.monitor.blindVote.conflictTable.headline=Hash dello stato del voto alla cieca da peer in conflitto -dao.monitor.blindVote.table.hash=Hash dello stato di voto cieco -dao.monitor.blindVote.table.prev=Hash precedente -dao.monitor.blindVote.table.numBlindVotes=N. di voti ciechi - -dao.factsAndFigures.menuItem.supply=Supply BSQ -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=90 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on 30 days average BSQ/USD price) -dao.factsAndFigures.dashboard.availableAmount=BSQ totale disponibile -dao.factsAndFigures.dashboard.volumeUsd=Total trade volume in USD -dao.factsAndFigures.dashboard.volumeBtc=Total trade volume in BTC -dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection=Average BSQ/USD trade price from selected time period in chart -dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection=Average BSQ/BTC trade price from selected time period in chart - -dao.factsAndFigures.supply.issuedVsBurnt=BSQ emessi v. BSQ bruciati - -dao.factsAndFigures.supply.issued=BSQ coniati -dao.factsAndFigures.supply.compReq=Compensation requests -dao.factsAndFigures.supply.reimbursement=Reimbursement requests -dao.factsAndFigures.supply.genesisIssueAmount=BSQ coniati nella trasazione di genesi -dao.factsAndFigures.supply.compRequestIssueAmount=BSQ coniati per le richieste di compensazione -dao.factsAndFigures.supply.reimbursementAmount=BSQ coniati per le richieste di rimborso -dao.factsAndFigures.supply.totalIssued=Total issued BSQ -dao.factsAndFigures.supply.totalBurned=Total burned BSQ -dao.factsAndFigures.supply.chart.tradeFee.toolTip={0}\n{1} -dao.factsAndFigures.supply.burnt=BSQ bruciati - -dao.factsAndFigures.supply.priceChat=BSQ price -dao.factsAndFigures.supply.volumeChat=Volume di scambio -dao.factsAndFigures.supply.tradeVolumeInUsd=Trade volume in USD -dao.factsAndFigures.supply.tradeVolumeInBtc=Trade volume in BTC -dao.factsAndFigures.supply.bsqUsdPrice=BSQ/USD price -dao.factsAndFigures.supply.bsqBtcPrice=BSQ/BTC price -dao.factsAndFigures.supply.btcUsdPrice=BTC/USD price - -dao.factsAndFigures.supply.locked=Stato globale dei BSQ bloccati -dao.factsAndFigures.supply.totalLockedUpAmount=Bloccati in bond -dao.factsAndFigures.supply.totalUnlockingAmount=Sbloccando i BSQ dai vincoli -dao.factsAndFigures.supply.totalUnlockedAmount=BSQ sbloccati dalle obbligazioni -dao.factsAndFigures.supply.totalConfiscatedAmount=BSQ confiscati dalle obbligazioni -dao.factsAndFigures.supply.proofOfBurn=Proof of Burn -dao.factsAndFigures.supply.bsqTradeFee=BSQ Trade fees -dao.factsAndFigures.supply.btcTradeFee=BTC Trade fees - -dao.factsAndFigures.transactions.genesis=Transazione genesi -dao.factsAndFigures.transactions.genesisBlockHeight=Altezza del blocco genesi -dao.factsAndFigures.transactions.genesisTxId=ID transazione genesi -dao.factsAndFigures.transactions.txDetails=Statistiche transazioni BSQ -dao.factsAndFigures.transactions.allTx=Numero di tutte le transazioni BSQ -dao.factsAndFigures.transactions.utxo=Numero di tutti gli output di transazione non spesi -dao.factsAndFigures.transactions.compensationIssuanceTx=Numero di tutte le transazioni di richieste di compensazione -dao.factsAndFigures.transactions.reimbursementIssuanceTx=Numero di tutte le transazioni di richieste di rimborso -dao.factsAndFigures.transactions.burntTx=Numero di tutte le transazioni di pagamento delle commissioni -dao.factsAndFigures.transactions.invalidTx=N. di tutte le transazioni invalide -dao.factsAndFigures.transactions.irregularTx=N. di tutte le transazioni irregolari - - - #################################################################### # Windows #################################################################### @@ -2120,8 +1406,6 @@ disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any emptyWalletWindow.headline={0} strumento portafoglio di emergenza emptyWalletWindow.info=Utilizzalo solo in caso di emergenza se non puoi accedere al tuo fondo dall'interfaccia utente.\n\nSi noti che tutte le offerte aperte verranno chiuse automaticamente quando si utilizza questo strumento.\n\nPrima di utilizzare questo strumento, eseguire il backup della directory dei dati. Puoi farlo in \"Account/Backup\".\n\nTi preghiamo di segnalarci il tuo problema e di presentare una segnalazione di bug su GitHub o sul forum Bisq in modo da poter esaminare la causa del problema. emptyWalletWindow.balance=Il saldo disponibile del tuo portafoglio -emptyWalletWindow.bsq.btcBalance=Saldo di satoshi non-BSQ - emptyWalletWindow.address=Il tuo indirizzo di destinazione emptyWalletWindow.button=Invia tutti i fondi emptyWalletWindow.openOffers.warn=Hai offerte aperte che verranno rimosse se svuoti il portafoglio.\nSei sicuro di voler svuotare il tuo portafoglio? @@ -2146,10 +1430,8 @@ filterWindow.seedNode=Nodi seme filtrati (separati con una virgola) filterWindow.priceRelayNode=Prezzo filtrato dai nodi relay (virgola sep. indirizzi onion) filterWindow.btcNode=Nodi Bitcoin filtrati (indirizzo + porta separati con una virgola) filterWindow.preventPublicBtcNetwork=Impedisci l'utilizzo della rete pubblica Bitcoin -filterWindow.disableDao=Disabilita DAO filterWindow.disableAutoConf=Disable auto-confirm filterWindow.autoConfExplorers=Filtered auto-confirm explorers (comma sep. addresses) -filterWindow.disableDaoBelowVersion=Versione minima richiesta per la DAO filterWindow.disableTradeBelowVersion=Versione minima richiesta per il trading filterWindow.add=Aggiungi filtro filterWindow.remove=Rimuovi filtro @@ -2223,7 +1505,6 @@ tradeDetailsWindow.detailData=Detail data txDetailsWindow.headline=Transaction Details txDetailsWindow.btc.note=You have sent BTC. -txDetailsWindow.bsq.note=You have sent BSQ funds. BSQ is colored bitcoin, so the transaction will not show in a BSQ explorer until it has been confirmed in a bitcoin block. txDetailsWindow.sentTo=Sent to txDetailsWindow.txId=TxId @@ -2235,9 +1516,6 @@ closedTradesSummaryWindow.totalMinerFee.title=Sum of all miner fees closedTradesSummaryWindow.totalMinerFee.value={0} ({1} of total trade amount) closedTradesSummaryWindow.totalTradeFeeInBtc.title=Sum of all trade fees paid in BTC closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} of total trade amount) -closedTradesSummaryWindow.totalTradeFeeInBsq.title=Sum of all trade fees paid in BSQ -closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} of total trade amount) - walletPasswordWindow.headline=Inserisci la password per sbloccare torNetworkSettingWindow.header=Impostazioni rete Tor @@ -2317,12 +1595,6 @@ popup.warning.tooLargePercentageValue=Non è possibile impostare una percentuale popup.warning.examplePercentageValue=Inserisci un numero percentuale come \"5.4\" per il 5,4% popup.warning.noPriceFeedAvailable=Non è disponibile alcun feed di prezzi per la valuta. Non è possibile utilizzare un prezzo basato su percentuale.\nSeleziona il prezzo fisso. popup.warning.sendMsgFailed=Invio del messaggio al tuo partner commerciale non riuscito.\nTi preghiamo di riprovare e se continua a fallire segnalare un bug. -popup.warning.insufficientBtcFundsForBsqTx=Non hai fondi BTC sufficienti per pagare la commissione di mining per quella transazione.\nPer favore finanzia il tuo portafoglio BTC.\nFondi mancanti: {0} -popup.warning.bsqChangeBelowDustException=Questa transazione crea un output di cambio BSQ che è inferiore al limite di polvere (5,46 BSQ) e verrebbe rifiutato dalla rete Bitcoin.\n\nÈ necessario inviare una quantità maggiore per evitare l'output di cambio (ad es. aggiungendo la quantità di polvere alla quantità di invio) o aggiungere più fondi BSQ al portafoglio in modo da evitare di generare una produzione di polvere.\n\nL'output della polvere è {0}. -popup.warning.btcChangeBelowDustException=Questa transazione crea un output di cambio che è al di sotto del limite di polvere (546 Satoshi) e verrebbe rifiutato dalla rete Bitcoin.\n\nÈ necessario aggiungere la quantità di polvere alla quantità di invio per evitare di generare un output di polvere.\n\nL'output della polvere è {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.noBsqFundsForBtcFeePayment=Il tuo portafoglio BSQ non ha fondi sufficienti per pagare la commissione commerciale in BSQ. popup.warning.messageTooLong=Il tuo messaggio supera la dimensione massima consentita. Si prega di inviarlo in più parti o caricarlo su un servizio come https://pastebin.com. popup.warning.lockedUpFunds=Hai bloccato i fondi da uno scambio fallito.\nSaldo bloccato: {0}\nIndirizzo tx deposito: {1}\nID scambio: {2}.\n\nApri un ticket di supporto selezionando lo scambio nella schermata degli scambi aperti e premendo \"alt + o\" o \"option + o\"." @@ -2336,8 +1608,6 @@ popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=ripetitore di prezzo popup.warning.seed=seme popup.warning.mandatoryUpdate.trading=Si prega di aggiornare Bisq all'ultima versione. È stato rilasciato un aggiornamento obbligatorio che disabilita il trading per le vecchie versioni. Per ulteriori informazioni, consultare il forum Bisq. -popup.warning.mandatoryUpdate.dao=Si prega di aggiornare Bisq all'ultima versione. È stato rilasciato un aggiornamento obbligatorio che disabilita Bisq DAO e BSQ per le vecchie versioni. Per ulteriori informazioni, consultare il forum Bisq.\n  -popup.warning.disable.dao=Bisq DAO e BSQ sono temporaneamente disabilitati. Per ulteriori informazioni, consultare il forum Bisq. popup.warning.noFilter=We did not receive a filter object from the seed nodes. This is a not expected situation. Please inform the Bisq developers. popup.warning.burnBTC=Questa transazione non è possibile, poiché le commissioni di mining di {0} supererebbero l'importo da trasferire di {1}. Attendi fino a quando le commissioni di mining non saranno nuovamente basse o fino a quando non avrai accumulato più BTC da trasferire. @@ -2356,7 +1626,6 @@ 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! @@ -2528,7 +1797,6 @@ navigation.settings.preferences=\"Impostazioni/Preferenze\" # suppress inspection "UnusedProperty" navigation.funds.transactions=\"Fondi/Transazioni\" navigation.support=\"Supporto\" -navigation.dao.wallet.receive=\"Portafoglio DAO/BSQ/Ricevi\" #################################################################### @@ -2558,12 +1826,6 @@ XMR_MAINNET=Mainnet Bitcoin XMR_TESTNET=Testnet Bitcoin # suppress inspection "UnusedProperty" XMR_STAGENET=Regtest Bitcoin -# suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Testnet DAO Bitcoin (deprecata) -# suppress inspection "UnusedProperty" -BTC_DAO_BETANET=Betanet Bisq DAO (Mainnet Bitcoin) -# suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=Regtest DAO Bitcoin time.year=Anno time.month=Mese @@ -2910,9 +2172,7 @@ validation.accountNrChars=Il numero di conto deve contenere {0} caratteri. validation.btc.invalidAddress=L'indirizzo non è corretto Si prega di controllare il formato dell'indirizzo. validation.integerOnly=Inserisci solo numeri interi. validation.inputError=Il tuo input ha causato un errore:\n{0} -validation.bsq.insufficientBalance=Il saldo disponibile è {0}. validation.btc.exceedsMaxTradeLimit=Il tuo limite commerciale è {0}. -validation.bsq.amountBelowMinAmount=L'importo minimo è di {0} validation.nationalAccountId={0} deve essere composto da {1} numeri. #new @@ -2934,7 +2194,6 @@ validation.bic.invalidLocationCode=BIC contiene un codice di posizione non valid validation.bic.invalidBranchCode=BIC contiene un codice di filiale non valido validation.bic.sepaRevolutBic=Gli account Revolut Sepa non sono supportati. validation.btc.invalidFormat=Invalid format for a Bitcoin address. -validation.bsq.invalidFormat=Invalid format for a BSQ address. validation.email.invalidAddress=Indirizzo non valido validation.iban.invalidCountryCode=Codice paese non valido validation.iban.checkSumNotNumeric=Il checksum deve essere numerico diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 4969fa52ad..6bb916d005 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -192,7 +192,6 @@ shared.tradeWalletBalance=トレードウォレット残高 shared.makerTxFee=メイカー: {0} shared.takerTxFee=テイカー: {0} shared.iConfirm=確認します -shared.tradingFeeInBsqInfo=≈ {0} shared.openURL={0} をオープン shared.fiat=法定通貨 shared.crypto=暗号通貨 @@ -204,9 +203,6 @@ shared.actions=アクション shared.buyerUpperCase=買い手 shared.sellerUpperCase=売り手 shared.new=新 -shared.blindVoteTxId=秘密投票トランザクションID -shared.proposal=提案 -shared.votes=投票 shared.learnMore=もっと詳しく知る shared.dismiss=却下する shared.selectedArbitrator=選択された調停人 @@ -240,7 +236,6 @@ mainView.menu.funds=資金 mainView.menu.support=サポート mainView.menu.settings=設定 mainView.menu.account=アカウント -mainView.menu.dao=DAO mainView.marketPriceWithProvider.label={0} による市場価格 mainView.marketPrice.bisqInternalPrice=Bisqにおける最新の取引価格 @@ -257,13 +252,11 @@ mainView.footer.localhostBitcoinNode=(ローカルホスト) mainView.footer.btcInfo={0} {1} mainView.footer.btcFeeRate=/ 手数料率: {0} サトシ/vB mainView.footer.btcInfo.initializing=ビットコインネットワークに接続中 -mainView.footer.bsqInfo.synchronizing=/ DAOと同期中 mainView.footer.btcInfo.synchronizingWith={0}と同期中、ブロック: {1} / {2} mainView.footer.btcInfo.synchronizedWith={0}と同期されています、ブロック{1}に mainView.footer.btcInfo.connectingTo=接続中: mainView.footer.btcInfo.connectionFailed=接続失敗 mainView.footer.p2pInfo=ビットコインネットワークピア: {0} / Bisqネットワークピア: {1} -mainView.footer.daoFullNode=DAOのフルノード mainView.bootstrapState.connectionToTorNetwork=(1/4) Torネットワークに接続中... mainView.bootstrapState.torNodeCreated=(2/4) Torノードが作成されました @@ -435,7 +428,6 @@ createOffer.fundsBox.networkFee=マイニング手数料 createOffer.fundsBox.placeOfferSpinnerInfo=オファー公開の処理中 ... createOffer.fundsBox.paymentLabel=次のIDとのBisqトレード: {0} createOffer.fundsBox.fundsStructure=({0} セキュリティデポジット, {1} 取引手数料, {2}マイニング手数料) -createOffer.fundsBox.fundsStructure.BSQ=({0} セキュリティデポジット, {1} 取引手数料) + {2}マイニング手数料 createOffer.success.headline=オファーが公開されました createOffer.success.info=自分のオファーは「ポートフォリオ/私のオープンオファー」で管理できます createOffer.info.sellAtMarketPrice=オファーの価格は継続的に更新されるため、常に市場価格で売却するでしょう。 @@ -636,7 +628,6 @@ portfolio.pending.step2_buyer.amountToTransfer=振替金額 portfolio.pending.step2_buyer.sellersAddress=売り手の{0}アドレス portfolio.pending.step2_buyer.buyerAccount=使用されるあなたの支払いアカウント portfolio.pending.step2_buyer.paymentStarted=支払いが開始されました -portfolio.pending.step2_buyer.fillInBsqWallet=Pay from BSQ wallet portfolio.pending.step2_buyer.warn={0}の支払いはまだ完了していません!\nトレードは{1}までに完了しなければなりません。 portfolio.pending.step2_buyer.openForDispute=支払いを完了していません!\nトレードの最大期間が経過しました。助けを求めるには調停人に連絡してください。 portfolio.pending.step2_buyer.paperReceipt.headline=領収書をBTCの売り手へ送付しましたか? @@ -882,7 +873,6 @@ funds.locked.locked=次のIDとのトレードはマルチシグでロック中 funds.tx.direction.sentTo=送信 to: funds.tx.direction.receivedWith=次に予約済: funds.tx.direction.genesisTx=ジェネシスTXから: -funds.tx.txFeePaymentForBsqTx=BSQのTXのマイニング手数料 funds.tx.createOfferFee=メイカーとTXの手数料: {0} funds.tx.takeOfferFee=テイカーとTXの手数料: {0} funds.tx.multiSigDeposit=マルチシグデポジット: {0} @@ -896,15 +886,11 @@ funds.tx.unknown=不明な理由: {0} funds.tx.noFundsFromDispute=係争からの返金はありません funds.tx.receivedFunds=受取済み資金 funds.tx.withdrawnFromWallet=ウォレットからの出金 -funds.tx.withdrawnFromBSQWallet=BSQウォレットから出金されたBTC funds.tx.memo=メモ funds.tx.noTxAvailable=利用できるトランザクションがありません funds.tx.revert=元に戻す funds.tx.txSent=トランザクションはローカルBisqウォレットの新しいアドレスに正常に送信されました。 funds.tx.direction.self=自分自身に送信済み -funds.tx.daoTxFee=BSQのTXのマイニング手数料 -funds.tx.reimbursementRequestTxFee=払い戻しリクエスト -funds.tx.compensationRequestTxFee=報酬リクエスト funds.tx.dustAttackTx=ダストを受取りました funds.tx.dustAttackTx.popup=このトランザクションはごくわずかなBTC金額をあなたのウォレットに送っているので、あなたのウォレットを盗もうとするチェーン解析会社による試みかもしれません。\n\nあなたが支払い取引でそのトランザクションアウトプットを使うならば、彼らはあなたが他のアドレスの所有者である可能性が高いことを学びます(コインマージ)。\n\nあなたのプライバシーを保護するために、Bisqウォレットは、支払い目的および残高表示において、そのようなダストアウトプットを無視します。 設定でアウトプットがダストと見なされるときのしきい値を設定できます。 @@ -996,9 +982,7 @@ settings.tab.about=About setting.preferences.general=一般設定 setting.preferences.explorer=ビットコインのエクスプローラ -setting.preferences.explorer.bsq=Bisqのエクスプローラ setting.preferences.deviation=市場価格からの最大偏差 -setting.preferences.bsqAverageTrimThreshold=BSQ率の外れ閾値 setting.preferences.avoidStandbyMode=スタンバイモードを避ける setting.preferences.autoConfirmXMR=XMR自動確認 setting.preferences.autoConfirmEnabled=有効されました @@ -1032,19 +1016,6 @@ setting.preferences.notifyOnPreRelease=Receive pre-release notifications setting.preferences.resetAllFlags=「次回から表示しない」フラグを全てリセット settings.preferences.languageChange=言語の変更をすべての画面に適用するには再起動が必要です。 settings.preferences.supportLanguageWarning=係争が発生した場合、調停は{0}で、仲裁は{1}で処理されることに注意して下さい。 -setting.preferences.daoOptions=DAO設定 -setting.preferences.dao.resyncFromGenesis.label=ジェネシスTXからDAO状態を再構築 -setting.preferences.dao.resyncFromResources.label=リソースからDAO状態を再構築 -setting.preferences.dao.resyncFromResources.popup=アプリケーションの再起動後、Bisqネットワークガバナンスデータはシードノードから再ロードされ、BSQのコンセンサス状態は最新リソースファイルから再構築されます。 -setting.preferences.dao.resyncFromGenesis.popup=ジェネシストランザクションからの再同期はかなりの時間をかけて、かなりのCPUリソースを消費します。本当によろしいですか?大抵の場合は最新リソースファイルからの再同期は十分、より早い選択です。\n\n進めたら、アプリケーションの再起動後、Bisqネットワークガバナンスデータはシードノードから再ロードされ、BSQのコンセンサス状態はジェネシストランザクションから再構築されるでしょう。 -setting.preferences.dao.resyncFromGenesis.resync=ジェネシスから再同期して終了 -setting.preferences.dao.isDaoFullNode=BisqをDAOのフルノードで実行 -setting.preferences.dao.rpcUser=RPCユーザ名 -setting.preferences.dao.rpcPw=RPCパスワード -setting.preferences.dao.blockNotifyPort=通知ポートをブロック -setting.preferences.dao.fullNodeInfo=DAOフルノードとしてBisqを実行するには、Bitcoin Coreをローカルで実行し、RPCを有効にする必要があります。すべての要件は '' {0} ''に文書化されています。\n\nモードを変更した後は、再起動する必要があります。 -setting.preferences.dao.fullNodeInfo.ok=ドキュメントページを開く -setting.preferences.dao.fullNodeInfo.cancel=いいえ、ライトノードモードを使い続けます settings.preferences.editCustomExplorer.headline=エクスプローラー設定 settings.preferences.editCustomExplorer.description=左のリストからシステム定義エクスプローラを選択、それともニーズや好みに合わせてカスタマイズする。 settings.preferences.editCustomExplorer.available=利用可能なエクスプローラ @@ -1090,7 +1061,7 @@ settings.net.needRestart=その変更を適用するには、アプリケーシ settings.net.notKnownYet=まだわかりません... settings.net.sentData=通信されたデータ: {0}, {1} メッセージ、 {2} メッセージ/秒 settings.net.receivedData=受信されたデータ: {0}, {1} メッセージ、 {2} メッセージ/秒 -settings.net.chainHeight=Bisq DAO chain height: {0} | Bitcoin Peers chain height: {1} +settings.net.chainHeight=Bitcoin Peers chain height: {1} settings.net.ips=[IPアドレス:ポート | ホスト名:ポート | onionアドレス:ポート](コンマ区切り)。デフォルト(8333)が使用される場合、ポートは省略できます。 settings.net.seedNode=シードノード settings.net.directPeer=ピア (ダイレクト) @@ -1144,14 +1115,10 @@ setting.about.shortcuts.walletDetails=ウォレット詳細ウィンドウを開 setting.about.shortcuts.openEmergencyBtcWalletTool=BTCウォレットに対する緊急ウォレットツールを開く -setting.about.shortcuts.openEmergencyBsqWalletTool=BSQウォレットに対する緊急ウォレットツールを開く - setting.about.shortcuts.showTorLogs=TorメッセージのログレベルをDEBUGとWARNを切り替える setting.about.shortcuts.manualPayoutTxWindow=2of2マルチシグ入金txから手動支払いのウィンドウを開く -setting.about.shortcuts.reRepublishAllGovernanceData=DAOガバナンスデータ(提案、票)を再発行する - setting.about.shortcuts.removeStuckTrade=Open popup to move failed trade to open trades tab again setting.about.shortcuts.removeStuckTrade.value=Select failed trade and press: {0} @@ -1337,687 +1304,6 @@ account.notifications.noWebCamFound.warning=ウェブカメラが見つかりま account.notifications.priceAlert.warning.highPriceTooLow=最高価格は最低価格よりも高い必要があります account.notifications.priceAlert.warning.lowerPriceTooHigh=最低価格は最高価格よりも低い必要があります - - - -#################################################################### -# DAO -#################################################################### - -dao.tab.factsAndFigures=正確な詳細 -dao.tab.bsqWallet=BSQウォレット -dao.tab.proposals=ガバナンス -dao.tab.bonding=担保 -dao.tab.proofOfBurn=アセットの上場手数料/プルーフ・オブ・バーン -dao.tab.monitor=ネットワークモニタ -dao.tab.news=ニュース - -dao.paidWithBsq=BSQで支払いました -dao.availableBsqBalance=支払い利用可能額(検証済+未承認の両替アウトプット) -dao.verifiedBsqBalance=検証された全UTXOの残高 -dao.unconfirmedChangeBalance=未承認の全両替アウトプットの残高 -dao.unverifiedBsqBalance=未検証の全トランザクション残高(ブロック承認待ち) -dao.lockedForVoteBalance=投票に使用済 -dao.lockedInBonds=ロック中の担保 -dao.availableNonBsqBalance=利用可能な非BSQ残高 (BTC) -dao.reputationBalance=メリット値(支出できません) - -dao.tx.published.success=トランザクションの発行に成功しました -dao.proposal.menuItem.make=提案する -dao.proposal.menuItem.browse=全ての提案を見る -dao.proposal.menuItem.vote=提案に投票する -dao.proposal.menuItem.result=投票結果 -dao.cycle.headline=投票サイクル -dao.cycle.overview.headline=投票サイクル概要 -dao.cycle.currentPhase=現在のフェーズ -dao.cycle.currentBlockHeight=現在のブロックの高さ -dao.cycle.proposal=提案フェーズ -dao.cycle.proposal.next=次の提案フェーズ -dao.cycle.blindVote=秘密投票フェーズ -dao.cycle.voteReveal=投票公開フェーズ -dao.cycle.voteResult=投票結果 -dao.cycle.phaseDuration={0} ブロック (≈{1}); ブロック {2} - {3} (≈{4} - ≈{5}) -dao.cycle.phaseDurationWithoutBlocks=ブロック {0} - {1} (≈{2} - ≈{3}) - -dao.voteReveal.txPublished.headLine=発行された公開トランザクションに投票する -dao.voteReveal.txPublished=トランザクションID {0}の公開投票トランザクションが正常に発行されました。\n\nこれは、DAO投票に参加している場合、ソフトウェアによって自動的に行われます。 - -dao.results.cycles.header=サイクル -dao.results.cycles.table.header.cycle=サイクル -dao.results.cycles.table.header.numProposals=提案 -dao.results.cycles.table.header.voteWeight=投票の重み -dao.results.cycles.table.header.issuance=発行 - -dao.results.results.table.item.cycle=サイクル {0} 開始: {1} - -dao.results.proposals.header=選択されたサイクルの提案 -dao.results.proposals.table.header.nameLink=名前/リンク -dao.results.proposals.table.header.details=詳細 -dao.results.proposals.table.header.myVote=自分の投票 -dao.results.proposals.table.header.result=投票結果 -dao.results.proposals.table.header.threshold=閾値 -dao.results.proposals.table.header.quorum=定足数 - -dao.results.proposals.voting.detail.header=選択された提案の投票結果 - -dao.results.exceptions=投票結果の例外 - -# suppress inspection "UnusedProperty" -dao.param.UNDEFINED=未定義 - -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BSQ=BSQメイカー手数料 -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BSQ=BSQテイカー手数料 -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BSQ=最小BSQメイカー手数料 -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BSQ=最小BSQテイカー手数料 -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BTC=BTCメイカー手数料 -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BTC=BTCテイカー手数料 -# suppress inspection "UnusedProperty" -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BTC=最小BTCメイカー手数料 -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BTC=最小BTCテイカー手数料 -# suppress inspection "UnusedProperty" - -# suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=BSQの提案手数料 -# suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=BSQの投票手数料 - -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=補償リクエストの最小BSQ量 -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=補償リクエストの最大BSQ量 -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MIN_AMOUNT=払い戻しリクエストの最小BSQ量 -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MAX_AMOUNT=払い戻しリクエストの最大BSQ量 - -# suppress inspection "UnusedProperty" -dao.param.QUORUM_GENERIC=一般的な提案に必要なBSQの定足数 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=補償リクエストに必要なBSQの定足数 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REIMBURSEMENT=払い戻しリクエストに必要なBSQの定足数 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=パラメーター変更に必要なBSQの定足数 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=アセット削除に必要なBSQの定足数 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=没収リクエストに必要なBSQの定足数 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=担保された役割に必要なBSQの定足数 - -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_GENERIC=一般的な提案に必要なしきい値(%) -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=補償リクエストに必要なしきい値(%) -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REIMBURSEMENT=払い戻しリクエストに必要なしきい値(%) -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=パラメーターの変更に必要なしきい値(%) -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=アセットを削除するために必要なしきい値(%) -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=没収リクエストのために必要なしきい値(%) -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_ROLE=担保された役割のリクエストに必要なしきい値(%) - -# suppress inspection "UnusedProperty" -dao.param.RECIPIENT_BTC_ADDRESS=受信者BTCアドレス - -# suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=1日あたりのアセットの上場手数料 -# suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=アセットの最小トレード額 - -# suppress inspection "UnusedProperty" -dao.param.LOCK_TIME_TRADE_PAYOUT=代替トレード支払いtxのロック時間 -# suppress inspection "UnusedProperty" -dao.param.ARBITRATOR_FEE=BTCでの調停手数料 - -# suppress inspection "UnusedProperty" -dao.param.MAX_TRADE_LIMIT=BTCでの最大トレード制限 - -# suppress inspection "UnusedProperty" -dao.param.BONDED_ROLE_FACTOR=BSQの担保された役割単位係数 -# suppress inspection "UnusedProperty" -dao.param.ISSUANCE_LIMIT=BSQのサイクルごとの発行制限 - -dao.param.currentValue=現在の値: {0} -dao.param.currentAndPastValue=現在の値: {0} (提案した時の値: {1} ) -dao.param.blocks={0} ブロック - -dao.results.invalidVotes=その投票サイクルで無効な投票がありました。投票がBisqネットワークでうまく配布されなかった場合、それが起こる可能性があります。\n{0} - -# suppress inspection "UnusedProperty" -dao.phase.PHASE_UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_PROPOSAL=提案フェーズ -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK1=ブレーク1 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=秘密投票フェーズ -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK2=ブレーク2 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=投票公開フェーズ -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK3=ブレーク3 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_RESULT=結果フェーズ - -dao.results.votes.table.header.stakeAndMerit=投票の重み -dao.results.votes.table.header.stake=ステーク -dao.results.votes.table.header.merit=獲得済 -dao.results.votes.table.header.vote=投票 - -dao.bond.menuItem.bondedRoles=担保された役割 -dao.bond.menuItem.reputation=担保された評判 -dao.bond.menuItem.bonds=担保 - -dao.bond.dashboard.bondsHeadline=担保のBSQ -dao.bond.dashboard.lockupAmount=ロックされた資金 -dao.bond.dashboard.unlockingAmount=アンロック中の資金(ロック時間が終わるまで待ってください) - - -dao.bond.reputation.header=評判の為に担保をロック -dao.bond.reputation.table.header=評判担保 -dao.bond.reputation.amount=ロックするBSQ額 -dao.bond.reputation.time=ブロック単位のアンロック時間 -dao.bond.reputation.salt=ソルト -dao.bond.reputation.hash=ハッシュ -dao.bond.reputation.lockupButton=ロック -dao.bond.reputation.lockup.headline=トランザクションのロックを承認 -dao.bond.reputation.lockup.details=ロックされる金額: {0}\nアンロック時間: {1}ブロック(≈{2})\n\nマイニング手数料: {3} ({4} Satoshis/vbyte)\nトランザクションvサイズ: {5} Kb\n\n続行してよろしいですか? -dao.bond.reputation.unlock.headline=トランザクションのアンロックを承認 -dao.bond.reputation.unlock.details=アンロックされる金額: {0}\nアンロック時間: {1}ブロック(≈{2})\n\nマイニング手数料: {3} ({4} Satoshis/vbyte)\nトランザクションvサイズ: {5} Kb\n\n続行してよろしいですか? - -dao.bond.allBonds.header=すべての担保 - -dao.bond.bondedReputation=担保された評判 -dao.bond.bondedRoles=担保された役割 - -dao.bond.details.header=役割の詳細 -dao.bond.details.role=役割 -dao.bond.details.requiredBond=必要なBSQ担保 -dao.bond.details.unlockTime=ブロック単位のアンロック時間 -dao.bond.details.link=役割の説明へのリンク -dao.bond.details.isSingleton=複数の役割保有者が取得できます -dao.bond.details.blocks={0} ブロック - -dao.bond.table.column.name=名前 -dao.bond.table.column.link=リンク -dao.bond.table.column.bondType=担保タイプ -dao.bond.table.column.details=詳細 -dao.bond.table.column.lockupTxId=ロック Tx ID -dao.bond.table.column.bondState=担保状態 -dao.bond.table.column.lockTime=アンロック時間 -dao.bond.table.column.lockupDate=ロック日 - -dao.bond.table.button.lockup=ロック -dao.bond.table.button.unlock=ロック解除 -dao.bond.table.button.revoke=取り消し - -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.bond.bondState.READY_FOR_LOCKUP=まだ担保されていない -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_PENDING=ロック保留中 -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_CONFIRMED=ロック中の担保 -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_PENDING=アンロック保留中 -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_CONFIRMED=アンロックTXが承認されました -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKING=アンロック中の担保 -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKED=アンロックされた担保 -# suppress inspection "UnusedProperty" -dao.bond.bondState.CONFISCATED=没収された担保 - -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.BONDED_ROLE=担保された役割 -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.REPUTATION=担保された評判 - -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub管理者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=フォーラム管理者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter管理者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase管理者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube管理者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=Bisqの維持者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-forkの維持者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayerの維持者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=ウェブサイト運営者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=フォーラム運営者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.SEED_NODE_OPERATOR=シードノード運営者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=価格ノード運営者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=ビットコインノード運営者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=市場運営者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=エクスプローラー運営者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=モバイル通知中継の運営者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=ドメインネーム保有者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DNS_ADMIN=DNS管理人 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=調停者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=調停人 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC寄付アドレスの所有者 - -dao.burnBsq.assetFee=アセットリスティング -dao.burnBsq.menuItem.assetFee=アセットの上場手数料 -dao.burnBsq.menuItem.proofOfBurn=プルーフ・オブ・バーン -dao.burnBsq.header=アセット上場の為の手数料 -dao.burnBsq.selectAsset=アセット選択 -dao.burnBsq.fee=手数料 -dao.burnBsq.trialPeriod=試用期間 -dao.burnBsq.payFee=手数料の支払い -dao.burnBsq.allAssets=全てのアセット -dao.burnBsq.assets.nameAndCode=アセット名 -dao.burnBsq.assets.state=状態 -dao.burnBsq.assets.tradeVolume=取引量 -dao.burnBsq.assets.lookBackPeriod=検証期間 -dao.burnBsq.assets.trialFee=試用期間の手数料 -dao.burnBsq.assets.totalFee=合計支払い手数料 -dao.burnBsq.assets.days={0}日 -dao.burnBsq.assets.toFewDays=アセット手数料が低すぎます。試用期間の最小日数は{0}。 - -# suppress inspection "UnusedProperty" -dao.assetState.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=試用期間中 -# suppress inspection "UnusedProperty" -dao.assetState.ACTIVELY_TRADED=アクティブトレード -# suppress inspection "UnusedProperty" -dao.assetState.DE_LISTED=非アクティブのためリストから除外 -# suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=投票により削除 - -dao.proofOfBurn.header=プルーフ・オブ・バーン -dao.proofOfBurn.amount=金額 -dao.proofOfBurn.preImage=プリイメージ -dao.proofOfBurn.burn=バーン -dao.proofOfBurn.allTxs=全プルーフ・オブ・バーントランザクション -dao.proofOfBurn.myItems=プルーフ・オブ・バーンのトランザクション -dao.proofOfBurn.date=日付 -dao.proofOfBurn.hash=ハッシュ -dao.proofOfBurn.txs=トランザクション -dao.proofOfBurn.pubKey=パブリックキー -dao.proofOfBurn.signature.window.title=バーントランザクションの証明からのキーでメッセージに署名 -dao.proofOfBurn.verify.window.title=バーントランザクションの証明からのキーでメッセージを検証 -dao.proofOfBurn.copySig=署名をクリップボードにコピーする -dao.proofOfBurn.sign=署名する -dao.proofOfBurn.message=メッセージ -dao.proofOfBurn.sig=署名 -dao.proofOfBurn.verify=検証する -dao.proofOfBurn.verificationResult.ok=検証成功 -dao.proofOfBurn.verificationResult.failed=検証失敗 - -# suppress inspection "UnusedProperty" -dao.phase.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.phase.PROPOSAL=提案フェーズ -# suppress inspection "UnusedProperty" -dao.phase.BREAK1=秘密投票フェーズの前に中断 -# suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=秘密投票フェーズ -# suppress inspection "UnusedProperty" -dao.phase.BREAK2=投票公開フェーズ前に中断 -# suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=投票公開フェーズ -# suppress inspection "UnusedProperty" -dao.phase.BREAK3=結果フェーズの前に中断 -# suppress inspection "UnusedProperty" -dao.phase.RESULT=投票結果フェーズ - -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.PROPOSAL=提案フェーズ -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=秘密投票 -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.VOTE_REVEAL=投票公開 -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.RESULT=投票結果 - -# suppress inspection "UnusedProperty" -dao.proposal.type.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.proposal.type.COMPENSATION_REQUEST=報酬リクエスト -# suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=払い戻しリクエスト -# suppress inspection "UnusedProperty" -dao.proposal.type.BONDED_ROLE=担保された役割の提案 -# suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=アセット削除の提案 -# suppress inspection "UnusedProperty" -dao.proposal.type.CHANGE_PARAM=パラメーターの変更の提案 -# suppress inspection "UnusedProperty" -dao.proposal.type.GENERIC=一般的な提案 -# suppress inspection "UnusedProperty" -dao.proposal.type.CONFISCATE_BOND=担保没収の提案 - -# suppress inspection "UnusedProperty" -dao.proposal.type.short.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.COMPENSATION_REQUEST=報酬リクエスト -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=払い戻しリクエスト -# suppress inspection "UnusedProperty" -dao.proposal.type.short.BONDED_ROLE=担保された役割 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ASSET=アルトコインを取り除く -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CHANGE_PARAM=パラメーターの変更 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.GENERIC=一般的な提案 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CONFISCATE_BOND=担保を没収 - -dao.proposal.details=提案の詳細 -dao.proposal.selectedProposal=選択された提案 -dao.proposal.active.header=現在のサイクルの提案 -dao.proposal.active.remove.confirm=その提案を削除してもよろしいですか?\n既に支払われた提案手数料は失われます。 -dao.proposal.active.remove.doRemove=はい、提案を削除します -dao.proposal.active.remove.failed=提案を削除できませんでした -dao.proposal.myVote.title=投票中 -dao.proposal.myVote.accept=提案を承認 -dao.proposal.myVote.reject=提案を拒否 -dao.proposal.myVote.removeMyVote=提案を無視 -dao.proposal.myVote.merit=獲得したBSQからの投票の重み -dao.proposal.myVote.stake=ステークからの投票の重み -dao.proposal.myVote.revealTxId=公開されたトランザクションIDへ投票 -dao.proposal.myVote.stake.prompt=投票に利用可能な最大ステーク: {0} -dao.proposal.votes.header=投票のステークをセットし、投票を発行する -dao.proposal.myVote.button=投票公開 -dao.proposal.myVote.setStake.description=すべての提案に投票した後、BSQをロックして投票のステークを設定する必要があります。ロックするBSQが多いほど、投票の重みが大きくなります。\n\n投票のためにロックされたBSQは、投票公開フェーズ中に再びロック解除されます。 -dao.proposal.create.selectProposalType=提案タイプの選択 -dao.proposal.create.phase.inactive=次の提案フェーズまでお待ち下さい -dao.proposal.create.proposalType=提案タイプ -dao.proposal.create.new=新しい提案を作成 -dao.proposal.create.button=提案する -dao.proposal.create.publish=提案の発行 -dao.proposal.create.publishing=提案の発行が進行中です... -dao.proposal=提案 -dao.proposal.display.type=提案タイプ -dao.proposal.display.name=正確なGithubのユーザ名 -dao.proposal.display.link=詳細情報へのリンク -dao.proposal.display.link.prompt=提案へのリンク -dao.proposal.display.requestedBsq=BSQのリクエスト額 -dao.proposal.display.txId=提案トランザクションID -dao.proposal.display.proposalFee=提案手数料 -dao.proposal.display.myVote=自分の投票 -dao.proposal.display.voteResult=投票結果の概要 -dao.proposal.display.bondedRoleComboBox.label=担保された役割のタイプ -dao.proposal.display.requiredBondForRole.label=役割に必要な担保 -dao.proposal.display.option=オプション - -dao.proposal.table.header.proposalType=提案タイプ -dao.proposal.table.header.link=リンク -dao.proposal.table.header.myVote=自分の投票 -# suppress inspection "UnusedProperty" -dao.proposal.table.header.remove=取り消す -dao.proposal.table.icon.tooltip.removeProposal=自分の提案を削除 -dao.proposal.table.icon.tooltip.changeVote=現在の投票:「{0}」。 次の投票へ変更:「{1}」 - -dao.proposal.display.myVote.accepted=承認 -dao.proposal.display.myVote.rejected=拒否 -dao.proposal.display.myVote.ignored=無視 -dao.proposal.display.myVote.unCounted=票は結果に含められませんでした -dao.proposal.myVote.summary=投票済: {0}; 投票の重さ: {1} (獲得済み: {2} + ステーク: {3}) {4} -dao.proposal.myVote.invalid=投票が無効でした - -dao.proposal.voteResult.success=承認 -dao.proposal.voteResult.failed=拒否 -dao.proposal.voteResult.summary=結果: {0}; しきい値: {1} (必要量 > {2}); 定足数: {3} (必要量 > {4}) - -dao.proposal.display.paramComboBox.label=変更するパラメーターを選択 -dao.proposal.display.paramValue=パラメーター値 - -dao.proposal.display.confiscateBondComboBox.label=担保を選択 -dao.proposal.display.assetComboBox.label=削除するアセット - -dao.blindVote=秘密投票 - -dao.blindVote.startPublishing=秘密投票トランザクションを発行中... -dao.blindVote.success=秘密投票トランザクションが正常に発行されました。\n\nBisqアプリケーションが投票公開トランザクションを発行できるように、投票公開フェーズではオンラインにする必要があることに注意してください。投票公開トランザクションがなければあなたの投票は無効となります! - -dao.wallet.menuItem.send=送金 -dao.wallet.menuItem.receive=受け取る -dao.wallet.menuItem.transactions=トランザクション - -dao.wallet.dashboard.myBalance=ウォレット残高 - -dao.wallet.receive.fundYourWallet=あなたのBSQ受信アドレス -dao.wallet.receive.bsqAddress=BSQウォレットアドレス(未使用の新しいアドレス) - -dao.wallet.send.sendFunds=送金する -dao.wallet.send.sendBtcFunds=非BSQ残高の送金 (BTC) -dao.wallet.send.amount=BSQの額 -dao.wallet.send.btcAmount=BTCの額(非BSQ残高) -dao.wallet.send.setAmount=出金額を設定(最少額は{0}) -dao.wallet.send.receiverAddress=受信者のBSQアドレス -dao.wallet.send.receiverBtcAddress=受信者のBTCアドレス -dao.wallet.send.setDestinationAddress=あなたの出金先アドレスを記入 -dao.wallet.send.send=BSQ残高の送信 -dao.wallet.send.inputControl=Select inputs -dao.wallet.send.sendBtc=BTC残高の送信 -dao.wallet.send.sendFunds.headline=出金リクエストを承認 -dao.wallet.send.sendFunds.details=送金中: {0}\n入金先アドレス: {1}\n必要なマイニング手数料: {2} ({3} サトシ/vbyte)\nトランザクションvサイズ: {4} vKb\n\n入金先の受け取る金額: {5}\n\n本当にこの金額を出金しますか? -dao.wallet.chainHeightSynced=最新検証済みブロック: {0} -dao.wallet.chainHeightSyncing=ブロック待機中... {1}のうち{0}ブロックを検証済み -dao.wallet.tx.type=タイプ - -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED_TX_TYPE=認識されていません -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNVERIFIED=未検証のBSQトランザクション -# suppress inspection "UnusedProperty" -dao.tx.type.enum.INVALID=無効なBSQのトランザクション -# suppress inspection "UnusedProperty" -dao.tx.type.enum.GENESIS=ジェネシストランザクション -# suppress inspection "UnusedProperty" -dao.tx.type.enum.TRANSFER_BSQ=BSQ送金 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.received.TRANSFER_BSQ=受信済BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.sent.TRANSFER_BSQ=送信済BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PAY_TRADE_FEE=トレード手数料 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.COMPENSATION_REQUEST=補償リクエストの手数料 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.REIMBURSEMENT_REQUEST=没収リクエストの手数料 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROPOSAL=提案の手数料 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=秘密投票の手数料 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.VOTE_REVEAL=投票公開 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.LOCKUP=担保をロック -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNLOCK=担保をアンロック -# suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=アセットの上場手数料 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=プルーフ・オブ・バーン -# suppress inspection "UnusedProperty" -dao.tx.type.enum.IRREGULAR=不整 - -dao.tx.withdrawnFromWallet=ウォレットから出金されたBTC -dao.tx.issuanceFromCompReq=補償 リクエスト/発行 -dao.tx.issuanceFromCompReq.tooltip=新しいBSQの発行につながる補償リクエスト。\n発行日: {0} -dao.tx.issuanceFromReimbursement=払い戻し リクエスト/発行 -dao.tx.issuanceFromReimbursement.tooltip=新しいBSQの発行につながる没収リクエスト。\n発行日: {0} -dao.proposal.create.missingBsqFunds=提案を作成するのに十分なBSQ残高がありません。未承認のBSQトランザクションがある場合、ブロックに含まれている場合にのみBSQが検証されるため、ブロックチェーンの承認を待つ必要があります。 \n不足: {0} - -dao.proposal.create.missingBsqFundsForBond=この役割に十分なBSQ残高がありません。この提案を発行することはできますが、この役割が受け入れられた場合、この役割に必要なBSQ全額が必要です。\n不足: {0} - -dao.proposal.create.missingMinerFeeFunds=提案トランザクションを作成するための十分なBTC残高がありません。すべてのBSQトランザクションには、BTCのマイナニング手数料が必要です。\n不足: {0} - -dao.proposal.create.missingIssuanceFunds=提案トランザクションを作成するための十分なBTC残高がありません。すべてのBSQトランザクションにはBTCのマイニング手数料が必要で、発行トランザクションにも要求されたBSQ金額({0} Satoshis/BSQ)のBTCが必要です。\n不足: {1} - -dao.feeTx.confirm={0}トランザクションの承認 -dao.feeTx.confirm.details={0}手数料: {1}\nマイニング手数料: {2} ({3} Satoshis/vbyte)\nトランザクションvサイズ {4} vKb\n\n本当に{5}トランザクションを発行しますか? - -dao.feeTx.issuanceProposal.confirm.details={0}手数料: {1}\nBSQ発行に必要なBTC: {2}({3} Satoshis/BSQ)\nマイニング手数料: {4} ({5} Satoshis/vbyte)\nトランザクションvサイズ {6} vKb\n\nリクエストが承認されると、あなたは2 BSQの提案料金を差し引いた金額を受け取ります。\n\n本当に{7}トランザクションを発行しますか? - -dao.news.bisqDAO.title=THE BISQ DAO -dao.news.bisqDAO.description=Bisqの交換が非中央集権化され検閲に抵抗するように、そのガバナンスモデルも、つまりBisq DAOとBSQトークンはそれを可能にするツールです。 -dao.news.bisqDAO.readMoreLink=Bisq DAOについてもっと詳しく知る - -dao.news.pastContribution.title=過去に貢献しましたか?BSQをリクエストしましょう -dao.news.pastContribution.description=Bisqに貢献している場合は、以下のBSQアドレスを使用して、BSQジェネシス配布に参加するようにリクエストしてください。 -dao.news.pastContribution.yourAddress=あなたのBSQウォレットアドレス -dao.news.pastContribution.requestNow=今すぐリクエスト - -dao.news.DAOOnTestnet.title=私達のテストネットでBISQ DAOを起動 -dao.news.DAOOnTestnet.description=メインネットBisq DAOはまだ起動されていませんが、私達のテストネットで実行することでBisq DAOについて学ぶことができます。 -dao.news.DAOOnTestnet.firstSection.title=1. DAOテストネットモードに切り替え -dao.news.DAOOnTestnet.firstSection.content=設定画面からDAOテストネットへ切り替え -dao.news.DAOOnTestnet.secondSection.title=2. BSQを取得する -dao.news.DAOOnTestnet.secondSection.content=SlackでBSQをリクエストするか、BisqでBSQを購入してください。 -dao.news.DAOOnTestnet.thirdSection.title=3. 投票サイクルに参加する -dao.news.DAOOnTestnet.thirdSection.content=Bisqのさまざまな側面を変更する提案を作成したり、提案に投票したりする。 -dao.news.DAOOnTestnet.fourthSection.title=4. BSQブロックエクスプローラーを使ってみる -dao.news.DAOOnTestnet.fourthSection.content=BSQはビットコインであるため、ビットコインブロックエクスプローラーでBSQトランザクションを確認できます。 -dao.news.DAOOnTestnet.readMoreLink=完全なドキュメントを読む - -dao.monitor.daoState=DAO状態 -dao.monitor.proposals=提案の状態 -dao.monitor.blindVotes=秘密投票の状態 - -dao.monitor.table.peers=ピア -dao.monitor.table.conflicts=競合 -dao.monitor.state=ステータス -dao.monitor.requestAlHashes=全ハッシュのリクエスト -dao.monitor.resync=DAO状態の再同期 -dao.monitor.table.header.cycleBlockHeight=サイクル / ブロックの高さ -dao.monitor.table.cycleBlockHeight=サイクル {0} / ブロック {1} -dao.monitor.table.seedPeers=シードノード: {0} - -dao.monitor.daoState.headline=DAO状態 -dao.monitor.daoState.table.headline=DAO状態ハッシュのチェーン -dao.monitor.daoState.table.blockHeight=ブロックの高さ -dao.monitor.daoState.table.hash=DAO状態のハッシュ -dao.monitor.daoState.table.prev=前のハッシュ -dao.monitor.daoState.conflictTable.headline=競合するピアからのDAO状態ハッシュ -dao.monitor.daoState.utxoConflicts=UTXOの競合 -dao.monitor.daoState.utxoConflicts.blockHeight=ブロックの高さ: {0} -dao.monitor.daoState.utxoConflicts.sumUtxo=全UTXOの合計: {0} BSQ -dao.monitor.daoState.utxoConflicts.sumBsq=全BSQの合計: {0} BSQ -dao.monitor.daoState.checkpoint.popup=DAOステートはネットワークと同期していません。再起動したらDAOステートは再同期します。 - -dao.monitor.proposal.headline=提案の状態 -dao.monitor.proposal.table.headline=提案状態のハッシュのチェーン -dao.monitor.proposal.conflictTable.headline=競合するピアからの提案状態ハッシュ - -dao.monitor.proposal.table.hash=提案状態のハッシュ -dao.monitor.proposal.table.prev=前のハッシュ -dao.monitor.proposal.table.numProposals=提案数 - -dao.monitor.isInConflictWithSeedNode=ローカルデータが、少なくとも1つのシードノードと一致していません。 DAO状態を再同期してください。 -dao.monitor.isInConflictWithNonSeedNode=ピアの1つがネットワークと一致していませんが、あなたのノードはシードノードと同期しています。 -dao.monitor.daoStateInSync=ローカルノードはネットワークと一致しています - -dao.monitor.blindVote.headline=秘密投票の状態 -dao.monitor.blindVote.table.headline=秘密投票状態ハッシュのチェーン -dao.monitor.blindVote.conflictTable.headline=競合するピアからの秘密投票状態ハッシュ -dao.monitor.blindVote.table.hash=秘密投票状態のハッシュ -dao.monitor.blindVote.table.prev=前のハッシュ -dao.monitor.blindVote.table.numBlindVotes=秘密投票数 - -dao.factsAndFigures.menuItem.supply=BSQ 供給 -dao.factsAndFigures.menuItem.transactions=BSQ トランザクション - -dao.factsAndFigures.dashboard.avgPrice90=90日間の平均BSQ/BTCのトレード価格 -dao.factsAndFigures.dashboard.avgPrice30=30日間の平均BSQ/BTCのトレード価格 -dao.factsAndFigures.dashboard.avgUSDPrice90=90 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on 30 days average BSQ/USD price) -dao.factsAndFigures.dashboard.availableAmount=合計利用可能BSQ -dao.factsAndFigures.dashboard.volumeUsd=Total trade volume in USD -dao.factsAndFigures.dashboard.volumeBtc=Total trade volume in BTC -dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection=Average BSQ/USD trade price from selected time period in chart -dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection=Average BSQ/BTC trade price from selected time period in chart - -dao.factsAndFigures.supply.issuedVsBurnt=発行されたBSQ v. バーンされたBSQ - -dao.factsAndFigures.supply.issued=発行されたBSQ -dao.factsAndFigures.supply.compReq=Compensation requests -dao.factsAndFigures.supply.reimbursement=Reimbursement requests -dao.factsAndFigures.supply.genesisIssueAmount=ジェネシストランザクションで発行されたBSQ -dao.factsAndFigures.supply.compRequestIssueAmount=報酬リクエストの為に発行されたBSQ -dao.factsAndFigures.supply.reimbursementAmount=払い戻しリクエストの為に発行されたBSQ -dao.factsAndFigures.supply.totalIssued=Total issued BSQ -dao.factsAndFigures.supply.totalBurned=Total burned BSQ -dao.factsAndFigures.supply.chart.tradeFee.toolTip={0}\n{1} -dao.factsAndFigures.supply.burnt=バーン済BSQ - -dao.factsAndFigures.supply.priceChat=BSQ price -dao.factsAndFigures.supply.volumeChat=取引量 -dao.factsAndFigures.supply.tradeVolumeInUsd=Trade volume in USD -dao.factsAndFigures.supply.tradeVolumeInBtc=Trade volume in BTC -dao.factsAndFigures.supply.bsqUsdPrice=BSQ/USD price -dao.factsAndFigures.supply.bsqBtcPrice=BSQ/BTC price -dao.factsAndFigures.supply.btcUsdPrice=BTC/USD price - -dao.factsAndFigures.supply.locked=ロックされたBSQのグローバル状態 -dao.factsAndFigures.supply.totalLockedUpAmount=担保でロックされている -dao.factsAndFigures.supply.totalUnlockingAmount=担保からアンロック中のBSQ -dao.factsAndFigures.supply.totalUnlockedAmount=担保からアンロックされたBSQ -dao.factsAndFigures.supply.totalConfiscatedAmount=担保から没収されたBSQ -dao.factsAndFigures.supply.proofOfBurn=Proof of Burn -dao.factsAndFigures.supply.bsqTradeFee=BSQ Trade fees -dao.factsAndFigures.supply.btcTradeFee=BTC Trade fees - -dao.factsAndFigures.transactions.genesis=ジェネシストランザクション -dao.factsAndFigures.transactions.genesisBlockHeight=ジェネシスブロックの高さ -dao.factsAndFigures.transactions.genesisTxId=ジェネシストランザクションID -dao.factsAndFigures.transactions.txDetails=BSQトランザクション統計 -dao.factsAndFigures.transactions.allTx=全BSQトランザクション数 -dao.factsAndFigures.transactions.utxo=未支払の全トランザクションアウトプット数 -dao.factsAndFigures.transactions.compensationIssuanceTx=全ての補償リクエスト発行トランザクションの数 -dao.factsAndFigures.transactions.reimbursementIssuanceTx=全ての没収リクエスト発行トランザクションの数 -dao.factsAndFigures.transactions.burntTx=全ての手数料支払いトランザクションの数 -dao.factsAndFigures.transactions.invalidTx=全ての無効なトランザクションの数 -dao.factsAndFigures.transactions.irregularTx=不整なトランザクションの数 - - - #################################################################### # Windows #################################################################### @@ -2120,8 +1406,6 @@ disputeSummaryWindow.close.noPayout.text=支払いなしで閉じてもよろし emptyWalletWindow.headline={0} 緊急ウォレットツール emptyWalletWindow.info=UIから資金にアクセスできない緊急時にのみ使用してください。\n\nこのツールを使用すると、開いているオファーはすべて自動的に閉じられることに注意してください。\n\nこのツールを使用する前に、データディレクトリをバックアップしてください。これは「アカウント/バックアップ」で行えます。\n\n問題の原因を調査できるように、問題を報告し、GitHubまたはBisqフォーラムにバグレポートを提出してください。 emptyWalletWindow.balance=あなたの利用可能なウォレット残高 -emptyWalletWindow.bsq.btcBalance=非BSQ Satoshisの残高 - emptyWalletWindow.address=あなたの宛先アドレス emptyWalletWindow.button=全ての資金を送る emptyWalletWindow.openOffers.warn=ウォレット空にすると削除されるオープンオファーがあります。 \n本当にウォレットを空にしますか? @@ -2146,10 +1430,8 @@ filterWindow.seedNode=フィルター済シードノード(コンマ区切り filterWindow.priceRelayNode=フィルター済価格中継ノード(コンマ区切り onionアドレス) filterWindow.btcNode=フィルター済ビットコインノード(コンマ区切り アドレス+ポート) filterWindow.preventPublicBtcNetwork=パブリックビットコインネットワークの使用を防止 -filterWindow.disableDao=DAOを無効化 filterWindow.disableAutoConf=自動確認を無効にする filterWindow.autoConfExplorers=フィルター済自動確認エクスプローラ(コンマ区切りアドレス) -filterWindow.disableDaoBelowVersion=DAOに必要な最低バージョン filterWindow.disableTradeBelowVersion=トレードに必要な最低バージョン filterWindow.add=フィルターを追加 filterWindow.remove=フィルターを削除 @@ -2223,7 +1505,6 @@ tradeDetailsWindow.detailData=詳細データ txDetailsWindow.headline=トランザクション詳細 txDetailsWindow.btc.note=BTCを送金しました。 -txDetailsWindow.bsq.note=BSQ残高を送信しました。 BSQはカラードビットコインなので、ビットコインのブロックで承認されるまでトランザクションはBSQエクスプローラに表示されません。 txDetailsWindow.sentTo=送信先 txDetailsWindow.txId=TxId @@ -2235,9 +1516,6 @@ closedTradesSummaryWindow.totalMinerFee.title=Sum of all miner fees closedTradesSummaryWindow.totalMinerFee.value={0} ({1} of total trade amount) closedTradesSummaryWindow.totalTradeFeeInBtc.title=Sum of all trade fees paid in BTC closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} of total trade amount) -closedTradesSummaryWindow.totalTradeFeeInBsq.title=Sum of all trade fees paid in BSQ -closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} of total trade amount) - walletPasswordWindow.headline=アンロックするためにパスワードを入力してください torNetworkSettingWindow.header=Torネットワークの設定 @@ -2317,12 +1595,6 @@ popup.warning.tooLargePercentageValue=100%以上のパーセントを設定で popup.warning.examplePercentageValue=パーセントの数字を入力してください。5.4%は「5.4」のように入力します。 popup.warning.noPriceFeedAvailable=その通貨で利用できる価格フィードはありません。パーセントベースの価格は使用できません。 固定価格を選択してください。 popup.warning.sendMsgFailed=トレード相手へのメッセージの送信に失敗しました。\nもう一度試してください。失敗し続ける場合はバグを報告してください。 -popup.warning.insufficientBtcFundsForBsqTx=あなたはそのトランザクションのマイニング手数料を支払うのに十分なBTC残高を持っていません。 BTCウォレットに資金を入金してください。 \n不足残高: {0} -popup.warning.bsqChangeBelowDustException=このトランザクションは、ダスト制限(5.46 BSQ)を下回るBSQおつりアウトプットを作成し、ビットコインネットワークによって拒否されます。\n\nおつりアウトプットを回避するために、より高い金額を送信する必要があります(たとえば、送金額にダスト額を追加することによって)、またはダストアウトプットを生成しないようにウォレットにBSQ残高を追加する必要があります。\n\nダストアウトプットは{0}。 -popup.warning.btcChangeBelowDustException=このトランザクションは、ダスト制限(546 Satoshi)を下回るBSQおつりアウトプットを作成し、ビットコインネットワークによって拒否されます。\n\nダストアウトプットを生成しないように、あなたの送金額にダスト額を追加する必要があります。\n\nダストアウトプットは{0}。 - -popup.warning.insufficientBsqFundsForBtcFeePayment=このトランザクションにはBSQが足りません。ビットコインプロトコルのダスト制限によると、ウォレットから最後の5.46BSQはトレード手数料に使われることができません。\n\nもっとBSQを買うか、BTCでトレード手数料を支払うことができます。\n\n不足している資金: {0} -popup.warning.noBsqFundsForBtcFeePayment=BSQウォレットにBSQのトレード手数料を支払うのに十分な残高がありません。 popup.warning.messageTooLong=メッセージが許容サイズ上限を超えています。いくつかに分けて送信するか、 https://pastebin.com のようなサービスにアップロードしてください。 popup.warning.lockedUpFunds=失敗したトレードから残高をロックしました。\nロックされた残高: {0} \nデポジットtxアドレス: {1} \nトレードID: {2}。\n\nオープントレード画面でこのトレードを選択し、「alt + o」または「option + o」を押してサポートチケットを開いてください。 @@ -2336,8 +1608,6 @@ popup.warning.nodeBanned={0}ノードの1つが禁止されました。 popup.warning.priceRelay=価格中継 popup.warning.seed=シード popup.warning.mandatoryUpdate.trading=最新のBisqバージョンに更新してください。古いバージョンのトレードを無効にする必須の更新プログラムがリリースされました。詳細については、Bisqフォーラムをご覧ください。 -popup.warning.mandatoryUpdate.dao=最新のBisqバージョンに更新してください。古いバージョンのBisq DAOとBSQを無効にする必須の更新プログラムがリリースされました。詳細については、Bisqフォーラムをご覧ください。 -popup.warning.disable.dao=Bisq DAOとBSQは一時的に無効になっています。詳細については、Bisqフォーラムをご覧ください。 popup.warning.noFilter=We did not receive a filter object from the seed nodes. This is a not expected situation. Please inform the Bisq developers. popup.warning.burnBTC={0}のマイニング手数料が{1}の送金額を超えるため、このトランザクションは利用不可です。マイニング手数料が再び低くなるか、送金するBTCがさらに蓄積されるまでお待ちください。 @@ -2356,7 +1626,6 @@ popup.info.cashDepositInfo.confirm=デポジットを作成できるか確認し popup.info.shutDownWithOpenOffers=Bisqはシャットダウン中ですが、オファーはあります。\n\nこれらのオファーは、Bisqがシャットダウンされている間はP2Pネットワークでは利用できませんが、次回Bisqを起動したときにP2Pネットワークに再公開されます。\n\nオファーをオンラインに保つには、Bisqを実行したままにして、このコンピュータもオンラインにしたままにします(つまり、スタンバイモードにならないようにしてください。モニタースタンバイは問題ありません)。 popup.info.qubesOSSetupInfo=Qubes OS内でBisqを実行しているようです。\n\nBisqのqubeはセットアップガイドに従って設定されていることを確かめて下さい: [HYPERLINK:https://bisq.wiki/Running_Bisq_on_Qubes] popup.warn.downGradePrevention=バージョン{0}からバージョン{1}に戻すことはサポートされていません。最新のBisqバージョンを利用して下さい。 -popup.warn.daoRequiresRestart=DAO状態の同期中に問題が発生しました。解決するにはアプリを再起動する必要があります。 popup.privateNotification.headline=重要なプライベート通知! @@ -2528,7 +1797,6 @@ navigation.settings.preferences=「設定/設定」 # suppress inspection "UnusedProperty" navigation.funds.transactions=「資金/トランザクション」 navigation.support=「サポート」 -navigation.dao.wallet.receive=「DAO/BSQウォレット/受取」 #################################################################### @@ -2558,12 +1826,6 @@ XMR_MAINNET=Monero Mainnet XMR_TESTNET=Monero Testnet # suppress inspection "UnusedProperty" XMR_STAGENET=Monero Stagenet -# suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=ビットコインDAOテストネット(非推奨) -# suppress inspection "UnusedProperty" -BTC_DAO_BETANET=BisqDAOベータネット(ビットコイン メインネット) -# suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=ビットコインDAO Regtest time.year=年 time.month=月 @@ -2910,9 +2172,7 @@ validation.accountNrChars=アカウント番号は{0}文字で構成されてい validation.btc.invalidAddress=アドレスが正しくありません。アドレス形式を確認してください。 validation.integerOnly=整数のみを入力してください。 validation.inputError=入力エラーを起こしました:\n{0} -validation.bsq.insufficientBalance=あなたの利用可能残高は{0}です。 validation.btc.exceedsMaxTradeLimit=あなたのトレード制限は{0}です。 -validation.bsq.amountBelowMinAmount=最小金額は{0}です validation.nationalAccountId={0}は{1}個の数字で構成されている必要があります。 #new @@ -2934,7 +2194,6 @@ validation.bic.invalidLocationCode=BICに不正なロケーションコードが validation.bic.invalidBranchCode=BICに不正な支店コードが含まれています validation.bic.sepaRevolutBic=Revolut Sepaのアカウントはサポートされていません。 validation.btc.invalidFormat=ビットコインアドレスにとって無効な形式です。 -validation.bsq.invalidFormat=BSQアドレスにとって無効な形式です。 validation.email.invalidAddress=不正なアドレス validation.iban.invalidCountryCode=不正な国コード validation.iban.checkSumNotNumeric=チェックサムは数値でなければなりません diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index 6288bdf612..1eedd02208 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -192,7 +192,6 @@ shared.tradeWalletBalance=Saldo da carteira de negociação shared.makerTxFee=Ofertante: {0} shared.takerTxFee=Aceitador: {0} shared.iConfirm=Eu confirmo -shared.tradingFeeInBsqInfo=≈ {0} shared.openURL=Aberto {0} shared.fiat=Fiat shared.crypto=Cripto @@ -240,7 +239,6 @@ mainView.menu.funds=Fundos mainView.menu.support=Suporte mainView.menu.settings=Configurações mainView.menu.account=Conta -mainView.menu.dao=DAO mainView.marketPriceWithProvider.label=Preço de mercado por {0} mainView.marketPrice.bisqInternalPrice=Preço da última negociação Bisq @@ -257,13 +255,11 @@ mainView.footer.localhostBitcoinNode=(localhost) mainView.footer.btcInfo={0} {1} mainView.footer.btcFeeRate=/ Fee rate: {0} sat/vB mainView.footer.btcInfo.initializing=Conectando-se à rede Bitcoin -mainView.footer.bsqInfo.synchronizing=/ Sincronizando DAO 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=Conectando-se a mainView.footer.btcInfo.connectionFailed=Falha na conexão à mainView.footer.p2pInfo=Bitcoin network peers: {0} / Bisq network peers: {1} -mainView.footer.daoFullNode=Full node da DAO mainView.bootstrapState.connectionToTorNetwork=(1/4) Conectando-se à rede Tor... mainView.bootstrapState.torNodeCreated=(2/4) Nó da rede Tor criado @@ -435,7 +431,6 @@ createOffer.fundsBox.networkFee=Taxa de mineração createOffer.fundsBox.placeOfferSpinnerInfo=Sua oferta está sendo publicada... createOffer.fundsBox.paymentLabel=Negociação Bisq com ID {0} createOffer.fundsBox.fundsStructure=({0} para o depósito de segurança, {1} para a taxa de transação e {2} para a taxa de mineração) -createOffer.fundsBox.fundsStructure.BSQ=({0} depósito de segurança, {1} taxa de mineração) + {2} taxa de transação createOffer.success.headline=Sua oferta foi publicada createOffer.success.info=Você pode gerenciar suas ofertas abertas em \"Portfolio/Minhas ofertas\". createOffer.info.sellAtMarketPrice=Você irá sempre vender a preço de mercado e o preço de sua oferta será atualizado constantemente. @@ -636,7 +631,6 @@ portfolio.pending.step2_buyer.amountToTransfer=Quantia a ser transferida portfolio.pending.step2_buyer.sellersAddress=Endereço {0} do vendedor portfolio.pending.step2_buyer.buyerAccount=A sua conta de pagamento a ser usada portfolio.pending.step2_buyer.paymentStarted=Pagamento iniciado -portfolio.pending.step2_buyer.fillInBsqWallet=Pay from BSQ wallet portfolio.pending.step2_buyer.warn=Você ainda não realizou seu pagamento de {0}!\nEssa negociação deve ser completada até {1}. portfolio.pending.step2_buyer.openForDispute=Você ainda não completou o seu pagamento!\nO período máximo para a negociação já passou. Entre em contato com o mediador para pedir assistência. portfolio.pending.step2_buyer.paperReceipt.headline=Você enviou o comprovante de depósito para o vendedor de BTC? @@ -882,7 +876,6 @@ funds.locked.locked=Travado em multisig para negociação com ID: {0} funds.tx.direction.sentTo=Enviado para: funds.tx.direction.receivedWith=Recebido com: funds.tx.direction.genesisTx=Da transação gênese: -funds.tx.txFeePaymentForBsqTx=Taxa de mineração para transação de BSQ funds.tx.createOfferFee=Taxa de oferta e transação: {0} funds.tx.takeOfferFee=Taxa de aceitação e transação: {0} funds.tx.multiSigDeposit=Depósito Multisig: {0} @@ -896,15 +889,11 @@ funds.tx.unknown=Razão desconhecida: {0} funds.tx.noFundsFromDispute=Nenhum reembolso de disputa funds.tx.receivedFunds=Fundos recebidos funds.tx.withdrawnFromWallet=Retirado da carteira -funds.tx.withdrawnFromBSQWallet=BTC retirado da carteira BSQ funds.tx.memo=Memo funds.tx.noTxAvailable=Sem transações disponíveis funds.tx.revert=Reverter funds.tx.txSent=Transação enviada com sucesso para um novo endereço em sua carteira Bisq local. funds.tx.direction.self=Enviar para você mesmo -funds.tx.daoTxFee=Taxa de mineração para transação de BSQ -funds.tx.reimbursementRequestTxFee=Solicitar reembolso -funds.tx.compensationRequestTxFee=Pedido de compensação funds.tx.dustAttackTx=Poeira recebida funds.tx.dustAttackTx.popup=Esta transação está enviando uma quantia muito pequena de BTC para a sua carteira e pode ser uma tentativa das empresas de análise da blockchain para espionar a sua carteira.\n\nSe você usar esse output em uma transação eles decobrirão que você provavelmente também é o proprietário de outros endereços (mistura de moedas).\n\nPara proteger sua privacidade a carteira Bisq ignora tais outputs de poeira para fins de consumo e na tela de saldo. Você pode definir a quantia limite a partir da qual um output é considerado poeira nas configurações." @@ -996,9 +985,7 @@ settings.tab.about=Sobre setting.preferences.general=Preferências gerais setting.preferences.explorer=Bitcoin Explorer -setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=Desvio máx. do preço do mercado -setting.preferences.bsqAverageTrimThreshold=Outlier threshold for BSQ rate setting.preferences.avoidStandbyMode=Impedir modo de economia de energia setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled @@ -1032,19 +1019,6 @@ setting.preferences.notifyOnPreRelease=Receive pre-release notifications setting.preferences.resetAllFlags=Esquecer marcações \"Não exibir novamente\" settings.preferences.languageChange=Aplicar a mudança de idioma em todas as telas requer uma reinicialização. settings.preferences.supportLanguageWarning=Em caso de disputa, por favor note que a mediação é feita em {0} e a arbitração em {1}. -setting.preferences.daoOptions=Opções da DAO -setting.preferences.dao.resyncFromGenesis.label=Reconstruir o estado da DAO à partir da 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.resync=Resync from genesis and shutdown -setting.preferences.dao.isDaoFullNode=Executar Bisq como nó completo DAO -setting.preferences.dao.rpcUser=Nome de usuário de RPC -setting.preferences.dao.rpcPw=Senha de RPC -setting.preferences.dao.blockNotifyPort=Bloquear porta de notificação -setting.preferences.dao.fullNodeInfo=Para executar o Bisq como nó completo da DAO você precisa ter Bitcoin Core em rodando localmente e RPC ativado. Todos os requisitos estão documentados em '' {0} ''. -setting.preferences.dao.fullNodeInfo.ok=Abrir página de documentos -setting.preferences.dao.fullNodeInfo.cancel=Não, eu fico com o modo nó lite settings.preferences.editCustomExplorer.headline=Explorer Settings settings.preferences.editCustomExplorer.description=Choose a system defined explorer from the list on the left, and/or customize to suit your own preferences. settings.preferences.editCustomExplorer.available=Available explorers @@ -1090,7 +1064,7 @@ settings.net.needRestart=Você precisa reiniciar o programa para aplicar esta al settings.net.notKnownYet=Ainda desconhecido... 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 DAO chain height: {0} | Bitcoin Peers chain height: {1} +settings.net.chainHeight=Bitcoin Peers chain height: {1} settings.net.ips=[Endeço IP:porta | nome do host:porta | endereço onion:porta] (seperados por vírgulas). A porta pode ser omitida quando a porta padrão (8333) for usada. settings.net.seedNode=Nó semente settings.net.directPeer=Par (direto) @@ -1144,14 +1118,10 @@ setting.about.shortcuts.walletDetails=Abrir janela de detalhes da carteira setting.about.shortcuts.openEmergencyBtcWalletTool=Abrir ferramenta de emergência da carteira BTC -setting.about.shortcuts.openEmergencyBsqWalletTool=Abrir ferramenta de emergência da carteira BSQ - setting.about.shortcuts.showTorLogs=Ativar registro de logs para mensagens Tor de níveis entre DEBUG e WARN setting.about.shortcuts.manualPayoutTxWindow=Abrir janela de pagamento manual do multisig 2-de-2 da transação de depósito -setting.about.shortcuts.reRepublishAllGovernanceData=Republicar dados de governança da DAO (propostas, votos) - setting.about.shortcuts.removeStuckTrade=Open popup to move failed trade to open trades tab again setting.about.shortcuts.removeStuckTrade.value=Select failed trade and press: {0} @@ -1338,686 +1308,6 @@ account.notifications.priceAlert.warning.highPriceTooLow=O preço mais alto deve account.notifications.priceAlert.warning.lowerPriceTooHigh=O preço mais baixo deve ser menor do que o preço mais alto - - -#################################################################### -# DAO -#################################################################### - -dao.tab.factsAndFigures=Fatos & Números -dao.tab.bsqWallet=Carteira BSQ -dao.tab.proposals=Governança -dao.tab.bonding=Vínculo -dao.tab.proofOfBurn=Taxa de listagem de ativos/Prova de destruição -dao.tab.monitor=Monitor da rede -dao.tab.news=Notícias - -dao.paidWithBsq=pago com BSQ -dao.availableBsqBalance=Disponível para gastos (verificado + outputs de trocos não-confirmados) -dao.verifiedBsqBalance=Saldo de todas UTXOs verificadas -dao.unconfirmedChangeBalance=Saldo de todos outputs de troco não confirmados -dao.unverifiedBsqBalance=Saldo de todas as transações não verificadas (aguardando confirmação do bloco) -dao.lockedForVoteBalance=Usado para votação -dao.lockedInBonds=Bloqueado em vínculos -dao.availableNonBsqBalance=Saldo não-BSQ disponível (BTC) -dao.reputationBalance=Valor de mérito (não gastável) - -dao.tx.published.success=Sua transação foi publicada com sucesso. -dao.proposal.menuItem.make=Fazer proposta -dao.proposal.menuItem.browse=Propostas abertas -dao.proposal.menuItem.vote=Votar em propostas -dao.proposal.menuItem.result=Resultados dos votos -dao.cycle.headline=Ciclo de votação -dao.cycle.overview.headline=Visão geral do ciclo de votação -dao.cycle.currentPhase=Fase atual -dao.cycle.currentBlockHeight=Altura do bloco atual -dao.cycle.proposal=Fase de proposta -dao.cycle.proposal.next=Próxima fase de proposta -dao.cycle.blindVote=Fase de voto fechado -dao.cycle.voteReveal=Fase de revelação dos votos -dao.cycle.voteResult=Resultado dos votos -dao.cycle.phaseDuration={0} blocos (≈{1}); Bloco {2} - {3} (≈{4} - ≈{5}) -dao.cycle.phaseDurationWithoutBlocks=Bloco {0} - {1} (≈{2} - ≈{3}) - -dao.voteReveal.txPublished.headLine=A transação da revelação da votação foi publicada -dao.voteReveal.txPublished=Sua transação de revelação do voto com o ID de transação {0} foi publicada com sucesso.\n\nIsso acontece automaticamente pelo software se você tiver participado na votação da DAO. - -dao.results.cycles.header=Ciclos -dao.results.cycles.table.header.cycle=Ciclo -dao.results.cycles.table.header.numProposals=Propostas -dao.results.cycles.table.header.voteWeight=Peso do voto -dao.results.cycles.table.header.issuance=Emissão - -dao.results.results.table.item.cycle=Ciclo {0} começou: {1} - -dao.results.proposals.header=Propostas do ciclo selecionado -dao.results.proposals.table.header.nameLink=Nome/link -dao.results.proposals.table.header.details=Detalhes -dao.results.proposals.table.header.myVote=Meu voto -dao.results.proposals.table.header.result=Resultado dos votos -dao.results.proposals.table.header.threshold=Limite -dao.results.proposals.table.header.quorum=Quorum - -dao.results.proposals.voting.detail.header=Resultados dos votos para a proposta selecionada - -dao.results.exceptions=Excepção(s) do resultado dos votos - -# suppress inspection "UnusedProperty" -dao.param.UNDEFINED=Indefinido - -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BSQ=taxa BSQ do ofertante -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BSQ=taxa BSQ do aceitador -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BSQ=Taxa mínima em BSQ do ofertante -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BSQ=Taxa mínima em BSQ do aceitador -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BTC=Taxa em BTC do ofertante -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BTC=Taxa em BTC do aceitador -# suppress inspection "UnusedProperty" -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BTC=Taxa mínima em BTC do ofertante -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BTC=Taxa mínima em BTC do aceitador -# suppress inspection "UnusedProperty" - -# suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=Taxa de proposta em BSQ -# suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=Taxa de votação em BSQ - -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=Quantia mínima de BSQ para pedido de compensação -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=Quantia máxima de BSQ para pedido de compensação -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MIN_AMOUNT=Quantia mínima de BSQ para pedido de reembolso -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MAX_AMOUNT=Quantia máxima de BSQ para pedido de reembolso - -# suppress inspection "UnusedProperty" -dao.param.QUORUM_GENERIC=Quórum necessário em BSQ para proposta genérica -# suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=Quórum necessário em BSQ para pedido de compensação -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REIMBURSEMENT=Quórum necessário em BSQ para pedido de reembolso -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=Quórum necessário em BSQ para mudança de parâmetro -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Quórum necessário em BSQ para remover um ativo -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=Quórum necessário em BSQ para pedido de confiscação -# suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=Quórum necessário em BSQ para pedidos de cargos vinculados - -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_GENERIC=Limite necessário em % para proposta genérica -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=Limite necessário em % para pedido de compensação -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REIMBURSEMENT=Limite necessário em % para pedido de reembolso -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=Limite necessário em % para mudança de parâmetro -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Limite necessário em % para remover um ativo -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=Limite necessário em % para pedido de confiscação -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_ROLE=Limite necessário em % para pedidos de cargos vinculados - -# suppress inspection "UnusedProperty" -dao.param.RECIPIENT_BTC_ADDRESS=Endereço BTC do destinatário - -# suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=Taxa de listagem do ativo por dia -# suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Volume mínimo de negócio para ativos - -# suppress inspection "UnusedProperty" -dao.param.LOCK_TIME_TRADE_PAYOUT=Tempo de bloqueio para tx de pagamento de negociação alternativa -# suppress inspection "UnusedProperty" -dao.param.ARBITRATOR_FEE=Taxa de arbitragem em BTC - -# suppress inspection "UnusedProperty" -dao.param.MAX_TRADE_LIMIT=Limite máximo de negociação em BTC - -# suppress inspection "UnusedProperty" -dao.param.BONDED_ROLE_FACTOR=Fator de unidade de cargo vinculado em BSQ -# suppress inspection "UnusedProperty" -dao.param.ISSUANCE_LIMIT=Limite de emissão por ciclo em BSQ - -dao.param.currentValue=Valor atual: {0} -dao.param.currentAndPastValue=Valor atual: {0} (Valor quando a proposta foi feita: {1}) -dao.param.blocks={0} blocos - -dao.results.invalidVotes=Tivemos votos inválidos naquele ciclo de votação. Isso pode acontecer se o voto não foi bem propagado pela rede da Bisq.\n{0} - -# suppress inspection "UnusedProperty" -dao.phase.PHASE_UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.phase.PHASE_PROPOSAL=Fase de proposta -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK1=Pausa 1 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=Fase de voto fechado -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK2=Pausa 2 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=Fase de revelação dos votos -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK3=Pausa 3 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_RESULT=Fase de resultado - -dao.results.votes.table.header.stakeAndMerit=Peso do voto -dao.results.votes.table.header.stake=Participação -dao.results.votes.table.header.merit=Ganho -dao.results.votes.table.header.vote=Votar - -dao.bond.menuItem.bondedRoles=Cargos vinculados -dao.bond.menuItem.reputation=Reputação vinculada -dao.bond.menuItem.bonds=Vínculos - -dao.bond.dashboard.bondsHeadline=BSQ vinculado -dao.bond.dashboard.lockupAmount=Bloquear fundos -dao.bond.dashboard.unlockingAmount=Desbloqueando fundos (espere até que o tempo de bloqueio termine) - - -dao.bond.reputation.header=Bloquear um vínculo para reputação -dao.bond.reputation.table.header=Os meus vínculos de reputação -dao.bond.reputation.amount=Quantia de BSQ a bloquear -dao.bond.reputation.time=Tempo de desbloqueio em blocos -dao.bond.reputation.salt=Sal -dao.bond.reputation.hash=Hash -dao.bond.reputation.lockupButton=Travar -dao.bond.reputation.lockup.headline=Confirmar transação de bloqueio -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.unlock.headline=Confirmar transação de desbloqueio -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.allBonds.header=Todos os vínculos - -dao.bond.bondedReputation=Reputação Vinculada -dao.bond.bondedRoles=Cargos vinculados - -dao.bond.details.header=Detalhes do cargo -dao.bond.details.role=Função -dao.bond.details.requiredBond=Vínculo de BSQ necessário -dao.bond.details.unlockTime=Tempo de desbloqueio em blocos -dao.bond.details.link=Link para descrição do cargo -dao.bond.details.isSingleton=Pode ser aceite por detentores de vários cargos -dao.bond.details.blocks={0} blocos - -dao.bond.table.column.name=Nome -dao.bond.table.column.link=Link -dao.bond.table.column.bondType=Tipo de vínculo -dao.bond.table.column.details=Detalhes -dao.bond.table.column.lockupTxId=ID da tx de bloqueio -dao.bond.table.column.bondState=Estado do vínculo -dao.bond.table.column.lockTime=Tempo para destravar -dao.bond.table.column.lockupDate=Travado em - -dao.bond.table.button.lockup=Travar -dao.bond.table.button.unlock=Destravar -dao.bond.table.button.revoke=Revogar - -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.bond.bondState.READY_FOR_LOCKUP=Ainda não vinculado -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_PENDING=Bloqueio pendente -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_CONFIRMED=Vínculo bloqueado -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_PENDING=Desbloqueio pendente -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_CONFIRMED=Tx de desbloqueio confirmada -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKING=Desbloqueio de vínculo -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKED=Vínculo bloqueado -# suppress inspection "UnusedProperty" -dao.bond.bondState.CONFISCATED=Vínculo confiscado - -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.BONDED_ROLE=Cargo vínculado -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.REPUTATION=Reputação vinculada - -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Admin do GitHub -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=Admin do Fórum -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=Admin do Twitter -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Admin do YouTube -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=Mantenedor Bisq -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=Mantenedor do BitcoinJ-fork -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Mantenedor Netlayer -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=Operador do Website -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=Operador do Fórum -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Operador do nó de semente -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Operador do node de preço -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Operador do nó de BTC -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Operador de mercados -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Operador da transmissão de notificações móveis -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Detentor do nome de domínio -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DNS_ADMIN=Admin do DNS -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=Mediador -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=Árbitro -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=Proprietário do endereço BTC de doação - -dao.burnBsq.assetFee=Listagem de ativos -dao.burnBsq.menuItem.assetFee=Taxa de listagem de ativos -dao.burnBsq.menuItem.proofOfBurn=Prova-de-queima -dao.burnBsq.header=Taxa para listagem de ativos -dao.burnBsq.selectAsset=Escolher Ativo -dao.burnBsq.fee=Taxa -dao.burnBsq.trialPeriod=Período de testes -dao.burnBsq.payFee=Pagar taxa -dao.burnBsq.allAssets=Todos os ativos -dao.burnBsq.assets.nameAndCode=Nome do ativo -dao.burnBsq.assets.state=Estado -dao.burnBsq.assets.tradeVolume=Volume de negociação -dao.burnBsq.assets.lookBackPeriod=Período de verificação -dao.burnBsq.assets.trialFee=Taxa para o período de testes -dao.burnBsq.assets.totalFee=Total de taxas pagas -dao.burnBsq.assets.days={0} dias -dao.burnBsq.assets.toFewDays=A taxa do ativo é muito baixa. A quantidade mínima de dias para o período de teste é {0}. - -# suppress inspection "UnusedProperty" -dao.assetState.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=Em período de testes -# suppress inspection "UnusedProperty" -dao.assetState.ACTIVELY_TRADED=Ativamente negociado -# suppress inspection "UnusedProperty" -dao.assetState.DE_LISTED=Retirado por inatividade -# suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=Removido por votação - -dao.proofOfBurn.header=Prova-de-queima -dao.proofOfBurn.amount=Quantia -dao.proofOfBurn.preImage=Pré-imagem -dao.proofOfBurn.burn=Queimar -dao.proofOfBurn.allTxs=Todas as transações de Prova-de-queima -dao.proofOfBurn.myItems=Minhas transações de Prova-de-queima -dao.proofOfBurn.date=Data -dao.proofOfBurn.hash=Hash -dao.proofOfBurn.txs=Transações -dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Assinar uma mensagem com a chave da transação de prova-de-queima -dao.proofOfBurn.verify.window.title=Verificar uma mensagem com a chave da transação de prova-de-queima -dao.proofOfBurn.copySig=Copiar assinatura para a área de trabalho -dao.proofOfBurn.sign=Assinar -dao.proofOfBurn.message=Mensagem -dao.proofOfBurn.sig=Assinatura -dao.proofOfBurn.verify=Verificar -dao.proofOfBurn.verificationResult.ok=Verificação realizada com sucesso -dao.proofOfBurn.verificationResult.failed=Erro na verificação - -# suppress inspection "UnusedProperty" -dao.phase.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.phase.PROPOSAL=Fase de proposta -# suppress inspection "UnusedProperty" -dao.phase.BREAK1=Pausa antes da fase de votação fechada -# suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=Fase de voto fechado -# suppress inspection "UnusedProperty" -dao.phase.BREAK2=Pausa antes da fase de revelação dos votos -# suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=Fase de revelação dos votos -# suppress inspection "UnusedProperty" -dao.phase.BREAK3=Pausa antes da fase de resultado -# suppress inspection "UnusedProperty" -dao.phase.RESULT=Fase de resultado da votação - -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.PROPOSAL=Fase de proposta -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=Voto cego -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.VOTE_REVEAL=Revelar voto -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.RESULT=Resultado dos votos - -# suppress inspection "UnusedProperty" -dao.proposal.type.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.proposal.type.COMPENSATION_REQUEST=Pedido de compensação -# suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=Pedido de reembolso -# suppress inspection "UnusedProperty" -dao.proposal.type.BONDED_ROLE=Proposta para um cargo vinculado -# suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=Proposta para remover um ativo -# suppress inspection "UnusedProperty" -dao.proposal.type.CHANGE_PARAM=Proposta para mudança de parâmetro -# suppress inspection "UnusedProperty" -dao.proposal.type.GENERIC=Proposta genérica -# suppress inspection "UnusedProperty" -dao.proposal.type.CONFISCATE_BOND=Proposta para confiscação de um vínculo - -# suppress inspection "UnusedProperty" -dao.proposal.type.short.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.proposal.type.short.COMPENSATION_REQUEST=Pedido de compensação -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=Pedido de reembolso -# suppress inspection "UnusedProperty" -dao.proposal.type.short.BONDED_ROLE=Cargo vínculado -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ASSET=Removendo uma altcoin -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CHANGE_PARAM=Modificando um parâmetro -# suppress inspection "UnusedProperty" -dao.proposal.type.short.GENERIC=Proposta genérica -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CONFISCATE_BOND=Confiscando um titulo de dívida - -dao.proposal.details=Detalhes da proposta -dao.proposal.selectedProposal=Proposta selecionada -dao.proposal.active.header=Propostas do ciclo atual -dao.proposal.active.remove.confirm=Tem certeza de que pretende remover essa proposta?\nA taxa da proposta paga será perdida. -dao.proposal.active.remove.doRemove=Sim, remover minha proposta -dao.proposal.active.remove.failed=Não foi possível remover a proposta. -dao.proposal.myVote.title=Votação -dao.proposal.myVote.accept=Aceitar proposta -dao.proposal.myVote.reject=Rejeitar proposta -dao.proposal.myVote.removeMyVote=Ignorar proposta -dao.proposal.myVote.merit=Peso de voto de BSQ ganho -dao.proposal.myVote.stake=Peso de voto de participação -dao.proposal.myVote.revealTxId=ID de transação de revelação de voto -dao.proposal.myVote.stake.prompt=Máx. participação disponível para votação: {0} -dao.proposal.votes.header=Definir participação para votar e publicar seus votos -dao.proposal.myVote.button=Publicar votos -dao.proposal.myVote.setStake.description=Depois de votar em todas as propostas, você deve definir a sua participação para votação bloqueando BSQ. Quanto mais BSQ você bloquear, mais peso o seu voto terá.\n\nBSQ bloqueado para votação será desbloqueado novamente durante a fase de revelação de voto. -dao.proposal.create.selectProposalType=Escolha o tipo de proposta -dao.proposal.create.phase.inactive=Por favor espere até a próxima fase -dao.proposal.create.proposalType=Tipo de proposta -dao.proposal.create.new=Fazer nova proposta -dao.proposal.create.button=Fazer proposta -dao.proposal.create.publish=Publicar proposta -dao.proposal.create.publishing=Publicação de proposta em progresso ... -dao.proposal=proposta -dao.proposal.display.type=Tipo de proposta -dao.proposal.display.name=Usuário no GitHub -dao.proposal.display.link=Link para mais detalhes -dao.proposal.display.link.prompt=Link para proposta -dao.proposal.display.requestedBsq=Quantia requerida em BSQ -dao.proposal.display.txId=ID de transação de proposta -dao.proposal.display.proposalFee=Taxa de proposta -dao.proposal.display.myVote=Meu voto -dao.proposal.display.voteResult=Resumo do resultado da votação -dao.proposal.display.bondedRoleComboBox.label=Tipo de cargo vinculado -dao.proposal.display.requiredBondForRole.label=Vínculo necessário para cargo -dao.proposal.display.option=Opção - -dao.proposal.table.header.proposalType=Tipo de proposta -dao.proposal.table.header.link=Link -dao.proposal.table.header.myVote=Meu voto -# suppress inspection "UnusedProperty" -dao.proposal.table.header.remove=Remover -dao.proposal.table.icon.tooltip.removeProposal=Remover minha proposta -dao.proposal.table.icon.tooltip.changeVote=Voto atual: ''{0}''. Mudar voto para: ''{1}'' - -dao.proposal.display.myVote.accepted=Aceito -dao.proposal.display.myVote.rejected=Rejeitado -dao.proposal.display.myVote.ignored=Ignorado -dao.proposal.display.myVote.unCounted=Voto não foi incluído no resultado -dao.proposal.myVote.summary=Votado: {0}; Peso do voto: {1} (ganho: {2} + stake {3}) {4} -dao.proposal.myVote.invalid=O voto era inválido - -dao.proposal.voteResult.success=Aceito -dao.proposal.voteResult.failed=Rejeitado -dao.proposal.voteResult.summary=Resultado: {0}; Limite: {1} (necessário > {2}); Quórum: {3} (necessário > {4}) - -dao.proposal.display.paramComboBox.label=Escolha parâmetro para modificar -dao.proposal.display.paramValue=Valor do parâmetro - -dao.proposal.display.confiscateBondComboBox.label=Escolha o vínculo -dao.proposal.display.assetComboBox.label=Ativo para remover - -dao.blindVote=voto fechado - -dao.blindVote.startPublishing=Publicando transação de voto fechado... -dao.blindVote.success=Sua transação de voto fechado foi publicada com sucesso.\n\nNote que você tem que estar online na fase de revelação de votos para que o Bisq possa publicar a transação de revelação de voto. Sem a transação da revelação do voto o seu voto seria inválido! - -dao.wallet.menuItem.send=Enviar -dao.wallet.menuItem.receive=Receber -dao.wallet.menuItem.transactions=Transações - -dao.wallet.dashboard.myBalance=Meu saldo de carteira - -dao.wallet.receive.fundYourWallet=Seu endereço de recebimento BSQ -dao.wallet.receive.bsqAddress=Endereço da carteira BSQ (endereço não utilizado) - -dao.wallet.send.sendFunds=Enviar fundos -dao.wallet.send.sendBtcFunds=Enviar fundos não-BSQ (BTC) -dao.wallet.send.amount=Quantia em BSQ -dao.wallet.send.btcAmount=Quantia em BTC (fundos não-BSQ) -dao.wallet.send.setAmount=Definir quantia a retirar (quantia mínima é {0}) -dao.wallet.send.receiverAddress=Endereço BSQ do destinatário -dao.wallet.send.receiverBtcAddress=Endereço BTC do destinatário -dao.wallet.send.setDestinationAddress=Preencha seu endereço de destino -dao.wallet.send.send=Enviar fundos BSQ -dao.wallet.send.inputControl=Select inputs -dao.wallet.send.sendBtc=Enviar fundos BTC -dao.wallet.send.sendFunds.headline=Confirmar solicitação de retirada. -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=Último bloco verificado: {0} -dao.wallet.chainHeightSyncing=Aguardando blocos... Verificados {0} blocos de {1} -dao.wallet.tx.type=Tipo - -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED_TX_TYPE=Não reconhecida -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNVERIFIED=Transação BSQ não verificada -# suppress inspection "UnusedProperty" -dao.tx.type.enum.INVALID=Transação BSQ inválida -# suppress inspection "UnusedProperty" -dao.tx.type.enum.GENESIS=Transação gênesis -# suppress inspection "UnusedProperty" -dao.tx.type.enum.TRANSFER_BSQ=Transferência BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.received.TRANSFER_BSQ=BSQ recebido -# suppress inspection "UnusedProperty" -dao.tx.type.enum.sent.TRANSFER_BSQ=BSQ enviado -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PAY_TRADE_FEE=Taxa de negociação -# suppress inspection "UnusedProperty" -dao.tx.type.enum.COMPENSATION_REQUEST=Taxa de pedido de compensação -# suppress inspection "UnusedProperty" -dao.tx.type.enum.REIMBURSEMENT_REQUEST=Taxa para pedido de reembolso -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROPOSAL=Taxa para proposta -# suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=Taxa para voto fechado -# suppress inspection "UnusedProperty" -dao.tx.type.enum.VOTE_REVEAL=Revelar voto -# suppress inspection "UnusedProperty" -dao.tx.type.enum.LOCKUP=Bloquear vínculo -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNLOCK=Desbloquear vínculo -# suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=Taxa de listagem de ativos -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=Prova-de-queima -# suppress inspection "UnusedProperty" -dao.tx.type.enum.IRREGULAR=Irregular - -dao.tx.withdrawnFromWallet=BTC retirado da carteira -dao.tx.issuanceFromCompReq=Pedido de compensação/emissão -dao.tx.issuanceFromCompReq.tooltip=Pedido de compensação que levou à emissão de novo BSQ.\nData de emissão: {0} -dao.tx.issuanceFromReimbursement=Pedido de reembolso/emissão -dao.tx.issuanceFromReimbursement.tooltip=Solicitação de reembolso que levou à emissão de novo BSQ.\nData de emissão: {0} -dao.proposal.create.missingBsqFunds=Você não tem fundos suficientes para criar a proposta. Se você tem uma transação de BSQ não confirmada, você precisa esperar por uma confirmação da blockchain porque o BSQ é validado somente se estiver incluído num bloco.\nFaltando: {0} - -dao.proposal.create.missingBsqFundsForBond=Você não tem BSQ suficiente para este cargo. Você ainda pode publicar essa proposta, mas precisará do valor completo de BSQ necessário para esse cargo se ela for aceito.\nEm falta: {0} - -dao.proposal.create.missingMinerFeeFunds=Você não tem BTC suficiente para criar a transação da proposta. Todas as transações de BSQ exigem uma taxa de mineração em BTC.\nEm falta: {0} - -dao.proposal.create.missingIssuanceFunds=Você não tem BTC suficiente para criar a transação da proposta. Todas as transações de BSQ exigem uma taxa de mineração em BTC, e as transações de emissão também exigem BTC pela quantia de BSQ solicitado ({0} satoshis/BSQ).\nEm falta: {1} - -dao.feeTx.confirm=Confirmar transação {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.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.news.bisqDAO.title=A DAO BISQ -dao.news.bisqDAO.description=O modelo de governança da Bisq é assim como a exchange - descentralizado e resistente à censura. As ferramentas que tornam isso realidade são a DAO da BISQ e o token BSQ. -dao.news.bisqDAO.readMoreLink=Ler mais sobre o DAO da Bisq - -dao.news.pastContribution.title=JÁ FEZ CONTRIBUIÇÕES? SOLICITE BSQ -dao.news.pastContribution.description=Se você já contribuiu para a Bisq, use o endereço BSQ abaixo e faça uma solicitação para fazer parte da distribuição gênese do BSQ. -dao.news.pastContribution.yourAddress=Seu Endereço de Carteira BSQ -dao.news.pastContribution.requestNow=Solicitar agora - -dao.news.DAOOnTestnet.title=RODE O DAO DA BISQ EM NOSSA TESTNET -dao.news.DAOOnTestnet.description=A rede principal da DAO do Bisq ainda não foi lançada, mas você pode aprender sobre a DAO do Bisq executando-a na nossa rede de testes. -dao.news.DAOOnTestnet.firstSection.title=1. Mude para o Modo Testnet da DAO -dao.news.DAOOnTestnet.firstSection.content=Mude para o Testnet da DAO na seção Configurações. -dao.news.DAOOnTestnet.secondSection.title=2. Adquira alguns BSQ -dao.news.DAOOnTestnet.secondSection.content=Solicite BSQ no Slack ou Compre BSQ na Bisq. -dao.news.DAOOnTestnet.thirdSection.title=3. Participe de um Ciclo de Votação -dao.news.DAOOnTestnet.thirdSection.content=Fazendo propostas e votando em propostas para mudar vários aspetos da Bisq. -dao.news.DAOOnTestnet.fourthSection.title=4. Use um Explorador de Blocos de BSQ -dao.news.DAOOnTestnet.fourthSection.content=Como o BSQ é bitcoin, você pode ver as transações BSQ em nosso explorador de blocos de bitcoin. -dao.news.DAOOnTestnet.readMoreLink=Leia a documentação completa - -dao.monitor.daoState=Estado da DAO -dao.monitor.proposals=Estado das propostas -dao.monitor.blindVotes=Estado dos votos fechados - -dao.monitor.table.peers=Pares -dao.monitor.table.conflicts=Conflitos -dao.monitor.state=Status -dao.monitor.requestAlHashes=Pedir todos os hashes -dao.monitor.resync=Re-sincronizar estado da DAO -dao.monitor.table.header.cycleBlockHeight=Ciclo / altura do bloco -dao.monitor.table.cycleBlockHeight=Ciclo {0} / bloco {1} -dao.monitor.table.seedPeers=Nó semente: {0} - -dao.monitor.daoState.headline=Estado da DAO -dao.monitor.daoState.table.headline=Corrente de hashes do estado da DAO -dao.monitor.daoState.table.blockHeight=Altura do bloco -dao.monitor.daoState.table.hash=Hash do estado da DAO -dao.monitor.daoState.table.prev=Hash anterior -dao.monitor.daoState.conflictTable.headline=Hashes do estado da DAO de pares em conflito -dao.monitor.daoState.utxoConflicts=Conflitos de UTXO -dao.monitor.daoState.utxoConflicts.blockHeight=Altura do bloco: {0} -dao.monitor.daoState.utxoConflicts.sumUtxo=Soma do total de UTXO: {0} BSQ -dao.monitor.daoState.utxoConflicts.sumBsq=Soma do total de BSQ: {0} BSQ -dao.monitor.daoState.checkpoint.popup=O estado da DAO não está sincronizado com a rede. Após reiniciar o estado da DAO vai resincronizar. - -dao.monitor.proposal.headline=Estado de propostas -dao.monitor.proposal.table.headline=Corrente dos hashes de estado da proposta -dao.monitor.proposal.conflictTable.headline=Hashes de estado da proposta de pares em conflito - -dao.monitor.proposal.table.hash=Hash do estado da proposta -dao.monitor.proposal.table.prev=Hash anterior -dao.monitor.proposal.table.numProposals=Nº de propostas - -dao.monitor.isInConflictWithSeedNode=Os seus dados locais não estão em consenso com pelo menos um nó semente. Por favor, re-sincronizar o estado da DAO. -dao.monitor.isInConflictWithNonSeedNode=Um dos seus pares não está em consenso com a rede, mas o seu nó está em sincronia com os nós semente. -dao.monitor.daoStateInSync=Seu nó local está em consenso com a rede - -dao.monitor.blindVote.headline=Estado de votos fechados -dao.monitor.blindVote.table.headline=Corrente de hashes de estado de voto fechado -dao.monitor.blindVote.conflictTable.headline=Hashes de estado de voto fechado de pares em conflito -dao.monitor.blindVote.table.hash=Hash de estado de voto fechado -dao.monitor.blindVote.table.prev=Hash anterior -dao.monitor.blindVote.table.numBlindVotes=Nº de votos fechados - -dao.factsAndFigures.menuItem.supply=Estoque de BSQ -dao.factsAndFigures.menuItem.transactions=Transações BSQ - -dao.factsAndFigures.dashboard.avgPrice90=Média de 90 dias BSQ/BTC -dao.factsAndFigures.dashboard.avgPrice30=Média de 30 dias BSQ/BTC -dao.factsAndFigures.dashboard.avgUSDPrice90=90 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on 30 days average BSQ/USD price) -dao.factsAndFigures.dashboard.availableAmount=Total de BSQ disponível -dao.factsAndFigures.dashboard.volumeUsd=Total trade volume in USD -dao.factsAndFigures.dashboard.volumeBtc=Total trade volume in BTC -dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection=Average BSQ/USD trade price from selected time period in chart -dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection=Average BSQ/BTC trade price from selected time period in chart - -dao.factsAndFigures.supply.issuedVsBurnt=BSQ emitido vs. BSQ queimado - -dao.factsAndFigures.supply.issued=BSQ emitido -dao.factsAndFigures.supply.compReq=Pedidos de compensação -dao.factsAndFigures.supply.reimbursement=Reimbursement requests -dao.factsAndFigures.supply.genesisIssueAmount=BSQ emitido na transação genesis -dao.factsAndFigures.supply.compRequestIssueAmount=BDQ emitido para pedidos de compensação -dao.factsAndFigures.supply.reimbursementAmount=BSQ emitido para pedidos de reembolso -dao.factsAndFigures.supply.totalIssued=Total issued BSQ -dao.factsAndFigures.supply.totalBurned=Total burned BSQ -dao.factsAndFigures.supply.chart.tradeFee.toolTip={0}\n{1} -dao.factsAndFigures.supply.burnt=BSQ destruído - -dao.factsAndFigures.supply.priceChat=BSQ price -dao.factsAndFigures.supply.volumeChat=Volume de negociação -dao.factsAndFigures.supply.tradeVolumeInUsd=Trade volume in USD -dao.factsAndFigures.supply.tradeVolumeInBtc=Trade volume in BTC -dao.factsAndFigures.supply.bsqUsdPrice=BSQ/USD price -dao.factsAndFigures.supply.bsqBtcPrice=BSQ/BTC price -dao.factsAndFigures.supply.btcUsdPrice=BTC/USD price - -dao.factsAndFigures.supply.locked=Estado global de BSQ bloqueado -dao.factsAndFigures.supply.totalLockedUpAmount=Bloqueado em vínculos -dao.factsAndFigures.supply.totalUnlockingAmount=Desbloqueando BSQ de vínculos -dao.factsAndFigures.supply.totalUnlockedAmount=BSQ desbloqueado de vínculos -dao.factsAndFigures.supply.totalConfiscatedAmount=BSQ confiscado de vínculos -dao.factsAndFigures.supply.proofOfBurn=Proof of Burn -dao.factsAndFigures.supply.bsqTradeFee=BSQ Trade fees -dao.factsAndFigures.supply.btcTradeFee=BTC Trade fees - -dao.factsAndFigures.transactions.genesis=Transação gênesis -dao.factsAndFigures.transactions.genesisBlockHeight=Altura do bloco gênese -dao.factsAndFigures.transactions.genesisTxId=ID da transação gênese -dao.factsAndFigures.transactions.txDetails=Estatísticas das transações de BSQ -dao.factsAndFigures.transactions.allTx=Nº de todas as transações BSQ -dao.factsAndFigures.transactions.utxo=Nº de todos os outputs de transações não gastos -dao.factsAndFigures.transactions.compensationIssuanceTx=Nº de todas as transações de emissão de pedido de compensação -dao.factsAndFigures.transactions.reimbursementIssuanceTx=Nº de todas as transações de emissão de pedido de reembolso -dao.factsAndFigures.transactions.burntTx=Nº de todas transações de pagamentos de taxa -dao.factsAndFigures.transactions.invalidTx=Nº de todas as transações inválidas -dao.factsAndFigures.transactions.irregularTx=Nº de todas as transações irregulares - - - #################################################################### # Windows #################################################################### @@ -2120,7 +1410,6 @@ disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any emptyWalletWindow.headline={0} ferramenta de emergência da carteira emptyWalletWindow.info=Por favor, utilize essa opção apenas em caso de emergência, caso você não consiga acessar seus fundos a partir do programa.\n\nNote que todas as ofertas abertas serão fechadas automaticamente quando você utilizar esta ferramenta.\n\nAntes de usar esta ferramenta, faça um backup da sua pasta de dados. Você pode fazer isso em \"Conta/Backup\".\n\nHavendo qualquer problema, avise-nos através do GitHub ou do fórum Bisq, para que assim possamos investigar o que causou o problema. emptyWalletWindow.balance=Seu saldo disponível na carteira -emptyWalletWindow.bsq.btcBalance=Saldo de satoshis não-BSQ emptyWalletWindow.address=Seu endereço de destino emptyWalletWindow.button=Enviar todos os fundos @@ -2146,10 +1435,8 @@ filterWindow.seedNode=Nós de semente filtrados (endereços onion sep. por vírg filterWindow.priceRelayNode=Nós de transmissão de preço filtrados (endereços onion sep. por vírgula) filterWindow.btcNode=Nós de Bitcoin filtrados (endereços + portas sep. por vírgula) filterWindow.preventPublicBtcNetwork=Prevenir uso da rede de Bitcoin pública -filterWindow.disableDao=Desativar DAO filterWindow.disableAutoConf=Disable auto-confirm filterWindow.autoConfExplorers=Filtered auto-confirm explorers (comma sep. addresses) -filterWindow.disableDaoBelowVersion=Versão mín. necessária para a DAO filterWindow.disableTradeBelowVersion=Versão mínima necessária para negociação filterWindow.add=Adicionar filtro filterWindow.remove=Remover filtro @@ -2223,7 +1510,6 @@ tradeDetailsWindow.detailData=Detail data txDetailsWindow.headline=Transaction Details txDetailsWindow.btc.note=You have sent BTC. -txDetailsWindow.bsq.note=You have sent BSQ funds. BSQ is colored bitcoin, so the transaction will not show in a BSQ explorer until it has been confirmed in a bitcoin block. txDetailsWindow.sentTo=Sent to txDetailsWindow.txId=TxId @@ -2235,8 +1521,6 @@ closedTradesSummaryWindow.totalMinerFee.title=Sum of all miner fees closedTradesSummaryWindow.totalMinerFee.value={0} ({1} of total trade amount) closedTradesSummaryWindow.totalTradeFeeInBtc.title=Sum of all trade fees paid in BTC closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} of total trade amount) -closedTradesSummaryWindow.totalTradeFeeInBsq.title=Sum of all trade fees paid in BSQ -closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} of total trade amount) walletPasswordWindow.headline=Digite senha para abrir: @@ -2317,12 +1601,8 @@ popup.warning.tooLargePercentageValue=Você não pode definir uma porcentagem su popup.warning.examplePercentageValue=Digite um número percentual, como \"5.4\" para 5.4% popup.warning.noPriceFeedAvailable=Não há feed de preços disponível para essa moeda. Você não pode usar um preço porcentual.\nPor favor selecione um preço fixo. popup.warning.sendMsgFailed=O envio da mensagem para seu parceiro de negociação falhou.\nFavor tentar novamente, e se o erro persistir reportar o erro (bug report). -popup.warning.insufficientBtcFundsForBsqTx=Você não possui fundos BTC suficientes para pagar a taxa de mineração para essa transação.\nPor favor, deposite BTC em sua carteira.\nFundos faltando: {0} -popup.warning.bsqChangeBelowDustException=Esta transação cria um troco BSQ menor do que o limite poeira (5.46 BSQ) e seria rejeitada pela rede Bitcoin.\nVocê precisa ou enviar uma quantia maior para evitar o troco (ex: adicionando a quantia poeira ao montante a ser enviado) ou adicionar mais fundos BSQ à sua carteira para evitar gerar uma saída de poeira.\nA saída de poeira é {0}. popup.warning.btcChangeBelowDustException=Esta transação cria um troco menor do que o limite poeira (546 Satoshi) e seria rejeitada pela rede Bitcoin.\nVocê precisa adicionar a quantia poeira ao montante de envio para evitar gerar uma saída de poeira.\nA saída de poeira é {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.noBsqFundsForBtcFeePayment=Sua carteira BSQ não possui fundos suficientes para pagar a taxa de transação em BSQ. popup.warning.messageTooLong=Sua mensagem excede o tamanho máximo permitido. Favor enviá-la em várias partes ou utilizando um serviço como https://pastebin.com. popup.warning.lockedUpFunds=Você possui fundos travados em uma negociação com erro.\nSaldo travado: {0}\nEndereço da transação de depósito: {1}\nID da negociação: {2}.\n\nPor favor, abra um ticket de suporte selecionando a negociação na tela de negociações em aberto e depois pressionando "\alt+o\" ou \"option+o\". @@ -2336,8 +1616,6 @@ popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=transmissão de preço popup.warning.seed=semente popup.warning.mandatoryUpdate.trading=Faça o update para a última versão do Bisq. Um update obrigatório foi lançado e desabilita negociações em versões antigas. Por favor, veja o Fórum do Bisq para mais informações. -popup.warning.mandatoryUpdate.dao=Faça o update para a última versão do Bisq. Um update obrigatório foi publicado, ele desabilita a DAO Bisq e BSQ em versões anteriores. Por gentileza, veja o Fórum do Bisq para mais informações.\n\n -popup.warning.disable.dao=A DAO Bisq e BSQ estão temporariamente desativados. Verifique o fórum Bisq para mais informações. popup.warning.noFilter=We did not receive a filter object from the seed nodes. This is a not expected situation. Please inform the Bisq developers. popup.warning.burnBTC=Esta transação não é possível, pois as taxas de mineração de {0} excederiam o montante a transferir de {1}. Aguarde até que as taxas de mineração estejam novamente baixas ou até você ter acumulado mais BTC para transferir. @@ -2356,7 +1634,6 @@ popup.info.cashDepositInfo.confirm=Eu confirmo que posso fazer o depósito popup.info.shutDownWithOpenOffers=O Bisq está desligando, mas há ofertas abertas.\n\nEstas ofertas não ficarão disponíveis na rede P2P enquanto o Bisq estiver desligado, mas elas serão republicadas na rede assim que você reiniciar o programa.\n\nPara manter suas ofertas online, mantenha o Bisq aberto e certifique-se de que o seu computador continua online (ex: certifique-se de que o computador não está entrando em modo de hibernação). 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ção privada importante! @@ -2528,7 +1805,6 @@ navigation.settings.preferences=\"Configurações/Preferências\" # suppress inspection "UnusedProperty" navigation.funds.transactions=\"Fundos/Transações\" navigation.support=\"Suporte\" -navigation.dao.wallet.receive=\"DAO/Carteira BSQ/Receber\" #################################################################### @@ -2558,12 +1834,6 @@ XMR_MAINNET=Mainnet do Monero XMR_TESTNET=Testnet do Monero # suppress inspection "UnusedProperty" XMR_STAGENET=Stagenet do Monero -# suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Testnet da DAO do Bitcoin (descontinuada) -# suppress inspection "UnusedProperty" -BTC_DAO_BETANET=Bisq DAO Betanet (Bitcoin Mainnet) -# suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=Regtest da DAO do Bitcoin time.year=Ano time.month=Mês @@ -2910,9 +2180,7 @@ validation.accountNrChars=O número da conta deve conter {0} caracteres. validation.btc.invalidAddress=O endereço está incorreto. Por favor, verifique o formato do endereço. validation.integerOnly=Por favor, insira apesar números inteiros validation.inputError=Os dados inseridos causaram um erro:\n{0} -validation.bsq.insufficientBalance=Seu saldo disponível é {0}. validation.btc.exceedsMaxTradeLimit=Seu limite de negociação é {0}. -validation.bsq.amountBelowMinAmount=A quantia mínima é {0} validation.nationalAccountId={0} deve consistir de {1} números. #new @@ -2934,7 +2202,6 @@ validation.bic.invalidLocationCode=BIC contém código de localização inválid validation.bic.invalidBranchCode=BIC contém código da agência inválido validation.bic.sepaRevolutBic=Contas Revolut Sepa não são suportadas. validation.btc.invalidFormat=Invalid format for a Bitcoin address. -validation.bsq.invalidFormat=Invalid format for a BSQ address. validation.email.invalidAddress=Endereço inválido validation.iban.invalidCountryCode=Código de país inválido validation.iban.checkSumNotNumeric=Código verificador deve ser numérico diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index 8b833bc5f5..ade9cb8857 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -192,7 +192,6 @@ shared.tradeWalletBalance=Saldo da carteira de negócio shared.makerTxFee=Ofertante: {0} shared.takerTxFee=Aceitador: {0} shared.iConfirm=Eu confirmo -shared.tradingFeeInBsqInfo=≈ {0} shared.openURL=Abrir {0} shared.fiat=Moeda fiduciária shared.crypto=Cripto @@ -204,9 +203,6 @@ shared.actions=Ações shared.buyerUpperCase=Comprador shared.sellerUpperCase=Vendedor shared.new=NOVO -shared.blindVoteTxId=ID de transação de voto cego -shared.proposal=Proposta -shared.votes=Votos shared.learnMore=Saber mais shared.dismiss=Ignorar shared.selectedArbitrator=Árbitro selecionado @@ -240,7 +236,6 @@ mainView.menu.funds=Fundos mainView.menu.support=Apoio mainView.menu.settings=Definições mainView.menu.account=Conta -mainView.menu.dao=OAD mainView.marketPriceWithProvider.label=Preço de mercado por {0} mainView.marketPrice.bisqInternalPrice=Preço do último negócio do Bisq @@ -257,13 +252,11 @@ mainView.footer.localhostBitcoinNode=(localhost) mainView.footer.btcInfo={0} {1} mainView.footer.btcFeeRate=/ Fee rate: {0} sat/vB mainView.footer.btcInfo.initializing=Conectando à rede Bitcoin -mainView.footer.bsqInfo.synchronizing=/ Sincronizando a OAD 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=Conectando à mainView.footer.btcInfo.connectionFailed=Connection failed to mainView.footer.p2pInfo=Bitcoin network peers: {0} / Bisq network peers: {1} -mainView.footer.daoFullNode=Nó completo da OAD mainView.bootstrapState.connectionToTorNetwork=(1/4) Conectando à rede Tor.... mainView.bootstrapState.torNodeCreated=(2/4) Nó da rede Tor criado @@ -435,7 +428,6 @@ createOffer.fundsBox.networkFee=Taxa de mineração createOffer.fundsBox.placeOfferSpinnerInfo=Oferta sendo publicada... createOffer.fundsBox.paymentLabel=negócio do Bisq com ID {0} createOffer.fundsBox.fundsStructure=({0} depósito de segurança, {1} taxa de negócio, {2} taxa de mineração) -createOffer.fundsBox.fundsStructure.BSQ=({0} depósito de segurança, {1} taxa de mineração) + {2} taxa de negócio createOffer.success.headline=Sua oferta foi publicada. createOffer.success.info=Você pode gerir as suas ofertas abertas em \"Portefólio/As minhas ofertas abertas\". createOffer.info.sellAtMarketPrice=Venderá sempre ao preço do mercado pois o preço da sua oferta será atualizado continuamente. @@ -636,7 +628,6 @@ portfolio.pending.step2_buyer.amountToTransfer=Quantia a transferir portfolio.pending.step2_buyer.sellersAddress=Endereço {0} do vendedor portfolio.pending.step2_buyer.buyerAccount=A sua conta de pagamento a ser usada portfolio.pending.step2_buyer.paymentStarted=Pagamento iniciado -portfolio.pending.step2_buyer.fillInBsqWallet=Pay from BSQ wallet portfolio.pending.step2_buyer.warn=Você ainda não fez o seu pagamento de {0}!\nSaiba que o negócio tem de ser concluído até {1}. portfolio.pending.step2_buyer.openForDispute=Você não completou o seu pagamento!\nO período máx. para o negócio acabou. Por favor entre em contacto com o mediador para assistência. portfolio.pending.step2_buyer.paperReceipt.headline=Você enviou o recibo de papel para o vendedor de BTC? @@ -882,7 +873,6 @@ funds.locked.locked=Bloqueado em transação multi-assinatura para o negócio co funds.tx.direction.sentTo=Enviado para: funds.tx.direction.receivedWith=Recebido com: funds.tx.direction.genesisTx=Da tx Genesis: -funds.tx.txFeePaymentForBsqTx=Taxa do mineiro para tx de BSQ funds.tx.createOfferFee=Taxa do ofertante e da tx: {0} funds.tx.takeOfferFee=Taxa do aceitador e da tx: {0} funds.tx.multiSigDeposit=Depósito multi-assinatura: {0} @@ -896,15 +886,11 @@ funds.tx.unknown=Razão desconhecida: {0} funds.tx.noFundsFromDispute=Nenhum reembolso de disputa funds.tx.receivedFunds=Fundos recebidos funds.tx.withdrawnFromWallet=Levantado da carteira -funds.tx.withdrawnFromBSQWallet=BTC levantado da carteira BSQ funds.tx.memo=Memo funds.tx.noTxAvailable=Sem transações disponíveis funds.tx.revert=Reverter funds.tx.txSent=Transação enviada com sucesso para um novo endereço em sua carteira Bisq local. funds.tx.direction.self=Enviado à você mesmo -funds.tx.daoTxFee=Taxa do mineiro para tx de BSQ -funds.tx.reimbursementRequestTxFee=Pedido de reembolso -funds.tx.compensationRequestTxFee=Pedido de compensação funds.tx.dustAttackTx=Poeira recebida funds.tx.dustAttackTx.popup=Esta transação está enviando uma quantia muito pequena de BTC para a sua carteira e pode ser uma tentativa das empresas de análise da blockchain para espionar a sua carteira.\n\nSe você usar esse output em uma transação eles decobrirão que você provavelmente também é o proprietário de outros endereços (mistura de moedas).\n\nPara proteger sua privacidade a carteira Bisq ignora tais outputs de poeira para fins de consumo e no ecrã de saldo. Você pode definir a quantia limite a partir da qual um output é considerado poeira nas definições." @@ -996,9 +982,7 @@ settings.tab.about=Sobre setting.preferences.general=Preferências gerais setting.preferences.explorer=Bitcoin Explorer -setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=Máx. desvio do preço de mercado -setting.preferences.bsqAverageTrimThreshold=Outlier threshold for BSQ rate setting.preferences.avoidStandbyMode=Evite o modo espera setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled @@ -1032,19 +1016,6 @@ setting.preferences.notifyOnPreRelease=Receive pre-release notifications setting.preferences.resetAllFlags=Reiniciar todos os marcadores \"Não mostrar novamente\" settings.preferences.languageChange=Para aplicar a mudança de língua em todas os ecrãs requer uma reinicialização. settings.preferences.supportLanguageWarning=Em caso de disputa, por favor saiba que a mediação será tratada em {0} e a arbitragem em {1}. -setting.preferences.daoOptions=Opções da OAD -setting.preferences.dao.resyncFromGenesis.label=Reconstruir o estado da OAD à partir da 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.resync=Resync from genesis and shutdown -setting.preferences.dao.isDaoFullNode=Executar Bisq como nó completo OAD -setting.preferences.dao.rpcUser=Nome de usuário de RPC -setting.preferences.dao.rpcPw=Senha de RPC -setting.preferences.dao.blockNotifyPort=Bloquear porta de notificação -setting.preferences.dao.fullNodeInfo=Para executar o Bisq como nó completo da OAD você precisa ter Bitcoin Core em execução local e RPC ativado. Todos os requerimentos estão documentados em '' {0} ''. -setting.preferences.dao.fullNodeInfo.ok=Abrir página de documentos -setting.preferences.dao.fullNodeInfo.cancel=Não, eu fico com o modo nó lite settings.preferences.editCustomExplorer.headline=Explorer Settings settings.preferences.editCustomExplorer.description=Choose a system defined explorer from the list on the left, and/or customize to suit your own preferences. settings.preferences.editCustomExplorer.available=Available explorers @@ -1090,7 +1061,7 @@ settings.net.needRestart=Você precisa reiniciar o programa para aplicar essa al settings.net.notKnownYet=Ainda desconhecido... 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 DAO chain height: {0} | Bitcoin Peers chain height: {1} +settings.net.chainHeight=Bitcoin Peers chain height: {1} settings.net.ips=[endereço IP:porta | nome de host:porta | endereço onion:porta] (separado por vírgula). A porta pode ser omitida se a padrão for usada (8333). settings.net.seedNode=Nó semente settings.net.directPeer=Par (direto) @@ -1144,14 +1115,10 @@ setting.about.shortcuts.walletDetails=Abrir janela de detalhes da carteira setting.about.shortcuts.openEmergencyBtcWalletTool=Abrir ferramenta e emergência da carteira para a carteira de BTC -setting.about.shortcuts.openEmergencyBsqWalletTool=Abrir ferramenta e emergência da carteira para a carteira de BSQ - setting.about.shortcuts.showTorLogs=Escolher o nível de log para mensagens Tor entre DEBUG e WARN setting.about.shortcuts.manualPayoutTxWindow=Abrir janela para pagamento manual da tx de depósito multi-assinatura 2de2 -setting.about.shortcuts.reRepublishAllGovernanceData=Republicar informação da governação da OAD (propostas, votos) - setting.about.shortcuts.removeStuckTrade=Open popup to move failed trade to open trades tab again setting.about.shortcuts.removeStuckTrade.value=Select failed trade and press: {0} @@ -1337,687 +1304,6 @@ account.notifications.noWebCamFound.warning=Nenhuma webcam foi encontrada.\n\nPo account.notifications.priceAlert.warning.highPriceTooLow=O preço mais alto deve ser maior que o preço mais baixo. account.notifications.priceAlert.warning.lowerPriceTooHigh=O preço mais baixo deve ser menor que o preço mais alto. - - - -#################################################################### -# DAO -#################################################################### - -dao.tab.factsAndFigures=Factos & Números -dao.tab.bsqWallet=Carteira BSQ -dao.tab.proposals=Governação -dao.tab.bonding=Vínculo -dao.tab.proofOfBurn=Taxa de listagem de ativos/Prova de destruição -dao.tab.monitor=Monitor de rede -dao.tab.news=Notícias - -dao.paidWithBsq=pago com BSQ -dao.availableBsqBalance=Disponível para gastos (verificado + outputs de trocos não-confirmados) -dao.verifiedBsqBalance=Saldo de todos UTXOs verificados -dao.unconfirmedChangeBalance=Saldo de todos outputs de trocos não confirmados -dao.unverifiedBsqBalance=Saldo de todas as transações não verificadas (aguardando confirmação do bloco) -dao.lockedForVoteBalance=Usado para votação -dao.lockedInBonds=Bloqueado em vínculos -dao.availableNonBsqBalance=Balanço não-BSQ disponível (BTC) -dao.reputationBalance=Valor de Mérito (não gastável) - -dao.tx.published.success=Sua transação foi publicada com sucesso. -dao.proposal.menuItem.make=Criar proposta -dao.proposal.menuItem.browse=Propostas abertas -dao.proposal.menuItem.vote=Votar em propostas -dao.proposal.menuItem.result=Resultado da votação -dao.cycle.headline=Ciclo de votação -dao.cycle.overview.headline=Visão geral do ciclo de votação -dao.cycle.currentPhase=Fase atual -dao.cycle.currentBlockHeight=Altura do bloco atual -dao.cycle.proposal=Fase de proposta -dao.cycle.proposal.next=Próxima fase de proposta -dao.cycle.blindVote=Fase the votação às cegas -dao.cycle.voteReveal=Fase de revelação de votos -dao.cycle.voteResult=Resultado da votação -dao.cycle.phaseDuration={0} blocos (≈{1}); Bloco {2} - {3} (≈{4} - ≈{5}) -dao.cycle.phaseDurationWithoutBlocks=Bloco {0} - {1} (≈{2} - ≈{3}) - -dao.voteReveal.txPublished.headLine=A transação da revelação da votação foi publicada -dao.voteReveal.txPublished=Sua transação de revelação do voto com o ID de transação {0} foi publicadq com sucesso.\n\nIsso acontece automaticamente pelo software se você tiver participado na votação da OAD. - -dao.results.cycles.header=Ciclos -dao.results.cycles.table.header.cycle=Ciclo -dao.results.cycles.table.header.numProposals=Propostas -dao.results.cycles.table.header.voteWeight=Peso do voto -dao.results.cycles.table.header.issuance=Emissão - -dao.results.results.table.item.cycle=Ciclo {0} começou: {1} - -dao.results.proposals.header=Propostas do ciclo selecionado -dao.results.proposals.table.header.nameLink=Nome/link -dao.results.proposals.table.header.details=Detalhes -dao.results.proposals.table.header.myVote=O meu voto -dao.results.proposals.table.header.result=Resultado da votação -dao.results.proposals.table.header.threshold=Limite -dao.results.proposals.table.header.quorum=Quórum - -dao.results.proposals.voting.detail.header=Resultado dos votos para a proposta selecionada - -dao.results.exceptions=Excepção(s) do resultado dos votos - -# suppress inspection "UnusedProperty" -dao.param.UNDEFINED=Indefinido - -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BSQ=taxa BSQ do ofertante -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BSQ=taxa BSQ do aceitador -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BSQ=Mín. taxa BSQ do ofertante -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BSQ=Mín. taxa BSQ do aceitador -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BTC=taxa BTC do ofertante -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BTC=taxa BTC do aceitador -# suppress inspection "UnusedProperty" -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BTC=Mín. taxa BTC do ofertante -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BTC=Mín. taxa BTC do aceitador -# suppress inspection "UnusedProperty" - -# suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=Taxa de proposta em BSQ -# suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=Taxa de votação em BSQ - -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=Mín. quantia de BSQ para pedido de compensação -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=Máx. quantia de BSQ para pedido de compensação -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MIN_AMOUNT=mín. quantia de BSQ para pedido de reembolso -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MAX_AMOUNT=máx. quantia de BSQ para pedido de reembolso - -# suppress inspection "UnusedProperty" -dao.param.QUORUM_GENERIC=Quórum necessário em BSQ para proposta genérica -# suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=Quórum necessário em BSQ para pedido de compensação -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REIMBURSEMENT=Quórum necessário em BSQ para pedido de reembolso -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=Quórum necessário em BSQ para mudança de parâmetro -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Quórum necessário em BSQ para remover um ativo -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=Quórum necessário em BSQ para pedido de confiscação -# suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=Quórum necessário em BSQ para pedidos de cargos vinculados - -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_GENERIC=Limite necessário em % para proposta genérica -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=Limite necessário em % para pedido de compensação -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REIMBURSEMENT=Limite necessário em % para pedido de reembolso -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=Limite necessário em % para mudança de parâmetro -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Limite necessário em % para remover um ativo -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=Limite necessário em % para pedido de confiscação -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_ROLE=Limite necessário em % para pedidos de cargos vinculados - -# suppress inspection "UnusedProperty" -dao.param.RECIPIENT_BTC_ADDRESS=Endereço BTC do recipiente - -# suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=Taxa de listagem do ativo por dia -# suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Mín. volume de negócio para ativos - -# suppress inspection "UnusedProperty" -dao.param.LOCK_TIME_TRADE_PAYOUT=Tempo de bloqueio para tx de pagamento de negócio alternativa -# suppress inspection "UnusedProperty" -dao.param.ARBITRATOR_FEE=Taxa do árbitro em BTC - -# suppress inspection "UnusedProperty" -dao.param.MAX_TRADE_LIMIT=Máx. limite de negócio em BTC - -# suppress inspection "UnusedProperty" -dao.param.BONDED_ROLE_FACTOR=Fator de unidade de cargo vinculado em BSQ -# suppress inspection "UnusedProperty" -dao.param.ISSUANCE_LIMIT=Limite de emissão por ciclo em BSQ - -dao.param.currentValue=Valor atual: {0} -dao.param.currentAndPastValue=Valor atual: {0} (Valor quando a proposta foi feita: {1}) -dao.param.blocks={0} blocos - -dao.results.invalidVotes=Tivemos votos inválidos naquele ciclo de votação. Isso pide acontecer se o voto não foi bem distribuído pela rede do Bisq.\n{0} - -# suppress inspection "UnusedProperty" -dao.phase.PHASE_UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.phase.PHASE_PROPOSAL=Fase de proposta -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK1=Pausa 1 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=Fase the votação às cegas -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK2=Pausa 2 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=Fase de revelação de votos -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK3=Pausa 3 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_RESULT=Fase de resultado - -dao.results.votes.table.header.stakeAndMerit=Peso do voto -dao.results.votes.table.header.stake=Participação -dao.results.votes.table.header.merit=Ganho -dao.results.votes.table.header.vote=Voto - -dao.bond.menuItem.bondedRoles=Cargos vinculados -dao.bond.menuItem.reputation=Reputação vinculada -dao.bond.menuItem.bonds=Vínculos - -dao.bond.dashboard.bondsHeadline=BSQ vinculado -dao.bond.dashboard.lockupAmount=Bloquear fundos -dao.bond.dashboard.unlockingAmount=Desbloqueando fundos (espere até que o tempo de bloqueio termine) - - -dao.bond.reputation.header=Bloquear um vínculo para reputação -dao.bond.reputation.table.header=Os meus vínculos de reputação -dao.bond.reputation.amount=Quantia de BSQ a bloquear -dao.bond.reputation.time=Tempo de desbloqueio em blocos -dao.bond.reputation.salt=Sal -dao.bond.reputation.hash=Hash -dao.bond.reputation.lockupButton=Bloquear -dao.bond.reputation.lockup.headline=Confirmar transação de bloqueio -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.unlock.headline=Confirmar transação de desbloqueio -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.allBonds.header=Todos os vínculos - -dao.bond.bondedReputation=Reputação Vinculada -dao.bond.bondedRoles=Cargos vinculados - -dao.bond.details.header=Detalhes do cargo -dao.bond.details.role=Cargo -dao.bond.details.requiredBond=Vínculo de BSQ necessário -dao.bond.details.unlockTime=Tempo de desbloqueio em blocos -dao.bond.details.link=Link para descrição do cargo -dao.bond.details.isSingleton=Pode ser aceite por detentores de vários cargos -dao.bond.details.blocks={0} blocos - -dao.bond.table.column.name=Nome -dao.bond.table.column.link=Link -dao.bond.table.column.bondType=Tipo de vínculo -dao.bond.table.column.details=Detalhes -dao.bond.table.column.lockupTxId=ID da tx de bloqueio -dao.bond.table.column.bondState=Estado de vínculo -dao.bond.table.column.lockTime=Unlock time -dao.bond.table.column.lockupDate=Data de bloqueio - -dao.bond.table.button.lockup=Bloqueio -dao.bond.table.button.unlock=Desbloquear -dao.bond.table.button.revoke=Revogar - -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.bond.bondState.READY_FOR_LOCKUP=Ainda não vinculado -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_PENDING=Bloqueio pendente -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_CONFIRMED=Vínculo bloqueado -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_PENDING=Desbloqueio pendente -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_CONFIRMED=Tx de desbloqueio confirmada -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKING=Desbloqueando vínculo -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKED=Vínculo bloqueado -# suppress inspection "UnusedProperty" -dao.bond.bondState.CONFISCATED=Vínculo confiscado - -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.BONDED_ROLE=Cargo vínculado -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.REPUTATION=Reputação vinculada - -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Admin de GitHub -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=Admin do Fórum -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=Admin do Twitter -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Admin do Youtube -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=Mantedor de Bisq -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=Mantedor do BitcoinJ-fork -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Mantedor Netlayer -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=Mantedor Website -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=Operador do Fórum -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Operador do nó de semente -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Price node operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Bitcoin node operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Operador dos mercados -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Operador da transmissão de notificações móveis -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Detentor do nome de domínio -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DNS_ADMIN=Admin do DNS -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=Mediador -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=Árbitro -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=Proprietário do endereço BTC de doação - -dao.burnBsq.assetFee=Listagem de ativos -dao.burnBsq.menuItem.assetFee=Taxa de listagem de ativos -dao.burnBsq.menuItem.proofOfBurn=Prova de destruição -dao.burnBsq.header=Taxa para listagem de ativos -dao.burnBsq.selectAsset=Selecionar ativo -dao.burnBsq.fee=Taxa -dao.burnBsq.trialPeriod=Período de teste -dao.burnBsq.payFee=Pagar taxa -dao.burnBsq.allAssets=Todos os ativos -dao.burnBsq.assets.nameAndCode=Nome do ativo -dao.burnBsq.assets.state=Estado -dao.burnBsq.assets.tradeVolume=Volume de negócio -dao.burnBsq.assets.lookBackPeriod=Período de verificação -dao.burnBsq.assets.trialFee=Taxa para período de teste -dao.burnBsq.assets.totalFee=Total de taxas pagas -dao.burnBsq.assets.days={0} dias -dao.burnBsq.assets.toFewDays=A taxa do ativo é demasiado baixa. A mín. quantidade de dias para o período de teste é {0}. - -# suppress inspection "UnusedProperty" -dao.assetState.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=No período de teste -# suppress inspection "UnusedProperty" -dao.assetState.ACTIVELY_TRADED=Ativamente negociado -# suppress inspection "UnusedProperty" -dao.assetState.DE_LISTED=Deslistado devido à inatividade -# suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=Removido por votação - -dao.proofOfBurn.header=Prova de destruição -dao.proofOfBurn.amount=Quantia -dao.proofOfBurn.preImage=Pré-imagem -dao.proofOfBurn.burn=Destruição -dao.proofOfBurn.allTxs=Todas as transações de prova de destruição -dao.proofOfBurn.myItems=A minha transação de prova de destruição -dao.proofOfBurn.date=Data -dao.proofOfBurn.hash=Hash -dao.proofOfBurn.txs=Transações -dao.proofOfBurn.pubKey=Chave Pública -dao.proofOfBurn.signature.window.title=Assinar uma mensagem com a chave da transação de prova de destruição -dao.proofOfBurn.verify.window.title=Verificar uma mensagem com a chave da transação de prova de destruição -dao.proofOfBurn.copySig=Copiar assinatura para o clipboard -dao.proofOfBurn.sign=Assinar -dao.proofOfBurn.message=Mensagem -dao.proofOfBurn.sig=Assinatura -dao.proofOfBurn.verify=Verificar -dao.proofOfBurn.verificationResult.ok=Verificação sucedida -dao.proofOfBurn.verificationResult.failed=Verificação falhada - -# suppress inspection "UnusedProperty" -dao.phase.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.phase.PROPOSAL=Fase de proposta -# suppress inspection "UnusedProperty" -dao.phase.BREAK1=Pausa antes da fase de votação às cegas -# suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=Fase de votação às cegas -# suppress inspection "UnusedProperty" -dao.phase.BREAK2=Pausa antes da fase de revelação dos votos -# suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=Fase de revelação de votos -# suppress inspection "UnusedProperty" -dao.phase.BREAK3=Pausa antes da fase de resultado -# suppress inspection "UnusedProperty" -dao.phase.RESULT=Fase de resultado da votação - -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.PROPOSAL=Fase de proposta -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=Voto às cegas -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.VOTE_REVEAL=Revelação de votos -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.RESULT=Resultado da votação - -# suppress inspection "UnusedProperty" -dao.proposal.type.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.proposal.type.COMPENSATION_REQUEST=Pedido de compensação -# suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=Pedido de reembolso -# suppress inspection "UnusedProperty" -dao.proposal.type.BONDED_ROLE=Proposta para um cargo vinculado -# suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=Proposta para remover um ativo -# suppress inspection "UnusedProperty" -dao.proposal.type.CHANGE_PARAM=Proposta para mudança de parâmetro -# suppress inspection "UnusedProperty" -dao.proposal.type.GENERIC=Proposta genérica -# suppress inspection "UnusedProperty" -dao.proposal.type.CONFISCATE_BOND=Proposta para confiscação de um vínculo - -# suppress inspection "UnusedProperty" -dao.proposal.type.short.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.proposal.type.short.COMPENSATION_REQUEST=Pedido de compensação -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=Pedido de reembolso -# suppress inspection "UnusedProperty" -dao.proposal.type.short.BONDED_ROLE=Cargo vinculado -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ASSET=Remoção de uma altcoin -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CHANGE_PARAM=Mudança de parâmetro -# suppress inspection "UnusedProperty" -dao.proposal.type.short.GENERIC=Proposta genérica -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CONFISCATE_BOND=Confiscação de um vínculo - -dao.proposal.details=Detalhes da proposta -dao.proposal.selectedProposal=Proposta selecionada -dao.proposal.active.header=Propostas do ciclo atual -dao.proposal.active.remove.confirm=Tem certeza de que pretende remover essa proposta?\nA taxa da proposta paga será perdida. -dao.proposal.active.remove.doRemove=Sim, remover a minha proposta -dao.proposal.active.remove.failed=Não foi possível remover a proposta. -dao.proposal.myVote.title=Votação -dao.proposal.myVote.accept=Aceitar proposta -dao.proposal.myVote.reject=Rejeitar proposta -dao.proposal.myVote.removeMyVote=Ignorar proposta -dao.proposal.myVote.merit=Peso de voto de BSQ ganho -dao.proposal.myVote.stake=Peso de voto de participação -dao.proposal.myVote.revealTxId=ID de transação de revelação de voto -dao.proposal.myVote.stake.prompt=Máx. participação disponível para votação: {0} -dao.proposal.votes.header=Definir participação para votar e publique seus votos -dao.proposal.myVote.button=Publicar votos -dao.proposal.myVote.setStake.description=Depois de votar em todas as propostas, você deve definir a sua participação para votação bloqueando BSQ. Quanto mais BSQ você bloquear, mais peso o seu voto terá.\n\nBSQ bloqueado para votação será desbloqueado novamente durante a fase de revelação de voto. -dao.proposal.create.selectProposalType=Selecionar o tipo de proposta -dao.proposal.create.phase.inactive=Por favor espere até a próxima fase -dao.proposal.create.proposalType=Tipo de proposta -dao.proposal.create.new=Criar novo pedido de compensação -dao.proposal.create.button=Criar proposta -dao.proposal.create.publish=Publicar proposta -dao.proposal.create.publishing=Publicação de proposta em progresso ... -dao.proposal=proposta -dao.proposal.display.type=Tipo de proposta -dao.proposal.display.name=Exacto nome de usuário no Github -dao.proposal.display.link=Link para informação detalhada -dao.proposal.display.link.prompt=Link para a proposta -dao.proposal.display.requestedBsq=Quantia requerida em BSQ -dao.proposal.display.txId=ID de transação de proposta -dao.proposal.display.proposalFee=Taxa de proposta -dao.proposal.display.myVote=O meu voto -dao.proposal.display.voteResult=Resumo do resultado da votação -dao.proposal.display.bondedRoleComboBox.label=Tipo de cargo vinculado -dao.proposal.display.requiredBondForRole.label=Vínculo necessário para cargo -dao.proposal.display.option=Opção - -dao.proposal.table.header.proposalType=Tipo de proposta -dao.proposal.table.header.link=Link -dao.proposal.table.header.myVote=O meu voto -# suppress inspection "UnusedProperty" -dao.proposal.table.header.remove=Remover -dao.proposal.table.icon.tooltip.removeProposal=Remover a minha proposta -dao.proposal.table.icon.tooltip.changeVote=Voto atual: ''{0}''. Mudar o voto para: ''{1}'' - -dao.proposal.display.myVote.accepted=Aceite -dao.proposal.display.myVote.rejected=Rejeitado -dao.proposal.display.myVote.ignored=Ignorado -dao.proposal.display.myVote.unCounted=O voto não foi incluído no resultado -dao.proposal.myVote.summary=Votou: {0}; Peso do voto: {1} (ganho: {2} + participação: {3}) {4} -dao.proposal.myVote.invalid=O voto foi inválido - -dao.proposal.voteResult.success=Aceite -dao.proposal.voteResult.failed=Rejeitado -dao.proposal.voteResult.summary=Resultado: {0}; Limite: {1} (necessário > {2}); Quórum: {3} (necessário > {4}) - -dao.proposal.display.paramComboBox.label=Selecione o parâmetro a mudar -dao.proposal.display.paramValue=Valor do parâmetro - -dao.proposal.display.confiscateBondComboBox.label=Escolha o vínculo -dao.proposal.display.assetComboBox.label=Ativo a remover - -dao.blindVote=voto cego - -dao.blindVote.startPublishing=Publicando transação de voto cego... -dao.blindVote.success=Sua transação de voto cego foi publicada com sucesso.\n\nPor favor, note que você tem que estar online na fase de revelação de votos para que o seu programa Bisq possa publicar a transação de revelação de voto. Sem a transação da revelação do voto o seu voto seria inválido! - -dao.wallet.menuItem.send=Enviar -dao.wallet.menuItem.receive=Receber -dao.wallet.menuItem.transactions=Transações - -dao.wallet.dashboard.myBalance=O saldo da minha carteira - -dao.wallet.receive.fundYourWallet=O seu endereço recipiente de BSQ -dao.wallet.receive.bsqAddress=Endereço da carteira BSQ (endereço não utilizado) - -dao.wallet.send.sendFunds=Enviar fundos -dao.wallet.send.sendBtcFunds=Enviar fundos não-BSQ (BTC) -dao.wallet.send.amount=Quantia em BSQ -dao.wallet.send.btcAmount=Quantia em BTC (fundos não-BSQ) -dao.wallet.send.setAmount=Definir quantia a levantar (mín. quantia é {0}) -dao.wallet.send.receiverAddress=Endereço BSQ do recipiente -dao.wallet.send.receiverBtcAddress=Endereço BTC do recipiente -dao.wallet.send.setDestinationAddress=Preencha seu endereço de destino -dao.wallet.send.send=Enviar fundos BSQ -dao.wallet.send.inputControl=Select inputs -dao.wallet.send.sendBtc=Enviar fundos BTC -dao.wallet.send.sendFunds.headline=Confirmar pedido de levantamento. -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=Último bloco verificado: {0} -dao.wallet.chainHeightSyncing=Esperando blocos... {0} dos {1} blocos verificados -dao.wallet.tx.type=Tipo - -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED=Indefinido -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED_TX_TYPE=Não reconhecido -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNVERIFIED=Transação BSQ não verificada -# suppress inspection "UnusedProperty" -dao.tx.type.enum.INVALID=Transação BSQ inválida -# suppress inspection "UnusedProperty" -dao.tx.type.enum.GENESIS=Transação gênesis -# suppress inspection "UnusedProperty" -dao.tx.type.enum.TRANSFER_BSQ=Transferir BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.received.TRANSFER_BSQ=BSQ recebido -# suppress inspection "UnusedProperty" -dao.tx.type.enum.sent.TRANSFER_BSQ=BSQ enviado -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PAY_TRADE_FEE=Taxa de negociação -# suppress inspection "UnusedProperty" -dao.tx.type.enum.COMPENSATION_REQUEST=Taxa de pedido de compensação -# suppress inspection "UnusedProperty" -dao.tx.type.enum.REIMBURSEMENT_REQUEST=Taxa para pedido de reembolso -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROPOSAL=Taxa para proposta -# suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=Taxa para voto cego -# suppress inspection "UnusedProperty" -dao.tx.type.enum.VOTE_REVEAL=Revelação de votos -# suppress inspection "UnusedProperty" -dao.tx.type.enum.LOCKUP=Bloquear vínculo -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNLOCK=Desbloquear vínculo -# suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=Taxa de listagem de ativos -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=Prova de destruição -# suppress inspection "UnusedProperty" -dao.tx.type.enum.IRREGULAR=Irregular - -dao.tx.withdrawnFromWallet=BTC levantado da carteira -dao.tx.issuanceFromCompReq=Pedido de compensação/emissão -dao.tx.issuanceFromCompReq.tooltip=Pedido de compensação que levou à emissão de novo BSQ.\nData de emissão: {0} -dao.tx.issuanceFromReimbursement=Pedido de reembolso/emissão -dao.tx.issuanceFromReimbursement.tooltip=Solicitação de reembolso que levou à emissão de novo BSQ.\nData de emissão: {0} -dao.proposal.create.missingBsqFunds=Você não tem fundos suficientes para criar a proposta. Se você tem uma transação de BSQ não confirmada, você precisa esperar por uma confirmação da blockchain porque o BSQ é validado somente se estiver incluído num bloco.\nEm falta: {0} - -dao.proposal.create.missingBsqFundsForBond=Você não tem fundos suficientes para este cargo. Você ainda pode publicar essa proposta, mas precisará do valor completo de BSQ necessário para esse cargo se ela for aceite.\nEm falta: {0} - -dao.proposal.create.missingMinerFeeFunds=Você não tem suficientes fundos de BTC para criar a transação da proposta. Todas as transações de BSQ exigem uma taxa do mineiro em BTC.\nEm falta: {0} - -dao.proposal.create.missingIssuanceFunds=Você não tem suficientes fundos de BTC para criar a transação da proposta. Todas as transações de BSQ exigem uma taxa do mineiro em BTC, e as transações de emissão também exigem BTC pela quantia de BSQ solicitado ({0} satoshis/BSQ).\nEm falta: {1} - -dao.feeTx.confirm=Confirmar transação {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.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.news.bisqDAO.title=A OAD DO BISQ -dao.news.bisqDAO.description=Assim como o mercado de câmbio do Bisq é descentralizado e resistente à censura, o seu modelo de governação também o é - e a OAD do Bisq e o token da BSQ são as ferramentas que tornam isso possível. -dao.news.bisqDAO.readMoreLink=Saber Mais Sobre a OAD do Bisq - -dao.news.pastContribution.title=FEZ CONTRIBUIÇÕES NO PASSADO? PEÇA BSQ -dao.news.pastContribution.description=Se você contribuiu para o Bisq, por favor, use o endereço BSQ abaixo e faça um pedido para participar da distribuição genesis de BSQ. -dao.news.pastContribution.yourAddress=O seu endereço da carteira BSQ -dao.news.pastContribution.requestNow=Solicitar agora - -dao.news.DAOOnTestnet.title=EXECUTE A OAD DO BISQ NA NOSSA REDE DE TESTES -dao.news.DAOOnTestnet.description=A mainnet da OAD do Bisq ainda não foi lançada, mas você pode aprender sobre a OAD do Bisq executando-a na nossa testnet. -dao.news.DAOOnTestnet.firstSection.title=1. Mudar para Modo Testnet da OAD -dao.news.DAOOnTestnet.firstSection.content=Mude para a Testnet da OAD no painel de Definições -dao.news.DAOOnTestnet.secondSection.title=2. Obtenha alguns BSQ -dao.news.DAOOnTestnet.secondSection.content=Solicite BSQ no Slack ou Compre BSQ no Bisq -dao.news.DAOOnTestnet.thirdSection.title=3. Participe num Ciclo de Votação -dao.news.DAOOnTestnet.thirdSection.content=Fazendo propostas e votando em propostas para mudar vários aspetos da Bisq. -dao.news.DAOOnTestnet.fourthSection.title=4. Explorar um Explorador de Blocos da BSQ -dao.news.DAOOnTestnet.fourthSection.content=Desde que BSQ é apenas bitcoin, você pode ver transações de BSQ no nosso explorador de blocos de bitcoin. -dao.news.DAOOnTestnet.readMoreLink=Leia a documentação completa - -dao.monitor.daoState=Estado da OAD -dao.monitor.proposals=Estado de propostas -dao.monitor.blindVotes=Estado de votos cegos - -dao.monitor.table.peers=Pares -dao.monitor.table.conflicts=Conflitos -dao.monitor.state=Estado -dao.monitor.requestAlHashes=Pedir todos os hashes -dao.monitor.resync=Re-sincronizar estado da OAD -dao.monitor.table.header.cycleBlockHeight=Ciclo / altura do bloco -dao.monitor.table.cycleBlockHeight=Ciclo {0} / bloco {1} -dao.monitor.table.seedPeers=Nó semente: {0} - -dao.monitor.daoState.headline=Estado da OAD -dao.monitor.daoState.table.headline=Corrente de hashes do estado da OAD -dao.monitor.daoState.table.blockHeight=Altura do bloco -dao.monitor.daoState.table.hash=Hash do estado da OAD -dao.monitor.daoState.table.prev=Hash anterior -dao.monitor.daoState.conflictTable.headline=Hashes do estado da OAD de pares em conflito -dao.monitor.daoState.utxoConflicts=Conflitos de UTXO -dao.monitor.daoState.utxoConflicts.blockHeight=Altura do bloco: {0} -dao.monitor.daoState.utxoConflicts.sumUtxo=Soma de todo UTXO: {0} BSQ -dao.monitor.daoState.utxoConflicts.sumBsq=Soma de todo BSQ: {0} BSQ -dao.monitor.daoState.checkpoint.popup=O estado da OAD não está sincronizado com a rede. Após o reinicio a OAD vai ressincronizar. - -dao.monitor.proposal.headline=Estado de propostas -dao.monitor.proposal.table.headline=Corrente hashes do estado da propostaa -dao.monitor.proposal.conflictTable.headline=Hashes de estado da proposta de pares em conflito - -dao.monitor.proposal.table.hash=Hash do estado da proposta -dao.monitor.proposal.table.prev=Hash anterior -dao.monitor.proposal.table.numProposals=Nº de propostas - -dao.monitor.isInConflictWithSeedNode=Os seus dados locais não estão em consenso com pelo menos um nó semente. Por favor, re-sincronizar o estado da OAD. -dao.monitor.isInConflictWithNonSeedNode=Um dos seus pares não está em consenso com a rede, mas o seu nó está em sincronia com os nós semente. -dao.monitor.daoStateInSync=Seu nó local está em consenso com a rede - -dao.monitor.blindVote.headline=Estado de votos cegos -dao.monitor.blindVote.table.headline=Corrente de hashes de estado de voto cego -dao.monitor.blindVote.conflictTable.headline=Hashes de estado de voto cego de pares em conflito -dao.monitor.blindVote.table.hash=Hash de estado de voto cego -dao.monitor.blindVote.table.prev=Hash anterior -dao.monitor.blindVote.table.numBlindVotes=Nº de votos cegos - -dao.factsAndFigures.menuItem.supply=Estoque de BSQ -dao.factsAndFigures.menuItem.transactions=Transações BSQ - -dao.factsAndFigures.dashboard.avgPrice90=Média de 90 dias do preço de negócio de BSQ/BTC -dao.factsAndFigures.dashboard.avgPrice30=Média de 30 dias do preço de negócio de BSQ/BTC -dao.factsAndFigures.dashboard.avgUSDPrice90=90 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on 30 days average BSQ/USD price) -dao.factsAndFigures.dashboard.availableAmount=Total BSQ disponível -dao.factsAndFigures.dashboard.volumeUsd=Total trade volume in USD -dao.factsAndFigures.dashboard.volumeBtc=Total trade volume in BTC -dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection=Average BSQ/USD trade price from selected time period in chart -dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection=Average BSQ/BTC trade price from selected time period in chart - -dao.factsAndFigures.supply.issuedVsBurnt=BSQ emitido v. BSQ queimado - -dao.factsAndFigures.supply.issued=BSQ emitido -dao.factsAndFigures.supply.compReq=Pedidos de compensação -dao.factsAndFigures.supply.reimbursement=Reimbursement requests -dao.factsAndFigures.supply.genesisIssueAmount=BSQ emitido na transação genesis -dao.factsAndFigures.supply.compRequestIssueAmount=BDQ emitido para pedidos de compensação -dao.factsAndFigures.supply.reimbursementAmount=BSQ emitido para pedidos de reembolso -dao.factsAndFigures.supply.totalIssued=Total issued BSQ -dao.factsAndFigures.supply.totalBurned=Total burned BSQ -dao.factsAndFigures.supply.chart.tradeFee.toolTip={0}\n{1} -dao.factsAndFigures.supply.burnt=BSQ destruído - -dao.factsAndFigures.supply.priceChat=BSQ price -dao.factsAndFigures.supply.volumeChat=Volume de negócio -dao.factsAndFigures.supply.tradeVolumeInUsd=Trade volume in USD -dao.factsAndFigures.supply.tradeVolumeInBtc=Trade volume in BTC -dao.factsAndFigures.supply.bsqUsdPrice=BSQ/USD price -dao.factsAndFigures.supply.bsqBtcPrice=BSQ/BTC price -dao.factsAndFigures.supply.btcUsdPrice=BTC/USD price - -dao.factsAndFigures.supply.locked=Estado global do BSQ bloqueado -dao.factsAndFigures.supply.totalLockedUpAmount=Bloqueado em vínculos -dao.factsAndFigures.supply.totalUnlockingAmount=Desbloqueando BSQ de vínculos -dao.factsAndFigures.supply.totalUnlockedAmount=BSQ desbloqueado de vínculos -dao.factsAndFigures.supply.totalConfiscatedAmount=BSQ confiscado de vínculos -dao.factsAndFigures.supply.proofOfBurn=Proof of Burn -dao.factsAndFigures.supply.bsqTradeFee=BSQ Trade fees -dao.factsAndFigures.supply.btcTradeFee=BTC Trade fees - -dao.factsAndFigures.transactions.genesis=Transação gênesis -dao.factsAndFigures.transactions.genesisBlockHeight=Altura do bloco genesis -dao.factsAndFigures.transactions.genesisTxId=ID da transação genesis -dao.factsAndFigures.transactions.txDetails=Estatísticas das transações de BSQ -dao.factsAndFigures.transactions.allTx=Nº de todas as transações de BSQ -dao.factsAndFigures.transactions.utxo=Nº de todos os outputs de transações não gastos -dao.factsAndFigures.transactions.compensationIssuanceTx=Nº de todas as transações de emissão de pedido de compensação -dao.factsAndFigures.transactions.reimbursementIssuanceTx=Nº de todas as trnsações de emissão de pedido de reembolso -dao.factsAndFigures.transactions.burntTx=Nº de todas transações de pagamentos de taxa -dao.factsAndFigures.transactions.invalidTx=Nº de todas as transações inválidas -dao.factsAndFigures.transactions.irregularTx=Nº de todas as transações irregulares - - - #################################################################### # Windows #################################################################### @@ -2120,8 +1406,6 @@ disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any emptyWalletWindow.headline={0} ferramenta de emergência da carteira emptyWalletWindow.info=Por favor, use isso apenas em caso de emergência, se você não puder aceder o seu fundo a partir da interface do utilizador.\n\nPor favor, note que todas as ofertas abertas serão fechadas automaticamente ao usar esta ferramenta.\n\nAntes de usar essa ferramenta, faça backup do seu diretório de dados. Você pode fazer isso em \"Conta/Backup\".\n\nPor favor comunique-nos o seu problema e envie um relatório de erros no Github ou no fórum Bisq para que possamos investigar o que causou o problema. emptyWalletWindow.balance=O saldo disponível da sua carteira: -emptyWalletWindow.bsq.btcBalance=Saldo de satoshis não-BSQ - emptyWalletWindow.address=Seu endereço de destino emptyWalletWindow.button=Enviar todos os fundos emptyWalletWindow.openOffers.warn=Você tem ofertas abertas que serão removidas se você esvaziar a carteira.\nTem certeza de que deseja esvaziar a sua carteira? @@ -2146,10 +1430,8 @@ filterWindow.seedNode=Nós de semente filtrados (endereços onion sep. por vírg filterWindow.priceRelayNode=Nós de transmissão de preço filtrados (endereços onion sep. por vírgula) filterWindow.btcNode=Nós de Bitcoin filtrados (endereços + portas sep. por vírgula) filterWindow.preventPublicBtcNetwork=Prevenir uso da rede de Bitcoin pública -filterWindow.disableDao=Desativar OAD filterWindow.disableAutoConf=Disable auto-confirm filterWindow.autoConfExplorers=Filtered auto-confirm explorers (comma sep. addresses) -filterWindow.disableDaoBelowVersion=Versão mín. necessária para a OAD filterWindow.disableTradeBelowVersion=Mín. versão necessária para negociação filterWindow.add=Adicionar filtro filterWindow.remove=Remover filtro @@ -2161,8 +1443,6 @@ offerDetailsWindow.minBtcAmount=Quantia mín. de BTC offerDetailsWindow.min=(mín. {0}) offerDetailsWindow.distance=(distância do preço de mercado: {0}) offerDetailsWindow.myTradingAccount=Minha conta de negociação -offerDetailsWindow.offererBankId=(ID bancário/BIC/SWIFT do ofertante) -offerDetailsWindow.offerersBankName=(nome do banco do ofertante) offerDetailsWindow.bankId=ID do banco (ex. BIC ou SWIFT) offerDetailsWindow.countryBank=País do banco do ofertante offerDetailsWindow.commitment=Compromisso @@ -2223,7 +1503,6 @@ tradeDetailsWindow.detailData=Detail data txDetailsWindow.headline=Transaction Details txDetailsWindow.btc.note=You have sent BTC. -txDetailsWindow.bsq.note=You have sent BSQ funds. BSQ is colored bitcoin, so the transaction will not show in a BSQ explorer until it has been confirmed in a bitcoin block. txDetailsWindow.sentTo=Sent to txDetailsWindow.txId=TxId @@ -2235,9 +1514,6 @@ closedTradesSummaryWindow.totalMinerFee.title=Sum of all miner fees closedTradesSummaryWindow.totalMinerFee.value={0} ({1} of total trade amount) closedTradesSummaryWindow.totalTradeFeeInBtc.title=Sum of all trade fees paid in BTC closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} of total trade amount) -closedTradesSummaryWindow.totalTradeFeeInBsq.title=Sum of all trade fees paid in BSQ -closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} of total trade amount) - walletPasswordWindow.headline=Digite senha para abrir: torNetworkSettingWindow.header=Definições de redes Tor @@ -2317,12 +1593,6 @@ popup.warning.tooLargePercentageValue=Você não pode definir uma percentagem su popup.warning.examplePercentageValue=Por favor digitar um número percentual como \"5.4\" para 5.4% popup.warning.noPriceFeedAvailable=Não há feed de preço disponível para essa moeda. Você não pode usar um preço baseado em percentagem.\nPor favor, selecione o preço fixo. popup.warning.sendMsgFailed=Enviar mensagem para seu par de negociação falhou.\nPor favor, tente novamente e se continuar a falhar relate um erro. -popup.warning.insufficientBtcFundsForBsqTx=Você não tem fundos BTC suficientes para pagar a taxa de mineração para essa transação.\nPor favor financie sua carteira BTC.\nFundos em falta: {0} -popup.warning.bsqChangeBelowDustException=Esta transação cria um output de trocos de BSQ que está abaixo do limite de poeira (5,46 BSQ) e seria rejeitada pela rede do bitcoin.\n\nVocê precisa enviar uma quantia mais elevada para evitar o output de trocos (por exemplo, adicionando a quantia de poeira ao seu montante a ser enviado) ou adicionar mais fundos de BSQ à sua carteira de modo a evitar a gerar um output de poeira.\n\nO output de poeira é {0}. -popup.warning.btcChangeBelowDustException=Esta transação cria um output de trocos que está abaixo do limite de poeira (546 satoshis) e seria rejeitada pela rede do bitcoin.\n\nVocê precisa adicionar a quantia de poeira ao seu montante à ser enviado para evitar gerar um output de poeira.\n\nO output de poeira é {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.noBsqFundsForBtcFeePayment=Sua carteira BSQ não possui fundos suficientes para pagar a taxa de negócio em BSQ. popup.warning.messageTooLong=Sua mensagem excede o tamanho máx. permitido. Por favor enviá-la em várias partes ou carregá-la utilizando um serviço como https://pastebin.com. popup.warning.lockedUpFunds=Você trancou fundos de um negócio falhado..\nSaldo trancado: {0} \nEndereço da tx de Depósito: {1}\nID de negócio: {2}.\n\nPor favor abra um bilhete de apoio selecionando o negócio no ecrã de negócios abertos e pressione \"alt + o\" ou \"option + o\"." @@ -2336,8 +1606,6 @@ popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=transmissão de preço popup.warning.seed=semente popup.warning.mandatoryUpdate.trading=Por favor, atualize para a versão mais recente do Bisq. Uma atualização obrigatória que desativa negociação para versões antigas foi lançada. Por favor, confira o Fórum Bisq para mais informações. -popup.warning.mandatoryUpdate.dao=Por favor, atualize para a versão mais recente Bisq. Uma atualização obrigatória que desativa a OAD do Bisq e BSQ para versões antigas foi lançada. Por favor, confira o Fórum Bisq para mais informações. -popup.warning.disable.dao=A OAD do Bisq e BSQ foram temporariamente desativados. Por favor, confira o Fórum Bisq para mais informações. popup.warning.noFilter=We did not receive a filter object from the seed nodes. This is a not expected situation. Please inform the Bisq developers. popup.warning.burnBTC=Esta transação não é possível, pois as taxas de mineração de {0} excederia o montante a transferir de {1}. Aguarde até que as taxas de mineração estejam novamente baixas ou até você ter acumulado mais BTC para transferir. @@ -2356,7 +1624,6 @@ popup.info.cashDepositInfo.confirm=Eu confirmo que eu posso fazer o depósito popup.info.shutDownWithOpenOffers=Bisq está sendo fechado, mas há ofertas abertas. \n\nEstas ofertas não estarão disponíveis na rede P2P enquanto o Bisq estiver desligado, mas elas serão publicadas novamente na rede P2P na próxima vez que você iniciar o Bisq.\n\nPara manter suas ofertas on-line, mantenha o Bisq em execução e certifique-se de que este computador também permaneça online (ou seja, certifique-se de que ele não entra no modo de espera... o modo de espera do monitor não causa 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ção privada importante! @@ -2528,7 +1795,6 @@ navigation.settings.preferences=\"Definições/Preferências\" # suppress inspection "UnusedProperty" navigation.funds.transactions=\"Fundos/Transações\" navigation.support=\"Apoio\" -navigation.dao.wallet.receive=\"OAD/Carteira BSQ/Receber\" #################################################################### @@ -2558,12 +1824,6 @@ XMR_MAINNET=Mainnet de Monero XMR_TESTNET=Testnet de Monero # suppress inspection "UnusedProperty" XMR_STAGENET=Stagenet Monero -# suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Testnet da OAD do Bitcoin (discontinuada) -# suppress inspection "UnusedProperty" -BTC_DAO_BETANET=Betanet da OAD do Bisq (Mainnet do Bitcoin) -# suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=Regtest da OAD do Bitcoin time.year=Ano time.month=Mês @@ -2910,9 +2170,7 @@ validation.accountNrChars=O número da conta deve conter {0} caracteres. validation.btc.invalidAddress=O endereço está incorreto. Por favor verificar o formato do endereço. validation.integerOnly=Por favor, insira apenas números inteiros validation.inputError=O seu input causou um erro:\n{0} -validation.bsq.insufficientBalance=O seu saldo disponível é de {0}. validation.btc.exceedsMaxTradeLimit=O seu limite de negócio é de {0}. -validation.bsq.amountBelowMinAmount=A quantia mín. é de {0} validation.nationalAccountId={0} tem de ser constituído por {1} números #new @@ -2934,7 +2192,6 @@ validation.bic.invalidLocationCode=BIC contém código de localização inválid validation.bic.invalidBranchCode=BIC contém código da agência inválido validation.bic.sepaRevolutBic=Contas Revolut SEPA não são suportadas. validation.btc.invalidFormat=Invalid format for a Bitcoin address. -validation.bsq.invalidFormat=Invalid format for a BSQ address. validation.email.invalidAddress=Endereço inválido validation.iban.invalidCountryCode=Código de país inválido validation.iban.checkSumNotNumeric=Soma de verificação deve ser numérica diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index a9a27ba4ea..77a573c991 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -192,7 +192,6 @@ shared.tradeWalletBalance=Баланс кошелька сделки shared.makerTxFee=Мейкер: {0} shared.takerTxFee=Тейкер: {0} shared.iConfirm=Подтверждаю -shared.tradingFeeInBsqInfo=≈ {0} shared.openURL=Открыть {0} shared.fiat=Нац. валюта shared.crypto=Криптовалюта @@ -204,9 +203,6 @@ shared.actions=Действия shared.buyerUpperCase=Покупатель shared.sellerUpperCase=Продавец shared.new=НОВОЕ -shared.blindVoteTxId=Идент. транзакции слепого голосования -shared.proposal=Предложение -shared.votes=Голоса shared.learnMore=Узнать больше shared.dismiss=Отмена shared.selectedArbitrator=Выбранный арбитр @@ -240,7 +236,6 @@ mainView.menu.funds=Средства mainView.menu.support=Поддержка mainView.menu.settings=Настройки mainView.menu.account=Счёт -mainView.menu.dao=ДАО mainView.marketPriceWithProvider.label=Рыночный курс {0} mainView.marketPrice.bisqInternalPrice=Курс последней сделки в Bisq @@ -257,13 +252,11 @@ mainView.footer.localhostBitcoinNode=(локальный узел) mainView.footer.btcInfo={0} {1} mainView.footer.btcFeeRate=/ Fee rate: {0} sat/vB mainView.footer.btcInfo.initializing=Подключение к сети Биткойн -mainView.footer.bsqInfo.synchronizing=/ Синхронизация ДАО 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} -mainView.footer.daoFullNode=Полный узел ДАО mainView.bootstrapState.connectionToTorNetwork=(1/4) Подключение к сети Tor... mainView.bootstrapState.torNodeCreated=(2/4) Создан узел Tor @@ -435,7 +428,6 @@ createOffer.fundsBox.networkFee=Комиссия майнера createOffer.fundsBox.placeOfferSpinnerInfo=Публикация предложения... createOffer.fundsBox.paymentLabel=Сделка Bisq с идентификатором {0} createOffer.fundsBox.fundsStructure=({0} — залог, {1} — комиссия за сделку, {2} — комиссия майнера) -createOffer.fundsBox.fundsStructure.BSQ=({0} — залог, {1} — комиссия майнера) + {2} — комиссия за сделку createOffer.success.headline=Ваше предложение опубликовано createOffer.success.info=Вы можете управлять текущими предложениями в разделе \«Сделки/Мои текущие предложения\». createOffer.info.sellAtMarketPrice=Вы всегда будете продавать по рыночному курсу, так как курс вашего предложения будет постоянно обновляться. @@ -636,7 +628,6 @@ portfolio.pending.step2_buyer.amountToTransfer=Сумма для перевод portfolio.pending.step2_buyer.sellersAddress={0}-адрес продавца portfolio.pending.step2_buyer.buyerAccount=Используемый платёжный счет portfolio.pending.step2_buyer.paymentStarted=Платёж начат -portfolio.pending.step2_buyer.fillInBsqWallet=Pay from BSQ wallet portfolio.pending.step2_buyer.warn=You still have not done your {0} payment!\nPlease note that the trade has to be completed by {1}. portfolio.pending.step2_buyer.openForDispute=You have not completed your payment!\nThe max. period for the trade has elapsed.Please contact the mediator for assistance. portfolio.pending.step2_buyer.paperReceipt.headline=Вы отослали бумажную квитанцию продавцу ВТС? @@ -882,7 +873,6 @@ funds.locked.locked=Заблокировано в multisig-адресе для funds.tx.direction.sentTo=Отправлено: funds.tx.direction.receivedWith=Получено: funds.tx.direction.genesisTx=Из первичной транзакции: -funds.tx.txFeePaymentForBsqTx=Комиссия майнера за транзакцию в BSQ funds.tx.createOfferFee=Комиссия мейкера и плата за сделку: {0} funds.tx.takeOfferFee=Комиссия тейкера и плата за сделку: {0} funds.tx.multiSigDeposit=Депозит на multisig-адрес: {0} @@ -896,15 +886,11 @@ funds.tx.unknown=Неизвестная причина: {0} funds.tx.noFundsFromDispute=Без возмещения по спору funds.tx.receivedFunds=Полученные средства funds.tx.withdrawnFromWallet=Выведено из кошелька -funds.tx.withdrawnFromBSQWallet=BTC выведен из кошелька BSQ funds.tx.memo=Memo funds.tx.noTxAvailable=Транзакции отсутствуют funds.tx.revert=Отменить funds.tx.txSent=Транзакция успешно отправлена на новый адрес локального кошелька Bisq. funds.tx.direction.self=Транзакция внутри кошелька -funds.tx.daoTxFee=Комиссия майнера за транзакцию в BSQ -funds.tx.reimbursementRequestTxFee=Запрос возмещения -funds.tx.compensationRequestTxFee=Запрос компенсации funds.tx.dustAttackTx=Полученная «пыль» funds.tx.dustAttackTx.popup=Вы получили очень маленькую сумму BTC, что может являться попыткой компаний, занимающихся анализом блокчейна, проследить за вашим кошельком.\n\nЕсли вы воспользуетесь этими средствами для совершения исходящей транзакции, они смогут узнать, что вы также являетесь вероятным владельцем другого адреса (т. н. «объединение монет»).\n\nДля защиты вашей конфиденциальности кошелёк Bisq игнорирует такую «пыль» при совершении исходящих транзакций и отображении баланса. Вы можете самостоятельно установить сумму, которая будет рассматриваться в качестве «пыли» в настройках. @@ -996,9 +982,7 @@ settings.tab.about=О проекте setting.preferences.general=Основные настройки setting.preferences.explorer=Bitcoin Explorer -setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=Макс. отклонение от рыночного курса -setting.preferences.bsqAverageTrimThreshold=Outlier threshold for BSQ rate setting.preferences.avoidStandbyMode=Избегать режима ожидания setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled @@ -1032,19 +1016,6 @@ setting.preferences.notifyOnPreRelease=Receive pre-release notifications setting.preferences.resetAllFlags=Сбросить все флажки \«Не показывать снова\» settings.preferences.languageChange=Изменение языка во всех разделах вступит в силу после перезагрузки приложения. settings.preferences.supportLanguageWarning=In case of a dispute, please note that mediation is handled in {0} and arbitration in {1}. -setting.preferences.daoOptions=Настройки ДАО -setting.preferences.dao.resyncFromGenesis.label=Перестроить состояние ДАО от первичной транзакции -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.resync=Resync from genesis and shutdown -setting.preferences.dao.isDaoFullNode=Запустить Bisq в режиме полного узла ДАО -setting.preferences.dao.rpcUser=Логин RPC -setting.preferences.dao.rpcPw=Пароль RPC -setting.preferences.dao.blockNotifyPort=Блокировать сообщающий порт -setting.preferences.dao.fullNodeInfo=Для запуска Bisq в качестве полного узла ДАО вам необходим локальный узел Bitcoin Core с включенным удаленным вызовом процедур (RPC). Другие требования описаны в «{0}».\n\nПосле смены режима необходимо перезапустить приложение. -setting.preferences.dao.fullNodeInfo.ok=Открыть документацию -setting.preferences.dao.fullNodeInfo.cancel=Нет, остаюсь в режиме облегчённого узла settings.preferences.editCustomExplorer.headline=Explorer Settings settings.preferences.editCustomExplorer.description=Choose a system defined explorer from the list on the left, and/or customize to suit your own preferences. settings.preferences.editCustomExplorer.available=Available explorers @@ -1090,7 +1061,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 DAO chain height: {0} | Bitcoin Peers chain height: {1} +settings.net.chainHeight=Bitcoin Peers chain height: {1} settings.net.ips=[IP-адрес:порт | хост:порт | onion-адрес:порт] (через запятые). Порт можно не указывать, если используется порт по умолчанию (8333). settings.net.seedNode=Исходный узел settings.net.directPeer=Пир (прямой) @@ -1144,14 +1115,10 @@ setting.about.shortcuts.walletDetails=Open wallet details window setting.about.shortcuts.openEmergencyBtcWalletTool=Open emergency wallet tool for BTC wallet -setting.about.shortcuts.openEmergencyBsqWalletTool=Open emergency wallet tool for BSQ wallet - setting.about.shortcuts.showTorLogs=Toggle log level for Tor messages between DEBUG and WARN setting.about.shortcuts.manualPayoutTxWindow=Open window for manual payout from 2of2 Multisig deposit tx -setting.about.shortcuts.reRepublishAllGovernanceData=Republish DAO governance data (proposals, votes) - setting.about.shortcuts.removeStuckTrade=Open popup to move failed trade to open trades tab again setting.about.shortcuts.removeStuckTrade.value=Select failed trade and press: {0} @@ -1337,687 +1304,6 @@ account.notifications.noWebCamFound.warning=Веб-камера не найде account.notifications.priceAlert.warning.highPriceTooLow=Более высокая цена должна быть выше более низкой цены. account.notifications.priceAlert.warning.lowerPriceTooHigh=Более низкая цена должна быть ниже более высокой цены. - - - -#################################################################### -# DAO -#################################################################### - -dao.tab.factsAndFigures=Факты и цифры -dao.tab.bsqWallet=BSQ-кошелёк -dao.tab.proposals=Управление -dao.tab.bonding=Гарантийный депозит -dao.tab.proofOfBurn=Сбор за листинг активов/Proof of burn -dao.tab.monitor=Монитор сети -dao.tab.news=Новости - -dao.paidWithBsq=выплачено в BSQ -dao.availableBsqBalance=Доступно для трат (проверенные + неподтвержденные суммы) -dao.verifiedBsqBalance=Баланс всех проверенных UTXO -dao.unconfirmedChangeBalance=Остаток всех неподтвержденных сумм -dao.unverifiedBsqBalance=Баланс всех неподтвержденных транзакций (ожидающих подтверждения) -dao.lockedForVoteBalance=Использовано для голосования -dao.lockedInBonds=Заблокировано в гарантийных депозитах -dao.availableNonBsqBalance=Доступный баланс в ВТС (без учёта BSQ) -dao.reputationBalance=Merit Value (not spendable) - -dao.tx.published.success=Ваша транзакция опубликована. -dao.proposal.menuItem.make=Выступить с предложением -dao.proposal.menuItem.browse=Просмотр открытых предложений -dao.proposal.menuItem.vote=Голосование по предложениям -dao.proposal.menuItem.result=Результаты голосования -dao.cycle.headline=Цикл голосования -dao.cycle.overview.headline=Обзор цикла голосования -dao.cycle.currentPhase=Текущий этап -dao.cycle.currentBlockHeight=Номер текущего блока -dao.cycle.proposal=Этап выдвижения предложений -dao.cycle.proposal.next=Следующий этап выдвижения предложений -dao.cycle.blindVote=Этап слепого голосования -dao.cycle.voteReveal=Этап выявления голосов -dao.cycle.voteResult=Результат голосования -dao.cycle.phaseDuration={0} блоков (≈{1}); блок {2} — {3} (≈{4} — ≈{5}) -dao.cycle.phaseDurationWithoutBlocks=Блок {0} — {1} (≈{2} — ≈{3}) - -dao.voteReveal.txPublished.headLine=Транзакция выявления голоса опубликована -dao.voteReveal.txPublished=Ваша транзакция выявления голоса с идентификатором {0} была успешно опубликована. \n\nЭто происходит автоматически, если вы участвовали в голосовании ДАО. - -dao.results.cycles.header=Циклы -dao.results.cycles.table.header.cycle=Цикл -dao.results.cycles.table.header.numProposals=Предложения -dao.results.cycles.table.header.voteWeight=Вес голоса -dao.results.cycles.table.header.issuance=Эмиссия - -dao.results.results.table.item.cycle=Цикл {0} начат: {1} - -dao.results.proposals.header=Предложения выбранного цикла -dao.results.proposals.table.header.nameLink=Name/link -dao.results.proposals.table.header.details=Подробности -dao.results.proposals.table.header.myVote=Мой голос -dao.results.proposals.table.header.result=Результат голосования -dao.results.proposals.table.header.threshold=Threshold -dao.results.proposals.table.header.quorum=Quorum - -dao.results.proposals.voting.detail.header=Результаты голосования по выбранному предложению - -dao.results.exceptions=Исключение (-я) результата голосования - -# suppress inspection "UnusedProperty" -dao.param.UNDEFINED=Неопределено - -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BSQ=Комиссия мейкера в BSQ -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BSQ=Комиссия тейкера в BSQ -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BSQ=Мин. комиссия мейкера в BSQ -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BSQ=Мин. комиссия тейкера в BSQ -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BTC=Комиссия мейкера в BТС -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BTC=Комиссия тейкера в BТС -# suppress inspection "UnusedProperty" -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BTC=Мин. комиссия мейкера в BТС -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BTC=Мин. комиссия тейкера в BТС -# suppress inspection "UnusedProperty" - -# suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=Комиссия за предложение (в BSQ) -# suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=Комиссия за голосование (в BSQ) - -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=Мин. сумма запроса компенсации в BSQ -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=Макс. сумма запроса компенсации в BSQ -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MIN_AMOUNT=Мин. сумма запроса на возмещение средств в BSQ -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MAX_AMOUNT=Макс. сумма запроса на возмещение средств в BSQ - -# suppress inspection "UnusedProperty" -dao.param.QUORUM_GENERIC=Требуемый кворум в BSQ для обычного предложения -# suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=Требуемый кворум в BSQ для запроса компенсации -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REIMBURSEMENT=Требуемый кворум в BSQ для запроса на возмещение средств -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=Требуемый кворум в BSQ для изменения параметра -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Необходимый кворум в BSQ для удаления актива -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=Необходимый кворум в BSQ для запроса конфискации -# suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=Требуемый кворум в BSQ для запроса обеспеченной роли - -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_GENERIC=Требуемый порог в % для обычного предложения -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=Требуемый порог в % для запроса компенсации -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REIMBURSEMENT=Требуемый порог в % для запроса возмещения средств -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=Требуемый порог в % для изменения параметра -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Требуемый порог в % для удаления актива -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=Требуемый порог в % для запроса конфискации -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_ROLE=Требуемый порог в % для запросов обеспеченной роли - -# suppress inspection "UnusedProperty" -dao.param.RECIPIENT_BTC_ADDRESS=BTC-адрес получателя - -# suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=Сбор за листинг актива в день -# suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Мин. объём торговли активами - -# suppress inspection "UnusedProperty" -dao.param.LOCK_TIME_TRADE_PAYOUT=Срок блокировки для альтернативных торговых выплат -# suppress inspection "UnusedProperty" -dao.param.ARBITRATOR_FEE=Комиссия арбитра в ВТС - -# suppress inspection "UnusedProperty" -dao.param.MAX_TRADE_LIMIT=Макс. торговый лимит в BTC - -# suppress inspection "UnusedProperty" -dao.param.BONDED_ROLE_FACTOR=Обеспеченная роль до принятия в расчёт BSQ -# suppress inspection "UnusedProperty" -dao.param.ISSUANCE_LIMIT=Лимит эмиссии на цикл в BSQ - -dao.param.currentValue=Текущее значение {0} -dao.param.currentAndPastValue=Current value: {0} (Value when proposal was made: {1}) -dao.param.blocks={0} блоков - -dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was not distributed well in the Bisq network.\n{0} - -# suppress inspection "UnusedProperty" -dao.phase.PHASE_UNDEFINED=Неопределено -# suppress inspection "UnusedProperty" -dao.phase.PHASE_PROPOSAL=Этап предложения -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK1=Пауза 1 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=Этап слепого голосования -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK2=Пауза 2 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=Этап выявления голосов -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK3=Пауза 3 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_RESULT=Этап результатов - -dao.results.votes.table.header.stakeAndMerit=Вес голоса -dao.results.votes.table.header.stake=Доля -dao.results.votes.table.header.merit=Заработано -dao.results.votes.table.header.vote=Голосование - -dao.bond.menuItem.bondedRoles=Обеспеченные роли -dao.bond.menuItem.reputation=Обеспеченная репутация -dao.bond.menuItem.bonds=Гарантийные депозиты - -dao.bond.dashboard.bondsHeadline=BSQ в гарантийных депозитах -dao.bond.dashboard.lockupAmount=Заблокировать средства -dao.bond.dashboard.unlockingAmount=Разблокировка средств (дождитесь окончания блокировки) - - -dao.bond.reputation.header=Заблокировать депозит для репутации -dao.bond.reputation.table.header=Мои залоговые депозиты для репутации -dao.bond.reputation.amount=Заблокировать BSQ на сумму -dao.bond.reputation.time=Срок разблокировки в блоках -dao.bond.reputation.salt=Соль -dao.bond.reputation.hash=Хеш -dao.bond.reputation.lockupButton=Заблокировать -dao.bond.reputation.lockup.headline=Подтвердить транзакцию блокировки -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.unlock.headline=Подтвердить транзакцию разблокировки -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.allBonds.header=Все гарантийные депозиты - -dao.bond.bondedReputation=Обеспеченная репутация -dao.bond.bondedRoles=Обеспеченные роли - -dao.bond.details.header=Описание роли -dao.bond.details.role=Роль -dao.bond.details.requiredBond=Необходимый гарантийный депозит в BSQ -dao.bond.details.unlockTime=Срок разблокировки в блоках -dao.bond.details.link=Ссылка на описание роли -dao.bond.details.isSingleton=Может быть принято несколькими обладателями роли -dao.bond.details.blocks={0} блока (-ов) - -dao.bond.table.column.name=Имя -dao.bond.table.column.link=Ссылка -dao.bond.table.column.bondType=Тип гарант. депозита -dao.bond.table.column.details=Подробности -dao.bond.table.column.lockupTxId=Идент. транз. блокировки -dao.bond.table.column.bondState=Состояние гарант. депозита -dao.bond.table.column.lockTime=Unlock time -dao.bond.table.column.lockupDate=Дата блокировки - -dao.bond.table.button.lockup=Заблокировать -dao.bond.table.button.unlock=Разблокировать -dao.bond.table.button.revoke=Аннулировать - -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNDEFINED=Не определено -# suppress inspection "UnusedProperty" -dao.bond.bondState.READY_FOR_LOCKUP=Ещё не обеспечено -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_PENDING=В стадии блокировки -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_CONFIRMED=Гарантийный депозит заблокирован -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_PENDING=В стадии разблокировки -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_CONFIRMED=Транз. разблок. подтверждена -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKING=Гарант. депозит в стадии разблокировки -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKED=Гарантийный депозит разблокирован -# suppress inspection "UnusedProperty" -dao.bond.bondState.CONFISCATED=Гарантийный депозит конфискован - -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.UNDEFINED=Не определено -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.BONDED_ROLE=Обеспеченная роль -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.REPUTATION=Обеспеченная репутация - -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.UNDEFINED=Не определено -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Администратор Github -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=Администратор форума -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=Администратор Twitter -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Администратор YouTube -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=Мейнтейнер Bisq -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=Мейнтейнер форка BitcoinJ -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Мейнтейнер сетевого уровня -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=Оператор веб-сайта -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=Оператор форума -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Оператор исходного узла -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Оператор узла данных курса -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Bitcoin node operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Оператор узла данных рынка -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Оператор ретранс. моб. уведомлений -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Владелец имени домена -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DNS_ADMIN=Администратор DNS -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=Посредник -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=Арбитр -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=Владелец адреса BTC для пожертвований - -dao.burnBsq.assetFee=Листинг актива -dao.burnBsq.menuItem.assetFee=Сбор за листинг актива -dao.burnBsq.menuItem.proofOfBurn=Proof of burn -dao.burnBsq.header=Сбор за листинг актива -dao.burnBsq.selectAsset=Выбрать актив -dao.burnBsq.fee=Сбор -dao.burnBsq.trialPeriod=Пробный период -dao.burnBsq.payFee=Оплатить сбор -dao.burnBsq.allAssets=Все активы -dao.burnBsq.assets.nameAndCode=Название актива -dao.burnBsq.assets.state=Состояние -dao.burnBsq.assets.tradeVolume=Объём сделки -dao.burnBsq.assets.lookBackPeriod=Период проверки -dao.burnBsq.assets.trialFee=Сбор за испытательный срок -dao.burnBsq.assets.totalFee=Сумма уплаченного сбора -dao.burnBsq.assets.days={0} дн. -dao.burnBsq.assets.toFewDays=Взнос за листинг актива слишком низок. Мин. срок пробного периода: {0} дн. - -# suppress inspection "UnusedProperty" -dao.assetState.UNDEFINED=Неопределено -# suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=Пробный период -# suppress inspection "UnusedProperty" -dao.assetState.ACTIVELY_TRADED=Активно торгуется -# suppress inspection "UnusedProperty" -dao.assetState.DE_LISTED=Удалён из-за неактивности -# suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=Удалён голосованием - -dao.proofOfBurn.header=Proof of burn -dao.proofOfBurn.amount=Количество -dao.proofOfBurn.preImage=Прообраз -dao.proofOfBurn.burn=Сжечь -dao.proofOfBurn.allTxs=Все транзакции proof of burn -dao.proofOfBurn.myItems=Мои транзакции proof of burn -dao.proofOfBurn.date=Дата -dao.proofOfBurn.hash=Хеш -dao.proofOfBurn.txs=Транзакции -dao.proofOfBurn.pubKey=Публичный ключ -dao.proofOfBurn.signature.window.title=Подписать сообщение ключом из транзакции proof or burn -dao.proofOfBurn.verify.window.title=Подтвердить сообщение ключом из транзакции proof or burn -dao.proofOfBurn.copySig=Скопировать подпись в буфер -dao.proofOfBurn.sign=Подписать -dao.proofOfBurn.message=Сообщение -dao.proofOfBurn.sig=Подпись -dao.proofOfBurn.verify=Подтвердить -dao.proofOfBurn.verificationResult.ok=Подтверждено -dao.proofOfBurn.verificationResult.failed=Проверка не удалась - -# suppress inspection "UnusedProperty" -dao.phase.UNDEFINED=Не определено -# suppress inspection "UnusedProperty" -dao.phase.PROPOSAL=Этап предложения -# suppress inspection "UnusedProperty" -dao.phase.BREAK1=Пауза перед началом этапа слепого голосования -# suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=Этап слепого голосования -# suppress inspection "UnusedProperty" -dao.phase.BREAK2=Пауза перед началом этапа выявления голосов -# suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=Этап выявления голосов -# suppress inspection "UnusedProperty" -dao.phase.BREAK3=Пауза перед публикацией результатов -# suppress inspection "UnusedProperty" -dao.phase.RESULT=Этап результатов голосования - -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.PROPOSAL=Этап предложения -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=Слепое голосование -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.VOTE_REVEAL=Выявление голосов -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.RESULT=Результаты голосования - -# suppress inspection "UnusedProperty" -dao.proposal.type.UNDEFINED=Не определено -# suppress inspection "UnusedProperty" -dao.proposal.type.COMPENSATION_REQUEST=Запрос компенсации -# suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=Запрос на возмещение затрат -# suppress inspection "UnusedProperty" -dao.proposal.type.BONDED_ROLE=Предложение учредить обеспеченную роль -# suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=Предложение удалить актив -# suppress inspection "UnusedProperty" -dao.proposal.type.CHANGE_PARAM=Предложение изменить параметр -# suppress inspection "UnusedProperty" -dao.proposal.type.GENERIC=Общее предложение -# suppress inspection "UnusedProperty" -dao.proposal.type.CONFISCATE_BOND=Предложение о конфискации гарантийного депозита - -# suppress inspection "UnusedProperty" -dao.proposal.type.short.UNDEFINED=Не определено -# suppress inspection "UnusedProperty" -dao.proposal.type.short.COMPENSATION_REQUEST=Запрос компенсации -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=Запрос на возмещение затрат -# suppress inspection "UnusedProperty" -dao.proposal.type.short.BONDED_ROLE=Обеспеченная роль -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ASSET=Удаление альткойна -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CHANGE_PARAM=Изменение параметра -# suppress inspection "UnusedProperty" -dao.proposal.type.short.GENERIC=Общее предложение -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CONFISCATE_BOND=Конфискация гарантийного депозита - -dao.proposal.details=Подробности предложения -dao.proposal.selectedProposal=Избранное предложение -dao.proposal.active.header=Предложения текущего цикла -dao.proposal.active.remove.confirm=Действительно хотите удалить это предложение?\nВзнос за его создание компенсации не подлежит. -dao.proposal.active.remove.doRemove=Да, удалить мое предложение -dao.proposal.active.remove.failed=Не удалось удалить предложение. -dao.proposal.myVote.title=Голосование -dao.proposal.myVote.accept=Принять предложение -dao.proposal.myVote.reject=Отклонить предложение -dao.proposal.myVote.removeMyVote=Игнорировать предложение -dao.proposal.myVote.merit=Вес голоса в зависимости от заработанных BSQ -dao.proposal.myVote.stake=Вес голоса в зависимости от доли -dao.proposal.myVote.revealTxId=Идент. транзакции выявления голоса -dao.proposal.myVote.stake.prompt=Макс. доступная доля для голосования: {0} -dao.proposal.votes.header=Установите долю для голосования и опубликуйте свои голоса -dao.proposal.myVote.button=Опубликовать голоса -dao.proposal.myVote.setStake.description=После голосования по всем предложениям необходимо установить долю для голосования, заблокировав какое-то количество BSQ. Чем больше BSQ вы заблокируете, тем больший вес будет у вашего голоса. \n\nЗаблокированные для голосования BSQ будут разблокированы на этапе выявления голосов. -dao.proposal.create.selectProposalType=Выберите тип предложения -dao.proposal.create.phase.inactive=Просьба дождаться следующего этапа выдвижения предложений -dao.proposal.create.proposalType=Тип предложения -dao.proposal.create.new=Создать новое предложение -dao.proposal.create.button=Создать предложение -dao.proposal.create.publish=Опубликовать предложение -dao.proposal.create.publishing=Публикация предложения... -dao.proposal=предложение -dao.proposal.display.type=Тип предложения -dao.proposal.display.name=Exact GitHub username -dao.proposal.display.link=Ссылка на подробную информацию -dao.proposal.display.link.prompt=Ссылка на предложение -dao.proposal.display.requestedBsq=Запрашиваемая сумма в BSQ -dao.proposal.display.txId=Идент. транзакции предложения -dao.proposal.display.proposalFee=Сбор за предложение -dao.proposal.display.myVote=Мой голос -dao.proposal.display.voteResult=Сводка итогов голосования -dao.proposal.display.bondedRoleComboBox.label=Тип обеспеченной роли -dao.proposal.display.requiredBondForRole.label=Требуемый гарантийный депозит для роли -dao.proposal.display.option=Вариант - -dao.proposal.table.header.proposalType=Тип предложения -dao.proposal.table.header.link=Ссылка -dao.proposal.table.header.myVote=Мой голос -# suppress inspection "UnusedProperty" -dao.proposal.table.header.remove=Удалить -dao.proposal.table.icon.tooltip.removeProposal=Удалить моё предложение -dao.proposal.table.icon.tooltip.changeVote=Текущий голос: «{0}». Изменить на: «{1}» - -dao.proposal.display.myVote.accepted=Принято -dao.proposal.display.myVote.rejected=Отклонено -dao.proposal.display.myVote.ignored=Проигнорировано -dao.proposal.display.myVote.unCounted=Vote was not included in result -dao.proposal.myVote.summary=Voted: {0}; Vote weight: {1} (earned: {2} + stake: {3}) {4} -dao.proposal.myVote.invalid=Голосование было недействительным - -dao.proposal.voteResult.success=Принято -dao.proposal.voteResult.failed=Отклонено -dao.proposal.voteResult.summary=Результат: {0}; порог: {1} (требуется > {2}); кворум: {3} (требуется > {4}) - -dao.proposal.display.paramComboBox.label=Выбрать параметр для изменения -dao.proposal.display.paramValue=Значение параметра - -dao.proposal.display.confiscateBondComboBox.label=Выбрать гарантийный депозит -dao.proposal.display.assetComboBox.label=Актив для удаления - -dao.blindVote=слепое голосование - -dao.blindVote.startPublishing=Публикация транзакции слепого голосования... -dao.blindVote.success=Ваша транзакция слепого голосованию успешно опубликована.\n\nОбратите внимание, что вы должны находиться в сети на стадии выявления голосов, для того чтобы приложение Bisq смогло опубликовать транзакцию выявления вашего голоса. Без этого ваш голос не будет засчитан! - -dao.wallet.menuItem.send=Отправить -dao.wallet.menuItem.receive=Получить -dao.wallet.menuItem.transactions=Транзакции - -dao.wallet.dashboard.myBalance=Баланс моего кошелька - -dao.wallet.receive.fundYourWallet=Ваш адрес BSQ -dao.wallet.receive.bsqAddress=Адрес кошелька BSQ (новый неиспользованный адрес) - -dao.wallet.send.sendFunds=Отправить средства -dao.wallet.send.sendBtcFunds=Отправить средства (BTC), исключая BSQ -dao.wallet.send.amount=Сумма в BSQ -dao.wallet.send.btcAmount=Сумма в ВТС (исключая BSQ) -dao.wallet.send.setAmount=Установленная сумма для вывода (мин. сумма равна {0}) -dao.wallet.send.receiverAddress=BSQ-адрес получателя -dao.wallet.send.receiverBtcAddress=BTC-адрес получателя -dao.wallet.send.setDestinationAddress=Укажите адрес получателя -dao.wallet.send.send=Отправить BSQ -dao.wallet.send.inputControl=Select inputs -dao.wallet.send.sendBtc=Отправить BTC -dao.wallet.send.sendFunds.headline=Подтвердите запрос на вывод средств -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=Тип - -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED=Не определено -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED_TX_TYPE=Не признано -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNVERIFIED=Непроверенная транзакция в BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.INVALID=Недействительная транзакция в BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.GENESIS=Первичная транзакция -# suppress inspection "UnusedProperty" -dao.tx.type.enum.TRANSFER_BSQ=Перевести BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.received.TRANSFER_BSQ=Полученные BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.sent.TRANSFER_BSQ=Отправленные BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PAY_TRADE_FEE=Торговый сбор -# suppress inspection "UnusedProperty" -dao.tx.type.enum.COMPENSATION_REQUEST=Сбор за запрос компенсации -# suppress inspection "UnusedProperty" -dao.tx.type.enum.REIMBURSEMENT_REQUEST=Сбор за запрос на возмещение затрат -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROPOSAL=Сбор за предложение -# suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=Сбор за голосование вслепую -# suppress inspection "UnusedProperty" -dao.tx.type.enum.VOTE_REVEAL=Выявление голосов -# suppress inspection "UnusedProperty" -dao.tx.type.enum.LOCKUP=Заблокировать гарантийный депозит -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNLOCK=Разблокировать гарантийный депозит -# suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=Сбор за размещение актива -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=Proof of burn -# suppress inspection "UnusedProperty" -dao.tx.type.enum.IRREGULAR=Неправильные - -dao.tx.withdrawnFromWallet=BTC выведен из кошелька -dao.tx.issuanceFromCompReq=Запрос/выдача компенсации -dao.tx.issuanceFromCompReq.tooltip=Запрос компенсации, который привел к эмиссии новых BSQ.\nДата эмиссии: {0} -dao.tx.issuanceFromReimbursement=Запрос/выдача возмещения -dao.tx.issuanceFromReimbursement.tooltip=Запрос на возмещение затрат, который привел к эмиссии новых BSQ.\nДата эмиссии: {0} -dao.proposal.create.missingBsqFunds=У вас недостаточно BSQ для создания предложения. Если у вас есть неподтвержденная транзакция в BSQ, необходимо дождаться подтверждения в блокчейне, так как подтверждение операций в BSQ происходит только после включения транзакции в блок.\nНе хватает: {0} - -dao.proposal.create.missingBsqFundsForBond=У вас недостаточно BSQ для этой роли. Вы все же можете опубликовать это предложение, но вам понадобится полная сумма в BSQ, необходимая для этой роли, если оно будет принято.\nНе хватает: {0} - -dao.proposal.create.missingMinerFeeFunds=У вас недостаточно BTC для создания предложения. Любая транзакция в BSQ требует оплаты комиссии майнера в ВТС.\nНе хватает {0} - -dao.proposal.create.missingIssuanceFunds=У вас недостаточно BTC для создания предложения. Любая транзакция в BSQ требует оплаты комиссии в ВТС. Все транзакции, в том числе связанные с эмиссией BSQ, для выплаты запрошенной суммы BSQ требуют оплаты комиссии в ВТС ({0} сатоши/BSQ).\nНе хватает: {1} - -dao.feeTx.confirm=Подтвердить транзакцию {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.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.news.bisqDAO.title=ДАО BISQ -dao.news.bisqDAO.description=Модель управления Bisq так же децентрализована и защищена от цензуры, как и сама биржа Bisq. Это возможно благодаря ДАО Bisq и токену BSQ. -dao.news.bisqDAO.readMoreLink=Подробнее о ДАО Bisq - -dao.news.pastContribution.title=ПОМОГАЛИ НАМ В ПРОШЛОМ? ЗАПРОСИТЕ BSQ -dao.news.pastContribution.description=Если вы помогли Bisq в прошлом, используйте свой адрес BSQ ниже и запросите участие в первоначальном распределении BSQ. -dao.news.pastContribution.yourAddress=Адрес вашего кошелька BSQ -dao.news.pastContribution.requestNow=Запросить - -dao.news.DAOOnTestnet.title=ЗАПУСТИТЬ ДАО BISQ В НАШЕЙ ТЕСТОВОЙ СЕТИ -dao.news.DAOOnTestnet.description=Основная сеть ДАО Bisq еще не запущена, но вы можете узнать о ней подробнее, запустив ДАО в тестовой сети. -dao.news.DAOOnTestnet.firstSection.title=1. Переключиться в режим тестовой сети ДАО -dao.news.DAOOnTestnet.firstSection.content=Переключитесь на тестовую сеть ДАО в настройках. -dao.news.DAOOnTestnet.secondSection.title=2. Приобрести BSQ -dao.news.DAOOnTestnet.secondSection.content=Запросите BSQ в Slack или купите BSQ в Bisq. -dao.news.DAOOnTestnet.thirdSection.title=3. Принять участие в цикле голосования -dao.news.DAOOnTestnet.thirdSection.content=Вносите предложения и голосуйте по предложениям об изменении различных аспектов Bisq. -dao.news.DAOOnTestnet.fourthSection.title=4. Ознакомиться с обозревателем блоков BSQ -dao.news.DAOOnTestnet.fourthSection.content=Поскольку BSQ — это тоже биткойн, транзакции BSQ видны в нашем обозревателе блоков Биткойн. -dao.news.DAOOnTestnet.readMoreLink=Ознакомиться с полной документацией - -dao.monitor.daoState=Состояние ДАО -dao.monitor.proposals=Состояние выдвижения предложений -dao.monitor.blindVotes=Состояние слепого голосования - -dao.monitor.table.peers=Пиры -dao.monitor.table.conflicts=Конфликты -dao.monitor.state=Статус -dao.monitor.requestAlHashes=Запросить все хеши -dao.monitor.resync=Повт. синхр. сост. ДАО -dao.monitor.table.header.cycleBlockHeight=Номер блока/цикла -dao.monitor.table.cycleBlockHeight=Цикл {0} / блок {1} -dao.monitor.table.seedPeers=Исходный узел: {0} - -dao.monitor.daoState.headline=Состояние ДАО -dao.monitor.daoState.table.headline=Цепочка хешей состояния ДАО -dao.monitor.daoState.table.blockHeight=Номер блока -dao.monitor.daoState.table.hash=Хеш состояния ДАО -dao.monitor.daoState.table.prev=Предыдущий хеш -dao.monitor.daoState.conflictTable.headline=Хеши состояния ДАО от конфликтующих пиров -dao.monitor.daoState.utxoConflicts=Конфликты UTXO -dao.monitor.daoState.utxoConflicts.blockHeight=Номер блока: {0} -dao.monitor.daoState.utxoConflicts.sumUtxo=Сумма всех UTXO: {0} BSQ -dao.monitor.daoState.utxoConflicts.sumBsq=Сумма всех BSQ: {0} BSQ -dao.monitor.daoState.checkpoint.popup=DAO state is not in sync with the network. After restart the DAO state will resync. - -dao.monitor.proposal.headline=Состояние выдвижения предложений -dao.monitor.proposal.table.headline=Цепочка хешей состояния выдвижения предложений -dao.monitor.proposal.conflictTable.headline=Хеши состояния выдвижения предложений от конфликтующих пиров - -dao.monitor.proposal.table.hash=Хеш состояния выдвижения предложений -dao.monitor.proposal.table.prev=Предыдущий хеш -dao.monitor.proposal.table.numProposals=Кол-во предложений - -dao.monitor.isInConflictWithSeedNode=Данные на вашем компьютере не достигли состояния консенсуса по крайней мере с одним исходным узлом. Необходима повторная синхронизация состояния ДАО. -dao.monitor.isInConflictWithNonSeedNode=Один из ваших пиров не достиг состояния консенсуса с сетью, однако ваш узел синхронизирован с исходными узлами. -dao.monitor.daoStateInSync=Узел на вашем компьютере находится в состоянии консенсуса с сетью - -dao.monitor.blindVote.headline=Состояние слепого голосования -dao.monitor.blindVote.table.headline=Цепочка хешей состояния слепого голосования -dao.monitor.blindVote.conflictTable.headline=Хеши состояния слепого голосования от конфликтующих пиров -dao.monitor.blindVote.table.hash=Хеш состояния слепого голосования -dao.monitor.blindVote.table.prev=Предыдущий хеш -dao.monitor.blindVote.table.numBlindVotes=Кол-во слепых голосов - -dao.factsAndFigures.menuItem.supply=Предложение BSQ -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 BSQ/USD price -dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on 30 days average BSQ/USD price) -dao.factsAndFigures.dashboard.availableAmount=Доступное количество BSQ -dao.factsAndFigures.dashboard.volumeUsd=Total trade volume in USD -dao.factsAndFigures.dashboard.volumeBtc=Total trade volume in BTC -dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection=Average BSQ/USD trade price from selected time period in chart -dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection=Average BSQ/BTC trade price from selected time period in chart - -dao.factsAndFigures.supply.issuedVsBurnt=BSQ issued v. BSQ burnt - -dao.factsAndFigures.supply.issued=Эмиссия BSQ -dao.factsAndFigures.supply.compReq=Запросы компенсации -dao.factsAndFigures.supply.reimbursement=Reimbursement requests -dao.factsAndFigures.supply.genesisIssueAmount=Эмиссия BSQ в первичной транзакции -dao.factsAndFigures.supply.compRequestIssueAmount=Эмиссия BSQ в качестве компенсации -dao.factsAndFigures.supply.reimbursementAmount=Эмиссия BSQ в качестве возмещения затрат -dao.factsAndFigures.supply.totalIssued=Total issued BSQ -dao.factsAndFigures.supply.totalBurned=Total burned BSQ -dao.factsAndFigures.supply.chart.tradeFee.toolTip={0}\n{1} -dao.factsAndFigures.supply.burnt=Выведено из обращения BSQ - -dao.factsAndFigures.supply.priceChat=BSQ price -dao.factsAndFigures.supply.volumeChat=Объём сделки -dao.factsAndFigures.supply.tradeVolumeInUsd=Trade volume in USD -dao.factsAndFigures.supply.tradeVolumeInBtc=Trade volume in BTC -dao.factsAndFigures.supply.bsqUsdPrice=BSQ/USD price -dao.factsAndFigures.supply.bsqBtcPrice=BSQ/BTC price -dao.factsAndFigures.supply.btcUsdPrice=BTC/USD price - -dao.factsAndFigures.supply.locked=Глобальное состояние заблокированных BSQ -dao.factsAndFigures.supply.totalLockedUpAmount=Заблокировано в гарантийных депозитах -dao.factsAndFigures.supply.totalUnlockingAmount=Разблокировка BSQ из гарантийных депозитов -dao.factsAndFigures.supply.totalUnlockedAmount=Разблокировано BSQ из гарантийных депозитов -dao.factsAndFigures.supply.totalConfiscatedAmount=Конфисковано BSQ из гарантийных депозитов -dao.factsAndFigures.supply.proofOfBurn=Proof of Burn -dao.factsAndFigures.supply.bsqTradeFee=BSQ Trade fees -dao.factsAndFigures.supply.btcTradeFee=BTC Trade fees - -dao.factsAndFigures.transactions.genesis=Первичная транзакция -dao.factsAndFigures.transactions.genesisBlockHeight=Номер первичного блока -dao.factsAndFigures.transactions.genesisTxId=Идент. первичной транзакции -dao.factsAndFigures.transactions.txDetails=Статистика транзакций BSQ -dao.factsAndFigures.transactions.allTx=Общее кол-во транзакций BSQ -dao.factsAndFigures.transactions.utxo=Общее кол-во неизрасходованных выводов (UTXO) -dao.factsAndFigures.transactions.compensationIssuanceTx=Общее кол-во транзакций, связанных с эмиссией BSQ для выплаты компенсаций -dao.factsAndFigures.transactions.reimbursementIssuanceTx=Общее кол-во транзакций, связанных с эмиссией BSQ для возмещения затрат -dao.factsAndFigures.transactions.burntTx=Общее кол-во транзакций, связанных с оплатой комиссий -dao.factsAndFigures.transactions.invalidTx=Кол-во недействительных транзакций -dao.factsAndFigures.transactions.irregularTx=Кол-во неправильных транзакций - - - #################################################################### # Windows #################################################################### @@ -2120,8 +1406,6 @@ disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any emptyWalletWindow.headline=Аварийный кошелёк {0} emptyWalletWindow.info=Используйте этот инструмент только в экстренном случае, если вам недоступны средства из пользовательского интерфейса.\n\nУчтите, что все открытые предложения будут автоматически закрыты при использовании этого инструмента.\n\nПрежде чем воспользоваться этим инструментом, создайте резервную копию своего каталога данных. Это можно сделать в разделе \«Счёт/Резервное копирование\».\n\nСообщите нам о неисправности и создайте отчёт о ней в Github или на форуме Bisq, чтобы мы могли выявить её причину. emptyWalletWindow.balance=Доступный баланс кошелька -emptyWalletWindow.bsq.btcBalance=Баланс в сатоши (без учёта BSQ) - emptyWalletWindow.address=Адрес получателя emptyWalletWindow.button=Отправить все средства emptyWalletWindow.openOffers.warn=У вас есть открытые предложения, которые будут удалены, если вы выведите все средства с кошелька.\nВывести все средства? @@ -2146,10 +1430,8 @@ filterWindow.seedNode=Отфильтрованные исходные узлы ( filterWindow.priceRelayNode=Отфильтрованные ретрансляторы курса (onion-адреса через запят.) filterWindow.btcNode=Отфильтрованные узлы Биткойн (адреса + порты через запят.) filterWindow.preventPublicBtcNetwork=Не использовать общедоступную сеть Биткойн -filterWindow.disableDao=Отключить ДАО filterWindow.disableAutoConf=Disable auto-confirm filterWindow.autoConfExplorers=Filtered auto-confirm explorers (comma sep. addresses) -filterWindow.disableDaoBelowVersion=Мин. версия, необходимая для работы с ДАО filterWindow.disableTradeBelowVersion=Мин. версия, необходимая для торговли filterWindow.add=Добавить фильтр filterWindow.remove=Удалить фильтр @@ -2223,7 +1505,6 @@ tradeDetailsWindow.detailData=Detail data txDetailsWindow.headline=Transaction Details txDetailsWindow.btc.note=You have sent BTC. -txDetailsWindow.bsq.note=You have sent BSQ funds. BSQ is colored bitcoin, so the transaction will not show in a BSQ explorer until it has been confirmed in a bitcoin block. txDetailsWindow.sentTo=Sent to txDetailsWindow.txId=TxId @@ -2235,9 +1516,6 @@ closedTradesSummaryWindow.totalMinerFee.title=Sum of all miner fees closedTradesSummaryWindow.totalMinerFee.value={0} ({1} of total trade amount) closedTradesSummaryWindow.totalTradeFeeInBtc.title=Sum of all trade fees paid in BTC closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} of total trade amount) -closedTradesSummaryWindow.totalTradeFeeInBsq.title=Sum of all trade fees paid in BSQ -closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} of total trade amount) - walletPasswordWindow.headline=Введите пароль для разблокировки torNetworkSettingWindow.header=Настройки сети Тоr @@ -2317,12 +1595,6 @@ popup.warning.tooLargePercentageValue=Нельзя установить проц popup.warning.examplePercentageValue=Введите процент, например \«5,4\» для 5,4% popup.warning.noPriceFeedAvailable=Источник рыночного курса для этой валюты отсутствует. Невозможно использовать процентный курс.\nПросьба указать фиксированный курс. popup.warning.sendMsgFailed=Не удалось отправить сообщение вашему контрагенту .\nПопробуйте еще раз, и если неисправность повторится, сообщите о ней. -popup.warning.insufficientBtcFundsForBsqTx=У вас недостаточно BTC для оплаты комиссии майнера за эту транзакцию.\nПополните свой кошелек BTC.\nНе хватает: {0} -popup.warning.bsqChangeBelowDustException=This transaction creates a BSQ change output which is below dust limit (5.46 BSQ) and would be rejected by the Bitcoin network.\n\nYou need to either send a higher amount to avoid the change output (e.g. by adding the dust amount to your sending amount) or add more BSQ funds to your wallet so you avoid to generate a dust output.\n\nThe dust output is {0}. -popup.warning.btcChangeBelowDustException=This transaction creates a change output which is below dust limit (546 Satoshi) and would be rejected by the Bitcoin network.\n\nYou need to add the dust amount to your sending amount to avoid to generate a dust output.\n\nThe dust output is {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.noBsqFundsForBtcFeePayment=В вашем кошельке BSQ недостаточно средств для оплаты комиссии за сделку в BSQ. popup.warning.messageTooLong=Ваше сообщение превышает макс. разрешённый размер. Разбейте его на несколько частей или загрузите в веб-приложение для работы с отрывками текста, например https://pastebin.com. popup.warning.lockedUpFunds=You have locked up funds from a failed trade.\nLocked up balance: {0} \nDeposit tx address: {1}\nTrade ID: {2}.\n\nPlease open a support ticket by selecting the trade in the open trades screen and pressing \"alt + o\" or \"option + o\"." @@ -2336,8 +1608,6 @@ popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=ретранслятор курса popup.warning.seed=мнемоническая фраза popup.warning.mandatoryUpdate.trading=Обновите Bisq до последней версии. Вышло обязательное обновление, которое делает невозможной торговлю в старых версиях приложения. Посетите форум Bisq, чтобы узнать подробности. -popup.warning.mandatoryUpdate.dao=Обновите Bisq до последней версии. Вышло обязательное обновление, которое делает невозможным участие в ДАО Bisq и торговлю BSQ в старых версиях приложения. Посетите форум Bisq, чтобы узнать подробности. -popup.warning.disable.dao=ДАО Bisq и торговля BSQ временно недоступны. Посетите форум Bisq, чтобы узнать подробности. popup.warning.noFilter=We did not receive a filter object from the seed nodes. This is a not expected situation. Please inform the Bisq developers. popup.warning.burnBTC=Данную транзакцию невозможно завершить, так как плата за нее ({0}) превышает сумму перевода ({1}). Подождите, пока плата за транзакцию не снизится или пока у вас не появится больше BTC для завершения перевода. @@ -2356,7 +1626,6 @@ popup.info.cashDepositInfo.confirm=Я подтверждаю, что могу в popup.info.shutDownWithOpenOffers=Bisq закрывается, но у вас есть открытые предложения.\n\nЭти предложения будут недоступны в сети P2P, пока приложение Bisq закрыто, но будут повторно опубликованы в сети P2P при следующем запуске Bisq.\n\nЧтобы ваши предложения были доступны в сети, компьютер и приложение должны быть включены и подключены к сети (убедитесь, что компьютер не перешёл в режим ожидания; переход монитора в спящий режим не влияет на работу приложения). 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=Важное личное уведомление! @@ -2528,7 +1797,6 @@ navigation.settings.preferences=\«Настройки/Параметры\» # suppress inspection "UnusedProperty" navigation.funds.transactions=\«Средства/Транзакции\» navigation.support=\«Поддержка\» -navigation.dao.wallet.receive=\«ДАО/BSQ-кошелёк/Получить\» #################################################################### @@ -2558,12 +1826,6 @@ XMR_MAINNET=XMR Mainnet XMR_TESTNET=XMR Testnet # suppress inspection "UnusedProperty" XMR_STAGENET=XMR Stagenet -# suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Тестовая сеть ДАО Биткойн (устаревшая) -# suppress inspection "UnusedProperty" -BTC_DAO_BETANET=Bisq DAO Betanet (Bitcoin Mainnet) -# suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=Режим регрессионного тестирования ДАО Биткойн time.year=Год time.month=Месяц @@ -2910,9 +2172,7 @@ validation.accountNrChars=Номер счёта должен состоять и validation.btc.invalidAddress=Неправильный адрес. Проверьте формат адреса. validation.integerOnly=Введите только целые числа. validation.inputError=Введённое значение вызвало ошибку:\n{0} -validation.bsq.insufficientBalance=Ваш доступный баланс составляет {0}. validation.btc.exceedsMaxTradeLimit=Ваш торговый лимит составляет {0}. -validation.bsq.amountBelowMinAmount=Мин. сумма {0} validation.nationalAccountId={0} должен состоять из {1} цифр. #new @@ -2934,7 +2194,6 @@ validation.bic.invalidLocationCode=BIC содержит недействител validation.bic.invalidBranchCode=BIC содержит недействительный код отделения validation.bic.sepaRevolutBic=Счета SEPA в Revolut не поддерживаются. validation.btc.invalidFormat=Invalid format for a Bitcoin address. -validation.bsq.invalidFormat=Invalid format for a BSQ address. validation.email.invalidAddress=Недействительный адрес validation.iban.invalidCountryCode=Код страны недействителен validation.iban.checkSumNotNumeric=Контрольная сумма должна иметь числовой формат diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index 7f72235543..467c367ce8 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -192,7 +192,6 @@ shared.tradeWalletBalance=ยอดคงเหลือของ Trade wallet shared.makerTxFee=ผู้ทำ: {0} shared.takerTxFee=ผู้รับ: {0} shared.iConfirm=ฉันยืนยัน -shared.tradingFeeInBsqInfo=≈ {0} shared.openURL=เปิด {0} shared.fiat=คำสั่ง shared.crypto=คริปโต @@ -204,9 +203,6 @@ shared.actions=การปฏิบัติการ shared.buyerUpperCase=ผู้ซื้อ shared.sellerUpperCase=ผู้ขาย shared.new=NEW -shared.blindVoteTxId=ID การทำธุรกรรมการลงคะแนนเสียงแบบไม่ระบุตัวตน -shared.proposal=คำขอ -shared.votes=โหวต shared.learnMore=Learn more shared.dismiss=Dismiss shared.selectedArbitrator=ผู้ไกล่เกลี่ยที่ได้รับการแต่งตั้ง @@ -240,7 +236,6 @@ mainView.menu.funds=เงิน mainView.menu.support=สนับสนุน mainView.menu.settings=ตั้งค่า mainView.menu.account=บัญชี -mainView.menu.dao=DAO mainView.marketPriceWithProvider.label=ราคาตลาดโดย {0} mainView.marketPrice.bisqInternalPrice=ราคาของการซื้อขาย Bisq ล่าสุด @@ -257,13 +252,11 @@ mainView.footer.localhostBitcoinNode=(แม่ข่ายเฉพาะที mainView.footer.btcInfo={0} {1} mainView.footer.btcFeeRate=/ Fee rate: {0} sat/vB mainView.footer.btcInfo.initializing=Connecting to Bitcoin network -mainView.footer.bsqInfo.synchronizing=/ Synchronizing DAO 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=Connecting to mainView.footer.btcInfo.connectionFailed=Connection failed to mainView.footer.p2pInfo=Bitcoin network peers: {0} / Bisq network peers: {1} -mainView.footer.daoFullNode=DAO full node mainView.bootstrapState.connectionToTorNetwork=(1/4) เชื่อมต่อไปยัง Tor network... mainView.bootstrapState.torNodeCreated=(2/4) Tor node ถูกสร้างแล้ว @@ -435,7 +428,6 @@ createOffer.fundsBox.networkFee=ค่าธรรมเนียมการข createOffer.fundsBox.placeOfferSpinnerInfo=การประกาศข้อเสนออยู่ระหว่างดำเนินการ ... createOffer.fundsBox.paymentLabel=การซื้อขาย Bisq ด้วย ID {0} createOffer.fundsBox.fundsStructure=({0} เงินประกัน {1} ค่าธรรมเนียมการซื้อขาย {2} ค่าธรรมเนียมการขุด) -createOffer.fundsBox.fundsStructure.BSQ=({0} security deposit, {1} mining fee) + {2} trade fee createOffer.success.headline=ข้อเสนอของคุณได้รับการเผยแพร่แล้ว createOffer.success.info=คุณสามารถจัดการข้อเสนอแบบเปิดของคุณได้ที่ \"Portfolio (แฟ้มผลงาน) / My open offers (ข้อเสนอแบบเปิดของฉัน) \" createOffer.info.sellAtMarketPrice=คุณจะขายในราคาตลาดเสมอ เนื่องจากราคาข้อเสนอของคุณจะได้รับการอัพเดตอย่างต่อเนื่อง @@ -636,7 +628,6 @@ portfolio.pending.step2_buyer.amountToTransfer=จำนวนเงินที portfolio.pending.step2_buyer.sellersAddress=ที่อยู่ของผู้ขาย {0} portfolio.pending.step2_buyer.buyerAccount=บัญชีการชำระเงินที่ต้องการใข้งาน portfolio.pending.step2_buyer.paymentStarted=การชำระเงินเริ่มต้นแล้ว -portfolio.pending.step2_buyer.fillInBsqWallet=Pay from BSQ wallet portfolio.pending.step2_buyer.warn=You still have not done your {0} payment!\nPlease note that the trade has to be completed by {1}. portfolio.pending.step2_buyer.openForDispute=You have not completed your payment!\nThe max. period for the trade has elapsed.Please contact the mediator for assistance. portfolio.pending.step2_buyer.paperReceipt.headline=คุณได้ส่งใบเสร็จรับเงินให้กับผู้ขาย BTC หรือไม่? @@ -882,7 +873,6 @@ funds.locked.locked=ถูกล็อคใน multisig สำหรับก funds.tx.direction.sentTo=ส่งไปยัง: funds.tx.direction.receivedWith=ได้รับโดย: funds.tx.direction.genesisTx=จากการทำธุรกรรมทั่วไป : -funds.tx.txFeePaymentForBsqTx=ค่าธรรมเนียมของนักขุดบิทคอยน์สำหรับการทำธุรกรรม BSQ funds.tx.createOfferFee=ผู้สร้างและค่าธรรมเนียมการทำธุรกรรม: {0} funds.tx.takeOfferFee=ค่าธรรมเนียมของผู้รับและการทำธุรกรรม: {0} funds.tx.multiSigDeposit=เงินฝาก Multisig (การรองรับหลายลายเซ็น): {0} @@ -896,15 +886,11 @@ funds.tx.unknown=เหตุผลที่ไม่ระบุ: {0} funds.tx.noFundsFromDispute=ไม่มีการคืนเงินจากการพิพาท funds.tx.receivedFunds=เงินที่ได้รับ funds.tx.withdrawnFromWallet=ถอนออกจาก wallet -funds.tx.withdrawnFromBSQWallet=BTC withdrawn from BSQ wallet funds.tx.memo=Memo funds.tx.noTxAvailable=ไม่มีธุรกรรมใด ๆ funds.tx.revert=กลับสู่สภาพเดิม funds.tx.txSent=ธุรกรรมถูกส่งสำเร็จไปยังที่อยู่ใหม่ใน Bisq wallet ท้องถิ่นแล้ว funds.tx.direction.self=ส่งถึงตัวคุณเอง -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. @@ -996,9 +982,7 @@ settings.tab.about=เกี่ยวกับ setting.preferences.general=การตั้งค่าทั่วไป setting.preferences.explorer=Bitcoin Explorer -setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=สูงสุด ส่วนเบี่ยงเบนจากราคาตลาด -setting.preferences.bsqAverageTrimThreshold=Outlier threshold for BSQ rate setting.preferences.avoidStandbyMode=หลีกเลี่ยงโหมดแสตนบายด์ setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled @@ -1032,19 +1016,6 @@ setting.preferences.notifyOnPreRelease=Receive pre-release notifications setting.preferences.resetAllFlags=รีเซ็ตทั้งหมด \"ไม่ต้องแสดงอีกครั้ง \" ปักธง settings.preferences.languageChange=หากต้องการเปลี่ยนภาษากับทุกหน้าต้องทำการรีสตาร์ท settings.preferences.supportLanguageWarning=In case of a dispute, please note that mediation is handled in {0} and arbitration in {1}. -setting.preferences.daoOptions=ตัวเลือก DAO -setting.preferences.dao.resyncFromGenesis.label=สร้างสถานะ DAO ใหม่จากธุรกรรมต้นกำเนิด -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.resync=Resync from genesis and shutdown -setting.preferences.dao.isDaoFullNode=ใช้งาน Bisq ในแบบโหนด DAO full node -setting.preferences.dao.rpcUser=ชื่อผู้ใช้ RPC -setting.preferences.dao.rpcPw=รหัส RPC -setting.preferences.dao.blockNotifyPort=Block notify port -setting.preferences.dao.fullNodeInfo=For running Bisq as DAO full node you need to have Bitcoin Core locally running and RPC enabled. All requirements are documented in ''{0}''.\n\nAfter changing the mode you need to restart. -setting.preferences.dao.fullNodeInfo.ok=เปิดหน้าเอกสาร -setting.preferences.dao.fullNodeInfo.cancel=ไม่ ฉันติดกับไลท์โหนดโหมด (lite node mode) settings.preferences.editCustomExplorer.headline=Explorer Settings settings.preferences.editCustomExplorer.description=Choose a system defined explorer from the list on the left, and/or customize to suit your own preferences. settings.preferences.editCustomExplorer.available=Available explorers @@ -1144,14 +1115,10 @@ setting.about.shortcuts.walletDetails=Open wallet details window setting.about.shortcuts.openEmergencyBtcWalletTool=Open emergency wallet tool for BTC wallet -setting.about.shortcuts.openEmergencyBsqWalletTool=Open emergency wallet tool for BSQ wallet - setting.about.shortcuts.showTorLogs=Toggle log level for Tor messages between DEBUG and WARN setting.about.shortcuts.manualPayoutTxWindow=Open window for manual payout from 2of2 Multisig deposit tx -setting.about.shortcuts.reRepublishAllGovernanceData=Republish DAO governance data (proposals, votes) - setting.about.shortcuts.removeStuckTrade=Open popup to move failed trade to open trades tab again setting.about.shortcuts.removeStuckTrade.value=Select failed trade and press: {0} @@ -1337,687 +1304,6 @@ account.notifications.noWebCamFound.warning=ไม่พบเว็บแคม account.notifications.priceAlert.warning.highPriceTooLow=ราคาที่สูงกว่าต้องเป็นจำนวนที่มากเหนือราคาที่ต่ำกว่า account.notifications.priceAlert.warning.lowerPriceTooHigh=ราคาที่ต่ำกว่าต้องต่ำกว่าราคาที่สูงขึ้น - - - -#################################################################### -# DAO -#################################################################### - -dao.tab.factsAndFigures=Facts & Figures -dao.tab.bsqWallet=กระเป๋าสตางค์ BSQ -dao.tab.proposals=การกำกับดูแลกิจการ -dao.tab.bonding=ค้ำประกัน -dao.tab.proofOfBurn=ค่าธรรมเนียมในการลงบันทึกรายการทรัพย์สิน/หลักฐานในการทำลายทิ้ง -dao.tab.monitor=Network monitor -dao.tab.news=News - -dao.paidWithBsq=จ่ายโดย BSQ -dao.availableBsqBalance=Available for spending (verified + unconfirmed change outputs) -dao.verifiedBsqBalance=Balance of all verified UTXOs -dao.unconfirmedChangeBalance=Balance of all unconfirmed change outputs -dao.unverifiedBsqBalance=Balance of all unverified transactions (awaiting block confirmation) -dao.lockedForVoteBalance=ถูกใช้สำหรับการโหวต -dao.lockedInBonds=ถูกล็อคไว้ในการค้ำประกัน -dao.availableNonBsqBalance=ยอดคงเหลือที่ไม่ใช่ BSQ (BTC) ซึ่งใช้งานได้ -dao.reputationBalance=Merit Value (not spendable) - -dao.tx.published.success=การทำธุรกรรมของคุณได้รับการเผยแพร่เรียบร้อยแล้ว -dao.proposal.menuItem.make=เสนอคำขอ -dao.proposal.menuItem.browse=เรียกดูข้อเสนอที่เปิด -dao.proposal.menuItem.vote=โหวตข้อเสนอ -dao.proposal.menuItem.result=ผลโหวต -dao.cycle.headline=รอบการลงคะแนนเสียง -dao.cycle.overview.headline=ภาพรวมรอบการโหวด -dao.cycle.currentPhase=ระยะปัจจุบัน -dao.cycle.currentBlockHeight=ความสูงของบล็อกปัจจุบัน -dao.cycle.proposal=ระยะเสนอ -dao.cycle.proposal.next=Next proposal phase -dao.cycle.blindVote=ขั้นตอนการลงคะแนนเสียงแบบไม่ระบุตัวตน -dao.cycle.voteReveal=ขั้นตอนการประกาศผลโหวต -dao.cycle.voteResult=ผลโหวต -dao.cycle.phaseDuration={0} บล็อก (≈{1}); บล็อก {2} - {3} (≈{4} - ≈{5}) -dao.cycle.phaseDurationWithoutBlocks=Block {0} - {1} (≈{2} - ≈{3}) - -dao.voteReveal.txPublished.headLine=Vote reveal transaction published -dao.voteReveal.txPublished=Your vote reveal transaction with transaction ID {0} was successfully published.\n\nThis happens automatically by the software if you have participated in the DAO voting. - -dao.results.cycles.header=รอบ -dao.results.cycles.table.header.cycle=วงจร -dao.results.cycles.table.header.numProposals=คำขอ -dao.results.cycles.table.header.voteWeight=น้ำหนักโหวต -dao.results.cycles.table.header.issuance=การออกหุ้น - -dao.results.results.table.item.cycle=วงจร {0} เริ่มต้น: {1} - -dao.results.proposals.header=ข้อเสนอของวงจรที่เลือก -dao.results.proposals.table.header.nameLink=Name/link -dao.results.proposals.table.header.details=รายละเอียด -dao.results.proposals.table.header.myVote=การลงคะแนนของฉัน -dao.results.proposals.table.header.result=ผลโหวต -dao.results.proposals.table.header.threshold=Threshold -dao.results.proposals.table.header.quorum=Quorum - -dao.results.proposals.voting.detail.header=ผลโหวตสำหรับข้อเสนอที่เลือก - -dao.results.exceptions=Vote result exception(s) - -# suppress inspection "UnusedProperty" -dao.param.UNDEFINED=ไม่ได้กำหนด - -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BSQ=ค่าธรรมเนียมของผู้สร้าง BSQ -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BSQ=ค่าธรรมเนียมของผู้รับ BSQ -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BSQ=ค่าธรรมเนียมของผู้สร้าง BSQ ขั้นต่ำ -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BSQ=ค่าธรรมเนียมของผู้รับ BSQ ขั้นต่ำ -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BTC=ค่าธรรมเนียมผู้สร้าง BTC -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BTC=ค่าธรรมเนียมผู้รับ BTC -# suppress inspection "UnusedProperty" -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BTC=ค่าธรรมเนียมของผู้สร้าง BTC ขั้นต่ำ -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BTC=ค่าธรรมเนียมของผู้รับ BTC ขั้นต่ำ -# suppress inspection "UnusedProperty" - -# suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=ค่าธรรมเนียมข้อเสนอ BSQ -# suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=ค่าธรรมเนียมในการโหวต BSQ - -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=ยอดรวม BSQ ขั้นต่ำในการยื่นคำร้องขอค่าชดเชย -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=ยอดรวม BSQ ขั้นสูงสุดในการยื่นคำร้องขอค่าชดเชย -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MIN_AMOUNT=ยอดรวม BSQ ขั้นต่ำในการยื่นคำร้องขอการชำระเงินคืน -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MAX_AMOUNT=ยอดรวม BSQ ขั้นสูงสุดในการยื่นคำร้องขอการชำระเงินคืน - -# suppress inspection "UnusedProperty" -dao.param.QUORUM_GENERIC=องค์ประกอบที่จำเป็นสำหรับข้อเสนอทั่วไปใน BSQ -# suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=องค์ประกอบที่จำเป็นสำหรับค่าชดเชยใน BSQ -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REIMBURSEMENT=องค์ประกอบที่จำเป็นสำหรับการชำระเงินคืนใน BSQ -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=องค์ประกอบที่จำเป็นสำหรับการเปลี่ยนพารามิเตอร์ใน BSQ -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=องค์ประกอบที่จำเป็นสำหรับในการในถอดถอนทรัพย์สินใน BSQ -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=องค์ประกอบที่จำเป็นสำหรับในการยื่นคำร้องต่อการยึดทรัพย์ใน BSQ -# suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=องค์ประกอบที่จำเป็นสำหรับใน BSQ ในการยื่นคำขอรับประกัน - -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_GENERIC=องค์ประกอบของเทรสโฮลด์คิดเป็น % สำหรับข้อเสนอทั่วไป -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=องค์ประกอบของเทรสโฮลด์คิดเป็น % สำหรับการยื่นคำขอค่าชดเชย -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REIMBURSEMENT=องค์ประกอบของเทรสโฮลด์คิดเป็น % สำหรับการชำระเงินคืน -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=องค์ประกอบของเทรสโฮลด์คิดเป็น % สำหรับการเปลี่ยนพารามิเตอร์ -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=องค์ประกอบของเทรสโฮลด์คิดเป็น % สำหรับการถอดถอนสินทรัพย์ -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=องค์ประกอบของเทรสโฮลด์คิดเป็น % สำหรับการยื่นคำร้องของการยึดทรัพย์ -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_ROLE=องค์ประกอบของเทรสโฮลด์คิดเป็น % สำหรับการร้องขอรับประกัน - -# suppress inspection "UnusedProperty" -dao.param.RECIPIENT_BTC_ADDRESS=ที่อยู่ BTC ของผู้รับ: - -# suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=ค่าธรรมเนียมในการลงบันทึกรายการทรัพย์สินต่อวัน -# suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Min. trade volume for assets - -# suppress inspection "UnusedProperty" -dao.param.LOCK_TIME_TRADE_PAYOUT=Lock time for alternative trade payout tx -# suppress inspection "UnusedProperty" -dao.param.ARBITRATOR_FEE=Arbitrator fee in BTC - -# suppress inspection "UnusedProperty" -dao.param.MAX_TRADE_LIMIT=Max. trade limit in BTC - -# suppress inspection "UnusedProperty" -dao.param.BONDED_ROLE_FACTOR=Bonded role unit factor in BSQ -# suppress inspection "UnusedProperty" -dao.param.ISSUANCE_LIMIT=Issuance limit per cycle in BSQ - -dao.param.currentValue=มูลค่าปัจจุบัน: {0} -dao.param.currentAndPastValue=Current value: {0} (Value when proposal was made: {1}) -dao.param.blocks={0} บล็อก - -dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was not distributed well in the Bisq network.\n{0} - -# suppress inspection "UnusedProperty" -dao.phase.PHASE_UNDEFINED=ไม่ได้กำหนด -# suppress inspection "UnusedProperty" -dao.phase.PHASE_PROPOSAL=ระยะเสนอ -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK1=พัก 1 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=ขั้นการลงคะแนนเสียงแบบไม่ระบุตัวตน -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK2=พัก 2 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=ขั้นเปิดเผยการลงคะแนน -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK3=พัก 3 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_RESULT=ระยะผลลัพธ์ - -dao.results.votes.table.header.stakeAndMerit=น้ำหนักโหวต -dao.results.votes.table.header.stake=Stake (เหรียญที่ล็อคไว้สำหรับสิทธิ์ในการโหวต) -dao.results.votes.table.header.merit=ที่ได้รับ -dao.results.votes.table.header.vote=โหวต - -dao.bond.menuItem.bondedRoles=บทบาทของการประกัน -dao.bond.menuItem.reputation=กิตติศัพท์ในการค้ำประกัน -dao.bond.menuItem.bonds=สัญญาผูกมัด - -dao.bond.dashboard.bondsHeadline=การประกัน BSQ -dao.bond.dashboard.lockupAmount=เงินทุนที่ล็อค -dao.bond.dashboard.unlockingAmount=การปลดล็อกเงิน (รอจนกว่าเวลาล็อคหมด) - - -dao.bond.reputation.header=ล็อคหลักประกันสำหรับมาตรฐานกิตติศัพท์ความน่าเชื่อถือ -dao.bond.reputation.table.header=หลักประกันกิตติศัพท์ของฉัน -dao.bond.reputation.amount=จำนวน BSQ เพื่อล็อคสิทธิ์ในการโหวต -dao.bond.reputation.time=ปลดล็อกเวลาในบล็อก -dao.bond.reputation.salt=ข้อมูลแบบสุ่ม -dao.bond.reputation.hash=Hash -dao.bond.reputation.lockupButton=ล็อค -dao.bond.reputation.lockup.headline=ยืนยันล็อคการทำรายการ -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.unlock.headline=ยืนยันการปลดล็อกธุรกรรม -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.allBonds.header=การค้ำประกันทั้งหมด - -dao.bond.bondedReputation=กิตติศัพท์ในการค้ำประกัน -dao.bond.bondedRoles=ตำแหน่งในการค้ำประกัน - -dao.bond.details.header=รายละเอียดบทบาทและหน้าที่ -dao.bond.details.role=บทบาท -dao.bond.details.requiredBond=ต้องได้รับการประกัน BSQ -dao.bond.details.unlockTime=ปลดล็อกเวลาในบล็อก -dao.bond.details.link=เชื่อมโยงกับคำอธิบายหลัก -dao.bond.details.isSingleton=สามารถรับได้ในจำนวนที่หลากหลายของผู้ถือหลัก -dao.bond.details.blocks={0} บล็อก - -dao.bond.table.column.name=ชื่อ -dao.bond.table.column.link=ลิงค์ -dao.bond.table.column.bondType=ประเภทของการประกัน -dao.bond.table.column.details=รายละเอียด -dao.bond.table.column.lockupTxId=ล็อคการทำธุรกรรม ID -dao.bond.table.column.bondState=สถานะการประกัน -dao.bond.table.column.lockTime=Unlock time -dao.bond.table.column.lockupDate=ล็อควันที่ - -dao.bond.table.button.lockup=ล็อค -dao.bond.table.button.unlock=ปลดล็อค -dao.bond.table.button.revoke=เพิกถอน - -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNDEFINED=ไม่ได้กำหนด -# suppress inspection "UnusedProperty" -dao.bond.bondState.READY_FOR_LOCKUP=ยังไม่ได้ประกัน -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_PENDING=รอดำเนินการล็อค -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_CONFIRMED=การประกันได้ล็อคไว้ -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_PENDING=รอปลดล็อค -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_CONFIRMED=ปลดล็อคการดำเนินงานสำเร็จ -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKING=กำลังปลดล็อกการกู้ยืม -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKED=การประกันปลดล็อคแล้ว -# suppress inspection "UnusedProperty" -dao.bond.bondState.CONFISCATED=หลักค้ำประกันถูกยึด - -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.UNDEFINED=ไม่ได้กำหนด -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.BONDED_ROLE=บทบาทการประกัน -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.REPUTATION=กิตติศัพท์ในการประกัน - -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.UNDEFINED=ไม่ได้กำหนด -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=GitHub admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=แอดมินฟอรั่ม -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=แอดมินทวิตเตอร์ -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=ผู้ดูแล Bisq -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork maintainer -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer maintainer -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=ผู้ดำเนินงานของเว็บไซต์ -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=ผู้ดำเนินการฟอรั่ม -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.SEED_NODE_OPERATOR=ตัวดำเนินการแหล่งข้อมูลในโหนด -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=ผู้ดำเนินการโหนดด้านราคา -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Bitcoin node operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=ผู้ดำเนินการด้านตลาด -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Mobile notifications relay operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=เจ้าของชื่อโดเมน -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DNS_ADMIN=แอดมิน DNS -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=ผู้ไกล่เกลี่ย -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=ผู้ไกล่เกลี่ย -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC donation address owner - -dao.burnBsq.assetFee=Asset listing -dao.burnBsq.menuItem.assetFee=ค่าธรรมเนียมในการลงบันทึกรายการทรัพย์สิน -dao.burnBsq.menuItem.proofOfBurn=พยานหลักฐานในการทำลายทิ้ง (Proof of burn) -dao.burnBsq.header=ค่าธรรมเนียมสำหรับการลงบันทึกรายการทรัพย์สิน -dao.burnBsq.selectAsset=เลือก Asset -dao.burnBsq.fee=ค่าธรรมเนียม -dao.burnBsq.trialPeriod=ระยะทดลองใช้ -dao.burnBsq.payFee=ชำระค่าธรรมเนียม -dao.burnBsq.allAssets=ทรัพย์สินทั้งหมด -dao.burnBsq.assets.nameAndCode=ชื่อของทรัพย์สิน -dao.burnBsq.assets.state=สถานะ -dao.burnBsq.assets.tradeVolume=ปริมาณการซื้อขาย -dao.burnBsq.assets.lookBackPeriod=ระยะเวลาของการตรวจสอบ -dao.burnBsq.assets.trialFee=ค่าธรรมเนียมสำหรับระยะทดลองใช้ -dao.burnBsq.assets.totalFee=ค่าธรรมเนียมที่ชำระแล้วทั้งหมด -dao.burnBsq.assets.days={0} วัน -dao.burnBsq.assets.toFewDays=ค่าธรรมเนียมสินทรัพย์มีน้อยเกินไป จำนวนระยะเวลาสำหรับการทดลองใช้คือ {0} - -# suppress inspection "UnusedProperty" -dao.assetState.UNDEFINED=ไม่ได้กำหนด -# suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=ในช่วงระยะเวลาทดลองใช้ -# suppress inspection "UnusedProperty" -dao.assetState.ACTIVELY_TRADED=ซื้อขายอย่างแข่งขัน -# suppress inspection "UnusedProperty" -dao.assetState.DE_LISTED=ถูกถอนออกจากรายชื่อเนื่องจากไม่มีการเคลื่อนไหว -# suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=ถูกลบออกจากการโหวต - -dao.proofOfBurn.header=พยานหลักฐานในการทำลายทิ้ง (Proof of burn) -dao.proofOfBurn.amount=จำนวน -dao.proofOfBurn.preImage=Pre-image -dao.proofOfBurn.burn=ทำลายทิ้ง (Burn) -dao.proofOfBurn.allTxs=หลักฐานทั้งหมดในธุรกรรมที่ทำลายทิ้ง -dao.proofOfBurn.myItems=หลักฐานการทำลายธุรกรรมทิ้งของฉัน -dao.proofOfBurn.date=วันที่ -dao.proofOfBurn.hash=แฮช -dao.proofOfBurn.txs=การทำธุรกรรม -dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Sign a message with key from proof of burn transaction -dao.proofOfBurn.verify.window.title=Verify a message with key from proof of burn transaction -dao.proofOfBurn.copySig=คัดลอกลายเซ็นไปยังคลิปบอร์ด -dao.proofOfBurn.sign=ลงนาม -dao.proofOfBurn.message=ข้อความ -dao.proofOfBurn.sig=ลายเซ็น -dao.proofOfBurn.verify=ยืนยัน -dao.proofOfBurn.verificationResult.ok=การตรวจสอบดำเนินการเรียบร้อยแล้ว -dao.proofOfBurn.verificationResult.failed=การตรวจสอบล้มเหลว - -# suppress inspection "UnusedProperty" -dao.phase.UNDEFINED=ไม่ได้กำหนด -# suppress inspection "UnusedProperty" -dao.phase.PROPOSAL=ระยะเสนอ -# suppress inspection "UnusedProperty" -dao.phase.BREAK1=พักก่อนการโหวตแบบไม่ระบุตัวตน -# suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=ขั้นตอนการลงคะแนนเสียงแบบไม่ระบุตัวตน -# suppress inspection "UnusedProperty" -dao.phase.BREAK2=พักเบรกก่อนช่วงการประกาศผลโหวต -# suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=ขั้นตอนการประกาศผลโหวต -# suppress inspection "UnusedProperty" -dao.phase.BREAK3=พักเบรกก่อนช่วงสรุปผล -# suppress inspection "UnusedProperty" -dao.phase.RESULT=ช่วงผลการลงคะแนนเสียง - -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.PROPOSAL=ระยะเสนอ -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=โหวตแบบไม่ระบุตัวตน -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.VOTE_REVEAL=การประกาศผลโหวต -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.RESULT=ผลโหวต - -# suppress inspection "UnusedProperty" -dao.proposal.type.UNDEFINED=ไม่ได้กำหนด -# suppress inspection "UnusedProperty" -dao.proposal.type.COMPENSATION_REQUEST=คำขอสำหรับค่าสินไหมตอบแทน -# suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=ยื่นคำขอการชำระเงินคืน -# suppress inspection "UnusedProperty" -dao.proposal.type.BONDED_ROLE=ข้อเสนอสำหรับเกณฑ์การประกัน -# suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=ข้อเสนอสำหรับการถอดถอนสินทรัพย์ -# suppress inspection "UnusedProperty" -dao.proposal.type.CHANGE_PARAM=ข้อเสนอสำหรับการเปลี่ยนข้อจำกัด -# suppress inspection "UnusedProperty" -dao.proposal.type.GENERIC=ข้อเสนอทั่วไป -# suppress inspection "UnusedProperty" -dao.proposal.type.CONFISCATE_BOND=ข้อเสนอในการริบการประกัน - -# suppress inspection "UnusedProperty" -dao.proposal.type.short.UNDEFINED=ไม่ได้กำหนด -# suppress inspection "UnusedProperty" -dao.proposal.type.short.COMPENSATION_REQUEST=คำขอค่าสินไหมทดแทน -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=ยื่นคำขอการชำระเงินคืน -# suppress inspection "UnusedProperty" -dao.proposal.type.short.BONDED_ROLE=ตำแหน่งการประกัน -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ASSET=กำลังลบ altcoin (เหรียญทางเลือก) -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CHANGE_PARAM=การเปลี่ยนพารามิเตอร์ -# suppress inspection "UnusedProperty" -dao.proposal.type.short.GENERIC=ข้อเสนอทั่วไป -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CONFISCATE_BOND=การริบตัวประกัน - -dao.proposal.details=รายละเอียดข้อเสนอ -dao.proposal.selectedProposal=ข้อเสนอที่เลือก -dao.proposal.active.header=ข้อเสนอของวงจรปัจจุบัน -dao.proposal.active.remove.confirm=คุณแน่ใจหรือไม่ที่จะลบข้อเสนอนั้น ข้อเสนอดังกล่าวที่มีการชำระค่าธรรมเนียมแล้วจะสูญหายหลังจากการลบข้อเสนอ -dao.proposal.active.remove.doRemove=ใช่ ลบข้อเสนอของฉัน -dao.proposal.active.remove.failed=ไม่สามารถลบข้อเสนอได้ -dao.proposal.myVote.title=การโหวต -dao.proposal.myVote.accept=รับข้อเสนอ -dao.proposal.myVote.reject=ปฏิเสธข้อเสนอ -dao.proposal.myVote.removeMyVote=ละเว้นข้อเสนอ -dao.proposal.myVote.merit=น้ำหนักผลคะแนนเสียงจาก BSQ ที่ได้รับ -dao.proposal.myVote.stake=น้ำหนักผลการลงคะแนนเสียงจาก Stake (เหรียญที่ล็อคไว้สำหรับสิทธิ์ในการโหวต) -dao.proposal.myVote.revealTxId=ID ธุรกรรมการแสดงผลการลงคะแนน -dao.proposal.myVote.stake.prompt=Max. available stake for voting: {0} -dao.proposal.votes.header=Set stake for voting and publish your votes -dao.proposal.myVote.button=Publish votes -dao.proposal.myVote.setStake.description=After voting on all proposals you have to set your stake for voting by locking up BSQ. The more BSQ you lock up, the more weight your vote will have. \n\nBSQ locked up for voting will be unlocked again during the vote reveal phase. -dao.proposal.create.selectProposalType=เลือกประเภทข้อเสนอ -dao.proposal.create.phase.inactive=Please wait until the next proposal phase -dao.proposal.create.proposalType=ประเภทข้อเสนอ -dao.proposal.create.new=สร้างข้อเสนอใหม่ -dao.proposal.create.button=เสนอคำขอ -dao.proposal.create.publish=Publish proposal -dao.proposal.create.publishing=Proposal publishing is in progress ... -dao.proposal=ข้อเสนอ -dao.proposal.display.type=ประเภทข้อเสนอ -dao.proposal.display.name=Exact GitHub username -dao.proposal.display.link=Link to detailed info -dao.proposal.display.link.prompt=Link to proposal -dao.proposal.display.requestedBsq=จำนวนที่ต้องการใน BSQ -dao.proposal.display.txId=รหัสธุรกรรมของข้อเสนอ -dao.proposal.display.proposalFee=ค่าธรรมเนียมข้อเสนอ -dao.proposal.display.myVote=การลงคะแนนของฉัน -dao.proposal.display.voteResult=สรุปผลโหวต -dao.proposal.display.bondedRoleComboBox.label=ประเภทของตำแหน่งหลักประกัน -dao.proposal.display.requiredBondForRole.label=การประกันที่จำเป็นโดยหลักการ -dao.proposal.display.option=ตัวเลือก - -dao.proposal.table.header.proposalType=ประเภทข้อเสนอ -dao.proposal.table.header.link=ลิงค์ -dao.proposal.table.header.myVote=การลงคะแนนของฉัน -# suppress inspection "UnusedProperty" -dao.proposal.table.header.remove=ลบออก -dao.proposal.table.icon.tooltip.removeProposal=ลบข้อเสนอของฉัน -dao.proposal.table.icon.tooltip.changeVote=ผลโหวตปัจจุบัน: ''{0}''. เปลี่ยนโหวตไปยัง: ''{1}'' - -dao.proposal.display.myVote.accepted=ได้รับการยืนยัน -dao.proposal.display.myVote.rejected=ปฏิเสธ -dao.proposal.display.myVote.ignored=ละเว้น -dao.proposal.display.myVote.unCounted=Vote was not included in result -dao.proposal.myVote.summary=Voted: {0}; Vote weight: {1} (earned: {2} + stake: {3}) {4} -dao.proposal.myVote.invalid=Vote was invalid - -dao.proposal.voteResult.success=ได้รับการยืนยัน -dao.proposal.voteResult.failed=ปฏิเสธ -dao.proposal.voteResult.summary=ผลลัพธ์: {0}; เกณฑ์: {1} (ที่กำหนดไว้ต้อง> {2}); องค์ประชุม: {3} (ที่กำหนดไว้ต้อง> {4}) - -dao.proposal.display.paramComboBox.label=เลือกพารามิเตอร์เพื่อเปลี่ยนแปลง -dao.proposal.display.paramValue=ค่าพารามิเตอร์ - -dao.proposal.display.confiscateBondComboBox.label=เลือกรูปแบบการประกัน -dao.proposal.display.assetComboBox.label=ทรัพย์สินที่จะถอนออก - -dao.blindVote=การลงคะแนนเสียงแบบไม่ระบุตัวตน - -dao.blindVote.startPublishing=กำลังเผยแพร่การทำธุรกรรมโหวตแบบไม่ระบุตัวตน ... -dao.blindVote.success=Your blind vote transaction has been successfully published.\n\nPlease note, that you have to be online in the vote reveal phase so that your Bisq application can publish the vote reveal transaction. Without the vote reveal transaction your vote would be invalid! - -dao.wallet.menuItem.send=ส่ง -dao.wallet.menuItem.receive=รับ -dao.wallet.menuItem.transactions=การทำธุรกรรม - -dao.wallet.dashboard.myBalance=ยอดคงเหลือในกระเป๋าสตางค์ของฉัน - -dao.wallet.receive.fundYourWallet=Your BSQ receive address -dao.wallet.receive.bsqAddress=BSQ wallet address (Fresh unused address) - -dao.wallet.send.sendFunds=ส่งเงิน -dao.wallet.send.sendBtcFunds=ส่งเงินทุนที่ไม่ใช่เครือ BSQ (BTC) -dao.wallet.send.amount=จำนวนเงินใน BSQ -dao.wallet.send.btcAmount=ยอดรวมทั้งหมดใน BTC (เงินทุนที่ไม่ใช่ในเครือ BSQ) -dao.wallet.send.setAmount=กำหนดจำนวนเงินที่จะถอน (จำนวนเงินขั้นต่ำคือ {0}) -dao.wallet.send.receiverAddress=ที่อยู่ BSQ ของผู้รับ -dao.wallet.send.receiverBtcAddress=ที่อยู่ BTC ของผู้รับ -dao.wallet.send.setDestinationAddress=กรอกที่อยู่ปลายทางของคุณ -dao.wallet.send.send=ส่งเงิน BSQ -dao.wallet.send.inputControl=Select inputs -dao.wallet.send.sendBtc=ส่งเงินทุน BTC -dao.wallet.send.sendFunds.headline=ยืนยันคำขอถอนเงิน -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=หมวด - -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED=ไม่ได้กำหนด -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED_TX_TYPE=จำไม่ได้ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNVERIFIED=ธุรกรรม BSQ ที่ไม่ได้รับการยืนยัน -# suppress inspection "UnusedProperty" -dao.tx.type.enum.INVALID=ธุรกรรม BSQ ไม่ถูกต้อง -# suppress inspection "UnusedProperty" -dao.tx.type.enum.GENESIS=ธุรกรรมต้นกำเนิด -# suppress inspection "UnusedProperty" -dao.tx.type.enum.TRANSFER_BSQ=โอน BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.received.TRANSFER_BSQ=ได้รับ BSQ แล้ว -# suppress inspection "UnusedProperty" -dao.tx.type.enum.sent.TRANSFER_BSQ=ส่ง BSQ แล้ว -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PAY_TRADE_FEE=ค่าธรรมเนียมการซื้อขาย -# suppress inspection "UnusedProperty" -dao.tx.type.enum.COMPENSATION_REQUEST=ค่าธรรมเนียมการขอค่าชดเชย -# suppress inspection "UnusedProperty" -dao.tx.type.enum.REIMBURSEMENT_REQUEST=ค่าธรรมเนียมสำหรับการยื่นคำร้องขอการชำระเงินคืน -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROPOSAL=ค่าธรรมเนียมสำหรับข้อเสนอ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=ค่าธรรมเนียมการโหวตแบบไม่ระบุตัวตน -# suppress inspection "UnusedProperty" -dao.tx.type.enum.VOTE_REVEAL=การประกาศผลโหวต -# suppress inspection "UnusedProperty" -dao.tx.type.enum.LOCKUP=ล็อคสิทธิ์ในการประกัน -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNLOCK=ปลดล็อกสิทธิ์การประกัน -# suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=ค่าธรรมเนียมในการลงบันทึกรายการทรัพย์สิน -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=การพิสูจน์หลักฐานในการทำลายทิ้ง (Proof of burn) -# suppress inspection "UnusedProperty" -dao.tx.type.enum.IRREGULAR=Irregular - -dao.tx.withdrawnFromWallet=BTC withdrawn from wallet -dao.tx.issuanceFromCompReq=คำขอหรือการออกค่าสินไหมทดแทน -dao.tx.issuanceFromCompReq.tooltip=คำขอค่าสินไหมทดแทน ซึ่งนำไปสู่การออก BSQ ใหม่\nวันที่ออก: {0} -dao.tx.issuanceFromReimbursement=การออกคำสั่ง/การยื่นคำร้องขอการชำระเงินคืน -dao.tx.issuanceFromReimbursement.tooltip=การเรียกร้องขอการชำระเงินคืนซึ่งเป็นคำสั่งภายใต้ BSQ ฉบับใหม่\nวันที่เริ่มทำการ: {0} -dao.proposal.create.missingBsqFunds=You don''t have sufficient BSQ funds for creating the proposal. If you have an unconfirmed BSQ transaction you need to wait for a blockchain confirmation because BSQ is validated only if it is included in a block.\nMissing: {0} - -dao.proposal.create.missingBsqFundsForBond=You don''t have sufficient BSQ funds for this role. You can still publish this proposal, but you''ll need the full BSQ amount required for this role if it gets accepted. \nMissing: {0} - -dao.proposal.create.missingMinerFeeFunds=You don''t have sufficient BTC funds for creating the proposal transaction. All BSQ transactions require a miner fee in BTC.\nMissing: {0} - -dao.proposal.create.missingIssuanceFunds=You don''t have sufficient BTC funds for creating the proposal transaction. All BSQ transactions require a miner fee in BTC, and issuance transactions also require BTC for the requested BSQ amount ({0} Satoshis/BSQ).\nMissing: {1} - -dao.feeTx.confirm=ยืนยันการทำรายการ {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.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.news.bisqDAO.title=THE BISQ DAO -dao.news.bisqDAO.description=Just as the Bisq exchange is decentralized and censorship-resistant, so is its governance model - and the Bisq DAO and BSQ token are the tools that make it possible. -dao.news.bisqDAO.readMoreLink=Learn More About the Bisq DAO - -dao.news.pastContribution.title=MADE PAST CONTRIBUTIONS? REQUEST BSQ -dao.news.pastContribution.description=If you have contributed to Bisq please use the BSQ address below and make a request for taking part of the BSQ genesis distribution. -dao.news.pastContribution.yourAddress=Your BSQ Wallet Address -dao.news.pastContribution.requestNow=Request now - -dao.news.DAOOnTestnet.title=RUN THE BISQ DAO ON OUR TESTNET -dao.news.DAOOnTestnet.description=The mainnet Bisq DAO is not launched yet but you can learn about the Bisq DAO by running it on our testnet. -dao.news.DAOOnTestnet.firstSection.title=1. Switch to DAO Testnet Mode -dao.news.DAOOnTestnet.firstSection.content=Switch to DAO Testnet from the Settings screen. -dao.news.DAOOnTestnet.secondSection.title=2. Acquire Some BSQ -dao.news.DAOOnTestnet.secondSection.content=Request BSQ on Slack or Buy BSQ on Bisq. -dao.news.DAOOnTestnet.thirdSection.title=3. Participate in a Voting Cycle -dao.news.DAOOnTestnet.thirdSection.content=Making proposals and voting on proposals to change various aspects of Bisq. -dao.news.DAOOnTestnet.fourthSection.title=4. Explore a BSQ Block Explorer -dao.news.DAOOnTestnet.fourthSection.content=Since BSQ is just bitcoin, you can see BSQ transactions on our bitcoin block explorer. -dao.news.DAOOnTestnet.readMoreLink=Read the full documentation - -dao.monitor.daoState=DAO state -dao.monitor.proposals=Proposals state -dao.monitor.blindVotes=Blind votes state - -dao.monitor.table.peers=Peers -dao.monitor.table.conflicts=Conflicts -dao.monitor.state=สถานะ -dao.monitor.requestAlHashes=Request all hashes -dao.monitor.resync=Resync DAO state -dao.monitor.table.header.cycleBlockHeight=Cycle / block height -dao.monitor.table.cycleBlockHeight=Cycle {0} / block {1} -dao.monitor.table.seedPeers=Seed node: {0} - -dao.monitor.daoState.headline=DAO state -dao.monitor.daoState.table.headline=Chain of DAO state hashes -dao.monitor.daoState.table.blockHeight=Block height -dao.monitor.daoState.table.hash=Hash of DAO state -dao.monitor.daoState.table.prev=Previous hash -dao.monitor.daoState.conflictTable.headline=DAO state hashes from peers in conflict -dao.monitor.daoState.utxoConflicts=UTXO conflicts -dao.monitor.daoState.utxoConflicts.blockHeight=Block height: {0} -dao.monitor.daoState.utxoConflicts.sumUtxo=Sum of all UTXO: {0} BSQ -dao.monitor.daoState.utxoConflicts.sumBsq=Sum of all BSQ: {0} BSQ -dao.monitor.daoState.checkpoint.popup=DAO state is not in sync with the network. After restart the DAO state will resync. - -dao.monitor.proposal.headline=Proposals state -dao.monitor.proposal.table.headline=Chain of proposal state hashes -dao.monitor.proposal.conflictTable.headline=Proposal state hashes from peers in conflict - -dao.monitor.proposal.table.hash=Hash of proposal state -dao.monitor.proposal.table.prev=Previous hash -dao.monitor.proposal.table.numProposals=No. proposals - -dao.monitor.isInConflictWithSeedNode=Your local data is not in consensus with at least one seed node. Please resync the DAO state. -dao.monitor.isInConflictWithNonSeedNode=One of your peers is not in consensus with the network but your node is in sync with the seed nodes. -dao.monitor.daoStateInSync=Your local node is in consensus with the network - -dao.monitor.blindVote.headline=Blind votes state -dao.monitor.blindVote.table.headline=Chain of blind vote state hashes -dao.monitor.blindVote.conflictTable.headline=Blind vote state hashes from peers in conflict -dao.monitor.blindVote.table.hash=Hash of blind vote state -dao.monitor.blindVote.table.prev=Previous hash -dao.monitor.blindVote.table.numBlindVotes=No. blind votes - -dao.factsAndFigures.menuItem.supply=BSQ Supply -dao.factsAndFigures.menuItem.transactions=BSQ Transactions - -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 BSQ/USD price -dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on 30 days average BSQ/USD price) -dao.factsAndFigures.dashboard.availableAmount=BSQ ที่ใช้งานได้ทั้งหมด -dao.factsAndFigures.dashboard.volumeUsd=Total trade volume in USD -dao.factsAndFigures.dashboard.volumeBtc=Total trade volume in BTC -dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection=Average BSQ/USD trade price from selected time period in chart -dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection=Average BSQ/BTC trade price from selected time period in chart - -dao.factsAndFigures.supply.issuedVsBurnt=BSQ issued v. BSQ burnt - -dao.factsAndFigures.supply.issued=BSQ issued -dao.factsAndFigures.supply.compReq=คำขอการชดเชย -dao.factsAndFigures.supply.reimbursement=Reimbursement requests -dao.factsAndFigures.supply.genesisIssueAmount=BSQ ที่ได้ดำเนินการในส่วนธุรกรรมทั่วไป -dao.factsAndFigures.supply.compRequestIssueAmount=BSQ ที่ได้ดำเนินการสำหรับการเรียกร้องค่าชดเชย -dao.factsAndFigures.supply.reimbursementAmount=BSQ ที่ได้ดำเนินการสำหรับการเรียกร้องการชำระเงินคืน -dao.factsAndFigures.supply.totalIssued=Total issued BSQ -dao.factsAndFigures.supply.totalBurned=Total burned BSQ -dao.factsAndFigures.supply.chart.tradeFee.toolTip={0}\n{1} -dao.factsAndFigures.supply.burnt=BSQ burnt - -dao.factsAndFigures.supply.priceChat=BSQ price -dao.factsAndFigures.supply.volumeChat=ปริมาณการซื้อขาย -dao.factsAndFigures.supply.tradeVolumeInUsd=Trade volume in USD -dao.factsAndFigures.supply.tradeVolumeInBtc=Trade volume in BTC -dao.factsAndFigures.supply.bsqUsdPrice=BSQ/USD price -dao.factsAndFigures.supply.bsqBtcPrice=BSQ/BTC price -dao.factsAndFigures.supply.btcUsdPrice=BTC/USD price - -dao.factsAndFigures.supply.locked=สถานะทั่วโลกของ BSQ ที่ล็อกไว้ -dao.factsAndFigures.supply.totalLockedUpAmount=ถูกล็อคไว้ในการประกัน -dao.factsAndFigures.supply.totalUnlockingAmount=การปลดล็อค BSQ จากการประกัน -dao.factsAndFigures.supply.totalUnlockedAmount=ปลดล็อค BSQ จากการประกันไว้แล้ว -dao.factsAndFigures.supply.totalConfiscatedAmount=ยึด BSQ จากการประกันไว้แล้ว -dao.factsAndFigures.supply.proofOfBurn=Proof of Burn -dao.factsAndFigures.supply.bsqTradeFee=BSQ Trade fees -dao.factsAndFigures.supply.btcTradeFee=BTC Trade fees - -dao.factsAndFigures.transactions.genesis=ธุรกรรมต้นกำเนิด -dao.factsAndFigures.transactions.genesisBlockHeight=ความสูงของบล็อกต้นกำเนิด (Genesis block) -dao.factsAndFigures.transactions.genesisTxId=ID การทำธุรกรรมต้นกำเนิด -dao.factsAndFigures.transactions.txDetails=สถิติในการทำธุรกรรม BSQ -dao.factsAndFigures.transactions.allTx=หมายเลขจำนวนธุรกรรม BSQ ทั้งหมด -dao.factsAndFigures.transactions.utxo=หมายเลขจำนวนธุรกรรมที่ยังใช้ไม่หมด (เงินทอน) -dao.factsAndFigures.transactions.compensationIssuanceTx=เลขที่ของการทำธุรกรรมสำหรับยื่นคำขอค่าตอบแทนทั้งหมด -dao.factsAndFigures.transactions.reimbursementIssuanceTx=เลขที่ของการทำธุรกรรมสำหรับการยื่นคำขอการชำระเงินคืนทั้งหมด -dao.factsAndFigures.transactions.burntTx=เลขที่การทำธุรกรรมของการชำระค่าธรรมเนียมทั้งหมด -dao.factsAndFigures.transactions.invalidTx=No. of all invalid transactions -dao.factsAndFigures.transactions.irregularTx=No. of all irregular transactions - - - #################################################################### # Windows #################################################################### @@ -2120,8 +1406,6 @@ disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any emptyWalletWindow.headline={0} กระเป๋าสตางค์ฉุกเฉิน emptyWalletWindow.info=โปรดใช้ในกรณีฉุกเฉินเท่านั้นหากคุณไม่สามารถเข้าถึงเงินจาก UI ได้\n\nโปรดทราบว่าข้อเสนอแบบเปิดทั้งหมดจะถูกปิดโดยอัตโนมัติเมื่อใช้เครื่องมือนี้\n\nก่อนที่คุณจะใช้เครื่องมือนี้โปรดสำรองข้อมูลในสารบบข้อมูลของคุณ คุณสามารถดำเนินการได้ที่ \"บัญชี / การสำรองข้อมูล \" \n\nโปรดรายงานปัญหาของคุณและส่งรายงานข้อบกพร่องเกี่ยวกับ GitHub หรือที่ฟอรัม Bisq เพื่อให้เราสามารถตรวจสอบสิ่งที่เป็นสาเหตุของปัญหาได้ emptyWalletWindow.balance=ยอดในกระเป๋าสตางค์ที่คงเหลือที่มีอยู่ -emptyWalletWindow.bsq.btcBalance=ยอดดุลของ Non-BSQ Satoshis - emptyWalletWindow.address=ที่อยู่ปลายทางของคุณ emptyWalletWindow.button=ส่งเงินทั้งหมด emptyWalletWindow.openOffers.warn=คุณมีข้อเสนอแบบเปิดซึ่งจะถูกปลดออกในกรณีที่คุณทำให้ กระเป๋าสตางค์ไม่มีเงินเหลืออยู่เลย\nคุณแน่ใจหรือไม่ว่าต้องการให้กระเป๋าสตางค์ของคุณนั้นว่างเปล่า? @@ -2146,10 +1430,8 @@ filterWindow.seedNode=แหล่งข้อมูลในโหนดเค filterWindow.priceRelayNode=โหนดผลัดเปลี่ยนราคาที่ได้รับการกรอง (คั่นด้วยเครื่องหมายจุลภาค ที่อยู่ onion) filterWindow.btcNode=โหนด Bitcoin ที่ได้รับการกรองแล้ว (คั่นด้วยเครื่องหมายจุลภาค ที่อยู่ + พอร์ต) filterWindow.preventPublicBtcNetwork=ป้องกันการใช้เครือข่าย Bitcoin สาธารณะ -filterWindow.disableDao=Disable DAO filterWindow.disableAutoConf=Disable auto-confirm filterWindow.autoConfExplorers=Filtered auto-confirm explorers (comma sep. addresses) -filterWindow.disableDaoBelowVersion=Min. version required for DAO filterWindow.disableTradeBelowVersion=Min. version required for trading filterWindow.add=เพิ่มตัวกรอง filterWindow.remove=ลบตัวกรอง @@ -2223,7 +1505,6 @@ tradeDetailsWindow.detailData=Detail data txDetailsWindow.headline=Transaction Details txDetailsWindow.btc.note=You have sent BTC. -txDetailsWindow.bsq.note=You have sent BSQ funds. BSQ is colored bitcoin, so the transaction will not show in a BSQ explorer until it has been confirmed in a bitcoin block. txDetailsWindow.sentTo=Sent to txDetailsWindow.txId=TxId @@ -2235,9 +1516,6 @@ closedTradesSummaryWindow.totalMinerFee.title=Sum of all miner fees closedTradesSummaryWindow.totalMinerFee.value={0} ({1} of total trade amount) closedTradesSummaryWindow.totalTradeFeeInBtc.title=Sum of all trade fees paid in BTC closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} of total trade amount) -closedTradesSummaryWindow.totalTradeFeeInBsq.title=Sum of all trade fees paid in BSQ -closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} of total trade amount) - walletPasswordWindow.headline=ป้อนรหัสผ่านเพื่อปลดล็อก torNetworkSettingWindow.header=ตั้งค่าเครือข่าย Tor @@ -2317,12 +1595,6 @@ popup.warning.tooLargePercentageValue=คุณไม่สามารถกำ popup.warning.examplePercentageValue=โปรดป้อนตัวเลขเปอร์เซ็นต์เช่น \"5.4 \" เป็น 5.4% popup.warning.noPriceFeedAvailable=ไม่มีฟีดราคาสำหรับสกุลเงินดังกล่าว คุณไม่สามารถใช้ราคาตามเปอร์เซ็นต์ได้\nโปรดเลือกราคาที่ถูกกำหนดไว้แแล้ว popup.warning.sendMsgFailed=การส่งข้อความไปยังคู่ค้าของคุณล้มเหลว\nโปรดลองอีกครั้งและหากยังคงเกิดขึ้นขึ้นเนื่อง โปรดรายงานข้อผิดพลาดต่อไป -popup.warning.insufficientBtcFundsForBsqTx=คุณไม่มีเงินทุน BTC เพียงพอสำหรับการจ่ายค่าธรรมเนียมขุดสำหรับการทำธุรกรรมดังกล่าว\nกรุณาใส่เงินในกระเป๋าสตางค์ BTC ของคุณ\nเงินขาดไป: {0} -popup.warning.bsqChangeBelowDustException=This transaction creates a BSQ change output which is below dust limit (5.46 BSQ) and would be rejected by the Bitcoin network.\n\nYou need to either send a higher amount to avoid the change output (e.g. by adding the dust amount to your sending amount) or add more BSQ funds to your wallet so you avoid to generate a dust output.\n\nThe dust output is {0}. -popup.warning.btcChangeBelowDustException=This transaction creates a change output which is below dust limit (546 Satoshi) and would be rejected by the Bitcoin network.\n\nYou need to add the dust amount to your sending amount to avoid to generate a dust output.\n\nThe dust output is {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.noBsqFundsForBtcFeePayment=กระเป๋าสตางค์ BSQ ของคุณไม่มีจำนวนเงินทุนที่มากพอสำหรับการชำระการเทรดใน BSQ popup.warning.messageTooLong=ข้อความของคุณเกินขีดจำกัดสูงสุดที่อนุญาต โปรดแบ่งส่งเป็นหลายส่วนหรืออัปโหลดไปยังบริการเช่น https://pastebin.com popup.warning.lockedUpFunds=You have locked up funds from a failed trade.\nLocked up balance: {0} \nDeposit tx address: {1}\nTrade ID: {2}.\n\nPlease open a support ticket by selecting the trade in the open trades screen and pressing \"alt + o\" or \"option + o\"." @@ -2336,8 +1608,6 @@ popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=ราคาผลัดเปลี่ยน popup.warning.seed=รหัสลับเพื่อกู้ข้อมูล popup.warning.mandatoryUpdate.trading=Please update to the latest Bisq version. A mandatory update was released which disables trading for old versions. Please check out the Bisq Forum for more information. -popup.warning.mandatoryUpdate.dao=Please update to the latest Bisq version. A mandatory update was released which disables the Bisq DAO and BSQ for old versions. Please check out the Bisq Forum for more information. -popup.warning.disable.dao=The Bisq DAO and BSQ are temporary disabled. Please check out the Bisq Forum for more information. popup.warning.noFilter=We did not receive a filter object from the seed nodes. This is a not expected situation. Please inform the Bisq developers. popup.warning.burnBTC=This transaction is not possible, as the mining fees of {0} would exceed the amount to transfer of {1}. Please wait until the mining fees are low again or until you''ve accumulated more BTC to transfer. @@ -2356,7 +1626,6 @@ popup.info.cashDepositInfo.confirm=ฉันยืนยันว่าฉัน popup.info.shutDownWithOpenOffers=Bisq คือกำลังจะปิดลง แต่ยังคงมีการเปิดขายข้อเสนอปกติ\nข้อเสนอเหล่านี้จะไม่ใข้งานได้บนเครือข่าย P2P network ในขณะที่ Bisq ปิดตัวลง แต่จะมีการเผยแพร่บนเครือข่าย P2P ครั้งถัดไปเมื่อคุณมีการเริ่มใช้งาน Bisq.\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=การแจ้งเตือนส่วนตัวที่สำคัญ! @@ -2528,7 +1797,6 @@ navigation.settings.preferences=\ "การตั้งค่า / สิ่ง # suppress inspection "UnusedProperty" navigation.funds.transactions=\"เงิน / ธุรกรรม\" navigation.support=\"ช่วยเหลือและสนับสนุน\" -navigation.dao.wallet.receive=\ "Wallet DAO / BSQ / การรับ \" #################################################################### @@ -2558,12 +1826,6 @@ XMR_MAINNET=Monero Mainnet XMR_TESTNET=Monero Testnet # suppress inspection "UnusedProperty" XMR_STAGENET=Monero Stagenet -# suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet (deprecated) -# suppress inspection "UnusedProperty" -BTC_DAO_BETANET=Bisq DAO Betanet (Bitcoin Mainnet) -# suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=Bitcoin DAO Regtest time.year=ปี time.month=เดือน @@ -2910,9 +2172,7 @@ validation.accountNrChars=หมายเลขบัญชีต้องปร validation.btc.invalidAddress=ที่อยู่ไม่ถูกต้อง โปรดตรวจสอบแบบฟอร์มที่อยู่ validation.integerOnly=โปรดป้อนตัวเลขจำนวนเต็มเท่านั้น validation.inputError=การป้อนข้อมูลของคุณเกิดข้อผิดพลาด: \n{0} -validation.bsq.insufficientBalance=ยอดคงเหลือของคุณ {0} validation.btc.exceedsMaxTradeLimit=ขีดจำกัดการเทรดของคุณคือ {0} -validation.bsq.amountBelowMinAmount=จำนวนเงินต่ำสุด {0} validation.nationalAccountId={0} ต้องประกอบด้วย {1} ตัวเลข #new @@ -2934,7 +2194,6 @@ validation.bic.invalidLocationCode=BIC มีรหัสตำแหน่ง validation.bic.invalidBranchCode=BIC มีรหัสสาขาไม่ถูกต้อง validation.bic.sepaRevolutBic=บัญชี Revolut Sepa ไม่รองรับ validation.btc.invalidFormat=Invalid format for a Bitcoin address. -validation.bsq.invalidFormat=Invalid format for a BSQ address. validation.email.invalidAddress=ที่อยู่ไม่ถูกต้อง validation.iban.invalidCountryCode=รหัสประเทศไม่ถูกต้อง validation.iban.checkSumNotNumeric=Checksum ต้องเป็นตัวเลข diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index e060d083af..10dc18a5f9 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -192,7 +192,6 @@ shared.tradeWalletBalance=Số dư ví giao dịch shared.makerTxFee=Người tạo: {0} shared.takerTxFee=Người nhận: {0} shared.iConfirm=Tôi xác nhận -shared.tradingFeeInBsqInfo=≈ {0} shared.openURL=Mở {0} shared.fiat=Tiền pháp định shared.crypto=Tiền mã hóa @@ -204,9 +203,6 @@ shared.actions=Hoạt động shared.buyerUpperCase=Người mua shared.sellerUpperCase=Người bán shared.new=MỚI -shared.blindVoteTxId=Mã giao dịch bỏ phiếu mù -shared.proposal=Đề xuất -shared.votes=Các phiếu bầu shared.learnMore=Tìm hiểu thêm shared.dismiss=Hủy shared.selectedArbitrator=Trọng tài được chọn @@ -240,7 +236,6 @@ mainView.menu.funds=Số tiền mainView.menu.support=Hỗ trợ mainView.menu.settings=Cài đặt mainView.menu.account=Tài khoản -mainView.menu.dao=DAO mainView.marketPriceWithProvider.label=Giá thị trường theo {0} mainView.marketPrice.bisqInternalPrice=Giá giao dịch Bisq gần nhất @@ -257,13 +252,11 @@ mainView.footer.localhostBitcoinNode=(Máy chủ nội bộ) mainView.footer.btcInfo={0} {1} mainView.footer.btcFeeRate=/ Fee rate: {0} sat/vB mainView.footer.btcInfo.initializing=Đang kết nối với mạng Bitcoin -mainView.footer.bsqInfo.synchronizing=/ Đang đồng bộ hóa DAO 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=Đang kết nối với mainView.footer.btcInfo.connectionFailed=Connection failed to mainView.footer.p2pInfo=Bitcoin network peers: {0} / Bisq network peers: {1} -mainView.footer.daoFullNode=Full node DAO mainView.bootstrapState.connectionToTorNetwork=(1/4) Kết nối với mạng ... mainView.bootstrapState.torNodeCreated=(2/4) Nút Tor được tạo @@ -435,7 +428,6 @@ createOffer.fundsBox.networkFee=Phí đào createOffer.fundsBox.placeOfferSpinnerInfo=Báo giá đang được công bố createOffer.fundsBox.paymentLabel=giao dịch Bisq với ID {0} createOffer.fundsBox.fundsStructure=({0} tiền đặt cọc, {1} phí giao dịch, {2} phí đào) -createOffer.fundsBox.fundsStructure.BSQ=({0} tiền đặt cọc, {1} phí đào)+ {2} phí giao dịch createOffer.success.headline=Chào giá của bạn đã được công bố createOffer.success.info=Bạn có thể quản lý báo giá hiện hành của bạn tại \"Portfolio/ báo giá hiện tại của bạn\". createOffer.info.sellAtMarketPrice=Bạn sẽ luôn bán với giá thị trường vì báo giá của bạn sẽ luôn được cập nhật. @@ -636,7 +628,6 @@ portfolio.pending.step2_buyer.amountToTransfer=Số tiền chuyển portfolio.pending.step2_buyer.sellersAddress=Địa chỉ của người bán {0} portfolio.pending.step2_buyer.buyerAccount=Tài khoản thanh toán sẽ sử dụng portfolio.pending.step2_buyer.paymentStarted=Bắt đầu thanh toán -portfolio.pending.step2_buyer.fillInBsqWallet=Pay from BSQ wallet portfolio.pending.step2_buyer.warn=You still have not done your {0} payment!\nPlease note that the trade has to be completed by {1}. portfolio.pending.step2_buyer.openForDispute=You have not completed your payment!\nThe max. period for the trade has elapsed.Please contact the mediator for assistance. portfolio.pending.step2_buyer.paperReceipt.headline=Bạn đã gửi giấy biên nhận cho người bán BTC chưa? @@ -882,7 +873,6 @@ funds.locked.locked=Khóa trong multisig để giao dịch với ID: {0} funds.tx.direction.sentTo=Gửi đến: funds.tx.direction.receivedWith=Nhận với: funds.tx.direction.genesisTx=Từ giao dịch gốc: -funds.tx.txFeePaymentForBsqTx=Phí đào cho giao dịch BSQ funds.tx.createOfferFee=Người tạo và phí tx: {0} funds.tx.takeOfferFee=Người nhận và phí tx: {0} funds.tx.multiSigDeposit=Tiền gửi Multisig: {0} @@ -896,13 +886,11 @@ funds.tx.unknown=Không rõ lý do: {0} funds.tx.noFundsFromDispute=KHÔNG HOÀN LẠI từ khiếu nại funds.tx.receivedFunds=Vốn đã nhận funds.tx.withdrawnFromWallet=rút từ ví -funds.tx.withdrawnFromBSQWallet=BTC được rút từ ví BSQ funds.tx.memo=Memo funds.tx.noTxAvailable=Không có giao dịch nào funds.tx.revert=Khôi phục funds.tx.txSent=GIao dịch đã gửi thành công tới địa chỉ mới trong ví Bisq nội bộ. funds.tx.direction.self=Gửi cho chính bạn -funds.tx.daoTxFee=Phí đào cho giao dịch BSQ funds.tx.reimbursementRequestTxFee=Yêu cầu bồi hoàn funds.tx.compensationRequestTxFee=Yêu cầu bồi thường funds.tx.dustAttackTx=Số dư nhỏ đã nhận @@ -996,9 +984,7 @@ settings.tab.about=Về setting.preferences.general=Tham khảo chung setting.preferences.explorer=Bitcoin Explorer -setting.preferences.explorer.bsq=Bisq Explorer setting.preferences.deviation=Sai lệch tối đa so với giá thị trường -setting.preferences.bsqAverageTrimThreshold=Outlier threshold for BSQ rate setting.preferences.avoidStandbyMode=Tránh để chế độ chờ setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled @@ -1032,19 +1018,6 @@ setting.preferences.notifyOnPreRelease=Receive pre-release notifications setting.preferences.resetAllFlags=Cài đặt lại tất cả nhãn \"Không hiển thị lại\" settings.preferences.languageChange=Áp dụng thay đổi ngôn ngữ cho tất cả màn hình yêu cầu khởi động lại. settings.preferences.supportLanguageWarning=In case of a dispute, please note that mediation is handled in {0} and arbitration in {1}. -setting.preferences.daoOptions=Tùy chọn DAO -setting.preferences.dao.resyncFromGenesis.label=Tái dựng trạng thái DAO từ giao dịch 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.resync=Resync from genesis and shutdown -setting.preferences.dao.isDaoFullNode=Chạy ứng dụng Bisq như một full node DAO -setting.preferences.dao.rpcUser=Tên người dùng RPC -setting.preferences.dao.rpcPw=Mật khẩu RPC -setting.preferences.dao.blockNotifyPort=Cổng thông báo chặn -setting.preferences.dao.fullNodeInfo=Để chạy Bisq như một DAO full node, bạn cần phải chạy Bitcoin Core trên máy tính của mình và cho phép RPC. Tất cả các yêu cầu khác được nêu rõ tại ''{0}''.\n\nSau khi thay đổi chế độ bạn cần phải khởi động lại. -setting.preferences.dao.fullNodeInfo.ok=Mở trang docs -setting.preferences.dao.fullNodeInfo.cancel=Không, tôi sẽ tiếp tục dùng chế độ lite node settings.preferences.editCustomExplorer.headline=Explorer Settings settings.preferences.editCustomExplorer.description=Choose a system defined explorer from the list on the left, and/or customize to suit your own preferences. settings.preferences.editCustomExplorer.available=Available explorers @@ -1090,7 +1063,7 @@ settings.net.needRestart=Bạn cần khởi động lại ứng dụng để tha settings.net.notKnownYet=Chưa biết... 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 DAO chain height: {0} | Bitcoin Peers chain height: {1} +settings.net.chainHeight=Bitcoin Peers chain height: {1} settings.net.ips=[Địa chỉ IP:tên cổng | máy chủ:cổng | Địa chỉ onion:cổng] (tách bằng dấu phẩy). Cổng có thể bỏ qua nếu sử dụng mặc định (8333). settings.net.seedNode=nút cung cấp thông tin settings.net.directPeer=Đối tác (trực tiếp) @@ -1144,14 +1117,10 @@ setting.about.shortcuts.walletDetails=Open wallet details window setting.about.shortcuts.openEmergencyBtcWalletTool=Open emergency wallet tool for BTC wallet -setting.about.shortcuts.openEmergencyBsqWalletTool=Open emergency wallet tool for BSQ wallet - setting.about.shortcuts.showTorLogs=Toggle log level for Tor messages between DEBUG and WARN setting.about.shortcuts.manualPayoutTxWindow=Open window for manual payout from 2of2 Multisig deposit tx -setting.about.shortcuts.reRepublishAllGovernanceData=Republish DAO governance data (proposals, votes) - setting.about.shortcuts.removeStuckTrade=Open popup to move failed trade to open trades tab again setting.about.shortcuts.removeStuckTrade.value=Select failed trade and press: {0} @@ -1337,687 +1306,6 @@ account.notifications.noWebCamFound.warning=Không tìm thấy webcam.\n\nVui l account.notifications.priceAlert.warning.highPriceTooLow=Giá cao hơn phải lớn hơn giá thấp hơn. account.notifications.priceAlert.warning.lowerPriceTooHigh=Giá thấp hơn phải nhỏ hơn giá cao hơn. - - - -#################################################################### -# DAO -#################################################################### - -dao.tab.factsAndFigures=Thông tin & Số liệu -dao.tab.bsqWallet=Ví BSQ -dao.tab.proposals=Đề xuất -dao.tab.bonding=Tài sản đảm bảo -dao.tab.proofOfBurn=Phí niêm yết tài sản/ Bằng chứng đốt -dao.tab.monitor=Giám sát mạng -dao.tab.news=Tin tức - -dao.paidWithBsq=thanh toán bằng BSQ -dao.availableBsqBalance=Số dư khả dụng (những dầu ra thay đổi đã và chưa xác nhận) -dao.verifiedBsqBalance=Số dư tất cả các đầu ra giao dịch chưa sử dụng đã xác minh -dao.unconfirmedChangeBalance=Số dư tất cả các đầu ra thay đổi chưa xác nhận -dao.unverifiedBsqBalance=Số dư tất cả các giao dịch chưa xác minh (đang chờ xác nhận) -dao.lockedForVoteBalance=Dùng để bỏ phiếu -dao.lockedInBonds=Bị khóa trong hợp đồng -dao.availableNonBsqBalance=Số dư không phải BSQ có sẵn (tính bằng BTC) -dao.reputationBalance=Merit Value (not spendable) - -dao.tx.published.success=Giao dịch của bạn đã được công bố thành công. -dao.proposal.menuItem.make=Tạo đề nghị -dao.proposal.menuItem.browse=Xem những chào giá đang mở -dao.proposal.menuItem.vote=Bỏ phiếu chọn đề xuất -dao.proposal.menuItem.result=Kết quả bỏ phiếu -dao.cycle.headline=Vòng bỏ phiếu -dao.cycle.overview.headline=Tổng quan vòng bỏ phiếu -dao.cycle.currentPhase=Giai đoạn hiện tại -dao.cycle.currentBlockHeight=Chiều cao khối hiện tại -dao.cycle.proposal=Giai đoạn đề xuất -dao.cycle.proposal.next=Giai đoạn đề xuất tiếp theo -dao.cycle.blindVote=Mở để bỏ phiếu -dao.cycle.voteReveal=Mở để công khai khóa bỏ phiếu -dao.cycle.voteResult=Kết quả bỏ phiếu -dao.cycle.phaseDuration={0} khối(≈{1}); Khối{2} - {3} (≈{4} - ≈{5}) -dao.cycle.phaseDurationWithoutBlocks=Khối {0} - {1} (≈{2} - ≈{3}) - -dao.voteReveal.txPublished.headLine=Giao dịch công khai phiếu bầu đã công bố -dao.voteReveal.txPublished=Giao dịch công khai phiếu bầu của bạn với ID giao dịch {0} dã được công bố thành công.\n\nQuá trình này xả ra tự động bở phần mềm nếu bạn đã tham gia bầu chọn DAO. - -dao.results.cycles.header=Các vòng -dao.results.cycles.table.header.cycle=Vòng -dao.results.cycles.table.header.numProposals=Đề xuất -dao.results.cycles.table.header.voteWeight=Sức nặng của phiếu bầu -dao.results.cycles.table.header.issuance=Phát hành - -dao.results.results.table.item.cycle=Vòng {0} bắt đầu: {1} - -dao.results.proposals.header=Các đề xuất của vòng được chọn -dao.results.proposals.table.header.nameLink=Name/link -dao.results.proposals.table.header.details=Thông tin chi tiết -dao.results.proposals.table.header.myVote=Phiếu bầu của tôi -dao.results.proposals.table.header.result=Kết quả bầu chọn -dao.results.proposals.table.header.threshold=Threshold -dao.results.proposals.table.header.quorum=Quorum - -dao.results.proposals.voting.detail.header=Kết quả của đề xuất được chọn - -dao.results.exceptions=(Các) kết quả bỏ phiếu ngoại lệ - -# suppress inspection "UnusedProperty" -dao.param.UNDEFINED=Không xác định - -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BSQ=Phí maker BSQ -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BSQ=Phí taker BSQ -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BSQ=Phí maker BSQ tối thiểu -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BSQ=Phí taker BSQ tối thiểu -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BTC=Phí maker BTC -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BTC=Phí taker BTC -# suppress inspection "UnusedProperty" -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BTC=Phí maker BTC tối thiểu -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BTC=Phí taker BTC tối thiểu -# suppress inspection "UnusedProperty" - -# suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=Phí đề xuất tính bằng BSQ -# suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=Phí bỏ phiếu tính bằng BSQ - -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=Lượng BSQ yêu cầu bồi thường tối thiểu -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=Lượng BSQ yêu cầu bồi thường tối đa -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MIN_AMOUNT=Lượng BSQ yêu cầu bồi hoàn tối thiểu -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MAX_AMOUNT=Lượng BSQ yêu cầu bồi hoàn tối đa - -# suppress inspection "UnusedProperty" -dao.param.QUORUM_GENERIC=Số đại biểu tính theo BSQ cần cho một đề xuất chung -# suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=Số đại biểu tính theo BSQ cần cho một yêu cầu bồi thường -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REIMBURSEMENT=Số đại biểu tính theo BSQ cần cho một yêu cầu bồi hoàn -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=Số đại biểu tính theo BSQ cần để thay đổi một thông số -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=Số đại biểu tính theo BSQ cần để gỡ bỏ một tài sản -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=Số đại biểu tính theo BSQ cần cho một yêu cầu tịch thu -# suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=Số đại biểu tính theo BSQ cần cho những yêu cầu về vai trò đảm bảo - -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_GENERIC=Ngưỡng cần thiết tính theo % cho một yêu cầu chung -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=Ngưỡng cần thiết tính theo % cho một yêu cầu bồi thường -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REIMBURSEMENT=Ngưỡng cần thiết tính theo % cho một yêu cầu bồi hoàn -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=Ngưỡng cần thiết tính theo % để thay đổi một thông số -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=Ngưỡng cần thiết tính theo % để gỡ bỏ một tài sản -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=Ngưỡng cần thiết tính theo % cho một yêu cầu tịch thu -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_ROLE=Ngưỡng cần thiết tính theo % cho các yêu cầu về vai trò đảm bảo - -# suppress inspection "UnusedProperty" -dao.param.RECIPIENT_BTC_ADDRESS=Địa chỉ BTC của người nhận - -# suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=Phí niêm yết tài sản/ ngày -# suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=Khối lượng giao dịch tối thiểu cho tài sản - -# suppress inspection "UnusedProperty" -dao.param.LOCK_TIME_TRADE_PAYOUT=Thời gian khóa dành cho tx trả phí giao dịch thay thế -# suppress inspection "UnusedProperty" -dao.param.ARBITRATOR_FEE=Phí trọng tài tính bằng BTC - -# suppress inspection "UnusedProperty" -dao.param.MAX_TRADE_LIMIT=Giới giạn giao dịch tối đa tính bằng BTC - -# suppress inspection "UnusedProperty" -dao.param.BONDED_ROLE_FACTOR=Yếu tố đơn vị của vai trò đảm bảo tính bằng BSQ -# suppress inspection "UnusedProperty" -dao.param.ISSUANCE_LIMIT=Giới hạn phát hành trên mỗi vòng tính bằng BSQ - -dao.param.currentValue=Giá trị hiện tại: {0} -dao.param.currentAndPastValue=Current value: {0} (Value when proposal was made: {1}) -dao.param.blocks={0} khối - -dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was not distributed well in the Bisq network.\n{0} - -# suppress inspection "UnusedProperty" -dao.phase.PHASE_UNDEFINED=Không xác định -# suppress inspection "UnusedProperty" -dao.phase.PHASE_PROPOSAL=Giai đoạn đề xuất -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK1=Tạm dừng 1 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=Mở để bỏ phiếu -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK2=Tạm dừng 2 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=Mở để công khai khóa bỏ phiếu -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK3=Tạm dừng 3 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_RESULT=Giai đoạn kết quả - -dao.results.votes.table.header.stakeAndMerit=Sức nặng của phiếu bầu -dao.results.votes.table.header.stake=Tiền đầu tư -dao.results.votes.table.header.merit=Kiếm được -dao.results.votes.table.header.vote=Bỏ phiếu - -dao.bond.menuItem.bondedRoles=Vai trò đảm bảo -dao.bond.menuItem.reputation=Danh tiếng tài sản đảm bảo -dao.bond.menuItem.bonds=Tài sản đảm bảo - -dao.bond.dashboard.bondsHeadline=Tài sản đảm bảo BSQ -dao.bond.dashboard.lockupAmount=Quỹ khóa -dao.bond.dashboard.unlockingAmount=Quỹ đang mở khóa (chờ tới hết thời gian khóa) - - -dao.bond.reputation.header=Khóa tài sản để tạo danh tiếng -dao.bond.reputation.table.header=Tài sản đảm bảo danh tiếng của tôi -dao.bond.reputation.amount=Lượng BSQ để khóa -dao.bond.reputation.time=Thời gian mở khóa tính bằng khối -dao.bond.reputation.salt=Salt -dao.bond.reputation.hash=Hash -dao.bond.reputation.lockupButton=Khóa -dao.bond.reputation.lockup.headline=Xác nhận giao dịch khóa -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.unlock.headline=Xác nhận giao dịch mở khóa -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.allBonds.header=Tất cả cách tài sản đảm bảo - -dao.bond.bondedReputation=Danh tiếng được đảm bảo bằng tài sản -dao.bond.bondedRoles=Vai trò đảm bảo - -dao.bond.details.header=Chi tiết về vai trò -dao.bond.details.role=Vai trò -dao.bond.details.requiredBond=Tài sản đảm bảo BSQ yêu cầu -dao.bond.details.unlockTime=Thời gian mở khóa tính bằng khối -dao.bond.details.link=Đường dẫn tới mô tả vai trò -dao.bond.details.isSingleton=Có thể được thực hiện bởi nhiều người với vai trò khác nhau -dao.bond.details.blocks={0} khối - -dao.bond.table.column.name=Tên -dao.bond.table.column.link=đường dẫn -dao.bond.table.column.bondType=Loại tài sản đảm bảo -dao.bond.table.column.details=Thông tin chi tiết -dao.bond.table.column.lockupTxId=Mã giao dịch khóa -dao.bond.table.column.bondState=Trạng thái tài sản đảm bảo -dao.bond.table.column.lockTime=Unlock time -dao.bond.table.column.lockupDate=Ngày khóa - -dao.bond.table.button.lockup=Khóa -dao.bond.table.button.unlock=Mở khóa -dao.bond.table.button.revoke=Hủy - -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNDEFINED=Không xác định -# suppress inspection "UnusedProperty" -dao.bond.bondState.READY_FOR_LOCKUP=Chưa có tài sản đảm bảo -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_PENDING=Chờ khóa -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_CONFIRMED=Tài sản đảm bảo đã khóa -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_PENDING=Chờ mở khóa -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_CONFIRMED=Giao dịch mở khóa đã xác nhận -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKING=Tài sản đảm bảo đang mở khóa -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKED=Tài sản đảm bảo đã mở khóa -# suppress inspection "UnusedProperty" -dao.bond.bondState.CONFISCATED=Tài sản đảm bảo đã bị tịch thu - -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.UNDEFINED=Không xác định -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.BONDED_ROLE=Vai trò đảm bảo -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.REPUTATION=Danh tiếng tài sản đảm bảo - -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.UNDEFINED=Không xác định -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Quản trị Github -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=Quản trị diễn đàn -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=Quản trị Twitter -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase admin -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=Quản trị Youtube -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=Nhân viên bảo trì Bisq -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=Bên bảo trì BitcoinJ-fork -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Bên bảo trì Netlayer -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=Điều hành website -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=Điều hành diễn đàn -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.SEED_NODE_OPERATOR=Người chạy seed node -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=Điều hành giá node -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=Bitcoin node operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=Điều hành thị trường -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=Explorer operator -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=Điều hành rơ-le thông báo điện thoại -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=Người giữ tên miền -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DNS_ADMIN=Quản trị DNS -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=Người hòa giải -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=Trọng tài -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=Chủ địa chỉ ví BTC hiến tặng - -dao.burnBsq.assetFee=Niêm yết tài sản -dao.burnBsq.menuItem.assetFee=Phí niêm yết tài sản -dao.burnBsq.menuItem.proofOfBurn=Bằng chứng đốt -dao.burnBsq.header=Phí để niêm yết tài sản -dao.burnBsq.selectAsset=Chọn tài sản -dao.burnBsq.fee=Phí -dao.burnBsq.trialPeriod=Giai đoạn dùng thử -dao.burnBsq.payFee=Trả phí -dao.burnBsq.allAssets=Tất cả tài sản -dao.burnBsq.assets.nameAndCode=Tên tài sản -dao.burnBsq.assets.state=Trạng thái -dao.burnBsq.assets.tradeVolume=Khối lượng giao dịch -dao.burnBsq.assets.lookBackPeriod=Giai đoạn xác minh -dao.burnBsq.assets.trialFee=Phí cho giai đoạn dùng thử -dao.burnBsq.assets.totalFee=Tổng số phí đã trả -dao.burnBsq.assets.days={0} ngày -dao.burnBsq.assets.toFewDays=Phí tài sản quá thấp. Số ngày tối thiểu cho giai đoạn dùng thử là {0}. - -# suppress inspection "UnusedProperty" -dao.assetState.UNDEFINED=Không xác định -# suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=Đang trong giai đoạn dùng thử -# suppress inspection "UnusedProperty" -dao.assetState.ACTIVELY_TRADED=Đang được giao dịch tích cực -# suppress inspection "UnusedProperty" -dao.assetState.DE_LISTED=Bị gỡ bỏ vì ít hoạt động -# suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=Bị gỡ bỏ thông qua bỏ phiếu - -dao.proofOfBurn.header=Bằng chứng đốt -dao.proofOfBurn.amount=Số tiền -dao.proofOfBurn.preImage=Pre-image -dao.proofOfBurn.burn=Đốt -dao.proofOfBurn.allTxs=Tất cả bằng chứng của các giao dịch đốt -dao.proofOfBurn.myItems=Bằng chứng giao dịch đốt của tôi -dao.proofOfBurn.date=Ngày -dao.proofOfBurn.hash=Hash -dao.proofOfBurn.txs=Giao dịch -dao.proofOfBurn.pubKey=Pubkey -dao.proofOfBurn.signature.window.title=Ký một thông điệp với khóa lấy từ bằng chứng giao dịch đót -dao.proofOfBurn.verify.window.title=Xác minh một thông điệp với khóa lấy từ bằng chứng giao dịch đốt -dao.proofOfBurn.copySig=Sao chép chữ ký tới clipboard -dao.proofOfBurn.sign=Ký -dao.proofOfBurn.message=Tin nhắn -dao.proofOfBurn.sig=Chữ ký -dao.proofOfBurn.verify=Xác minh -dao.proofOfBurn.verificationResult.ok=Xác minh thành công -dao.proofOfBurn.verificationResult.failed=Xác minh thất bại - -# suppress inspection "UnusedProperty" -dao.phase.UNDEFINED=Không xác định -# suppress inspection "UnusedProperty" -dao.phase.PROPOSAL=Giai đoạn đề xuất -# suppress inspection "UnusedProperty" -dao.phase.BREAK1=Ngừng trước khi bắt đầu bỏ phiếu -# suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=Mở để bỏ phiếu -# suppress inspection "UnusedProperty" -dao.phase.BREAK2=Ngừng trước khi bắt đầu xác nhận bỏ phiếu -# suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=Mở để công khai khóa bỏ phiếu -# suppress inspection "UnusedProperty" -dao.phase.BREAK3=Ngừng trước khi bắt đầu giai đoạn phát hành -# suppress inspection "UnusedProperty" -dao.phase.RESULT=giai đoạn kết quả bỏ phiếu - -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.PROPOSAL=Giai đoạn đề xuất -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=Bỏ phiếu mù -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.VOTE_REVEAL=Công khai phiếu bầu -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.RESULT=Kết quả bỏ phiếu - -# suppress inspection "UnusedProperty" -dao.proposal.type.UNDEFINED=Không xác định -# suppress inspection "UnusedProperty" -dao.proposal.type.COMPENSATION_REQUEST=Yêu cầu bồi thường -# suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=Yêu cầu bồi hoàn -# suppress inspection "UnusedProperty" -dao.proposal.type.BONDED_ROLE=Đề xuất một vai trò đảm bảo -# suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=Đề xuất gỡ bỏ một tài sản -# suppress inspection "UnusedProperty" -dao.proposal.type.CHANGE_PARAM=Đề xuất thay đổi một thông số -# suppress inspection "UnusedProperty" -dao.proposal.type.GENERIC=Đề xuất chung -# suppress inspection "UnusedProperty" -dao.proposal.type.CONFISCATE_BOND=Đề xuất tịch thu tài sản đảm bảo - -# suppress inspection "UnusedProperty" -dao.proposal.type.short.UNDEFINED=Không xác định -# suppress inspection "UnusedProperty" -dao.proposal.type.short.COMPENSATION_REQUEST=Yêu cầu bồi thường -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=Yêu cầu bồi hoàn -# suppress inspection "UnusedProperty" -dao.proposal.type.short.BONDED_ROLE=Vai trò đảm bảo -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ASSET=Gỡ bỏ một altcoin -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CHANGE_PARAM=Thay đổi một thông số -# suppress inspection "UnusedProperty" -dao.proposal.type.short.GENERIC=Đề xuất chung -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CONFISCATE_BOND=Tịch thu tài sản đảm bảo - -dao.proposal.details=Thông tin về đề xuất -dao.proposal.selectedProposal=Đề xuất được chọn -dao.proposal.active.header=Các đề xuất có hiệu lực -dao.proposal.active.remove.confirm=Bạn có chắc bạn muốn gỡ bỏ đề xuất này?\nPhí đề xuất đã trả sẽ bị mất. -dao.proposal.active.remove.doRemove=Vâng, xin hãy gở đề xuất của tôi -dao.proposal.active.remove.failed=Không thể gỡ bỏ đề xuất. -dao.proposal.myVote.title=Bỏ phiếu -dao.proposal.myVote.accept=Chấp nhận đề xuất -dao.proposal.myVote.reject=Từ chối đề xuất -dao.proposal.myVote.removeMyVote=Bỏ qua đề xuất -dao.proposal.myVote.merit=Trọng lượng phiếu bầu từ BSQ kiếm được -dao.proposal.myVote.stake=Tiền đầu tư BSQ để bỏ phiếu -dao.proposal.myVote.revealTxId=Mã giao dịch công khai phiếu bầu -dao.proposal.myVote.stake.prompt=Số tiền cọc khả dụng tối đa để bỏ phiếu: {0} -dao.proposal.votes.header=Cài đặt tiền cọc dành cho bỏ phiếu và công bố các phiếu bầu -dao.proposal.myVote.button=Công bố phiếu bầu -dao.proposal.myVote.setStake.description=Sau khi bỏ phiếu chọn các đề xuất bạn phải cài đặt số tiền cọc dành cho việc bỏ phiếu bằng cách khóa BSQ lại. Khóa càng nhiều BSQ phiếu bầu của bạn càng có nhiều trọng lượng.\n\nBSQ bị khóa trong quá trình bỏ phiếu sẽ được mở khóa lại trong giai đoạn công khai phiếu bầu. -dao.proposal.create.selectProposalType=Chọn kiểu đề xuất -dao.proposal.create.phase.inactive=Vui lòng chờ tới giai đoạn đề xuất tiếp theo -dao.proposal.create.proposalType=Kiểu đề xuất -dao.proposal.create.new=Tạo đề xuất mới -dao.proposal.create.button=Tạo đề nghị -dao.proposal.create.publish=Công bố đề xuất -dao.proposal.create.publishing=Đang tiến hành công bố đề xuất... -dao.proposal=đề xuất -dao.proposal.display.type=Kiểu đề xuất -dao.proposal.display.name=Exact GitHub username -dao.proposal.display.link=Link to detailed info -dao.proposal.display.link.prompt=Link to proposal -dao.proposal.display.requestedBsq=Số lượng yêu cầu tính theo BSQ -dao.proposal.display.txId=ID giao dịch đề xuất -dao.proposal.display.proposalFee=Phí đề xuất -dao.proposal.display.myVote=Phiếu bầu của tôi -dao.proposal.display.voteResult=Tóm tắt kết quả bỏ phiếu -dao.proposal.display.bondedRoleComboBox.label=Loại vi trò được đảm bảo -dao.proposal.display.requiredBondForRole.label=Tài sản đảm bảo BSQ yêu cầu cho vai trò -dao.proposal.display.option=Tùy chọn - -dao.proposal.table.header.proposalType=Kiểu đề xuất -dao.proposal.table.header.link=đường dẫn -dao.proposal.table.header.myVote=Phiếu bầu của tôi -# suppress inspection "UnusedProperty" -dao.proposal.table.header.remove=Xoá -dao.proposal.table.icon.tooltip.removeProposal=Gỡ bỏ đề xuất của tôi -dao.proposal.table.icon.tooltip.changeVote=phiếu bầu hiện tại: ''{0}''. Thay đổi phiếu bầu tới: ''{1}'' - -dao.proposal.display.myVote.accepted=Chấp nhận -dao.proposal.display.myVote.rejected=Từ chối -dao.proposal.display.myVote.ignored=Bỏ qua -dao.proposal.display.myVote.unCounted=Vote was not included in result -dao.proposal.myVote.summary=Voted: {0}; Vote weight: {1} (earned: {2} + stake: {3}) {4} -dao.proposal.myVote.invalid=Phiếu bầu không hợp lệ - -dao.proposal.voteResult.success=Chấp nhận -dao.proposal.voteResult.failed=Từ chối -dao.proposal.voteResult.summary=Kết quả: {0}; Ngưỡng: {1} (yêu cầu > {2}); Đại biểu: {3} (yêu cầu > {4}) - -dao.proposal.display.paramComboBox.label=Hãy chọn thông số cần thay đổi -dao.proposal.display.paramValue=Giá trị thông số - -dao.proposal.display.confiscateBondComboBox.label=Chọn tài sản đảm bảo -dao.proposal.display.assetComboBox.label=Tài sản cần gỡ bỏ - -dao.blindVote=bỏ phiếu mù - -dao.blindVote.startPublishing=Công bố giao dịch bỏ phiếu mù... -dao.blindVote.success=Giao dịch bỏ phiếu mù của bạn đã được công bố thành công.\n\nVui lòng chú ý rằng, bạn phải trực tuyến trong giai đoạn công khai phiếu bầu thì ứng dụng Bisq của bạn mới có thể công bố giao dịch công khai phiếu bầu. Nếu không có giao dịch công khai phiếu bầu, phiếu của bạn sẽ không hợp lệ! - -dao.wallet.menuItem.send=Gửi -dao.wallet.menuItem.receive=Nhận -dao.wallet.menuItem.transactions=Giao dịch - -dao.wallet.dashboard.myBalance=Số dư trong ví của tôi - -dao.wallet.receive.fundYourWallet=Địa chỉ nhận BSQ của bạn -dao.wallet.receive.bsqAddress=Đại chỉ ví BSQ (Địa chỉ mới chưa sử dụng) - -dao.wallet.send.sendFunds=Gửi vốn -dao.wallet.send.sendBtcFunds=Gửi các loại tiền không phải BSQ (BTC) -dao.wallet.send.amount=Số tiền tính bằng BQS -dao.wallet.send.btcAmount=Số tiền tính bằng BTC (các loại tiền không phải BSQ) -dao.wallet.send.setAmount=Cài đặt số tiền được rút (số tiền tối thiểu là {0}) -dao.wallet.send.receiverAddress=Địa chỉ BSQ của người nhận -dao.wallet.send.receiverBtcAddress=Địa chỉ BTC của người nhận -dao.wallet.send.setDestinationAddress=Điền địa chỉ đến của bạn -dao.wallet.send.send=Gửi vốn BSQ -dao.wallet.send.inputControl=Select inputs -dao.wallet.send.sendBtc=Gửi vốn BTC -dao.wallet.send.sendFunds.headline=Xác nhận yêu cầu rút -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=Khối đã xác minh mới nhất: {0} -dao.wallet.chainHeightSyncing=Đang chờ khối mới... Đã xác nhận{0} / {1} khối -dao.wallet.tx.type=Loại - -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED=Không xác định -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED_TX_TYPE=Không xác định -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNVERIFIED=Giao dịch BSQ chưa xác minh -# suppress inspection "UnusedProperty" -dao.tx.type.enum.INVALID=Giao dịch BSQ không có hiệu lực -# suppress inspection "UnusedProperty" -dao.tx.type.enum.GENESIS=Giao dịch chung -# suppress inspection "UnusedProperty" -dao.tx.type.enum.TRANSFER_BSQ=Chuyển BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.received.TRANSFER_BSQ=BSQ đã nhận -# suppress inspection "UnusedProperty" -dao.tx.type.enum.sent.TRANSFER_BSQ=BSQ đã gửi -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PAY_TRADE_FEE=Phí giao dịch -# suppress inspection "UnusedProperty" -dao.tx.type.enum.COMPENSATION_REQUEST=Phí yêu cầu bồi thường -# suppress inspection "UnusedProperty" -dao.tx.type.enum.REIMBURSEMENT_REQUEST=Phí trả cho yêu cầu bồi hoàn -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROPOSAL=Phí đề xuất -# suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=Phí cho phiếu không -# suppress inspection "UnusedProperty" -dao.tx.type.enum.VOTE_REVEAL=Tiết lộ phiếu bầu -# suppress inspection "UnusedProperty" -dao.tx.type.enum.LOCKUP=Tài sản khóa -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNLOCK=Tài sản mở khóa -# suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=Phí niêm yết tài sản -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=Bằng chứng đốt -# suppress inspection "UnusedProperty" -dao.tx.type.enum.IRREGULAR=Không theo quy cách - -dao.tx.withdrawnFromWallet=BTC rút từ ví -dao.tx.issuanceFromCompReq=Yêu cầu bồi thường/ban hành -dao.tx.issuanceFromCompReq.tooltip=Yêu cầu bồi thường dẫn đến ban hành BSQ mới.\nNgày ban hành: {0} -dao.tx.issuanceFromReimbursement=Yêu cầu/ Phát hành bồi hoàn -dao.tx.issuanceFromReimbursement.tooltip=Yêu cầu bồi hoàn dẫn đến ban hành BSQ mới.\nNgày ban hành: {0} -dao.proposal.create.missingBsqFunds=Bạn không có đủ BSQ để tạo đề xuất. Nếu bạn đang có một giao dịch BSQ chưa xác nhận, bạn phải chờ một xác nhận blockchain bởi vì BSQ chỉ được xác thực khi nó được bao gồm vào một khối. \nCòn thiếu: {0} - -dao.proposal.create.missingBsqFundsForBond=Bạn không có đủ BSQ cho vai trò này. Bạn vẫn có thể công bố đề xuất ngày, nhưng bạn sẽ cần đủ lượng BSQ yêu cầu cho vai trò này nếu nó được chấp nhận.\nCòn thiếu: {0} - -dao.proposal.create.missingMinerFeeFunds=Bạn không đủ BTC để tạo giao dịch đề xuất này. Tất cả các giao dịch BSQ đều yêu cầu phí đào bằng BTC.\nCòn thiếu: {0} - -dao.proposal.create.missingIssuanceFunds=Bạn không có đủ BTC để tạo giao dịch đề xuất này. Tất cả các giao dịch BSQ đều yêu cầu phí đào bằng BTC, và các giao dịch phát hành cũng yêu cầu BTC cho lượng BSQ yêu cầu ({0} Satoshis/BSQ).\nCòn thiếu: {1} - -dao.feeTx.confirm=Xác nhận {0} giao dịch -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.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.news.bisqDAO.title=DAO BISQ -dao.news.bisqDAO.description=Vì BIsq là sàn giao dịch phi tập trung và không bị kiểm duyệt, bởi vậy mô hình vận hành của nó, DAO Bisq và đồng BSQ là công cụ giúp điều này trở thành hiện thực. -dao.news.bisqDAO.readMoreLink=Tìm hiểu thêm về DAO Bisq - -dao.news.pastContribution.title=BẠN ĐÃ THAM GIA ĐÓNG GÓP? YÊU CẦU BSQ -dao.news.pastContribution.description=Nếu như bạn đã tham giao đóng góp cho Bisq, vui lòng sử dụng ví BSQ phía dưới và thực hiện một yêu cầu tham gia vào sự kiện phát hành BSQ genesis. -dao.news.pastContribution.yourAddress=Ví BSQ của bạn -dao.news.pastContribution.requestNow=Yêu cầu ngay - -dao.news.DAOOnTestnet.title=CHẠY DAO BISQ TRÊN TESTNET CỦA CHÚNG TÔI -dao.news.DAOOnTestnet.description=Mainnet DAO Bisq chưa ra mắt nhưng bạn vẫn có thể tìm hiểu về DAO Bisq bằng cách chạy nó trên testnet. -dao.news.DAOOnTestnet.firstSection.title=1. Chuyển qua chế độ Testnet DAO -dao.news.DAOOnTestnet.firstSection.content=1. Chuyển qua chế độ Testnet DAO từ màn hình cài đặt -dao.news.DAOOnTestnet.secondSection.title=2. Kiếm BSQ -dao.news.DAOOnTestnet.secondSection.content=Yêu cầu BSQ trên Slack hoặc Mua BSQ trên Bisq -dao.news.DAOOnTestnet.thirdSection.title=3. Tham gia một vòng bỏ phiếu -dao.news.DAOOnTestnet.thirdSection.content=Tạo đề xuất và bỏ phiếu cho đề xuất để thanh đổi nhiều khía cạnh của Bisq. -dao.news.DAOOnTestnet.fourthSection.title=4. Tìm hiểu về BSQ Block Explorer -dao.news.DAOOnTestnet.fourthSection.content=Vì BSQ chỉa là bitcoin, bạn có thể thấy các giao dịch BSQ trên trình duyện bitcoin Block Explorer của chúng tôi. -dao.news.DAOOnTestnet.readMoreLink=Đọc tài liệu đầy đủ - -dao.monitor.daoState=Trạng thái DAO -dao.monitor.proposals=Trạng thái đề xuất -dao.monitor.blindVotes=Trạng thái bỏ phiếu mù - -dao.monitor.table.peers=Đối tác -dao.monitor.table.conflicts=Xung đột -dao.monitor.state=Trạng thái -dao.monitor.requestAlHashes=Yêu cầu tất cả các Hash -dao.monitor.resync=Đồng bộ lại trạng thái DAO -dao.monitor.table.header.cycleBlockHeight=Chiêu cao khói/vòng -dao.monitor.table.cycleBlockHeight=Vòng{0} / khối{1} -dao.monitor.table.seedPeers=Seed node: {0} - -dao.monitor.daoState.headline=Trạng thái DAO -dao.monitor.daoState.table.headline=Chuỗi Hash trạng thái DAO -dao.monitor.daoState.table.blockHeight=Chiều cao khối -dao.monitor.daoState.table.hash=Hash của trạng thái DAO -dao.monitor.daoState.table.prev=Hash trước đó -dao.monitor.daoState.conflictTable.headline=Hash trạng thái DAO từ đối tác đang trong xung dột -dao.monitor.daoState.utxoConflicts=Xung đột đầu ra giao dịch chưa sử dụng -dao.monitor.daoState.utxoConflicts.blockHeight=Chiều cao khối: {0} -dao.monitor.daoState.utxoConflicts.sumUtxo=Tỏng đầu ra giao dịch chưa sử dụng: {0} BSQ -dao.monitor.daoState.utxoConflicts.sumBsq=Tổng BSQ: {0} BSQ -dao.monitor.daoState.checkpoint.popup=DAO state is not in sync with the network. After restart the DAO state will resync. - -dao.monitor.proposal.headline=Trạng thái đề xuất -dao.monitor.proposal.table.headline=Chuỗi Hash trạng thái đề xuất -dao.monitor.proposal.conflictTable.headline=Hash trạng thái đề xuất từ đối tác đang trong xung dột - -dao.monitor.proposal.table.hash=Hash trạng thái đề xuất -dao.monitor.proposal.table.prev=Hash trước đó -dao.monitor.proposal.table.numProposals=Số đề xuất - -dao.monitor.isInConflictWithSeedNode=Dữ liệu trên máy bạn không đồng bộ với ít nhất một seed node. Vui lòng đồng bộ lại trạng thái DAO. -dao.monitor.isInConflictWithNonSeedNode=Một trong các đối tác của bạn không đồng bộ với mạng nhưng node của bạn vẫn đang đồng bộ với các seed node. -dao.monitor.daoStateInSync=Node trên máy tính của bạn đang dồng bộ với mạng - -dao.monitor.blindVote.headline=Trạng thái bỏ phiếu mù -dao.monitor.blindVote.table.headline=Chuỗi hash trạng thái bỏ phiếu mù -dao.monitor.blindVote.conflictTable.headline=Hash trạng thái bỏ phiếu mù từ đối tác đang trong xung dột -dao.monitor.blindVote.table.hash=Hash trạng thái bỏ phiếu mù -dao.monitor.blindVote.table.prev=Hash trước đó -dao.monitor.blindVote.table.numBlindVotes=Số lượng phiếu mù - -dao.factsAndFigures.menuItem.supply=Lượng cung BSQ -dao.factsAndFigures.menuItem.transactions=Giao dịch 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 BSQ/USD price -dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on 30 days average BSQ/USD price) -dao.factsAndFigures.dashboard.availableAmount=Tổng lượng BSQ hiện có -dao.factsAndFigures.dashboard.volumeUsd=Total trade volume in USD -dao.factsAndFigures.dashboard.volumeBtc=Total trade volume in BTC -dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection=Average BSQ/USD trade price from selected time period in chart -dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection=Average BSQ/BTC trade price from selected time period in chart - -dao.factsAndFigures.supply.issuedVsBurnt=BSQ issued v. BSQ burnt - -dao.factsAndFigures.supply.issued=BSQ đã phát hành -dao.factsAndFigures.supply.compReq=Các yêu cầu bồi thường -dao.factsAndFigures.supply.reimbursement=Reimbursement requests -dao.factsAndFigures.supply.genesisIssueAmount=Lượng BSQ phát hành tại giao dịch Genesis -dao.factsAndFigures.supply.compRequestIssueAmount=Lượng BSQ phát hành dành cho yêu cầu bồi thường -dao.factsAndFigures.supply.reimbursementAmount=Lượng BSQ phát hành dành cho yêu cầu bồi hoàn -dao.factsAndFigures.supply.totalIssued=Total issued BSQ -dao.factsAndFigures.supply.totalBurned=Total burned BSQ -dao.factsAndFigures.supply.chart.tradeFee.toolTip={0}\n{1} -dao.factsAndFigures.supply.burnt=BSQ đã đốt - -dao.factsAndFigures.supply.priceChat=BSQ price -dao.factsAndFigures.supply.volumeChat=Khối lượng giao dịch -dao.factsAndFigures.supply.tradeVolumeInUsd=Trade volume in USD -dao.factsAndFigures.supply.tradeVolumeInBtc=Trade volume in BTC -dao.factsAndFigures.supply.bsqUsdPrice=BSQ/USD price -dao.factsAndFigures.supply.bsqBtcPrice=BSQ/BTC price -dao.factsAndFigures.supply.btcUsdPrice=BTC/USD price - -dao.factsAndFigures.supply.locked=Trạng thái toàn cầu của BSQ đã khóa -dao.factsAndFigures.supply.totalLockedUpAmount=Bị khóa làm tải sản đảm bảo -dao.factsAndFigures.supply.totalUnlockingAmount=Đang mở khóa BSQ từ tài sản đảm bảo -dao.factsAndFigures.supply.totalUnlockedAmount=BSQ đã được mở khóa từ tài sản đảm bảo -dao.factsAndFigures.supply.totalConfiscatedAmount=Lượng BSQ đã tịch thu từ tài sản đảm bảo -dao.factsAndFigures.supply.proofOfBurn=Proof of Burn -dao.factsAndFigures.supply.bsqTradeFee=BSQ Trade fees -dao.factsAndFigures.supply.btcTradeFee=BTC Trade fees - -dao.factsAndFigures.transactions.genesis=Giao dịch chung -dao.factsAndFigures.transactions.genesisBlockHeight=Chiều cao khối Genesis -dao.factsAndFigures.transactions.genesisTxId=ID giao dịch Genesis -dao.factsAndFigures.transactions.txDetails=Thống kê giao dịch BSQ -dao.factsAndFigures.transactions.allTx=Tổng số giao dịch BSQ -dao.factsAndFigures.transactions.utxo=Tổng đầu ra giao dịch chưa sử dụng -dao.factsAndFigures.transactions.compensationIssuanceTx=Tổng số lượng giao dịch phát hành yêu cầu bồi thường -dao.factsAndFigures.transactions.reimbursementIssuanceTx=Tổng số lượng giao dịch phát hành yêu cầu bồi hoàn -dao.factsAndFigures.transactions.burntTx=Tổng số lượng giao dịch thanh toán phí -dao.factsAndFigures.transactions.invalidTx=Số lượng tất cả các giao dịch không hợp lệ -dao.factsAndFigures.transactions.irregularTx=Số lượng tất cả các giao dịch lạ - - - #################################################################### # Windows #################################################################### @@ -2120,8 +1408,6 @@ disputeSummaryWindow.close.noPayout.text=Do you want to close without doing any emptyWalletWindow.headline=Công cụ ví khẩn cấp emptyWalletWindow.info=Vui lòng chỉ sử dụng trong trường hợp khẩn cấp nếu bạn không thể truy cập vốn của bạn từ UI.\n\nLưu ý rằng tất cả Báo giá mở sẽ được tự động đóng khi sử dụng công cụ này.\n\nTrước khi sử dụng công cụ này, vui lòng sao lưu dự phòng thư mục dữ liệu của bạn. Bạn có thể sao lưu tại \"Tài khoản/Sao lưu dự phòng\".\n\nVui lòng báo với chúng tôi vấn đề của bạn và lập báo cáo sự cố trên GitHub hoặc diễn đàn Bisq để chúng tôi có thể điều tra điều gì gây nên vấn đề đó. emptyWalletWindow.balance=Số dư ví hiện tại của bạn -emptyWalletWindow.bsq.btcBalance=Số dư tính bằng Satoshi của tài sản không phải là BSQ - emptyWalletWindow.address=Địa chỉ đến của bạn emptyWalletWindow.button=Gửi tất cả vốn emptyWalletWindow.openOffers.warn=Bạn có chào giá mở sẽ được gỡ bỏ khi bạn rút hết trong ví.\nBạn có chắc chắn muốn rút hết ví của bạn? @@ -2146,10 +1432,8 @@ filterWindow.seedNode=Node cung cấp thông tin đã lọc (địa chỉ onion filterWindow.priceRelayNode=nút rơle giá đã lọc (địa chỉ onion cách nhau bằng dấu phẩy) filterWindow.btcNode=nút Bitcoin đã lọc (địa chỉ cách nhau bằng dấu phẩy + cửa) filterWindow.preventPublicBtcNetwork=Ngăn sử dụng mạng Bitcoin công cộng -filterWindow.disableDao=Tắt DAO filterWindow.disableAutoConf=Disable auto-confirm filterWindow.autoConfExplorers=Filtered auto-confirm explorers (comma sep. addresses) -filterWindow.disableDaoBelowVersion=Phiên bản tối thiểu yêu cầu cho DAO filterWindow.disableTradeBelowVersion=Phiên bản tối thiể yêu cầu cho giao dịch filterWindow.add=Thêm bộ lọc filterWindow.remove=Gỡ bỏ bộ lọc @@ -2223,7 +1507,6 @@ tradeDetailsWindow.detailData=Detail data txDetailsWindow.headline=Transaction Details txDetailsWindow.btc.note=You have sent BTC. -txDetailsWindow.bsq.note=You have sent BSQ funds. BSQ is colored bitcoin, so the transaction will not show in a BSQ explorer until it has been confirmed in a bitcoin block. txDetailsWindow.sentTo=Sent to txDetailsWindow.txId=TxId @@ -2235,9 +1518,6 @@ closedTradesSummaryWindow.totalMinerFee.title=Sum of all miner fees closedTradesSummaryWindow.totalMinerFee.value={0} ({1} of total trade amount) closedTradesSummaryWindow.totalTradeFeeInBtc.title=Sum of all trade fees paid in BTC closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} of total trade amount) -closedTradesSummaryWindow.totalTradeFeeInBsq.title=Sum of all trade fees paid in BSQ -closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} of total trade amount) - walletPasswordWindow.headline=Nhập mật khẩu để mở khóa torNetworkSettingWindow.header=Cài đặt mạng Tor @@ -2317,12 +1597,6 @@ popup.warning.tooLargePercentageValue=Bạn không thể cài đặt phần tră popup.warning.examplePercentageValue=Vui lòng nhập số phần trăm như \"5.4\" cho 5,4% popup.warning.noPriceFeedAvailable=Không có giá cung cấp cho tiền tệ này. Bạn không thể sử dụng giá dựa trên tỷ lệ.\nVui lòng chọn giá cố định. popup.warning.sendMsgFailed=Gửi tin nhắn Đối tác giao dịch không thành công.\nVui lòng thử lại và nếu tiếp tục không thành công thì báo cáo sự cố. -popup.warning.insufficientBtcFundsForBsqTx=Bạn không có đủ vốn BTC để thanh toán phí đào cho giao dịch BSQ này.\nVui lòng nộp tiền vào ví BTC của bạn để có thể chuyển giao BSQ.\nSố tiền còn thiếu: {0} -popup.warning.bsqChangeBelowDustException=This transaction creates a BSQ change output which is below dust limit (5.46 BSQ) and would be rejected by the Bitcoin network.\n\nYou need to either send a higher amount to avoid the change output (e.g. by adding the dust amount to your sending amount) or add more BSQ funds to your wallet so you avoid to generate a dust output.\n\nThe dust output is {0}. -popup.warning.btcChangeBelowDustException=This transaction creates a change output which is below dust limit (546 Satoshi) and would be rejected by the Bitcoin network.\n\nYou need to add the dust amount to your sending amount to avoid to generate a dust output.\n\nThe dust output is {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.noBsqFundsForBtcFeePayment=Ví BSQ của bạn không đủ tiền để trả phí giao dịch bằng BSQ. popup.warning.messageTooLong=Tin nhắn của bạn vượt quá kích cỡ tối đa cho phép. Vui lòng gửi thành nhiều lần hoặc tải lên mạng như https://pastebin.com. popup.warning.lockedUpFunds=You have locked up funds from a failed trade.\nLocked up balance: {0} \nDeposit tx address: {1}\nTrade ID: {2}.\n\nPlease open a support ticket by selecting the trade in the open trades screen and pressing \"alt + o\" or \"option + o\"." @@ -2336,8 +1610,6 @@ popup.warning.nodeBanned=One of the {0} nodes got banned. popup.warning.priceRelay=rơle giá popup.warning.seed=seed popup.warning.mandatoryUpdate.trading=Please update to the latest Bisq version. A mandatory update was released which disables trading for old versions. Please check out the Bisq Forum for more information. -popup.warning.mandatoryUpdate.dao=Please update to the latest Bisq version. A mandatory update was released which disables the Bisq DAO and BSQ for old versions. Please check out the Bisq Forum for more information. -popup.warning.disable.dao=The Bisq DAO and BSQ are temporary disabled. Please check out the Bisq Forum for more information. popup.warning.noFilter=We did not receive a filter object from the seed nodes. This is a not expected situation. Please inform the Bisq developers. popup.warning.burnBTC=Không thể thực hiện giao dịch, vì phí đào {0} vượt quá số lượng {1} cần chuyển. Vui lòng chờ tới khi phí đào thấp xuống hoặc khi bạn tích lũy đủ BTC để chuyển. @@ -2356,7 +1628,6 @@ popup.info.cashDepositInfo.confirm=Tôi xác nhận tôi đã gửi tiền popup.info.shutDownWithOpenOffers=Bisq đang đóng, nhưng vẫn có các chào giá đang mở. \n\nNhững chào giá này sẽ không có tại mạng P2P khi Bisq đang đóng, nhưng chúng sẽ được công bố lại trên mạng P2P vào lần tiếp theo bạn khởi động Bisq.\nĐể giữ các chào giá luôn trực tuyến, vui lòng để Bisq chạy và đảm bảo là máy tính của bạn cũng đang trực tuyến(có nghĩa là đảm bảo là máy tính của bạn không chuyển về chế độ chờ...nếu màn hình về chế độ chờ thì không sao). 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=Thông báo riêng tư quan trọng! @@ -2528,7 +1799,6 @@ navigation.settings.preferences=\"Cài đặt/Tham khảo\" # suppress inspection "UnusedProperty" navigation.funds.transactions=\"Vốn/Giao dịch\" navigation.support=\"Hỗ trợ\" -navigation.dao.wallet.receive=\"DAO/Ví BSQ/Nhận\" #################################################################### @@ -2558,12 +1828,6 @@ XMR_MAINNET=Bitcoin Mainnet XMR_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" XMR_STAGENET=Bitcoin Regtest -# suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet (không tán thành) -# suppress inspection "UnusedProperty" -BTC_DAO_BETANET=Bisq DAO Betanet (Bitcoin Mainnet) -# suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=Bitcoin DAO Regtest time.year=Năm time.month=Tháng @@ -2910,9 +2174,7 @@ validation.accountNrChars=Số tài khoản phải có {0} ký tự. validation.btc.invalidAddress=Địa chỉ không đúng. Vui lỏng kiểm tra lại định dạng địa chỉ. validation.integerOnly=Vui lòng chỉ nhập số nguyên. validation.inputError=Giá trị nhập của bạn gây lỗi:\n{0} -validation.bsq.insufficientBalance=Số dư hiện tại của bạn là {0}. validation.btc.exceedsMaxTradeLimit=Giới hạn giao dịch của bạn là {0}. -validation.bsq.amountBelowMinAmount=Giá trị nhỏ nhất là {0} validation.nationalAccountId={0} phải có {1} số. #new @@ -2934,7 +2196,6 @@ validation.bic.invalidLocationCode=BIC chứa mã vị trí không hợp lệ validation.bic.invalidBranchCode=BIC chứa mã chi nhánh không hợp lệ validation.bic.sepaRevolutBic=Tài khoản Revolut Sepa không được hỗ trợ. validation.btc.invalidFormat=Invalid format for a Bitcoin address. -validation.bsq.invalidFormat=Invalid format for a BSQ address. validation.email.invalidAddress=Địa chỉ không hợp lệ validation.iban.invalidCountryCode=Mã quốc gia không hợp lệ validation.iban.checkSumNotNumeric=Mã kiểm tra phải là số diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index 952e1cc047..76abb22fcf 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -192,7 +192,6 @@ shared.tradeWalletBalance=交易钱包余额 shared.makerTxFee=卖家:{0} shared.takerTxFee=买家:{0} shared.iConfirm=我确认 -shared.tradingFeeInBsqInfo=≈ {0} shared.openURL=打开 {0} shared.fiat=法定货币 shared.crypto=加密 @@ -204,9 +203,6 @@ shared.actions=操作 shared.buyerUpperCase=买家 shared.sellerUpperCase=买家 shared.new=新 -shared.blindVoteTxId=匿名投票交易 ID -shared.proposal=建议 -shared.votes=投票 shared.learnMore=了解更多 shared.dismiss=忽略 shared.selectedArbitrator=选中的仲裁者 @@ -240,7 +236,6 @@ mainView.menu.funds=资金 mainView.menu.support=帮助 mainView.menu.settings=设置 mainView.menu.account=账户 -mainView.menu.dao=DAO mainView.marketPriceWithProvider.label=交易所价格提供商:{0} mainView.marketPrice.bisqInternalPrice=最新 Bisq 交易的价格 @@ -257,13 +252,11 @@ mainView.footer.localhostBitcoinNode=(本地主机) mainView.footer.btcInfo={0} {1} mainView.footer.btcFeeRate=/ 矿工手费率:{0} 聪/字节 mainView.footer.btcInfo.initializing=连接至比特币网络 -mainView.footer.bsqInfo.synchronizing=正在同步 DAO mainView.footer.btcInfo.synchronizingWith=正在通过{0}同步区块:{1}/{2} mainView.footer.btcInfo.synchronizedWith=已通过{0}同步至区块{1} mainView.footer.btcInfo.connectingTo=连接至 mainView.footer.btcInfo.connectionFailed=连接失败: mainView.footer.p2pInfo=比特币网络节点:{0} / Bisq 网络节点:{1} -mainView.footer.daoFullNode=DAO 全节点 mainView.bootstrapState.connectionToTorNetwork=(1/4) 连接至 Tor 网络... mainView.bootstrapState.torNodeCreated=(2/4) Tor 节点已创建 @@ -435,7 +428,6 @@ createOffer.fundsBox.networkFee=矿工手续费 createOffer.fundsBox.placeOfferSpinnerInfo=正在发布报价中... createOffer.fundsBox.paymentLabel=Bisq 交易 ID {0} createOffer.fundsBox.fundsStructure=({0} 保证金,{1} 交易费,{2} 采矿费) -createOffer.fundsBox.fundsStructure.BSQ=({0} 保证金,{1} 采矿费)+ {2} 交易费 createOffer.success.headline=你的报价已经发布 createOffer.success.info=你可以在“业务/未完成报价”页面内管理您的未完成报价。 createOffer.info.sellAtMarketPrice=由于您的价格是持续更新的,因此您将始终以市场价格进行出售。 @@ -636,7 +628,6 @@ portfolio.pending.step2_buyer.amountToTransfer=划转数量 portfolio.pending.step2_buyer.sellersAddress=卖家的 {0} 地址 portfolio.pending.step2_buyer.buyerAccount=您的付款帐户将被使用 portfolio.pending.step2_buyer.paymentStarted=付款开始 -portfolio.pending.step2_buyer.fillInBsqWallet=Pay from BSQ wallet portfolio.pending.step2_buyer.warn=你还没有完成你的 {0} 付款!\n请注意,交易必须在 {1} 之前完成。 portfolio.pending.step2_buyer.openForDispute=您还没有完成您的付款!\n最大交易期限已过。请联系调解员寻求帮助。 portfolio.pending.step2_buyer.paperReceipt.headline=您是否将纸质收据发送给 BTC 卖家? @@ -882,7 +873,6 @@ funds.locked.locked=多重验证冻结交易 ID:{0} funds.tx.direction.sentTo=发送至: funds.tx.direction.receivedWith=接收到: funds.tx.direction.genesisTx=从初始 tx: -funds.tx.txFeePaymentForBsqTx=BSQ tx 的矿工手续费支付 funds.tx.createOfferFee=挂单和tx费用:{0} funds.tx.takeOfferFee=下单和tx费用:{0} funds.tx.multiSigDeposit=多重验证保证金:{0} @@ -896,15 +886,11 @@ funds.tx.unknown=未知原因:{0} funds.tx.noFundsFromDispute=没有退款的纠纷 funds.tx.receivedFunds=收到的资金: funds.tx.withdrawnFromWallet=从钱包提现 -funds.tx.withdrawnFromBSQWallet=BTC 已从 BSQ 钱包中取出 funds.tx.memo=备注 funds.tx.noTxAvailable=没有可用交易 funds.tx.revert=还原 funds.tx.txSent=交易成功发送到本地 Bisq 钱包中的新地址。 funds.tx.direction.self=内部钱包交易 -funds.tx.daoTxFee=BSQ tx 的矿工手续费支付 -funds.tx.reimbursementRequestTxFee=退还申请 -funds.tx.compensationRequestTxFee=报偿申请 funds.tx.dustAttackTx=接受零头 funds.tx.dustAttackTx.popup=这笔交易是发送一个非常小的比特币金额到您的钱包,可能是区块链分析公司尝试监控您的交易。\n\n如果您在交易中使用该交易输出,他们将了解到您很可能也是其他地址的所有者(资金归集)。\n\n为了保护您的隐私,Bisq 钱包忽略了这种零头的消费和余额显示。可以在设置中将输出视为零头时设置阈值量。 @@ -996,9 +982,7 @@ settings.tab.about=关于我们 setting.preferences.general=通用偏好 setting.preferences.explorer=比特币区块浏览器 -setting.preferences.explorer.bsq=Bisq 区块浏览器 setting.preferences.deviation=与市场价格最大差价 -setting.preferences.bsqAverageTrimThreshold=BSQ 率已超过阈值 setting.preferences.avoidStandbyMode=避免待机模式 setting.preferences.autoConfirmXMR=XMR 自动确认 setting.preferences.autoConfirmEnabled=启用 @@ -1032,19 +1016,6 @@ setting.preferences.notifyOnPreRelease=Receive pre-release notifications setting.preferences.resetAllFlags=重置所有“不再提示”的提示 settings.preferences.languageChange=同意重启请求以更换语言 settings.preferences.supportLanguageWarning=如有任何争议,请注意调解在 {0} 处理,仲裁在 {1} 处理。 -setting.preferences.daoOptions=DAO 选项 -setting.preferences.dao.resyncFromGenesis.label=从初始 tx 重构 DAO 状态 -setting.preferences.dao.resyncFromResources.label=从指定资源重新构建 DAO 状态 -setting.preferences.dao.resyncFromResources.popup=应用程序重新启动后,Bisq 网络治理数据将从种子节点重新加载,而 BSQ 同步状态将从创始交易中重新构建。 -setting.preferences.dao.resyncFromGenesis.popup=从创始交易中出现同步会消耗大量时间以及 CPU 资源。您确定要重新同步吗?通常,从最新资源文件进行重新同步就足够了,而且速度更快。\n\n应用程序重新启动后,Bisq 网络治理数据将从种子节点重新加载,而 BSQ 同步状态将从初始交易中重新构建。 -setting.preferences.dao.resyncFromGenesis.resync=从创始区块重新同步并关闭 -setting.preferences.dao.isDaoFullNode=以 DAO 全节点运行 Bisq -setting.preferences.dao.rpcUser=RPC 用户名 -setting.preferences.dao.rpcPw=PRC 密码 -setting.preferences.dao.blockNotifyPort=区块通知端口 -setting.preferences.dao.fullNodeInfo=如果要将 Bisq 以 DAO 全节点运行,您需要在本地运行比特币核心并启用 RPC 。所有的需求都记录在“ {0} ”中。 -setting.preferences.dao.fullNodeInfo.ok=打开文档页面 -setting.preferences.dao.fullNodeInfo.cancel=不,我坚持使用轻节点模式 settings.preferences.editCustomExplorer.headline=浏览设置。 settings.preferences.editCustomExplorer.description=从左侧列表中选择一个系统默认浏览器,或使用您偏好的自定义设置。 settings.preferences.editCustomExplorer.available=可用浏览器 @@ -1090,7 +1061,7 @@ settings.net.needRestart=您需要重启应用程序以同意这次变更。\n settings.net.notKnownYet=至今未知... settings.net.sentData=已发送数据 {0},{1} 条消息,{2} 条消息/秒 settings.net.receivedData=已接收数据 {0},{1} 条消息,{2} 条消息/秒 -settings.net.chainHeight=Bisq DAO chain height: {0} | Bitcoin Peers chain height: {1} +settings.net.chainHeight=Bitcoin Peers chain height: {1} settings.net.ips=添加逗号分隔的 IP 地址及端口,如使用8333端口可不填写。 settings.net.seedNode=种子节点 settings.net.directPeer=节点(直连) @@ -1144,14 +1115,10 @@ setting.about.shortcuts.walletDetails=打开钱包详情窗口 setting.about.shortcuts.openEmergencyBtcWalletTool=打开应急 BTC 钱包工具 -setting.about.shortcuts.openEmergencyBsqWalletTool=打开应急 BSQ 钱包工具 - setting.about.shortcuts.showTorLogs=在 DEBUG 与 WARN 之间切换 Tor 日志等级 setting.about.shortcuts.manualPayoutTxWindow=打开窗口手动支付双重验证存款交易 -setting.about.shortcuts.reRepublishAllGovernanceData=重新推送 DAO 众议厅数据(包括提案以及投票) - setting.about.shortcuts.removeStuckTrade=Open popup to move failed trade to open trades tab again setting.about.shortcuts.removeStuckTrade.value=Select failed trade and press: {0} @@ -1337,687 +1304,6 @@ account.notifications.noWebCamFound.warning=未找到网络摄像头。\n\n请 account.notifications.priceAlert.warning.highPriceTooLow=较高的价格必须大于较低的价格。 account.notifications.priceAlert.warning.lowerPriceTooHigh=较低的价格必须低于较高的价格。 - - - -#################################################################### -# DAO -#################################################################### - -dao.tab.factsAndFigures=确切消息 -dao.tab.bsqWallet=BSQ 钱包 -dao.tab.proposals=管理 -dao.tab.bonding=关系 -dao.tab.proofOfBurn=资产清单挂牌费/烧毁证明 -dao.tab.monitor=网络监视器 -dao.tab.news=新闻 - -dao.paidWithBsq=已用 BSQ 支付 -dao.availableBsqBalance=可用于支出(已验证的+未确认的变更输出) -dao.verifiedBsqBalance=所有已验证的 UTXO 余额 -dao.unconfirmedChangeBalance=已验证的+未确认的变更输出 -dao.unverifiedBsqBalance=所有未验证交易的余额(等待区块确认) -dao.lockedForVoteBalance=用于投票 -dao.lockedInBonds=冻结余额 -dao.availableNonBsqBalance=可用的非 BSQ 余额(BTC) -dao.reputationBalance=声望值(不会花费) - -dao.tx.published.success=你的交易已经成功发布 -dao.proposal.menuItem.make=创建要求 -dao.proposal.menuItem.browse=浏览开启的报偿申请 -dao.proposal.menuItem.vote=为报偿申请投票 -dao.proposal.menuItem.result=投票结果 -dao.cycle.headline=投票周期 -dao.cycle.overview.headline=投票周期总览 -dao.cycle.currentPhase=现阶段 -dao.cycle.currentBlockHeight=当前区块高度: -dao.cycle.proposal=提议阶段 -dao.cycle.proposal.next=下一个提议阶段 -dao.cycle.blindVote=匿名投票阶段 -dao.cycle.voteReveal=投票公示阶段 -dao.cycle.voteResult=投票结果 -dao.cycle.phaseDuration=区块 {0} (≈{1});区块 {2} - {3})(≈{4} - ≈{5}) -dao.cycle.phaseDurationWithoutBlocks=区块 {0} - {1} (≈{2} - ≈{3} ) - -dao.voteReveal.txPublished.headLine=投票公示交易发布 -dao.voteReveal.txPublished=你的交易 ID 为 {0} 投票公示交易已经成功发布。\n\n如果您已经参与了 DAO 投票,那么这将由软件自动完成。 - -dao.results.cycles.header=周期 -dao.results.cycles.table.header.cycle=周期 -dao.results.cycles.table.header.numProposals=请求 -dao.results.cycles.table.header.voteWeight=投票权重 -dao.results.cycles.table.header.issuance=发行 - -dao.results.results.table.item.cycle=周期 {0} 开始于:{1} - -dao.results.proposals.header=选定周期的请求 -dao.results.proposals.table.header.nameLink=名称/链接 -dao.results.proposals.table.header.details=详情 -dao.results.proposals.table.header.myVote=我的投票 -dao.results.proposals.table.header.result=投票结果 -dao.results.proposals.table.header.threshold=阈值 -dao.results.proposals.table.header.quorum=法定人数 - -dao.results.proposals.voting.detail.header=选定提案的投票结果 - -dao.results.exceptions=投票结果异常 - -# suppress inspection "UnusedProperty" -dao.param.UNDEFINED=未定义 - -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BSQ=BSQ 挂单费 -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BSQ=BSQ 买单费 -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BSQ=最小 BSQ 挂单费 -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BSQ=最小 BSQ 买单费 -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BTC=BTC 挂单费 -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BTC=BTC 买单费 -# suppress inspection "UnusedProperty" -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BTC=最小 BTC 挂单费 -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BTC=最小 BTC 买单费 -# suppress inspection "UnusedProperty" - -# suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=BSQ 提案手续费 -# suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=BSQ 投票手续费 - -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=最低 BSQ 报偿申请数量 -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=最高 BSQ 报偿申请数量 -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MIN_AMOUNT=最低 BSQ 退还申请数量 -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MAX_AMOUNT=最高 BSQ 退还申请数量 - -# suppress inspection "UnusedProperty" -dao.param.QUORUM_GENERIC=BSQ 要求的一般提案的仲裁人数 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=BSQ 要求的报偿申请的仲裁人数 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REIMBURSEMENT=BSQ 要求的退还申请的仲裁人数 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=BSQ 要求的改变参数的仲裁人数 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=BSQ 要求的移除资产要求的人数 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=BSQ 要求的没收申请的仲裁人数 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=BSQ 要求的绑定角色的仲裁人数 - -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_GENERIC=普通提案的要求百分比 -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=报偿申请的要求百分比 -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REIMBURSEMENT=退还申请的要求百分比 -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=改变参数的要求百分比 -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=移除资产的要求百分比 -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=没收申请的要求百分比 -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_ROLE=担保角色的要求百分比 - -# suppress inspection "UnusedProperty" -dao.param.RECIPIENT_BTC_ADDRESS=接收方 BTC 地址 - -# suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=资产清单挂牌费每日支付 -# suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=最小资产交易量 - -# suppress inspection "UnusedProperty" -dao.param.LOCK_TIME_TRADE_PAYOUT=其他交易支出tx的锁定时间 -# suppress inspection "UnusedProperty" -dao.param.ARBITRATOR_FEE=BTC 仲裁费 - -# suppress inspection "UnusedProperty" -dao.param.MAX_TRADE_LIMIT=最高 BTC 交易限额 - -# suppress inspection "UnusedProperty" -dao.param.BONDED_ROLE_FACTOR=担保角色对 BSQ 的影响 -# suppress inspection "UnusedProperty" -dao.param.ISSUANCE_LIMIT=每个周期的 BSQ 发行限额 - -dao.param.currentValue=当前值:{0} -dao.param.currentAndPastValue=当前余额:{0}(提案时的余额:{1}) -dao.param.blocks={0} 区块 - -dao.results.invalidVotes=在那个投票周期中,我们有无效的投票。如果投票没有在 Bisq 网络中很好地分布,就会发生这种情况。\n{0} - -# suppress inspection "UnusedProperty" -dao.phase.PHASE_UNDEFINED=未定义 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_PROPOSAL=提议阶段 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK1=休息1 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=匿名投票阶段 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK2=休息2 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=投票公示阶段 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK3=休息3 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_RESULT=结果阶段 - -dao.results.votes.table.header.stakeAndMerit=投票权重 -dao.results.votes.table.header.stake=份额 -dao.results.votes.table.header.merit=获得的 -dao.results.votes.table.header.vote=投票 - -dao.bond.menuItem.bondedRoles=担保角色 -dao.bond.menuItem.reputation=担保的名誉 -dao.bond.menuItem.bonds=担保 - -dao.bond.dashboard.bondsHeadline=担保式 BSQ -dao.bond.dashboard.lockupAmount=锁定资金 -dao.bond.dashboard.unlockingAmount=正在锁定资金(等到锁定时间过后) - - -dao.bond.reputation.header=为名誉锁定担保 -dao.bond.reputation.table.header=我的名誉担保 -dao.bond.reputation.amount=锁定的 BSQ 数量 -dao.bond.reputation.time=在区块中的解锁时间 -dao.bond.reputation.salt=盐 -dao.bond.reputation.hash=哈希 -dao.bond.reputation.lockupButton=锁定 -dao.bond.reputation.lockup.headline=确认锁定交易 -dao.bond.reputation.lockup.details=锁定数量:{0}\n解锁时间:{1} 区块(≈{2})\n\n矿工手续费:{3}({4} 聪/字节)\n交易大小:{5} 字节\n\n你确定想要继续? -dao.bond.reputation.unlock.headline=确认解锁交易 -dao.bond.reputation.unlock.details=解锁金额:{0}\n解锁时间:{1} 区块(≈{2})\n\n挖矿手续费:{3}({4} 聪/Byte)\n交易大小:{5} Kb\n\n你想继续这个操作吗? - -dao.bond.allBonds.header=所有担保 - -dao.bond.bondedReputation=担保的名誉 -dao.bond.bondedRoles=担保角色 - -dao.bond.details.header=交易方详情 -dao.bond.details.role=角色 -dao.bond.details.requiredBond=需要的 BSQ 担保 -dao.bond.details.unlockTime=在区块中的解锁时间 -dao.bond.details.link=链接到交易方描述 -dao.bond.details.isSingleton=是否可由多个交易方担任 -dao.bond.details.blocks={0} 区块 - -dao.bond.table.column.name=名称 -dao.bond.table.column.link=绑定 -dao.bond.table.column.bondType=连接类型 -dao.bond.table.column.details=详情 -dao.bond.table.column.lockupTxId=锁定 Tx ID -dao.bond.table.column.bondState=连接状态 -dao.bond.table.column.lockTime=解锁时间 -dao.bond.table.column.lockupDate=锁定日期 - -dao.bond.table.button.lockup=锁定 -dao.bond.table.button.unlock=解锁 -dao.bond.table.button.revoke=撤销 - -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNDEFINED=未定义 -# suppress inspection "UnusedProperty" -dao.bond.bondState.READY_FOR_LOCKUP=尚未担保 -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_PENDING=等待锁定 -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_CONFIRMED=锁定的担保 -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_PENDING=等待解锁 -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_CONFIRMED=解锁 Tx 确认 -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKING=正在解锁担保 -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKED=担保已解锁 -# suppress inspection "UnusedProperty" -dao.bond.bondState.CONFISCATED=没收的担保 - -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.UNDEFINED=未定义 -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.BONDED_ROLE=担保角色 -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.REPUTATION=担保名誉 - -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.UNDEFINED=未定义 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Github 管理 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=论坛管理 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter 管理 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase 管理员 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube 管理 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=Bisq 运维人员 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork 运维人员 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer 运维人员 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=网站运营者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=论坛运营者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.SEED_NODE_OPERATOR=种子节点运营者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=价格节点运营者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=比特币节点运营者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=交易所运营者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=浏览器运营者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=移动通知中继运营者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=域名持有者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DNS_ADMIN=DNS 管理者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=调解员 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=仲裁员 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC 赞助地址所有者 - -dao.burnBsq.assetFee=资产清单 -dao.burnBsq.menuItem.assetFee=资产清单挂牌费 -dao.burnBsq.menuItem.proofOfBurn=烧毁证明 -dao.burnBsq.header=资产清单挂牌费 -dao.burnBsq.selectAsset=选择资产 -dao.burnBsq.fee=手续费 -dao.burnBsq.trialPeriod=试用期 -dao.burnBsq.payFee=支付手续费 -dao.burnBsq.allAssets=所有资产 -dao.burnBsq.assets.nameAndCode=资产名称 -dao.burnBsq.assets.state=状态 -dao.burnBsq.assets.tradeVolume=交易量 -dao.burnBsq.assets.lookBackPeriod=确认期 -dao.burnBsq.assets.trialFee=试用期手续费 -dao.burnBsq.assets.totalFee=总共已支付费用 -dao.burnBsq.assets.days={0} 天 -dao.burnBsq.assets.toFewDays=资产清单挂牌费过低。在试用期中最低数量为 {0}。 - -# suppress inspection "UnusedProperty" -dao.assetState.UNDEFINED=未定义 -# suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=在试用期 -# suppress inspection "UnusedProperty" -dao.assetState.ACTIVELY_TRADED=活跃的交易 -# suppress inspection "UnusedProperty" -dao.assetState.DE_LISTED=因不活跃而被取消 -# suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=移除投票 - -dao.proofOfBurn.header=烧毁证明 -dao.proofOfBurn.amount=数量 -dao.proofOfBurn.preImage=预览 -dao.proofOfBurn.burn=烧毁 -dao.proofOfBurn.allTxs=所有烧毁证明交易 -dao.proofOfBurn.myItems=我的烧毁证明交易 -dao.proofOfBurn.date=日期 -dao.proofOfBurn.hash=哈希 -dao.proofOfBurn.txs=交易记录 -dao.proofOfBurn.pubKey=公钥 -dao.proofOfBurn.signature.window.title=使用烧毁证明交易中的密钥验证消息 -dao.proofOfBurn.verify.window.title=使用烧毁证明交易中的密钥确认消息 -dao.proofOfBurn.copySig=将验证复制到剪贴板 -dao.proofOfBurn.sign=验证 -dao.proofOfBurn.message=消息 -dao.proofOfBurn.sig=验证 -dao.proofOfBurn.verify=确认 -dao.proofOfBurn.verificationResult.ok=确认成功 -dao.proofOfBurn.verificationResult.failed=确认失败 - -# suppress inspection "UnusedProperty" -dao.phase.UNDEFINED=未定义 -# suppress inspection "UnusedProperty" -dao.phase.PROPOSAL=提议阶段 -# suppress inspection "UnusedProperty" -dao.phase.BREAK1=匿名投票前的休息阶段 -# suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=匿名投票阶段 -# suppress inspection "UnusedProperty" -dao.phase.BREAK2=投票公示前的休息阶段 -# suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=投票公示阶段 -# suppress inspection "UnusedProperty" -dao.phase.BREAK3=公布结果前的休息阶段 -# suppress inspection "UnusedProperty" -dao.phase.RESULT=投票结果阶段 - -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.PROPOSAL=提议阶段 -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=匿名投票 -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.VOTE_REVEAL=投票公示 -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.RESULT=投票结果 - -# suppress inspection "UnusedProperty" -dao.proposal.type.UNDEFINED=未定义 -# suppress inspection "UnusedProperty" -dao.proposal.type.COMPENSATION_REQUEST=报偿申请 -# suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=退还申请 -# suppress inspection "UnusedProperty" -dao.proposal.type.BONDED_ROLE=担保角色的提案 -# suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=移除资产提案 -# suppress inspection "UnusedProperty" -dao.proposal.type.CHANGE_PARAM=修改参数的提议 -# suppress inspection "UnusedProperty" -dao.proposal.type.GENERIC=一般提议 -# suppress inspection "UnusedProperty" -dao.proposal.type.CONFISCATE_BOND=没收担保请求 - -# suppress inspection "UnusedProperty" -dao.proposal.type.short.UNDEFINED=未定义 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.COMPENSATION_REQUEST=报偿申请 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=退还申请 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.BONDED_ROLE=担保角色 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ASSET=移除一个数字货币 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CHANGE_PARAM=修改参数 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.GENERIC=一般提议 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CONFISCATE_BOND=没收担保 - -dao.proposal.details=提议细节 -dao.proposal.selectedProposal=选定赔偿要求 -dao.proposal.active.header=当前周期的提案 -dao.proposal.active.remove.confirm=您确定要移除该提案吗?\n如果您删除该提案,提案费将会丢失。 -dao.proposal.active.remove.doRemove=是,移除我的提案 -dao.proposal.active.remove.failed=不能移除提案。 -dao.proposal.myVote.title=投票 -dao.proposal.myVote.accept=接受提议 -dao.proposal.myVote.reject=拒绝提案 -dao.proposal.myVote.removeMyVote=忽略提案 -dao.proposal.myVote.merit=所得的 BSQ 的投票权重 -dao.proposal.myVote.stake=Stake 的投票权重 -dao.proposal.myVote.revealTxId=投票公示的交易 ID -dao.proposal.myVote.stake.prompt=在投票中最大可用份额:{0} -dao.proposal.votes.header=设置投票的份额,并发布您的投票 -dao.proposal.myVote.button=发布投票 -dao.proposal.myVote.setStake.description=在对所有提案进行投票后,您必须锁定BSQ来设置投票的份额。您锁定的 BSQ 越多,你的投票权重就越大。\n\n投票会锁定 BSQ 将在投票显示阶段再次解锁。 -dao.proposal.create.selectProposalType=选择提案类型 -dao.proposal.create.phase.inactive=请等到下一个提案阶段 -dao.proposal.create.proposalType=提议类型 -dao.proposal.create.new=创建新的赔偿要求 -dao.proposal.create.button=创建赔偿要求 -dao.proposal.create.publish=发布提案 -dao.proposal.create.publishing=正在发布提案中... -dao.proposal=提案 -dao.proposal.display.type=提议类型 -dao.proposal.display.name=确切的 GitHub 的用户名 -dao.proposal.display.link=详情的链接 -dao.proposal.display.link.prompt=提案的链接 -dao.proposal.display.requestedBsq=申请的 BSQ 数量 -dao.proposal.display.txId=提案交易 ID -dao.proposal.display.proposalFee=提案手续费 -dao.proposal.display.myVote=我的投票 -dao.proposal.display.voteResult=投票结果总结 -dao.proposal.display.bondedRoleComboBox.label=担保角色类型 -dao.proposal.display.requiredBondForRole.label=角色需要的担保 -dao.proposal.display.option=选项 - -dao.proposal.table.header.proposalType=提议类型 -dao.proposal.table.header.link=绑定 -dao.proposal.table.header.myVote=我的投票 -# suppress inspection "UnusedProperty" -dao.proposal.table.header.remove=移除 -dao.proposal.table.icon.tooltip.removeProposal=移除我的提案 -dao.proposal.table.icon.tooltip.changeVote=当前投票:“{0}”。更改投票至:“{1}” - -dao.proposal.display.myVote.accepted=已接受 -dao.proposal.display.myVote.rejected=已拒绝 -dao.proposal.display.myVote.ignored=已忽略 -dao.proposal.display.myVote.unCounted=投票结果不包括在内 -dao.proposal.myVote.summary=已投票:{0};投票权重:{1}(获得的:{2} + 奖金:{3})({4}) -dao.proposal.myVote.invalid=投票无效 - -dao.proposal.voteResult.success=已接受 -dao.proposal.voteResult.failed=已拒绝 -dao.proposal.voteResult.summary=结果:{0};阈值:{1}(要求> {2});仲裁人数:{3}(要求> {4}) - -dao.proposal.display.paramComboBox.label=选择需要改变的参数 -dao.proposal.display.paramValue=参数值 - -dao.proposal.display.confiscateBondComboBox.label=选择担保 -dao.proposal.display.assetComboBox.label=需要移除的资产 - -dao.blindVote=匿名投票 - -dao.blindVote.startPublishing=发布匿名投票交易中 -dao.blindVote.success=我们的匿名投票交易已经成功发布。\n\n请注意,您必须在线在投票公示阶段,以便您的 Bisq 应用程序可以发布投票公示交易。没有投票公示交易,您的投票将无效! - -dao.wallet.menuItem.send=发送 -dao.wallet.menuItem.receive=接收 -dao.wallet.menuItem.transactions=交易记录 - -dao.wallet.dashboard.myBalance=我的钱包余额 - -dao.wallet.receive.fundYourWallet=你的 BSQ 接收地址 -dao.wallet.receive.bsqAddress=BSQ 钱包地址(刷新未使用地址) - -dao.wallet.send.sendFunds=提现 -dao.wallet.send.sendBtcFunds=发送非 BSQ 资金(BTC) -dao.wallet.send.amount=BSQ 数量 -dao.wallet.send.btcAmount=BTC 数量(无 BSQ 资金) -dao.wallet.send.setAmount=设置提现数量(最小量 {0}) -dao.wallet.send.receiverAddress=接收者的 BSQ 地址 -dao.wallet.send.receiverBtcAddress=接收者的 BTC 地址 -dao.wallet.send.setDestinationAddress=输入您的目标地址 -dao.wallet.send.send=发送 BSQ 资金 -dao.wallet.send.inputControl=Select inputs -dao.wallet.send.sendBtc=发送 BTC 资金 -dao.wallet.send.sendFunds.headline=确定提现申请 -dao.wallet.send.sendFunds.details=发送:{0}\n来自:{1}\n要求的矿工手续费:{2}({3}比特/节)\n交易大小:{4}字节\n\n接收方会收到:{5}\n\n您确定您想要提现这些数量吗? -dao.wallet.chainHeightSynced=最新确认区块:{0} -dao.wallet.chainHeightSyncing=等待区块... 已确认{0}/{1}区块 -dao.wallet.tx.type=类型 - -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED=未定义 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED_TX_TYPE=不被认可 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNVERIFIED=未验证的 BSQ 交易 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.INVALID=无效的 BSQ 交易 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.GENESIS=初始交易 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.TRANSFER_BSQ=划转 BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.received.TRANSFER_BSQ=接收 BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.sent.TRANSFER_BSQ=发送 BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PAY_TRADE_FEE=付交易费记录 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.COMPENSATION_REQUEST=报偿申请记录 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.REIMBURSEMENT_REQUEST=退还申请的手续费 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROPOSAL=提案手续费 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=投票记录 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.VOTE_REVEAL=投票公示 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.LOCKUP=锁定担保 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNLOCK=解锁担保 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=资产清单挂牌费 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=烧毁证明 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.IRREGULAR=不正常 - -dao.tx.withdrawnFromWallet=BTC 已从钱包中取出 -dao.tx.issuanceFromCompReq=报偿申请/发放 -dao.tx.issuanceFromCompReq.tooltip=导致新 BSQ 发行的报偿请求。\n发行日期:{0} -dao.tx.issuanceFromReimbursement=退还申请/发放 -dao.tx.issuanceFromReimbursement.tooltip=导致新 BSQ 发放的退还申请。\n发放日期: {0} -dao.proposal.create.missingBsqFunds=您没有足够的 BSQ 资金来创建提案。如果您有一个未经确认的 BSQ 交易,您需要等待一个区块链确认,因为 BSQ 只有在包含在一个区块中时才会被验证。\n缺失:{0} - -dao.proposal.create.missingBsqFundsForBond=你没有足够的 BSQ 资金来承担这个角色。您仍然可以发布这个提案,但如果它被接受,您将需要这个角色所需的全部 BSQ 金额。\n缺少:{0} - -dao.proposal.create.missingMinerFeeFunds=您没有足够的BTC资金来支付该提案交易。所有的 BSQ交易需要用 BTC 支付挖矿手续费。\n缺少:{0} - -dao.proposal.create.missingIssuanceFunds=您没有足够的BTC资金来支付该提案交易。所有的 BSQ交易需要用 BTC 支付挖矿手续费以及发起交易也需要用 BTC 支付所需的 BSQ 数量({0} 聪/BSQ)\n缺少:{1} - -dao.feeTx.confirm=确认 {0} 交易 -dao.feeTx.confirm.details={0}手续费:{1}\n矿工手续费:{2}({3} 聪/byte)\n交易大小:{4} Kb\n\n您确定您要发送这个 {5} 交易吗? - -dao.feeTx.issuanceProposal.confirm.details={0}手续费:{1}\n为 BSQ 提案所需要的BTC:{2}({3}聪 / BSQ)\n挖矿手续费:{4}({5}聪 /字节)\n交易大小:{6}Kb\n\n如果你的要求被批准,你将收到你要求数量的 2 个 BSQ 提议的费用。\n\n你确定你想要发布{7}交易? - -dao.news.bisqDAO.title=Bisq DAO -dao.news.bisqDAO.description=正如 Bisq交易是分散的,并且不受审查,它的治理模型也是如此—— Bisq DAO 和 BSQ 是使其成为可能的工具。 -dao.news.bisqDAO.readMoreLink=了解有关 Bisq DAO 的更多信息 - -dao.news.pastContribution.title=过去有所贡献?申请 BSQ -dao.news.pastContribution.description=如果您对 Bisq 有贡献,请使用下面的 BSQ 地址,并申请参与 BSQ 初始分发。 -dao.news.pastContribution.yourAddress=你的 BSQ 钱包地址 -dao.news.pastContribution.requestNow=现在申请 - -dao.news.DAOOnTestnet.title=在我们的测试网络上运行 BISQ DAO -dao.news.DAOOnTestnet.description=核心网络 Bisq DAO 还没有启动,但是您可以通过在我们的测试网络上运行它来了解 Bisq DAO 。 -dao.news.DAOOnTestnet.firstSection.title=1.切换至 DAO 测试网络模式 -dao.news.DAOOnTestnet.firstSection.content=从设置页面切换到 DAO 测试网络。 -dao.news.DAOOnTestnet.secondSection.title=2.获得一些 BSQ -dao.news.DAOOnTestnet.secondSection.content=在 Slack 上申请 BSQ 或在 Bisq 上购买 BSQ 。 -dao.news.DAOOnTestnet.thirdSection.title=3.参与投票周期 -dao.news.DAOOnTestnet.thirdSection.content=就修改 Bisq 的各个方面提出建议并进行表决。 -dao.news.DAOOnTestnet.fourthSection.title=4.探索 BSQ 区块链浏览器 -dao.news.DAOOnTestnet.fourthSection.content=由于 BSQ 只是比特币,你可以看到 BSQ 交易在我们的比特币区块浏览器。 -dao.news.DAOOnTestnet.readMoreLink=阅读完整的文档 - -dao.monitor.daoState=DAO 状态 -dao.monitor.proposals=提案状态 -dao.monitor.blindVotes=匿名投票状态 - -dao.monitor.table.peers=节点 -dao.monitor.table.conflicts=矛盾 -dao.monitor.state=状态 -dao.monitor.requestAlHashes=要求所有哈希 -dao.monitor.resync=重新同步 DAO 状态 -dao.monitor.table.header.cycleBlockHeight=周期/区块高度 -dao.monitor.table.cycleBlockHeight=周期 {0} /区块 {1} -dao.monitor.table.seedPeers=种子节点:{0} - -dao.monitor.daoState.headline=DAO 状态 -dao.monitor.daoState.table.headline=DAO 状态的哈希链 -dao.monitor.daoState.table.blockHeight=区块高度 -dao.monitor.daoState.table.hash=DAO 状态的哈希 -dao.monitor.daoState.table.prev=以前的哈希 -dao.monitor.daoState.conflictTable.headline=来自不同实体的 DAO 状态哈希 -dao.monitor.daoState.utxoConflicts=UTXO 冲突 -dao.monitor.daoState.utxoConflicts.blockHeight=区块高度:{0} -dao.monitor.daoState.utxoConflicts.sumUtxo=所有 UTXO 的总和:{0} BSQ -dao.monitor.daoState.utxoConflicts.sumBsq=所有 BSQ 的总和:{0} BSQ -dao.monitor.daoState.checkpoint.popup=DAO 状态与网络不同步。重启之后,DAO 状态将重新同步。 - -dao.monitor.proposal.headline=提案状态 -dao.monitor.proposal.table.headline=提案状态的哈希链 -dao.monitor.proposal.conflictTable.headline=来自不同实体的提案状态哈希 - -dao.monitor.proposal.table.hash=提案状态的哈希 -dao.monitor.proposal.table.prev=以前的哈希 -dao.monitor.proposal.table.numProposals=提案编号 - -dao.monitor.isInConflictWithSeedNode=您的本地数据与至少一个种子节点不一致。请重新同步 DAO 状态。 -dao.monitor.isInConflictWithNonSeedNode=您的一个对等节点与网络不一致,但您的节点与种子节点同步。 -dao.monitor.daoStateInSync=您的本地节点与网络一致 - -dao.monitor.blindVote.headline=匿名投票状态 -dao.monitor.blindVote.table.headline=匿名投票状态的哈希链 -dao.monitor.blindVote.conflictTable.headline=来自不同实体的匿名投票状态哈希 -dao.monitor.blindVote.table.hash=匿名投票状态的哈希 -dao.monitor.blindVote.table.prev=以前的哈希 -dao.monitor.blindVote.table.numBlindVotes=匿名投票编号 - -dao.factsAndFigures.menuItem.supply=BSQ 供给 -dao.factsAndFigures.menuItem.transactions=BSQ 交易 - -dao.factsAndFigures.dashboard.avgPrice90=90天平均 BSQ/BTC 交易价格 -dao.factsAndFigures.dashboard.avgPrice30=30天平均 BSQ/BTC 交易价格 -dao.factsAndFigures.dashboard.avgUSDPrice90=90 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on 30 days average BSQ/USD price) -dao.factsAndFigures.dashboard.availableAmount=总共可用的 BSQ -dao.factsAndFigures.dashboard.volumeUsd=Total trade volume in USD -dao.factsAndFigures.dashboard.volumeBtc=Total trade volume in BTC -dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection=Average BSQ/USD trade price from selected time period in chart -dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection=Average BSQ/BTC trade price from selected time period in chart - -dao.factsAndFigures.supply.issuedVsBurnt=已发放的 BSQ 已销毁的 BSQ - -dao.factsAndFigures.supply.issued=已发放的 BSQ -dao.factsAndFigures.supply.compReq=赔偿要求 -dao.factsAndFigures.supply.reimbursement=Reimbursement requests -dao.factsAndFigures.supply.genesisIssueAmount=在初始交易中心有问题的 BSQ -dao.factsAndFigures.supply.compRequestIssueAmount=报偿申请发放的 BSQ -dao.factsAndFigures.supply.reimbursementAmount=退还申请发放的 BSQ -dao.factsAndFigures.supply.totalIssued=Total issued BSQ -dao.factsAndFigures.supply.totalBurned=Total burned BSQ -dao.factsAndFigures.supply.chart.tradeFee.toolTip={0}\n{1} -dao.factsAndFigures.supply.burnt=BSQ 烧毁总量 - -dao.factsAndFigures.supply.priceChat=BSQ price -dao.factsAndFigures.supply.volumeChat=交易总量 -dao.factsAndFigures.supply.tradeVolumeInUsd=Trade volume in USD -dao.factsAndFigures.supply.tradeVolumeInBtc=Trade volume in BTC -dao.factsAndFigures.supply.bsqUsdPrice=BSQ/USD price -dao.factsAndFigures.supply.bsqBtcPrice=BSQ/BTC price -dao.factsAndFigures.supply.btcUsdPrice=BTC/USD price - -dao.factsAndFigures.supply.locked=BSQ 全局锁定状态 -dao.factsAndFigures.supply.totalLockedUpAmount=担保的锁定 -dao.factsAndFigures.supply.totalUnlockingAmount=正在从担保解锁 BSQ -dao.factsAndFigures.supply.totalUnlockedAmount=已从担保解锁 BSQ -dao.factsAndFigures.supply.totalConfiscatedAmount=已从担保没收 BSQ -dao.factsAndFigures.supply.proofOfBurn=Proof of Burn -dao.factsAndFigures.supply.bsqTradeFee=BSQ Trade fees -dao.factsAndFigures.supply.btcTradeFee=BTC Trade fees - -dao.factsAndFigures.transactions.genesis=创始交易 -dao.factsAndFigures.transactions.genesisBlockHeight=初始区块高度 -dao.factsAndFigures.transactions.genesisTxId=初始交易 ID -dao.factsAndFigures.transactions.txDetails=BSQ 交易统计 -dao.factsAndFigures.transactions.allTx=所有 BSQ 交易记录 -dao.factsAndFigures.transactions.utxo=所有未用交易的量 -dao.factsAndFigures.transactions.compensationIssuanceTx=所有报偿请求问题的交易记录 -dao.factsAndFigures.transactions.reimbursementIssuanceTx=所有退回申请问题的交易记录 -dao.factsAndFigures.transactions.burntTx=所有费用支付记录 -dao.factsAndFigures.transactions.invalidTx=所有无效交易记录 -dao.factsAndFigures.transactions.irregularTx=所有不正常的交易记录: - - - #################################################################### # Windows #################################################################### @@ -2120,7 +1406,6 @@ disputeSummaryWindow.close.noPayout.text=你想要在未作支付的情况下关 emptyWalletWindow.headline={0} 钱包急救工具 emptyWalletWindow.info=请在紧急情况下使用,如果您无法从 UI 中访问您的资金。\n\n请注意,使用此工具时,所有未结报价将自动关闭。\n\n在使用此工具之前,请备份您的数据目录。您可以在“帐户/备份”中执行此操作。\n\n请报告我们您的问题,并在 Github 或 Bisq 论坛上提交错误报告,以便我们可以调查导致问题的原因。 emptyWalletWindow.balance=您的可用钱包余额 -emptyWalletWindow.bsq.btcBalance=非 BSQ 聪余额 emptyWalletWindow.address=输入您的目标地址 emptyWalletWindow.button=发送全部资金 @@ -2146,10 +1431,8 @@ filterWindow.seedNode=筛选后的种子节点(用逗号“,”隔开的洋葱 filterWindow.priceRelayNode=筛选后的价格中继节点(用逗号“,”隔开的洋葱地址) filterWindow.btcNode=筛选后的比特币节点(用逗号“,”隔开的地址+端口) filterWindow.preventPublicBtcNetwork=禁止使用公共比特币网络 -filterWindow.disableDao=禁用 DAO filterWindow.disableAutoConf=禁用自动确认 filterWindow.autoConfExplorers=已过滤自动确认浏览器(逗号分隔地址) -filterWindow.disableDaoBelowVersion=DAO 最低所需要的版本 filterWindow.disableTradeBelowVersion=交易最低所需要的版本 filterWindow.add=添加筛选 filterWindow.remove=移除筛选 @@ -2223,7 +1506,6 @@ tradeDetailsWindow.detailData=详情数据 txDetailsWindow.headline=Transaction Details txDetailsWindow.btc.note=You have sent BTC. -txDetailsWindow.bsq.note=You have sent BSQ funds. BSQ is colored bitcoin, so the transaction will not show in a BSQ explorer until it has been confirmed in a bitcoin block. txDetailsWindow.sentTo=Sent to txDetailsWindow.txId=TxId @@ -2235,8 +1517,6 @@ closedTradesSummaryWindow.totalMinerFee.title=Sum of all miner fees closedTradesSummaryWindow.totalMinerFee.value={0} ({1} of total trade amount) closedTradesSummaryWindow.totalTradeFeeInBtc.title=Sum of all trade fees paid in BTC closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} of total trade amount) -closedTradesSummaryWindow.totalTradeFeeInBsq.title=Sum of all trade fees paid in BSQ -closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} of total trade amount) walletPasswordWindow.headline=输入密码解锁 @@ -2317,12 +1597,8 @@ popup.warning.tooLargePercentageValue=您不能设置100%或更大的百分比 popup.warning.examplePercentageValue=请输入百分比数字,如 5.4% 是“5.4” popup.warning.noPriceFeedAvailable=该货币没有可用的价格。 你不能使用基于百分比的价格。\n请选择固定价格。 popup.warning.sendMsgFailed=向您的交易对象发送消息失败。\n请重试,如果继续失败报告错误。 -popup.warning.insufficientBtcFundsForBsqTx=你没有足够的 BTC 资金支付这笔交易的挖矿手续费。\n请充值您的 BTC 钱包。\n缺少的资金:{0} -popup.warning.bsqChangeBelowDustException=该交易产生的 BSQ 变化输出低于零头限制(5.46 BSQ),将被比特币网络拒绝。\n\n您需要发送更高的金额以避免更改输出(例如,通过在您的发送金额中添加零头),或者向您的钱包中添加更多的 BSQ 资金,以避免生成零头输出。\n\n零头输出为 {0}。 popup.warning.btcChangeBelowDustException=该交易创建的更改输出低于零头限制(546 聪),将被比特币网络拒绝。\n\n您需要将零头添加到发送量中,以避免生成零头输出。\n\n零头输出为{0}。 -popup.warning.insufficientBsqFundsForBtcFeePayment=您需要更多的 BSQ 去完成这笔交易 - 钱包中最后剩余 5.46 BSQ 将无法用于支付交易手续费因为 BTC 协议中的零头限制。\n\n你可以购买更多的 BSQ 或用 BTC支付交易手续费\n\n缺少 BSQ 资金:{0} -popup.warning.noBsqFundsForBtcFeePayment=您的 BSQ 钱包没有足够的资金支付 BSQ 的交易费用。 popup.warning.messageTooLong=您的信息超过最大允许的大小。请将其分成多个部分发送,或将其上传到 https://pastebin.com 之类的服务器。 popup.warning.lockedUpFunds=你已经从一个失败的交易中冻结了资金。\n冻结余额:{0}\n存款tx地址:{1}\n交易单号:{2}\n\n请通过选择待处理交易界面中的交易并点击“alt + o”或“option+ o”打开帮助话题。 @@ -2336,8 +1612,6 @@ popup.warning.nodeBanned=其中一个 {0} 节点已被禁用 popup.warning.priceRelay=价格传递 popup.warning.seed=种子 popup.warning.mandatoryUpdate.trading=请更新到最新的 Bisq 版本。强制更新禁止了旧版本进行交易。更多信息请访问 Bisq 论坛。 -popup.warning.mandatoryUpdate.dao=请更新到最新的 Bisq 版本。强制更新禁止了旧版本旧版本的 Bisq DAO 和 BSQ 。更多信息请访问 Bisq 论坛。 -popup.warning.disable.dao=Bisq DAO 和 BSQ 被临时禁用的。更多信息请访问 Bisq 论坛。 popup.warning.noFilter=We did not receive a filter object from the seed nodes. This is a not expected situation. Please inform the Bisq developers. popup.warning.burnBTC=这笔交易是无法实现,因为 {0} 的挖矿手续费用会超过 {1} 的转账金额。请等到挖矿手续费再次降低或您积累了更多的 BTC 来转账。 @@ -2356,7 +1630,6 @@ popup.info.cashDepositInfo.confirm=我确认我可以支付保证金 popup.info.shutDownWithOpenOffers=Bisq 正在被关闭,但仍有公开的报价。\n\n当 Bisq 关闭时,这些提供将不能在 P2P 网络上使用,但是它们将在您下次启动 Bisq 时重新发布到 P2P 网络上。\n\n为了让您的报价在线,保持 Bisq 运行,并确保这台计算机也在线(即,确保它不会进入待机模式…显示器待机不是问题)。 popup.info.qubesOSSetupInfo=你似乎好像在 Qubes OS 上运行 Bisq。\n\n请确保您的 Bisq qube 是参考设置指南的说明设置的 https://bisq.wiki/Running_Bisq_on_Qubes popup.warn.downGradePrevention=不支持从 {0} 版本降级到 {1} 版本。请使用最新的 Bisq 版本。 -popup.warn.daoRequiresRestart=在同步 DAO 状态时发生问题。你需要重启应用以修复此问题。 popup.privateNotification.headline=重要私人通知! @@ -2528,7 +1801,6 @@ navigation.settings.preferences=“设置/偏好” # suppress inspection "UnusedProperty" navigation.funds.transactions=“资金/交易记录” navigation.support=“帮助” -navigation.dao.wallet.receive=“DAO/BSQ 钱包/接收” #################################################################### @@ -2558,12 +1830,6 @@ XMR_MAINNET=XMR Mainnet XMR_TESTNET=XMR Testnet # suppress inspection "UnusedProperty" XMR_STAGENET=XMR Stagenet -# suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=比特币 DAO 测试网络(弃用) -# suppress inspection "UnusedProperty" -BTC_DAO_BETANET=Bisq DAO 测试网络(比特币主要网络) -# suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=比特币 DAO 回归测试 time.year=年线 time.month=月线 @@ -2910,9 +2176,7 @@ validation.accountNrChars=账户必须由 {0} 个字符构成。 validation.btc.invalidAddress=地址不正确,请检查地址格式。 validation.integerOnly=请输入整数。 validation.inputError=您的输入引起了错误:\n{0} -validation.bsq.insufficientBalance=您的可用钱包余额为 {0}。 validation.btc.exceedsMaxTradeLimit=您的交易限额为 {0}。 -validation.bsq.amountBelowMinAmount=最小金额为 {0} validation.nationalAccountId={0} 必须由{1}个数字组成。 #new @@ -2934,7 +2198,6 @@ validation.bic.invalidLocationCode=BIC 包含无效的地址代码 validation.bic.invalidBranchCode=BIC 包含无效的分行代码 validation.bic.sepaRevolutBic=不支持 Revolut Sepa 账户 validation.btc.invalidFormat=无效格式的比特币地址 -validation.bsq.invalidFormat=无效格式的 BSQ 地址 validation.email.invalidAddress=无效地址 validation.iban.invalidCountryCode=国家或地区代码无效 validation.iban.checkSumNotNumeric=校验必须是数字 diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index 0c6b98595b..06acc9f3ae 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -192,7 +192,6 @@ shared.tradeWalletBalance=交易錢包餘額 shared.makerTxFee=賣家:{0} shared.takerTxFee=買家:{0} shared.iConfirm=我確認 -shared.tradingFeeInBsqInfo=≈ {0} shared.openURL=打開 {0} shared.fiat=法定貨幣 shared.crypto=加密 @@ -204,9 +203,6 @@ shared.actions=操作 shared.buyerUpperCase=買家 shared.sellerUpperCase=買家 shared.new=新 -shared.blindVoteTxId=匿名投票交易 ID -shared.proposal=建議 -shared.votes=投票 shared.learnMore=瞭解更多 shared.dismiss=忽略 shared.selectedArbitrator=選中的仲裁者 @@ -240,7 +236,6 @@ mainView.menu.funds=資金 mainView.menu.support=幫助 mainView.menu.settings=設置 mainView.menu.account=賬户 -mainView.menu.dao=DAO mainView.marketPriceWithProvider.label=交易所價格提供商:{0} mainView.marketPrice.bisqInternalPrice=最新 Bisq 交易的價格 @@ -257,13 +252,11 @@ mainView.footer.localhostBitcoinNode=(本地主機) 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=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=連接失敗: mainView.footer.p2pInfo=比特幣網絡節點:{0} / Bisq 網絡節點:{1} -mainView.footer.daoFullNode=DAO 全節點 mainView.bootstrapState.connectionToTorNetwork=(1/4) 連接至 Tor 網絡... mainView.bootstrapState.torNodeCreated=(2/4) Tor 節點已創建 @@ -435,7 +428,6 @@ createOffer.fundsBox.networkFee=礦工手續費 createOffer.fundsBox.placeOfferSpinnerInfo=正在發佈報價中... createOffer.fundsBox.paymentLabel=Bisq 交易 ID {0} createOffer.fundsBox.fundsStructure=({0} 保證金,{1} 交易費,{2} 採礦費) -createOffer.fundsBox.fundsStructure.BSQ=({0} 保證金,{1} 採礦費)+ {2} 交易費 createOffer.success.headline=你的報價已經發布 createOffer.success.info=你可以在“業務/未完成報價”頁面內管理您的未完成報價。 createOffer.info.sellAtMarketPrice=由於您的價格是持續更新的,因此您將始終以市場價格進行出售。 @@ -636,7 +628,6 @@ portfolio.pending.step2_buyer.amountToTransfer=劃轉數量 portfolio.pending.step2_buyer.sellersAddress=賣家的 {0} 地址 portfolio.pending.step2_buyer.buyerAccount=您的付款帳户將被使用 portfolio.pending.step2_buyer.paymentStarted=付款開始 -portfolio.pending.step2_buyer.fillInBsqWallet=Pay from BSQ wallet portfolio.pending.step2_buyer.warn=你還沒有完成你的 {0} 付款!\n請注意,交易必須在 {1} 之前完成。 portfolio.pending.step2_buyer.openForDispute=您還沒有完成您的付款!\n最大交易期限已過。請聯繫調解員尋求幫助。 portfolio.pending.step2_buyer.paperReceipt.headline=您是否將紙質收據發送給 BTC 賣家? @@ -882,7 +873,6 @@ funds.locked.locked=多重驗證凍結交易 ID:{0} funds.tx.direction.sentTo=發送至: funds.tx.direction.receivedWith=接收到: funds.tx.direction.genesisTx=從初始 tx: -funds.tx.txFeePaymentForBsqTx=BSQ tx 的礦工手續費支付 funds.tx.createOfferFee=掛單和tx費用:{0} funds.tx.takeOfferFee=下單和tx費用:{0} funds.tx.multiSigDeposit=多重驗證保證金:{0} @@ -896,15 +886,11 @@ funds.tx.unknown=未知原因:{0} funds.tx.noFundsFromDispute=沒有退款的糾紛 funds.tx.receivedFunds=收到的資金: funds.tx.withdrawnFromWallet=從錢包提現 -funds.tx.withdrawnFromBSQWallet=BTC 已從 BSQ 錢包中取出 funds.tx.memo=備註 funds.tx.noTxAvailable=沒有可用交易 funds.tx.revert=還原 funds.tx.txSent=交易成功發送到本地 Bisq 錢包中的新地址。 funds.tx.direction.self=內部錢包交易 -funds.tx.daoTxFee=BSQ tx 的礦工手續費支付 -funds.tx.reimbursementRequestTxFee=退還申請 -funds.tx.compensationRequestTxFee=報償申請 funds.tx.dustAttackTx=接受零頭 funds.tx.dustAttackTx.popup=這筆交易是發送一個非常小的比特幣金額到您的錢包,可能是區塊鏈分析公司嘗試監控您的交易。\n\n如果您在交易中使用該交易輸出,他們將瞭解到您很可能也是其他地址的所有者(資金歸集)。\n\n為了保護您的隱私,Bisq 錢包忽略了這種零頭的消費和餘額顯示。可以在設置中將輸出視為零頭時設置閾值量。 @@ -996,9 +982,7 @@ settings.tab.about=關於我們 setting.preferences.general=通用偏好 setting.preferences.explorer=比特幣區塊瀏覽器 -setting.preferences.explorer.bsq=Bisq 區塊瀏覽器 setting.preferences.deviation=與市場價格最大差價 -setting.preferences.bsqAverageTrimThreshold=BSQ 率已超過閾值 setting.preferences.avoidStandbyMode=避免待機模式 setting.preferences.autoConfirmXMR=XMR 自動確認 setting.preferences.autoConfirmEnabled=啟用 @@ -1032,19 +1016,6 @@ setting.preferences.notifyOnPreRelease=Receive pre-release notifications setting.preferences.resetAllFlags=重置所有“不再提示”的提示 settings.preferences.languageChange=同意重啟請求以更換語言 settings.preferences.supportLanguageWarning=如有任何爭議,請注意調解在 {0} 處理,仲裁在 {1} 處理。 -setting.preferences.daoOptions=DAO 選項 -setting.preferences.dao.resyncFromGenesis.label=從初始 tx 重構 DAO 狀態 -setting.preferences.dao.resyncFromResources.label=從指定資源重新構建 DAO 狀態 -setting.preferences.dao.resyncFromResources.popup=應用程序重新啟動後,Bisq 網絡治理數據將從種子節點重新加載,而 BSQ 同步狀態將從創始交易中重新構建。 -setting.preferences.dao.resyncFromGenesis.popup=從創始交易中出現同步會消耗大量時間以及 CPU 資源。您確定要重新同步嗎?通常,從最新資源文件進行重新同步就足夠了,而且速度更快。\n\n應用程序重新啟動後,Bisq 網絡治理數據將從種子節點重新加載,而 BSQ 同步狀態將從初始交易中重新構建。 -setting.preferences.dao.resyncFromGenesis.resync=從創始區塊重新同步並關閉 -setting.preferences.dao.isDaoFullNode=以 DAO 全節點運行 Bisq -setting.preferences.dao.rpcUser=RPC 用户名 -setting.preferences.dao.rpcPw=PRC 密碼 -setting.preferences.dao.blockNotifyPort=區塊通知端口 -setting.preferences.dao.fullNodeInfo=如果要將 Bisq 以 DAO 全節點運行,您需要在本地運行比特幣核心並啟用 RPC 。所有的需求都記錄在“ {0} ”中。 -setting.preferences.dao.fullNodeInfo.ok=打開文檔頁面 -setting.preferences.dao.fullNodeInfo.cancel=不,我堅持使用輕節點模式 settings.preferences.editCustomExplorer.headline=瀏覽設置。 settings.preferences.editCustomExplorer.description=從左側列表中選擇一個系統默認瀏覽器,或使用您偏好的自定義設置。 settings.preferences.editCustomExplorer.available=可用瀏覽器 @@ -1090,7 +1061,7 @@ settings.net.needRestart=您需要重啟應用程序以同意這次變更。\n settings.net.notKnownYet=至今未知... settings.net.sentData=已發送數據 {0},{1} 條消息,{2} 條消息/秒 settings.net.receivedData=已接收數據 {0},{1} 條消息,{2} 條消息/秒 -settings.net.chainHeight=Bisq DAO chain height: {0} | Bitcoin Peers chain height: {1} +settings.net.chainHeight=Bitcoin Peers chain height: {1} settings.net.ips=添加逗號分隔的 IP 地址及端口,如使用8333端口可不填寫。 settings.net.seedNode=種子節點 settings.net.directPeer=節點(直連) @@ -1144,14 +1115,10 @@ setting.about.shortcuts.walletDetails=打開錢包詳情窗口 setting.about.shortcuts.openEmergencyBtcWalletTool=打開應急 BTC 錢包工具 -setting.about.shortcuts.openEmergencyBsqWalletTool=打開應急 BSQ 錢包工具 - setting.about.shortcuts.showTorLogs=在 DEBUG 與 WARN 之間切換 Tor 日誌等級 setting.about.shortcuts.manualPayoutTxWindow=打開窗口手動支付雙重驗證存款交易 -setting.about.shortcuts.reRepublishAllGovernanceData=重新推送 DAO 眾議廳數據(包括提案以及投票) - setting.about.shortcuts.removeStuckTrade=Open popup to move failed trade to open trades tab again setting.about.shortcuts.removeStuckTrade.value=Select failed trade and press: {0} @@ -1337,687 +1304,6 @@ account.notifications.noWebCamFound.warning=未找到網絡攝像頭。\n\n請 account.notifications.priceAlert.warning.highPriceTooLow=較高的價格必須大於較低的價格。 account.notifications.priceAlert.warning.lowerPriceTooHigh=較低的價格必須低於較高的價格。 - - - -#################################################################### -# DAO -#################################################################### - -dao.tab.factsAndFigures=確切消息 -dao.tab.bsqWallet=BSQ 錢包 -dao.tab.proposals=管理 -dao.tab.bonding=關係 -dao.tab.proofOfBurn=資產清單掛牌費/燒燬證明 -dao.tab.monitor=網絡監視器 -dao.tab.news=新聞 - -dao.paidWithBsq=已用 BSQ 支付 -dao.availableBsqBalance=可用於支出(已驗證的+未確認的變更輸出) -dao.verifiedBsqBalance=所有已驗證的 UTXO 餘額 -dao.unconfirmedChangeBalance=已驗證的+未確認的變更輸出 -dao.unverifiedBsqBalance=所有未驗證交易的餘額(等待區塊確認) -dao.lockedForVoteBalance=用於投票 -dao.lockedInBonds=凍結餘額 -dao.availableNonBsqBalance=可用的非 BSQ 餘額(BTC) -dao.reputationBalance=聲望值(不會花費) - -dao.tx.published.success=你的交易已經成功發佈 -dao.proposal.menuItem.make=創建要求 -dao.proposal.menuItem.browse=瀏覽開啟的報償申請 -dao.proposal.menuItem.vote=為報償申請投票 -dao.proposal.menuItem.result=投票結果 -dao.cycle.headline=投票週期 -dao.cycle.overview.headline=投票週期總覽 -dao.cycle.currentPhase=現階段 -dao.cycle.currentBlockHeight=當前區塊高度: -dao.cycle.proposal=提議階段 -dao.cycle.proposal.next=下一個提議階段 -dao.cycle.blindVote=匿名投票階段 -dao.cycle.voteReveal=投票公示階段 -dao.cycle.voteResult=投票結果 -dao.cycle.phaseDuration=區塊 {0} (≈{1});區塊 {2} - {3})(≈{4} - ≈{5}) -dao.cycle.phaseDurationWithoutBlocks=區塊 {0} - {1} (≈{2} - ≈{3} ) - -dao.voteReveal.txPublished.headLine=投票公示交易發佈 -dao.voteReveal.txPublished=你的交易 ID 為 {0} 投票公示交易已經成功發佈。\n\n如果您已經參與了 DAO 投票,那麼這將由軟件自動完成。 - -dao.results.cycles.header=週期 -dao.results.cycles.table.header.cycle=週期 -dao.results.cycles.table.header.numProposals=請求 -dao.results.cycles.table.header.voteWeight=投票權重 -dao.results.cycles.table.header.issuance=發行 - -dao.results.results.table.item.cycle=週期 {0} 開始於:{1} - -dao.results.proposals.header=選定週期的請求 -dao.results.proposals.table.header.nameLink=名稱/鏈接 -dao.results.proposals.table.header.details=詳情 -dao.results.proposals.table.header.myVote=我的投票 -dao.results.proposals.table.header.result=投票結果 -dao.results.proposals.table.header.threshold=閾值 -dao.results.proposals.table.header.quorum=法定人數 - -dao.results.proposals.voting.detail.header=選定提案的投票結果 - -dao.results.exceptions=投票結果異常 - -# suppress inspection "UnusedProperty" -dao.param.UNDEFINED=未定義 - -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BSQ=BSQ 掛單費 -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BSQ=BSQ 買單費 -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BSQ=最小 BSQ 掛單費 -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BSQ=最小 BSQ 買單費 -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_MAKER_FEE_BTC=BTC 掛單費 -# suppress inspection "UnusedProperty" -dao.param.DEFAULT_TAKER_FEE_BTC=BTC 買單費 -# suppress inspection "UnusedProperty" -# suppress inspection "UnusedProperty" -dao.param.MIN_MAKER_FEE_BTC=最小 BTC 掛單費 -# suppress inspection "UnusedProperty" -dao.param.MIN_TAKER_FEE_BTC=最小 BTC 買單費 -# suppress inspection "UnusedProperty" - -# suppress inspection "UnusedProperty" -dao.param.PROPOSAL_FEE=BSQ 提案手續費 -# suppress inspection "UnusedProperty" -dao.param.BLIND_VOTE_FEE=BSQ 投票手續費 - -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=最低 BSQ 報償申請數量 -# suppress inspection "UnusedProperty" -dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=最高 BSQ 報償申請數量 -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MIN_AMOUNT=最低 BSQ 退還申請數量 -# suppress inspection "UnusedProperty" -dao.param.REIMBURSEMENT_MAX_AMOUNT=最高 BSQ 退還申請數量 - -# suppress inspection "UnusedProperty" -dao.param.QUORUM_GENERIC=BSQ 要求的一般提案的仲裁人數 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_COMP_REQUEST=BSQ 要求的報償申請的仲裁人數 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REIMBURSEMENT=BSQ 要求的退還申請的仲裁人數 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CHANGE_PARAM=BSQ 要求的改變參數的仲裁人數 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_REMOVE_ASSET=BSQ 要求的移除資產要求的人數 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_CONFISCATION=BSQ 要求的沒收申請的仲裁人數 -# suppress inspection "UnusedProperty" -dao.param.QUORUM_ROLE=BSQ 要求的綁定角色的仲裁人數 - -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_GENERIC=普通提案的要求百分比 -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_COMP_REQUEST=報償申請的要求百分比 -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REIMBURSEMENT=退還申請的要求百分比 -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CHANGE_PARAM=改變參數的要求百分比 -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_REMOVE_ASSET=移除資產的要求百分比 -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_CONFISCATION=沒收申請的要求百分比 -# suppress inspection "UnusedProperty" -dao.param.THRESHOLD_ROLE=擔保角色的要求百分比 - -# suppress inspection "UnusedProperty" -dao.param.RECIPIENT_BTC_ADDRESS=接收方 BTC 地址 - -# suppress inspection "UnusedProperty" -dao.param.ASSET_LISTING_FEE_PER_DAY=資產清單掛牌費每日支付 -# suppress inspection "UnusedProperty" -dao.param.ASSET_MIN_VOLUME=最小資產交易量 - -# suppress inspection "UnusedProperty" -dao.param.LOCK_TIME_TRADE_PAYOUT=其他交易支出tx的鎖定時間 -# suppress inspection "UnusedProperty" -dao.param.ARBITRATOR_FEE=BTC 仲裁費 - -# suppress inspection "UnusedProperty" -dao.param.MAX_TRADE_LIMIT=最高 BTC 交易限額 - -# suppress inspection "UnusedProperty" -dao.param.BONDED_ROLE_FACTOR=擔保角色對 BSQ 的影響 -# suppress inspection "UnusedProperty" -dao.param.ISSUANCE_LIMIT=每個週期的 BSQ 發行限額 - -dao.param.currentValue=當前值:{0} -dao.param.currentAndPastValue=當前餘額:{0}(提案時的餘額:{1}) -dao.param.blocks={0} 區塊 - -dao.results.invalidVotes=在那個投票週期中,我們有無效的投票。如果投票沒有在 Bisq 網絡中很好地分佈,就會發生這種情況。\n{0} - -# suppress inspection "UnusedProperty" -dao.phase.PHASE_UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_PROPOSAL=提議階段 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK1=休息1 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BLIND_VOTE=匿名投票階段 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK2=休息2 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_VOTE_REVEAL=投票公示階段 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_BREAK3=休息3 -# suppress inspection "UnusedProperty" -dao.phase.PHASE_RESULT=結果階段 - -dao.results.votes.table.header.stakeAndMerit=投票權重 -dao.results.votes.table.header.stake=份額 -dao.results.votes.table.header.merit=獲得的 -dao.results.votes.table.header.vote=投票 - -dao.bond.menuItem.bondedRoles=擔保角色 -dao.bond.menuItem.reputation=擔保的名譽 -dao.bond.menuItem.bonds=擔保 - -dao.bond.dashboard.bondsHeadline=擔保式 BSQ -dao.bond.dashboard.lockupAmount=鎖定資金 -dao.bond.dashboard.unlockingAmount=正在鎖定資金(等到鎖定時間過後) - - -dao.bond.reputation.header=為名譽鎖定擔保 -dao.bond.reputation.table.header=我的名譽擔保 -dao.bond.reputation.amount=鎖定的 BSQ 數量 -dao.bond.reputation.time=在區塊中的解鎖時間 -dao.bond.reputation.salt=鹽 -dao.bond.reputation.hash=哈希 -dao.bond.reputation.lockupButton=鎖定 -dao.bond.reputation.lockup.headline=確認鎖定交易 -dao.bond.reputation.lockup.details=鎖定數量:{0}\n解鎖時間:{1} 區塊(≈{2})\n\n礦工手續費:{3}({4} 聰/字節)\n交易大小:{5} 字節\n\n你確定想要繼續? -dao.bond.reputation.unlock.headline=確認解鎖交易 -dao.bond.reputation.unlock.details=解鎖金額:{0}\n解鎖時間:{1} 區塊(≈{2})\n\n挖礦手續費:{3}({4} 聰/Byte)\n交易大小:{5} Kb\n\n你想繼續這個操作嗎? - -dao.bond.allBonds.header=所有擔保 - -dao.bond.bondedReputation=擔保的名譽 -dao.bond.bondedRoles=擔保角色 - -dao.bond.details.header=交易方詳情 -dao.bond.details.role=角色 -dao.bond.details.requiredBond=需要的 BSQ 擔保 -dao.bond.details.unlockTime=在區塊中的解鎖時間 -dao.bond.details.link=鏈接到交易方描述 -dao.bond.details.isSingleton=是否可由多個交易方擔任 -dao.bond.details.blocks={0} 區塊 - -dao.bond.table.column.name=名稱 -dao.bond.table.column.link=綁定 -dao.bond.table.column.bondType=連接類型 -dao.bond.table.column.details=詳情 -dao.bond.table.column.lockupTxId=鎖定 Tx ID -dao.bond.table.column.bondState=連接狀態 -dao.bond.table.column.lockTime=解鎖時間 -dao.bond.table.column.lockupDate=鎖定日期 - -dao.bond.table.button.lockup=鎖定 -dao.bond.table.button.unlock=解鎖 -dao.bond.table.button.revoke=撤銷 - -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.bond.bondState.READY_FOR_LOCKUP=尚未擔保 -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_PENDING=等待鎖定 -# suppress inspection "UnusedProperty" -dao.bond.bondState.LOCKUP_TX_CONFIRMED=鎖定的擔保 -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_PENDING=等待解鎖 -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCK_TX_CONFIRMED=解鎖 Tx 確認 -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKING=正在解鎖擔保 -# suppress inspection "UnusedProperty" -dao.bond.bondState.UNLOCKED=擔保已解鎖 -# suppress inspection "UnusedProperty" -dao.bond.bondState.CONFISCATED=沒收的擔保 - -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.BONDED_ROLE=擔保角色 -# suppress inspection "UnusedProperty" -dao.bond.lockupReason.REPUTATION=擔保名譽 - -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.GITHUB_ADMIN=Github 管理 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_ADMIN=論壇管理 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.TWITTER_ADMIN=Twitter 管理 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ROCKET_CHAT_ADMIN=Keybase 管理員 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.YOUTUBE_ADMIN=YouTube 管理 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BISQ_MAINTAINER=Bisq 運維人員 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BITCOINJ_MAINTAINER=BitcoinJ-fork 運維人員 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.NETLAYER_MAINTAINER=Netlayer 運維人員 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.WEBSITE_OPERATOR=網站運營者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.FORUM_OPERATOR=論壇運營者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.SEED_NODE_OPERATOR=種子節點運營者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DATA_RELAY_NODE_OPERATOR=價格節點運營者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_NODE_OPERATOR=比特幣節點運營者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MARKETS_OPERATOR=交易所運營者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=瀏覽器運營者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MOBILE_NOTIFICATIONS_RELAY_OPERATOR=移動通知中繼運營者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DOMAIN_NAME_HOLDER=域名持有者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.DNS_ADMIN=DNS 管理者 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.MEDIATOR=調解員 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.ARBITRATOR=仲裁員 -# suppress inspection "UnusedProperty" -dao.bond.bondedRoleType.BTC_DONATION_ADDRESS_OWNER=BTC 贊助地址所有者 - -dao.burnBsq.assetFee=資產清單 -dao.burnBsq.menuItem.assetFee=資產清單掛牌費 -dao.burnBsq.menuItem.proofOfBurn=燒燬證明 -dao.burnBsq.header=資產清單掛牌費 -dao.burnBsq.selectAsset=選擇資產 -dao.burnBsq.fee=手續費 -dao.burnBsq.trialPeriod=試用期 -dao.burnBsq.payFee=支付手續費 -dao.burnBsq.allAssets=所有資產 -dao.burnBsq.assets.nameAndCode=資產名稱 -dao.burnBsq.assets.state=狀態 -dao.burnBsq.assets.tradeVolume=交易量 -dao.burnBsq.assets.lookBackPeriod=確認期 -dao.burnBsq.assets.trialFee=試用期手續費 -dao.burnBsq.assets.totalFee=總共已支付費用 -dao.burnBsq.assets.days={0} 天 -dao.burnBsq.assets.toFewDays=資產清單掛牌費過低。在試用期中最低數量為 {0}。 - -# suppress inspection "UnusedProperty" -dao.assetState.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.assetState.IN_TRIAL_PERIOD=在試用期 -# suppress inspection "UnusedProperty" -dao.assetState.ACTIVELY_TRADED=活躍的交易 -# suppress inspection "UnusedProperty" -dao.assetState.DE_LISTED=因不活躍而被取消 -# suppress inspection "UnusedProperty" -dao.assetState.REMOVED_BY_VOTING=移除投票 - -dao.proofOfBurn.header=燒燬證明 -dao.proofOfBurn.amount=數量 -dao.proofOfBurn.preImage=預覽 -dao.proofOfBurn.burn=燒燬 -dao.proofOfBurn.allTxs=所有燒燬證明交易 -dao.proofOfBurn.myItems=我的燒燬證明交易 -dao.proofOfBurn.date=日期 -dao.proofOfBurn.hash=哈希 -dao.proofOfBurn.txs=交易記錄 -dao.proofOfBurn.pubKey=公鑰 -dao.proofOfBurn.signature.window.title=使用燒燬證明交易中的密鑰驗證消息 -dao.proofOfBurn.verify.window.title=使用燒燬證明交易中的密鑰確認消息 -dao.proofOfBurn.copySig=將驗證複製到剪貼板 -dao.proofOfBurn.sign=驗證 -dao.proofOfBurn.message=消息 -dao.proofOfBurn.sig=驗證 -dao.proofOfBurn.verify=確認 -dao.proofOfBurn.verificationResult.ok=確認成功 -dao.proofOfBurn.verificationResult.failed=確認失敗 - -# suppress inspection "UnusedProperty" -dao.phase.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.phase.PROPOSAL=提議階段 -# suppress inspection "UnusedProperty" -dao.phase.BREAK1=匿名投票前的休息階段 -# suppress inspection "UnusedProperty" -dao.phase.BLIND_VOTE=匿名投票階段 -# suppress inspection "UnusedProperty" -dao.phase.BREAK2=投票公示前的休息階段 -# suppress inspection "UnusedProperty" -dao.phase.VOTE_REVEAL=投票公示階段 -# suppress inspection "UnusedProperty" -dao.phase.BREAK3=公佈結果前的休息階段 -# suppress inspection "UnusedProperty" -dao.phase.RESULT=投票結果階段 - -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.PROPOSAL=提議階段 -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.BLIND_VOTE=匿名投票 -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.VOTE_REVEAL=投票公示 -# suppress inspection "UnusedProperty" -dao.phase.separatedPhaseBar.RESULT=投票結果 - -# suppress inspection "UnusedProperty" -dao.proposal.type.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.proposal.type.COMPENSATION_REQUEST=報償申請 -# suppress inspection "UnusedProperty" -dao.proposal.type.REIMBURSEMENT_REQUEST=退還申請 -# suppress inspection "UnusedProperty" -dao.proposal.type.BONDED_ROLE=擔保角色的提案 -# suppress inspection "UnusedProperty" -dao.proposal.type.REMOVE_ASSET=移除資產提案 -# suppress inspection "UnusedProperty" -dao.proposal.type.CHANGE_PARAM=修改參數的提議 -# suppress inspection "UnusedProperty" -dao.proposal.type.GENERIC=一般提議 -# suppress inspection "UnusedProperty" -dao.proposal.type.CONFISCATE_BOND=沒收擔保請求 - -# suppress inspection "UnusedProperty" -dao.proposal.type.short.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.COMPENSATION_REQUEST=報償申請 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REIMBURSEMENT_REQUEST=退還申請 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.BONDED_ROLE=擔保角色 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.REMOVE_ASSET=移除一個數字貨幣 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CHANGE_PARAM=修改參數 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.GENERIC=一般提議 -# suppress inspection "UnusedProperty" -dao.proposal.type.short.CONFISCATE_BOND=沒收擔保 - -dao.proposal.details=提議細節 -dao.proposal.selectedProposal=選定賠償要求 -dao.proposal.active.header=當前週期的提案 -dao.proposal.active.remove.confirm=您確定要移除該提案嗎?\n如果您刪除該提案,提案費將會丟失。 -dao.proposal.active.remove.doRemove=是,移除我的提案 -dao.proposal.active.remove.failed=不能移除提案。 -dao.proposal.myVote.title=投票 -dao.proposal.myVote.accept=接受提議 -dao.proposal.myVote.reject=拒絕提案 -dao.proposal.myVote.removeMyVote=忽略提案 -dao.proposal.myVote.merit=所得的 BSQ 的投票權重 -dao.proposal.myVote.stake=Stake 的投票權重 -dao.proposal.myVote.revealTxId=投票公示的交易 ID -dao.proposal.myVote.stake.prompt=在投票中最大可用份額:{0} -dao.proposal.votes.header=設置投票的份額,併發布您的投票 -dao.proposal.myVote.button=發佈投票 -dao.proposal.myVote.setStake.description=在對所有提案進行投票後,您必須鎖定BSQ來設置投票的份額。您鎖定的 BSQ 越多,你的投票權重就越大。\n\n投票會鎖定 BSQ 將在投票顯示階段再次解鎖。 -dao.proposal.create.selectProposalType=選擇提案類型 -dao.proposal.create.phase.inactive=請等到下一個提案階段 -dao.proposal.create.proposalType=提議類型 -dao.proposal.create.new=創建新的賠償要求 -dao.proposal.create.button=創建賠償要求 -dao.proposal.create.publish=發佈提案 -dao.proposal.create.publishing=正在發佈提案中... -dao.proposal=提案 -dao.proposal.display.type=提議類型 -dao.proposal.display.name=確切的 GitHub 的用户名 -dao.proposal.display.link=詳情的鏈接 -dao.proposal.display.link.prompt=提案的鏈接 -dao.proposal.display.requestedBsq=申請的 BSQ 數量 -dao.proposal.display.txId=提案交易 ID -dao.proposal.display.proposalFee=提案手續費 -dao.proposal.display.myVote=我的投票 -dao.proposal.display.voteResult=投票結果總結 -dao.proposal.display.bondedRoleComboBox.label=擔保角色類型 -dao.proposal.display.requiredBondForRole.label=角色需要的擔保 -dao.proposal.display.option=選項 - -dao.proposal.table.header.proposalType=提議類型 -dao.proposal.table.header.link=綁定 -dao.proposal.table.header.myVote=我的投票 -# suppress inspection "UnusedProperty" -dao.proposal.table.header.remove=移除 -dao.proposal.table.icon.tooltip.removeProposal=移除我的提案 -dao.proposal.table.icon.tooltip.changeVote=當前投票:“{0}”。更改投票至:“{1}” - -dao.proposal.display.myVote.accepted=已接受 -dao.proposal.display.myVote.rejected=已拒絕 -dao.proposal.display.myVote.ignored=已忽略 -dao.proposal.display.myVote.unCounted=投票結果不包括在內 -dao.proposal.myVote.summary=已投票:{0};投票權重:{1}(獲得的:{2} + 獎金:{3})({4}) -dao.proposal.myVote.invalid=投票無效 - -dao.proposal.voteResult.success=已接受 -dao.proposal.voteResult.failed=已拒絕 -dao.proposal.voteResult.summary=結果:{0};閾值:{1}(要求> {2});仲裁人數:{3}(要求> {4}) - -dao.proposal.display.paramComboBox.label=選擇需要改變的參數 -dao.proposal.display.paramValue=參數值 - -dao.proposal.display.confiscateBondComboBox.label=選擇擔保 -dao.proposal.display.assetComboBox.label=需要移除的資產 - -dao.blindVote=匿名投票 - -dao.blindVote.startPublishing=發佈匿名投票交易中 -dao.blindVote.success=我們的匿名投票交易已經成功發佈。\n\n請注意,您必須在線在投票公示階段,以便您的 Bisq 應用程序可以發佈投票公示交易。沒有投票公示交易,您的投票將無效! - -dao.wallet.menuItem.send=發送 -dao.wallet.menuItem.receive=接收 -dao.wallet.menuItem.transactions=交易記錄 - -dao.wallet.dashboard.myBalance=我的錢包餘額 - -dao.wallet.receive.fundYourWallet=你的 BSQ 接收地址 -dao.wallet.receive.bsqAddress=BSQ 錢包地址(刷新未使用地址) - -dao.wallet.send.sendFunds=提現 -dao.wallet.send.sendBtcFunds=發送非 BSQ 資金(BTC) -dao.wallet.send.amount=BSQ 數量 -dao.wallet.send.btcAmount=BTC 數量(無 BSQ 資金) -dao.wallet.send.setAmount=設置提現數量(最小量 {0}) -dao.wallet.send.receiverAddress=接收者的 BSQ 地址 -dao.wallet.send.receiverBtcAddress=接收者的 BTC 地址 -dao.wallet.send.setDestinationAddress=輸入您的目標地址 -dao.wallet.send.send=發送 BSQ 資金 -dao.wallet.send.inputControl=Select inputs -dao.wallet.send.sendBtc=發送 BTC 資金 -dao.wallet.send.sendFunds.headline=確定提現申請 -dao.wallet.send.sendFunds.details=發送:{0}\n來自:{1}\n要求的礦工手續費:{2}({3}比特/節)\n交易大小:{4}字節\n\n接收方會收到:{5}\n\n您確定您想要提現這些數量嗎? -dao.wallet.chainHeightSynced=最新確認區塊:{0} -dao.wallet.chainHeightSyncing=等待區塊... 已確認{0}/{1}區塊 -dao.wallet.tx.type=類型 - -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED=未定義 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNDEFINED_TX_TYPE=不被認可 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNVERIFIED=未驗證的 BSQ 交易 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.INVALID=無效的 BSQ 交易 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.GENESIS=初始交易 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.TRANSFER_BSQ=劃轉 BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.received.TRANSFER_BSQ=接收 BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.sent.TRANSFER_BSQ=發送 BSQ -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PAY_TRADE_FEE=付交易費記錄 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.COMPENSATION_REQUEST=報償申請記錄 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.REIMBURSEMENT_REQUEST=退還申請的手續費 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROPOSAL=提案手續費 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.BLIND_VOTE=投票記錄 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.VOTE_REVEAL=投票公示 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.LOCKUP=鎖定擔保 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.UNLOCK=解鎖擔保 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.ASSET_LISTING_FEE=資產清單掛牌費 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.PROOF_OF_BURN=燒燬證明 -# suppress inspection "UnusedProperty" -dao.tx.type.enum.IRREGULAR=不正常 - -dao.tx.withdrawnFromWallet=BTC 已從錢包中取出 -dao.tx.issuanceFromCompReq=報償申請/發放 -dao.tx.issuanceFromCompReq.tooltip=導致新 BSQ 發行的報償請求。\n發行日期:{0} -dao.tx.issuanceFromReimbursement=退還申請/發放 -dao.tx.issuanceFromReimbursement.tooltip=導致新 BSQ 發放的退還申請。\n發放日期: {0} -dao.proposal.create.missingBsqFunds=您沒有足夠的 BSQ 資金來創建提案。如果您有一個未經確認的 BSQ 交易,您需要等待一個區塊鏈確認,因為 BSQ 只有在包含在一個區塊中時才會被驗證。\n缺失:{0} - -dao.proposal.create.missingBsqFundsForBond=你沒有足夠的 BSQ 資金來承擔這個角色。您仍然可以發佈這個提案,但如果它被接受,您將需要這個角色所需的全部 BSQ 金額。\n缺少:{0} - -dao.proposal.create.missingMinerFeeFunds=您沒有足夠的BTC資金來支付該提案交易。所有的 BSQ交易需要用 BTC 支付挖礦手續費。\n缺少:{0} - -dao.proposal.create.missingIssuanceFunds=您沒有足夠的BTC資金來支付該提案交易。所有的 BSQ交易需要用 BTC 支付挖礦手續費以及發起交易也需要用 BTC 支付所需的 BSQ 數量({0} 聰/BSQ)\n缺少:{1} - -dao.feeTx.confirm=確認 {0} 交易 -dao.feeTx.confirm.details={0}手續費:{1}\n礦工手續費:{2}({3} 聰/byte)\n交易大小:{4} Kb\n\n您確定您要發送這個 {5} 交易嗎? - -dao.feeTx.issuanceProposal.confirm.details={0}手續費:{1}\n為 BSQ 提案所需要的BTC:{2}({3}聰 / BSQ)\n挖礦手續費:{4}({5}聰 /字節)\n交易大小:{6}Kb\n\n如果你的要求被批准,你將收到你要求數量的 2 個 BSQ 提議的費用。\n\n你確定你想要發佈{7}交易? - -dao.news.bisqDAO.title=Bisq DAO -dao.news.bisqDAO.description=正如 Bisq交易是分散的,並且不受審查,它的治理模型也是如此—— Bisq DAO 和 BSQ 是使其成為可能的工具。 -dao.news.bisqDAO.readMoreLink=瞭解有關 Bisq DAO 的更多信息 - -dao.news.pastContribution.title=過去有所貢獻?申請 BSQ -dao.news.pastContribution.description=如果您對 Bisq 有貢獻,請使用下面的 BSQ 地址,並申請參與 BSQ 初始分發。 -dao.news.pastContribution.yourAddress=你的 BSQ 錢包地址 -dao.news.pastContribution.requestNow=現在申請 - -dao.news.DAOOnTestnet.title=在我們的測試網絡上運行 BISQ DAO -dao.news.DAOOnTestnet.description=核心網絡 Bisq DAO 還沒有啟動,但是您可以通過在我們的測試網絡上運行它來了解 Bisq DAO 。 -dao.news.DAOOnTestnet.firstSection.title=1.切換至 DAO 測試網絡模式 -dao.news.DAOOnTestnet.firstSection.content=從設置頁面切換到 DAO 測試網絡。 -dao.news.DAOOnTestnet.secondSection.title=2.獲得一些 BSQ -dao.news.DAOOnTestnet.secondSection.content=在 Slack 上申請 BSQ 或在 Bisq 上購買 BSQ 。 -dao.news.DAOOnTestnet.thirdSection.title=3.參與投票週期 -dao.news.DAOOnTestnet.thirdSection.content=就修改 Bisq 的各個方面提出建議並進行表決。 -dao.news.DAOOnTestnet.fourthSection.title=4.探索 BSQ 區塊鏈瀏覽器 -dao.news.DAOOnTestnet.fourthSection.content=由於 BSQ 只是比特幣,你可以看到 BSQ 交易在我們的比特幣區塊瀏覽器。 -dao.news.DAOOnTestnet.readMoreLink=閲讀完整的文檔 - -dao.monitor.daoState=DAO 狀態 -dao.monitor.proposals=提案狀態 -dao.monitor.blindVotes=匿名投票狀態 - -dao.monitor.table.peers=節點 -dao.monitor.table.conflicts=矛盾 -dao.monitor.state=狀態 -dao.monitor.requestAlHashes=要求所有哈希 -dao.monitor.resync=重新同步 DAO 狀態 -dao.monitor.table.header.cycleBlockHeight=週期/區塊高度 -dao.monitor.table.cycleBlockHeight=週期 {0} /區塊 {1} -dao.monitor.table.seedPeers=種子節點:{0} - -dao.monitor.daoState.headline=DAO 狀態 -dao.monitor.daoState.table.headline=DAO 狀態的哈希鏈 -dao.monitor.daoState.table.blockHeight=區塊高度 -dao.monitor.daoState.table.hash=DAO 狀態的哈希 -dao.monitor.daoState.table.prev=以前的哈希 -dao.monitor.daoState.conflictTable.headline=來自不同實體的 DAO 狀態哈希 -dao.monitor.daoState.utxoConflicts=UTXO 衝突 -dao.monitor.daoState.utxoConflicts.blockHeight=區塊高度:{0} -dao.monitor.daoState.utxoConflicts.sumUtxo=所有 UTXO 的總和:{0} BSQ -dao.monitor.daoState.utxoConflicts.sumBsq=所有 BSQ 的總和:{0} BSQ -dao.monitor.daoState.checkpoint.popup=DAO 狀態與網絡不同步。重啟之後,DAO 狀態將重新同步。 - -dao.monitor.proposal.headline=提案狀態 -dao.monitor.proposal.table.headline=提案狀態的哈希鏈 -dao.monitor.proposal.conflictTable.headline=來自不同實體的提案狀態哈希 - -dao.monitor.proposal.table.hash=提案狀態的哈希 -dao.monitor.proposal.table.prev=以前的哈希 -dao.monitor.proposal.table.numProposals=提案編號 - -dao.monitor.isInConflictWithSeedNode=您的本地數據與至少一個種子節點不一致。請重新同步 DAO 狀態。 -dao.monitor.isInConflictWithNonSeedNode=您的一個對等節點與網絡不一致,但您的節點與種子節點同步。 -dao.monitor.daoStateInSync=您的本地節點與網絡一致 - -dao.monitor.blindVote.headline=匿名投票狀態 -dao.monitor.blindVote.table.headline=匿名投票狀態的哈希鏈 -dao.monitor.blindVote.conflictTable.headline=來自不同實體的匿名投票狀態哈希 -dao.monitor.blindVote.table.hash=匿名投票狀態的哈希 -dao.monitor.blindVote.table.prev=以前的哈希 -dao.monitor.blindVote.table.numBlindVotes=匿名投票編號 - -dao.factsAndFigures.menuItem.supply=BSQ 供給 -dao.factsAndFigures.menuItem.transactions=BSQ 交易 - -dao.factsAndFigures.dashboard.avgPrice90=90天平均 BSQ/BTC 交易價格 -dao.factsAndFigures.dashboard.avgPrice30=30天平均 BSQ/BTC 交易價格 -dao.factsAndFigures.dashboard.avgUSDPrice90=90 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.avgUSDPrice30=30 days volume weighted average BSQ/USD price -dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on 30 days average BSQ/USD price) -dao.factsAndFigures.dashboard.availableAmount=總共可用的 BSQ -dao.factsAndFigures.dashboard.volumeUsd=Total trade volume in USD -dao.factsAndFigures.dashboard.volumeBtc=Total trade volume in BTC -dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection=Average BSQ/USD trade price from selected time period in chart -dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection=Average BSQ/BTC trade price from selected time period in chart - -dao.factsAndFigures.supply.issuedVsBurnt=已發放的 BSQ 已銷燬的 BSQ - -dao.factsAndFigures.supply.issued=已發放的 BSQ -dao.factsAndFigures.supply.compReq=Compensation requests -dao.factsAndFigures.supply.reimbursement=Reimbursement requests -dao.factsAndFigures.supply.genesisIssueAmount=在初始交易中心有問題的 BSQ -dao.factsAndFigures.supply.compRequestIssueAmount=報償申請發放的 BSQ -dao.factsAndFigures.supply.reimbursementAmount=退還申請發放的 BSQ -dao.factsAndFigures.supply.totalIssued=Total issued BSQ -dao.factsAndFigures.supply.totalBurned=Total burned BSQ -dao.factsAndFigures.supply.chart.tradeFee.toolTip={0}\n{1} -dao.factsAndFigures.supply.burnt=BSQ 燒燬總量 - -dao.factsAndFigures.supply.priceChat=BSQ price -dao.factsAndFigures.supply.volumeChat=交易總量 -dao.factsAndFigures.supply.tradeVolumeInUsd=Trade volume in USD -dao.factsAndFigures.supply.tradeVolumeInBtc=Trade volume in BTC -dao.factsAndFigures.supply.bsqUsdPrice=BSQ/USD price -dao.factsAndFigures.supply.bsqBtcPrice=BSQ/BTC price -dao.factsAndFigures.supply.btcUsdPrice=BTC/USD price - -dao.factsAndFigures.supply.locked=BSQ 全局鎖定狀態 -dao.factsAndFigures.supply.totalLockedUpAmount=擔保的鎖定 -dao.factsAndFigures.supply.totalUnlockingAmount=正在從擔保解鎖 BSQ -dao.factsAndFigures.supply.totalUnlockedAmount=已從擔保解鎖 BSQ -dao.factsAndFigures.supply.totalConfiscatedAmount=已從擔保沒收 BSQ -dao.factsAndFigures.supply.proofOfBurn=Proof of Burn -dao.factsAndFigures.supply.bsqTradeFee=BSQ Trade fees -dao.factsAndFigures.supply.btcTradeFee=BTC Trade fees - -dao.factsAndFigures.transactions.genesis=創始交易 -dao.factsAndFigures.transactions.genesisBlockHeight=初始區塊高度 -dao.factsAndFigures.transactions.genesisTxId=初始交易 ID -dao.factsAndFigures.transactions.txDetails=BSQ 交易統計 -dao.factsAndFigures.transactions.allTx=所有 BSQ 交易記錄 -dao.factsAndFigures.transactions.utxo=所有未用交易的量 -dao.factsAndFigures.transactions.compensationIssuanceTx=所有報償請求問題的交易記錄 -dao.factsAndFigures.transactions.reimbursementIssuanceTx=所有退回申請問題的交易記錄 -dao.factsAndFigures.transactions.burntTx=所有費用支付記錄 -dao.factsAndFigures.transactions.invalidTx=所有無效交易記錄 -dao.factsAndFigures.transactions.irregularTx=所有不正常的交易記錄: - - - #################################################################### # Windows #################################################################### @@ -2120,7 +1406,6 @@ disputeSummaryWindow.close.noPayout.text=你想要在未作支付的情況下關 emptyWalletWindow.headline={0} 錢包急救工具 emptyWalletWindow.info=請在緊急情況下使用,如果您無法從 UI 中訪問您的資金。\n\n請注意,使用此工具時,所有未結報價將自動關閉。\n\n在使用此工具之前,請備份您的數據目錄。您可以在“帳户/備份”中執行此操作。\n\n請報吿我們您的問題,並在 Github 或 Bisq 論壇上提交錯誤報吿,以便我們可以調查導致問題的原因。 emptyWalletWindow.balance=您的可用錢包餘額 -emptyWalletWindow.bsq.btcBalance=非 BSQ 聰餘額 emptyWalletWindow.address=輸入您的目標地址 emptyWalletWindow.button=發送全部資金 @@ -2146,10 +1431,8 @@ filterWindow.seedNode=篩選後的種子節點(用逗號“,”隔開的洋葱 filterWindow.priceRelayNode=篩選後的價格中繼節點(用逗號“,”隔開的洋葱地址) filterWindow.btcNode=篩選後的比特幣節點(用逗號“,”隔開的地址+端口) filterWindow.preventPublicBtcNetwork=禁止使用公共比特幣網絡 -filterWindow.disableDao=禁用 DAO filterWindow.disableAutoConf=禁用自動確認 filterWindow.autoConfExplorers=已過濾自動確認瀏覽器(逗號分隔地址) -filterWindow.disableDaoBelowVersion=DAO 最低所需要的版本 filterWindow.disableTradeBelowVersion=交易最低所需要的版本 filterWindow.add=添加篩選 filterWindow.remove=移除篩選 @@ -2223,7 +1506,6 @@ tradeDetailsWindow.detailData=Detail data txDetailsWindow.headline=Transaction Details txDetailsWindow.btc.note=You have sent BTC. -txDetailsWindow.bsq.note=You have sent BSQ funds. BSQ is colored bitcoin, so the transaction will not show in a BSQ explorer until it has been confirmed in a bitcoin block. txDetailsWindow.sentTo=Sent to txDetailsWindow.txId=TxId @@ -2235,9 +1517,6 @@ closedTradesSummaryWindow.totalMinerFee.title=Sum of all miner fees closedTradesSummaryWindow.totalMinerFee.value={0} ({1} of total trade amount) closedTradesSummaryWindow.totalTradeFeeInBtc.title=Sum of all trade fees paid in BTC closedTradesSummaryWindow.totalTradeFeeInBtc.value={0} ({1} of total trade amount) -closedTradesSummaryWindow.totalTradeFeeInBsq.title=Sum of all trade fees paid in BSQ -closedTradesSummaryWindow.totalTradeFeeInBsq.value={0} ({1} of total trade amount) - walletPasswordWindow.headline=輸入密碼解鎖 torNetworkSettingWindow.header=Tor 網絡設置 @@ -2262,7 +1541,6 @@ torNetworkSettingWindow.bridges.header=Tor 網絡被屏蔽? torNetworkSettingWindow.bridges.info=如果 Tor 被您的 Internet 提供商或您的國家或地區屏蔽,您可以嘗試使用 Tor 網橋。\n \n訪問 Tor 網頁:https://bridges.torproject.org/bridges,瞭解關於網橋和可插拔傳輸的更多信息。 feeOptionWindow.headline=選擇貨幣支付交易手續費 -feeOptionWindow.info=您可以選擇用 BSQ 或 BTC 支付交易費用。如果您選擇 BSQ ,您會感謝這些交易手續費折扣。 feeOptionWindow.optionsLabel=選擇貨幣支付交易手續費 feeOptionWindow.useBTC=使用 BTC feeOptionWindow.fee={0}(≈ {1}) @@ -2317,12 +1595,6 @@ popup.warning.tooLargePercentageValue=您不能設置100%或更大的百分比 popup.warning.examplePercentageValue=請輸入百分比數字,如 5.4% 是“5.4” popup.warning.noPriceFeedAvailable=該貨幣沒有可用的價格。 你不能使用基於百分比的價格。\n請選擇固定價格。 popup.warning.sendMsgFailed=向您的交易對象發送消息失敗。\n請重試,如果繼續失敗報吿錯誤。 -popup.warning.insufficientBtcFundsForBsqTx=你沒有足夠的 BTC 資金支付這筆交易的挖礦手續費。\n請充值您的 BTC 錢包。\n缺少的資金:{0} -popup.warning.bsqChangeBelowDustException=該交易產生的 BSQ 變化輸出低於零頭限制(5.46 BSQ),將被比特幣網絡拒絕。\n\n您需要發送更高的金額以避免更改輸出(例如,通過在您的發送金額中添加零頭),或者向您的錢包中添加更多的 BSQ 資金,以避免生成零頭輸出。\n\n零頭輸出為 {0}。 -popup.warning.btcChangeBelowDustException=該交易創建的更改輸出低於零頭限制(546 聰),將被比特幣網絡拒絕。\n\n您需要將零頭添加到發送量中,以避免生成零頭輸出。\n\n零頭輸出為{0}。 - -popup.warning.insufficientBsqFundsForBtcFeePayment=您需要更多的 BSQ 去完成這筆交易 - 錢包中最後剩餘 5.46 BSQ 將無法用於支付交易手續費因為 BTC 協議中的零頭限制。\n\n你可以購買更多的 BSQ 或用 BTC支付交易手續費\n\n缺少 BSQ 資金:{0} -popup.warning.noBsqFundsForBtcFeePayment=您的 BSQ 錢包沒有足夠的資金支付 BSQ 的交易費用。 popup.warning.messageTooLong=您的信息超過最大允許的大小。請將其分成多個部分發送,或將其上傳到 https://pastebin.com 之類的服務器。 popup.warning.lockedUpFunds=你已經從一個失敗的交易中凍結了資金。\n凍結餘額:{0}\n存款tx地址:{1}\n交易單號:{2}\n\n請通過選擇待處理交易界面中的交易並點擊“alt + o”或“option+ o”打開幫助話題。 @@ -2336,8 +1608,6 @@ popup.warning.nodeBanned=其中一個 {0} 節點已被禁用 popup.warning.priceRelay=價格傳遞 popup.warning.seed=種子 popup.warning.mandatoryUpdate.trading=請更新到最新的 Bisq 版本。強制更新禁止了舊版本進行交易。更多信息請訪問 Bisq 論壇。 -popup.warning.mandatoryUpdate.dao=請更新到最新的 Bisq 版本。強制更新禁止了舊版本舊版本的 Bisq DAO 和 BSQ 。更多信息請訪問 Bisq 論壇。 -popup.warning.disable.dao=Bisq DAO 和 BSQ 被臨時禁用的。更多信息請訪問 Bisq 論壇。 popup.warning.noFilter=We did not receive a filter object from the seed nodes. This is a not expected situation. Please inform the Bisq developers. popup.warning.burnBTC=這筆交易是無法實現,因為 {0} 的挖礦手續費用會超過 {1} 的轉賬金額。請等到挖礦手續費再次降低或您積累了更多的 BTC 來轉賬。 @@ -2356,7 +1626,6 @@ popup.info.cashDepositInfo.confirm=我確認我可以支付保證金 popup.info.shutDownWithOpenOffers=Bisq 正在被關閉,但仍有公開的報價。\n\n當 Bisq 關閉時,這些提供將不能在 P2P 網絡上使用,但是它們將在您下次啟動 Bisq 時重新發布到 P2P 網絡上。\n\n為了讓您的報價在線,保持 Bisq 運行,並確保這台計算機也在線(即,確保它不會進入待機模式…顯示器待機不是問題)。 popup.info.qubesOSSetupInfo=你似乎好像在 Qubes OS 上運行 Bisq。\n\n請確保您的 Bisq qube 是參考設置指南的説明設置的 https://bisq.wiki/Running_Bisq_on_Qubes popup.warn.downGradePrevention=不支持從 {0} 版本降級到 {1} 版本。請使用最新的 Bisq 版本。 -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=重要私人通知! @@ -2528,7 +1797,6 @@ navigation.settings.preferences=“設置/偏好” # suppress inspection "UnusedProperty" navigation.funds.transactions=“資金/交易記錄” navigation.support=“幫助” -navigation.dao.wallet.receive=“DAO/BSQ 錢包/接收” #################################################################### @@ -2558,12 +1826,6 @@ XMR_MAINNET=XMR Mainnet XMR_TESTNET=XMR Testnet # suppress inspection "UnusedProperty" XMR_STAGENET=XMR Stagenet -# suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=比特幣 DAO 測試網絡(棄用) -# suppress inspection "UnusedProperty" -BTC_DAO_BETANET=Bisq DAO 測試網絡(比特幣主要網絡) -# suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=比特幣 DAO 迴歸測試 time.year=年線 time.month=月線 @@ -2910,9 +2172,7 @@ validation.accountNrChars=賬户必須由 {0} 個字符構成。 validation.btc.invalidAddress=地址不正確,請檢查地址格式。 validation.integerOnly=請輸入整數。 validation.inputError=您的輸入引起了錯誤:\n{0} -validation.bsq.insufficientBalance=您的可用錢包餘額為 {0}。 validation.btc.exceedsMaxTradeLimit=您的交易限額為 {0}。 -validation.bsq.amountBelowMinAmount=最小金額為 {0} validation.nationalAccountId={0} 必須由{1}個數字組成。 #new @@ -2934,7 +2194,6 @@ validation.bic.invalidLocationCode=BIC 包含無效的地址代碼 validation.bic.invalidBranchCode=BIC 包含無效的分行代碼 validation.bic.sepaRevolutBic=不支持 Revolut Sepa 賬户 validation.btc.invalidFormat=無效格式的比特幣地址 -validation.bsq.invalidFormat=無效格式的 BSQ 地址 validation.email.invalidAddress=無效地址 validation.iban.invalidCountryCode=國家或地區代碼無效 validation.iban.checkSumNotNumeric=校驗必須是數字 diff --git a/core/src/test/java/bisq/core/dao/governance/ballot/BallotListServiceTest.java b/core/src/test/java/bisq/core/dao/governance/ballot/BallotListServiceTest.java deleted file mode 100644 index 3c5da9c169..0000000000 --- a/core/src/test/java/bisq/core/dao/governance/ballot/BallotListServiceTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package bisq.core.dao.governance.ballot; - -import bisq.core.dao.governance.ballot.BallotListService.BallotListChangeListener; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.ProposalService; -import bisq.core.dao.governance.proposal.ProposalValidatorProvider; -import bisq.core.dao.governance.proposal.storage.appendonly.ProposalPayload; - -import bisq.common.persistence.PersistenceManager; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; - -import org.junit.Test; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.RETURNS_DEEP_STUBS; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class BallotListServiceTest { - @Test - @SuppressWarnings("unchecked") - public void testAddListenersWhenNewPayloadAdded() { - // given - ObservableList payloads = FXCollections.observableArrayList(); - - ProposalService proposalService = mock(ProposalService.class); - when(proposalService.getProposalPayloads()).thenReturn(payloads); - - BallotListService service = new BallotListService(proposalService, mock(PeriodService.class), - mock(ProposalValidatorProvider.class), mock(PersistenceManager.class)); - - BallotListChangeListener listener = mock(BallotListChangeListener.class); - service.addListener(listener); - - service.addListeners(); - - // when - payloads.add(mock(ProposalPayload.class, RETURNS_DEEP_STUBS)); - - // then - verify(listener).onListChanged(any()); - } -} diff --git a/core/src/test/java/bisq/core/dao/governance/proposal/MyProposalListServiceTest.java b/core/src/test/java/bisq/core/dao/governance/proposal/MyProposalListServiceTest.java deleted file mode 100644 index a851e6cdab..0000000000 --- a/core/src/test/java/bisq/core/dao/governance/proposal/MyProposalListServiceTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package bisq.core.dao.governance.proposal; - -import bisq.core.btc.wallet.WalletsManager; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.state.DaoStateService; - -import bisq.network.p2p.P2PService; - -import bisq.common.crypto.PubKeyRing; -import bisq.common.persistence.PersistenceManager; - -import javafx.beans.property.SimpleIntegerProperty; - -import org.junit.Test; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class MyProposalListServiceTest { - @Test - public void canInstantiate() { - P2PService p2PService = mock(P2PService.class); - when(p2PService.getNumConnectedPeers()).thenReturn(new SimpleIntegerProperty(0)); - PersistenceManager persistenceManager = mock(PersistenceManager.class); - MyProposalListService service = new MyProposalListService(p2PService, - mock(DaoStateService.class), - mock(PeriodService.class), mock(WalletsManager.class), persistenceManager, mock(PubKeyRing.class) - ); - } -} diff --git a/core/src/test/java/bisq/core/dao/governance/proposal/ProposalServiceP2PDataStorageListenerTest.java b/core/src/test/java/bisq/core/dao/governance/proposal/ProposalServiceP2PDataStorageListenerTest.java deleted file mode 100644 index 7945a241b2..0000000000 --- a/core/src/test/java/bisq/core/dao/governance/proposal/ProposalServiceP2PDataStorageListenerTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal; - -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.governance.proposal.storage.appendonly.ProposalStorageService; -import bisq.core.dao.governance.proposal.storage.temp.TempProposalPayload; -import bisq.core.dao.governance.proposal.storage.temp.TempProposalStorageService; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.dao.state.model.governance.Proposal; - -import bisq.network.p2p.P2PService; -import bisq.network.p2p.storage.payload.ProtectedStorageEntry; -import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService; -import bisq.network.p2p.storage.persistence.ProtectedDataStoreService; - -import javafx.collections.ListChangeListener; - -import java.util.Arrays; -import java.util.Collections; - -import org.junit.Before; -import org.junit.Test; - -import static org.mockito.Mockito.*; - -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - - -/** - * Tests of the P2PDataStorage::onRemoved callback behavior to ensure that the proper number of signal events occur. - */ -public class ProposalServiceP2PDataStorageListenerTest { - private ProposalService proposalService; - - @Mock - private PeriodService periodService; - - @Mock - private DaoStateService daoStateService; - - @Mock - private ListChangeListener tempProposalListener; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - this.proposalService = new ProposalService( - mock(P2PService.class), - this.periodService, - mock(ProposalStorageService.class), - mock(TempProposalStorageService.class), - mock(AppendOnlyDataStoreService.class), - mock(ProtectedDataStoreService.class), - this.daoStateService, - mock(ProposalValidatorProvider.class), - true); - - // Create a state so that all added/removed Proposals will actually update the tempProposals list. - when(this.periodService.isInPhase(anyInt(), any(DaoPhase.Phase.class))).thenReturn(true); - when(this.daoStateService.isParseBlockChainComplete()).thenReturn(false); - } - - private static ProtectedStorageEntry buildProtectedStorageEntry() { - ProtectedStorageEntry protectedStorageEntry = mock(ProtectedStorageEntry.class); - TempProposalPayload tempProposalPayload = mock(TempProposalPayload.class); - Proposal tempProposal = mock(Proposal.class); - when(protectedStorageEntry.getProtectedStoragePayload()).thenReturn(tempProposalPayload); - when(tempProposalPayload.getProposal()).thenReturn(tempProposal); - - return protectedStorageEntry; - } - - // TESTCASE: If an onRemoved callback is called which does not remove anything the tempProposals listeners - // are not signaled. - @Test - public void onRemoved_noSignalIfNoChange() { - this.proposalService.onRemoved(Collections.singletonList(mock(ProtectedStorageEntry.class))); - - verify(this.tempProposalListener, never()).onChanged(any()); - } - - // TESTCASE: If an onRemoved callback is called with 1 element AND it creates a remove of 1 element, the tempProposal - // listeners are signaled once. - @Test - public void onRemoved_signalOnceOnOneChange() { - ProtectedStorageEntry one = buildProtectedStorageEntry(); - this.proposalService.onAdded(Collections.singletonList(one)); - this.proposalService.getTempProposals().addListener(this.tempProposalListener); - - this.proposalService.onRemoved(Collections.singletonList(one)); - - verify(this.tempProposalListener).onChanged(any()); - } - - // TESTCASE: If an onRemoved callback is called with 2 elements AND it creates a remove of 2 elements, the - // tempProposal listeners are signaled once. - @Test - public void onRemoved_signalOnceOnMultipleChanges() { - ProtectedStorageEntry one = buildProtectedStorageEntry(); - ProtectedStorageEntry two = buildProtectedStorageEntry(); - this.proposalService.onAdded(Arrays.asList(one, two)); - this.proposalService.getTempProposals().addListener(this.tempProposalListener); - - this.proposalService.onRemoved(Arrays.asList(one, two)); - - verify(this.tempProposalListener).onChanged(any()); - } -} diff --git a/core/src/test/java/bisq/core/dao/governance/proposal/param/ChangeParamValidatorTest.java b/core/src/test/java/bisq/core/dao/governance/proposal/param/ChangeParamValidatorTest.java deleted file mode 100644 index 0ba4bed83d..0000000000 --- a/core/src/test/java/bisq/core/dao/governance/proposal/param/ChangeParamValidatorTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.governance.proposal.param; - -import bisq.core.dao.governance.param.Param; -import bisq.core.locale.Res; -import bisq.core.util.coin.BsqFormatter; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class ChangeParamValidatorTest { - @Before - public void setup() { - Res.setup(); - } - - @Test - public void testGetChangeValidationResult() throws ParamValidationException { - ChangeParamValidator changeParamValidator = new ChangeParamValidator(null, null, new BsqFormatter()); - try { - changeParamValidator.validationChange(0, 0, 2, 2, Param.UNDEFINED); - Assert.fail(); - } catch (ParamValidationException e) { - Assert.assertEquals(e.getError(), ParamValidationException.ERROR.SAME); - } - - try { - changeParamValidator.validationChange(0, 1, 2, 2, Param.UNDEFINED); - Assert.fail(); - } catch (ParamValidationException e) { - Assert.assertEquals(e.getError(), ParamValidationException.ERROR.NO_CHANGE_POSSIBLE); - } - - try { - changeParamValidator.validationChange(0, -1, 2, 2, Param.UNDEFINED); - Assert.fail(); - } catch (ParamValidationException e) { - Assert.assertEquals(e.getError(), ParamValidationException.ERROR.NO_CHANGE_POSSIBLE); - } - - try { - changeParamValidator.validationChange(2, 4, 2, 1.1, Param.UNDEFINED); - Assert.fail(); - } catch (ParamValidationException e) { - Assert.assertEquals(e.getError(), ParamValidationException.ERROR.TOO_HIGH); - } - - try { - changeParamValidator.validationChange(4, 2, 1.5, 2, Param.UNDEFINED); - Assert.fail(); - } catch (ParamValidationException e) { - Assert.assertEquals(e.getError(), ParamValidationException.ERROR.TOO_LOW); - } - - changeParamValidator.validationChange(4, 2, 2, 2, Param.UNDEFINED); - changeParamValidator.validationChange(2, 4, 2, 2, Param.UNDEFINED); - changeParamValidator.validationChange(0, 1, 0, 0, Param.UNDEFINED); - changeParamValidator.validationChange(0, -1, 0, 0, Param.UNDEFINED); - changeParamValidator.validationChange(-1, 0, 0, 0, Param.UNDEFINED); - changeParamValidator.validationChange(1, 0, 0, 0, Param.UNDEFINED); - } -} diff --git a/core/src/test/java/bisq/core/dao/node/full/BlockParserTest.java b/core/src/test/java/bisq/core/dao/node/full/BlockParserTest.java deleted file mode 100644 index 21b34eb59a..0000000000 --- a/core/src/test/java/bisq/core/dao/node/full/BlockParserTest.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full; - -// not converting this test because it is already ignored. -// Intro to jmockit can be found at http://jmockit.github.io/tutorial/Mocking.html -//@Ignore -/* -public class BlockParserTest { - // @Tested classes are instantiated automatically when needed in a test case, - // using injection where possible, see http://jmockit.github.io/tutorial/Mocking.html#tested - // To force instantiate earlier, use availableDuringSetup - @Tested(fullyInitialized = true, availableDuringSetup = true) - BlockParser blockParser; - - @Tested(fullyInitialized = true, availableDuringSetup = true) - DaoStateService daoStateService; - - // @Injectable are mocked resources used to for injecting into @Tested classes - // The naming of these resources doesn't matter, any resource that fits will be used for injection - - // Used by daoStateService - @Injectable - PersistenceProtoResolver persistenceProtoResolver; - @Injectable - File storageDir; - @Injectable - String genesisTxId = "genesisTxId"; - @Injectable - int genesisBlockHeight = 200; - - // Used by fullNodeParser - @Injectable - RpcService rpcService; - @Tested(fullyInitialized = true, availableDuringSetup = true) - DaoStateService writeModel; - @Tested(fullyInitialized = true, availableDuringSetup = true) - TxParser txParser; - - //FIXME - @Test - public void testIsBsqTx() { - // Setup a basic transaction with two inputs - int height = 200; - String hash = "abc123"; - long time = new Date().getTime(); - final List inputs = asList(new TxInput("tx1", 0, null), - new TxInput("tx1", 1, null)); - final List outputs = asList(new RawTxOutput(0, 101, "tx1", null, null, null, height)); - RawTx rawTx = new RawTx("vo", height, hash, time, - ImmutableList.copyOf(inputs), - ImmutableList.copyOf(outputs)); - - // Return one spendable txoutputs with value, for three test cases - // 1) - null, 0 -> not BSQ transaction - // 2) - 100, null -> BSQ transaction - // 3) - 0, 100 -> BSQ transaction - new Expectations(daoStateService) {{ - // Expectations can be recorded on mocked instances, either with specific matching arguments or catch all - // http://jmockit.github.io/tutorial/Mocking.html#results - // Results are returned in the order they're recorded, so in this case for the first call to - // getSpendableTxOutput("tx1", 0) the return value will be Optional.empty() - // for the second call the return is Optional.of(new TxOutput(0,... and so on - daoStateService.getUnspentTxOutput(new TxOutputKey("tx1", 0)); - result = Optional.empty(); - result = Optional.of(new RawTxOutput(0, 100, "txout1", null, null, null, height)); - result = Optional.of(new RawTxOutput(0, 0, "txout1", null, null, null, height)); - - daoStateService.getUnspentTxOutput(new TxOutputKey("tx1", 1)); - result = Optional.of(new RawTxOutput(0, 0, "txout2", null, null, null, height)); - result = Optional.empty(); - result = Optional.of(new RawTxOutput(0, 100, "txout2", null, null, null, height)); - }}; - String genesisTxId = "genesisTxId"; - int blockHeight = 200; - String blockHash = "abc123"; - Coin genesisTotalSupply = Coin.parseCoin("2.5"); - - // First time there is no BSQ value to spend so it's not a bsq transaction - assertFalse(txParser.findTx(rawTx, genesisTxId, blockHeight, genesisTotalSupply).isPresent()); - // Second time there is BSQ in the first txout - assertTrue(txParser.findTx(rawTx, genesisTxId, blockHeight, genesisTotalSupply).isPresent()); - // Third time there is BSQ in the second txout - assertTrue(txParser.findTx(rawTx, genesisTxId, blockHeight, genesisTotalSupply).isPresent()); - } - - @Test - public void testParseBlocks() { - // Setup blocks to test, starting before genesis - // Only the transactions related to bsq are relevant, no checks are done on correctness of blocks or other txs - // so hashes and most other data don't matter - long time = new Date().getTime(); - int genesisHeight = 200; - int startHeight = 199; - int headHeight = 201; - Coin issuance = Coin.parseCoin("2.5"); - RawTransaction genTx = new RawTransaction("gen block hash", 0, 0L, 0L, genesisTxId); - - // Blockhashes - String bh199 = "blockhash199"; - String bh200 = "blockhash200"; - String bh201 = "blockhash201"; - - // Block 199 - String cbId199 = "cbid199"; - RawTransaction tx199 = new RawTransaction(bh199, 0, 0L, 0L, cbId199); - RawTx cbTx199 = new RawTx(cbId199, 199, bh199, time, - ImmutableList.copyOf(new ArrayList()), - ImmutableList.copyOf(asList(new RawTxOutput(0, 25, cbId199, null, null, null, 199)))); - RawBlock block199 = new RawBlock(bh199, 10, 10, 199, 2, "root", asList(tx199), time, Long.parseLong("1234"), "bits", BigDecimal.valueOf(1), "chainwork", "previousBlockHash", bh200); - - // Genesis Block - String cbId200 = "cbid200"; - RawTransaction tx200 = new RawTransaction(bh200, 0, 0L, 0L, cbId200); - RawTx cbTx200 = new RawTx(cbId200, 200, bh200, time, - ImmutableList.copyOf(new ArrayList()), - ImmutableList.copyOf(asList(new RawTxOutput(0, 25, cbId200, null, null, null, 200)))); - RawTx genesisTx = new RawTx(genesisTxId, 200, bh200, time, - ImmutableList.copyOf(asList(new TxInput("someoldtx", 0, null))), - ImmutableList.copyOf(asList(new RawTxOutput(0, issuance.getValue(), genesisTxId, null, null, null, 200)))); - RawBlock block200 = new RawBlock(bh200, 10, 10, 200, 2, "root", asList(tx200, genTx), time, Long.parseLong("1234"), "bits", BigDecimal.valueOf(1), "chainwork", bh199, bh201); - - // Block 201 - // Make a bsq transaction - String cbId201 = "cbid201"; - String bsqTx1Id = "bsqtx1"; - RawTransaction tx201 = new RawTransaction(bh201, 0, 0L, 0L, cbId201); - RawTransaction txbsqtx1 = new RawTransaction(bh201, 0, 0L, 0L, bsqTx1Id); - long bsqTx1Value1 = Coin.parseCoin("2.4").getValue(); - long bsqTx1Value2 = Coin.parseCoin("0.04").getValue(); - RawTx cbTx201 = new RawTx(cbId201, 201, bh201, time, - ImmutableList.copyOf(new ArrayList()), - ImmutableList.copyOf(asList(new RawTxOutput(0, 25, cbId201, null, null, null, 201)))); - RawTx bsqTx1 = new RawTx(bsqTx1Id, 201, bh201, time, - ImmutableList.copyOf(asList(new TxInput(genesisTxId, 0, null))), - ImmutableList.copyOf(asList(new RawTxOutput(0, bsqTx1Value1, bsqTx1Id, null, null, null, 201), - new RawTxOutput(1, bsqTx1Value2, bsqTx1Id, null, null, null, 201)))); - RawBlock block201 = new RawBlock(bh201, 10, 10, 201, 2, "root", asList(tx201, txbsqtx1), time, Long.parseLong("1234"), "bits", BigDecimal.valueOf(1), "chainwork", bh200, "nextBlockHash"); - - // TODO update test with new API - /* - new Expectations(rpcService) {{ - rpcService.requestBlock(199); - result = block199; - rpcService.requestBlock(200); - result = block200; - rpcService.requestBlock(201); - result = block201; - - rpcService.requestTx(cbId199, 199); - result = cbTx199; - rpcService.requestTx(cbId200, genesisHeight); - result = cbTx200; - rpcService.requestTx(genesisTxId, genesisHeight); - result = genesisTx; - rpcService.requestTx(cbId201, 201); - result = cbTx201; - rpcService.requestTx(bsqTx1Id, 201); - result = bsqTx1; - }}; - - // Running parseBlocks to build the bsq blockchain - fullNodeParser.parseBlocks(startHeight, headHeight, block -> { - }); -*/ - -// Verify that the genesis tx has been added to the bsq blockchain with the correct issuance amount - /* assertTrue(daoStateService.getGenesisTx().get() == genesisTx); - assertTrue(daoStateService.getGenesisTotalSupply().getValue() == issuance.getValue()); - - // And that other txs are not added - assertFalse(daoStateService.containsTx(cbId199)); - assertFalse(daoStateService.containsTx(cbId200)); - assertFalse(daoStateService.containsTx(cbId201)); - - // But bsq txs are added - assertTrue(daoStateService.containsTx(bsqTx1Id)); - TxOutput bsqOut1 = daoStateService.getUnspentAndMatureTxOutput(bsqTx1Id, 0).get(); - assertTrue(daoStateService.isUnspent(bsqOut1)); - assertTrue(bsqOut1.getValue() == bsqTx1Value1); - TxOutput bsqOut2 = daoStateService.getUnspentAndMatureTxOutput(bsqTx1Id, 1).get(); - assertTrue(daoStateService.isUnspent(bsqOut2)); - assertTrue(bsqOut2.getValue() == bsqTx1Value2); - assertFalse(daoStateService.isTxOutputSpendable(genesisTxId, 0)); - assertTrue(daoStateService.isTxOutputSpendable(bsqTx1Id, 0)); - assertTrue(daoStateService.isTxOutputSpendable(bsqTx1Id, 1)); - - } - } - */ diff --git a/core/src/test/java/bisq/core/dao/node/full/RpcServiceTest.java b/core/src/test/java/bisq/core/dao/node/full/RpcServiceTest.java deleted file mode 100644 index 4b10a835ef..0000000000 --- a/core/src/test/java/bisq/core/dao/node/full/RpcServiceTest.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full; - -import bisq.core.dao.node.full.rpc.dto.RawDtoInput; -import bisq.core.dao.node.full.rpc.dto.DtoSignatureScript; - -import java.util.List; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -public class RpcServiceTest { - private static final String SIGNATURE = "3045" + - "022100b6c2fa10587d6fed3a0eecfd098b160f69a850beca139fe03ef65bec4cba1c5b" + - "02204a833a16c22bbd32722243ea3270e672f646ee9406e8797e11093951e92efbd5"; - private static final String SIGNATURE_1 = "3044" + - "02201f00d9a4aab1a3a239f1ad95a910092c0c55423480d609eaad4599cf7ecb7f48" + - "0220668b1a9cf5624b1c4ece6da3f64bc6021e509f588ae1006601acd8a9f83b3576"; - private static final String SIGNATURE_2 = "3045" + - "022100982eca77a72a2bdba51b9231afd4521400bee1bb7830634eb26db2b0c621bc46" + - "022073d7325916e2b5ceb1d2e510a5161fd9115105a8dafa94068864624bb10d190e"; - private static final String PUB_KEY = - "03dcca91c2ec7229f1b4f4c4f664c92d3303dddef8d38736f6a7f28de16f3ce416"; - private static final String PUB_KEY_1 = - "0229713ad5c604c585128b3a5da6de20d78fc33bd3b595e9991f4c0e1fee99f845"; - private static final String PUB_KEY_2 = - "0398ad45a74bf5a5c5a8ec31de6815d2e805a23e68c0f8001770e74bc4c17c5b31"; - private static final String MULTISIG_REDEEM_SCRIPT_HEX = - "5221" + PUB_KEY_1 + "21" + PUB_KEY_2 + "52ae"; // OP_2 pub1 pub2 OP_2 OP_CHECKMULTISIG - private static final String P2WPKH_REDEEM_SCRIPT_HEX = - "0014" + "9bc809698674ec7c01d35d438e9d0de1aa87b6c8"; // 0 hash160 - private static final String P2WSH_REDEEM_SCRIPT_HEX = - "0020" + "223d978073802f79e6ecdc7591e5dc1f0ea7030d6466f73c6b90391bc72e886f"; // 0 hash256 - - @Test - public void testExtractPubKeyAsHex_coinbase() { - checkExtractPubKeyAsHexReturnsNull(new RawDtoInput()); - } - - @Test - public void testExtractPubKeyAsHex_P2PK() { - var input = rawInput(SIGNATURE + "[ALL]"); - checkExtractPubKeyAsHexReturnsNull(input); - } - - @Test - public void testExtractPubKeyAsHex_P2PKH() { - var input = rawInput(SIGNATURE + "[ALL] " + PUB_KEY); - assertEquals(PUB_KEY, RpcService.extractPubKeyAsHex(input, true)); - assertEquals(PUB_KEY, RpcService.extractPubKeyAsHex(input, false)); - } - - @Test - public void testExtractPubKeyAsHex_P2WPKH() { - var input = rawInput("", SIGNATURE + "01", PUB_KEY); - assertEquals(PUB_KEY, RpcService.extractPubKeyAsHex(input, true)); - assertNull(RpcService.extractPubKeyAsHex(input, false)); - } - - @Test - public void testExtractPubKeyAsHex_P2SH_P2WPKH() { - var input = rawInput(P2WPKH_REDEEM_SCRIPT_HEX, SIGNATURE + "01", PUB_KEY); - assertEquals(PUB_KEY, RpcService.extractPubKeyAsHex(input, true)); - assertNull(RpcService.extractPubKeyAsHex(input, false)); - } - - @Test - public void testExtractPubKeyAsHex_P2PKH_nonDefaultSighash() { - var input = rawInput(SIGNATURE + "[SINGLE|ANYONECANPAY] " + PUB_KEY); - checkExtractPubKeyAsHexReturnsNull(input); - } - - @Test - public void testExtractPubKeyAsHex_P2WPKH_nonDefaultSighash() { - var input = rawInput("", SIGNATURE + "82", PUB_KEY); - checkExtractPubKeyAsHexReturnsNull(input); - } - - @Test - public void testExtractPubKeyAsHex_P2SH_P2WPKH_nonDefaultSighash() { - var input = rawInput(P2WPKH_REDEEM_SCRIPT_HEX, SIGNATURE + "82", PUB_KEY); - checkExtractPubKeyAsHexReturnsNull(input); - } - - @Test - public void testExtractPubKeyAsHex_P2SH_multisig() { - var input = rawInput("0 " + SIGNATURE_1 + "[ALL] " + SIGNATURE_2 + "[ALL] " + MULTISIG_REDEEM_SCRIPT_HEX); - checkExtractPubKeyAsHexReturnsNull(input); - } - - @Test - public void testExtractPubKeyAsHex_P2SH_multisig_nonDefaultSighash() { - var input = rawInput("0 " + SIGNATURE_1 + "[ALL] " + SIGNATURE_2 + "[NONE] " + MULTISIG_REDEEM_SCRIPT_HEX); - checkExtractPubKeyAsHexReturnsNull(input); - } - - @Test - public void testExtractPubKeyAsHex_P2WSH_multisig() { - var input = rawInput("", "", SIGNATURE_1 + "01", SIGNATURE_2 + "01", MULTISIG_REDEEM_SCRIPT_HEX); - checkExtractPubKeyAsHexReturnsNull(input); - } - - @Test - public void testExtractPubKeyAsHex_P2SH_P2WSH_multisig() { - var input = rawInput(P2WSH_REDEEM_SCRIPT_HEX, "", SIGNATURE_1 + "01", SIGNATURE_2 + "01", MULTISIG_REDEEM_SCRIPT_HEX); - checkExtractPubKeyAsHexReturnsNull(input); - } - - private static void checkExtractPubKeyAsHexReturnsNull(RawDtoInput input) { - assertNull(RpcService.extractPubKeyAsHex(input, true)); - assertNull(RpcService.extractPubKeyAsHex(input, false)); - } - - private static RawDtoInput rawInput(String asm, String... txInWitness) { - var input = new RawDtoInput(); - var scriptSig = new DtoSignatureScript(); - scriptSig.setAsm(asm); - input.setScriptSig(scriptSig); - input.setTxInWitness(txInWitness.length > 0 ? List.of(txInWitness) : null); - return input; - } -} diff --git a/core/src/test/java/bisq/core/dao/node/full/rpc/BitcoindClientTest.java b/core/src/test/java/bisq/core/dao/node/full/rpc/BitcoindClientTest.java deleted file mode 100644 index ab3548cb98..0000000000 --- a/core/src/test/java/bisq/core/dao/node/full/rpc/BitcoindClientTest.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full.rpc; - -import bisq.core.dao.node.full.rpc.dto.RawDtoBlock; -import bisq.core.dao.node.full.rpc.dto.RawDtoTransaction; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import java.net.ConnectException; -import java.net.HttpURLConnection; -import java.net.Proxy; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLStreamHandler; - -import java.nio.file.Files; -import java.nio.file.Paths; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import org.junit.Before; -import org.junit.Test; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - - - -import com.googlecode.jsonrpc4j.HttpException; -import com.googlecode.jsonrpc4j.JsonRpcClientException; -import com.googlecode.jsonrpc4j.RequestIDGenerator; -import kotlin.text.Charsets; - -public class BitcoindClientTest { - private static final String TEST_BLOCK_HASH = "015f37a20d517645a11a6cdd316049f41bc77b4a4057b2dd092114b78147f42c"; - private static final String TEST_BLOCK_VERBOSITY_0 = readFromResourcesUnPrettified("getblock-result-verbosity-0.txt"); - private static final String TEST_BLOCK_VERBOSITY_1 = readFromResourcesUnPrettified("getblock-result-verbosity-1.json"); - private static final String TEST_BLOCK_VERBOSITY_2 = readFromResourcesUnPrettified("getblock-result-verbosity-2.json"); - private static final String TEST_NETWORK_INFO = readFromResourcesUnPrettified("getnetworkinfo-result.json"); - - private BitcoindClient client; - private int mockResponseCode = 200; - private boolean canConnect = true; - private ByteArrayInputStream mockResponse; - private ByteArrayInputStream mockErrorResponse; - private ByteArrayOutputStream mockOutputStream = new ByteArrayOutputStream(); - - @Before - public void setUp() throws Exception { - var mockURLStreamHandler = mock(MyURLStreamHandler.class); - var mockRequestIDGenerator = mock(RequestIDGenerator.class); - - client = BitcoindClient.builder() - .rpcHost("127.0.0.1") - .rpcPort(18443) - .rpcUser("bisqdao") - .rpcPassword("bsq") - .urlStreamHandler(mockURLStreamHandler) - .requestIDGenerator(mockRequestIDGenerator) - .build(); - - when(mockURLStreamHandler.openConnection(any(), any())).then(inv -> { - var connection = mock(HttpURLConnection.class); - if (canConnect) { - when(connection.getOutputStream()).thenReturn(mockOutputStream); - if (mockResponseCode < 400) { - when(connection.getInputStream()).thenReturn(mockResponse); - } else { - when(connection.getInputStream()).thenThrow(IOException.class); - when(connection.getErrorStream()).thenReturn(mockErrorResponse); - } - } else { - doThrow(ConnectException.class).when(connection).connect(); - } - return connection; - }); - when(mockRequestIDGenerator.generateID()).thenReturn("987654321"); - } - - @Test - public void testGetBlockCount() throws Exception { - var expectedRequest = toJson("{'id':'987654321','jsonrpc':'2.0','method':'getblockcount','params':[]}"); - mockResponse = toJsonIS("{'result':'150','error':null,'id':'123456789'}"); - - assertEquals((Integer) 150, client.getBlockCount()); - assertEquals(expectedRequest, mockOutputStream.toString(UTF_8)); - } - - @Test(expected = ConnectException.class) - public void testGetBlockCount_noConnection() throws Exception { - canConnect = false; - - client.getBlockCount(); - } - - @Test(expected = HttpException.class) - public void testGetBlockCount_wrongCredentials() throws Exception { - mockResponseCode = 401; -// mockResponseCustomHeaders.put("WWW-Authenticate", "[Basic realm=\"jsonrpc\"]"); - - client.getBlockCount(); - } - - @Test - public void testGetBlockHash() throws Exception { - var expectedRequest = toJson("{'id':'987654321','jsonrpc':'2.0','method':'getblockhash','params':[139]}"); - mockResponse = toJsonIS("{'result':'" + TEST_BLOCK_HASH + "','error':null,'id':'123456789'}"); - - assertEquals(TEST_BLOCK_HASH, client.getBlockHash(139)); - assertEquals(expectedRequest, mockOutputStream.toString(UTF_8)); - } - - @Test - public void testGetBestBlockHash() throws Exception { - var expectedRequest = toJson("{'id':'987654321','jsonrpc':'2.0','method':'getbestblockhash','params':[]}"); - mockResponse = toJsonIS("{'result':'" + TEST_BLOCK_HASH + "','error':null,'id':'123456789'}"); - - assertEquals(TEST_BLOCK_HASH, client.getBestBlockHash()); - assertEquals(expectedRequest, mockOutputStream.toString(UTF_8)); - } - - @Test(expected = JsonRpcClientException.class) - public void testGetBlockHash_heightOutOfRange() throws Exception { - mockResponseCode = 500; - mockErrorResponse = toJsonIS("{'result':null,'error':{'code':-8,'message':'Block height out of range'},'id':'123456789'}"); - - client.getBlockHash(151); - } - - @Test - public void testGetBlock_verbosity_0() throws Exception { - doTestGetBlock(0, "\"" + TEST_BLOCK_VERBOSITY_0 + "\""); - } - - @Test - public void testGetBlock_verbosity_1() throws Exception { - doTestGetBlock(1, TEST_BLOCK_VERBOSITY_1); - } - - @Test - public void testGetBlock_verbosity_2() throws Exception { - doTestGetBlock(2, TEST_BLOCK_VERBOSITY_2); - } - - private void doTestGetBlock(int verbosity, String blockJson) throws Exception { - var expectedRequest = toJson("{'id':'987654321','jsonrpc':'2.0','method':'getblock','params':['" - + TEST_BLOCK_HASH + "'," + verbosity + "]}"); - mockResponse = toJsonIS("{'result':" + blockJson + ",'error':null,'id':'123456789'}"); - - var block = client.getBlock(TEST_BLOCK_HASH, verbosity); - var blockJsonRoundTripped = new ObjectMapper().writeValueAsString(block); - - assertEquals(verbosity == 0, block instanceof RawDtoBlock.Summarized); - assertEquals(verbosity == 1, block.getTx() != null && - block.getTx().stream().allMatch(tx -> tx instanceof RawDtoTransaction.Summarized)); - - assertEquals(blockJson, blockJsonRoundTripped); - assertEquals(expectedRequest, mockOutputStream.toString(UTF_8)); - } - - @Test(expected = JsonRpcClientException.class) - public void testGetBlock_blockNotFound() throws Exception { - mockResponseCode = 500; - mockErrorResponse = toJsonIS("{'result':null,'error':{'code':-5,'message':'Block not found'},'id':'123456789'}"); - - client.getBlock(TEST_BLOCK_HASH.replace('f', 'e'), 2); - } - - @Test(expected = JsonRpcClientException.class) - public void testGetBlock_malformedHash() throws Exception { - mockResponseCode = 500; - mockErrorResponse = toJsonIS("{'result':null,'error':{'code':-8,'message':'blockhash must be of length 64 " + - "(not 3, for \\'foo\\')'},'id':'123456789'}"); - - client.getBlock("foo", 2); - } - - @Test - public void testGetNetworkInfo() throws Exception { - var expectedRequest = toJson("{'id':'987654321','jsonrpc':'2.0','method':'getnetworkinfo','params':[]}"); - mockResponse = toJsonIS("{'result':" + TEST_NETWORK_INFO + ",'error':null,'id':'123456789'}"); - - var networkInfo = client.getNetworkInfo(); - var networkInfoRoundTripped = new ObjectMapper().writeValueAsString(networkInfo); - var expectedNetworkInfoStr = TEST_NETWORK_INFO.replace("MY_CUSTOM_SERVICE", "UNKNOWN"); - - assertEquals(expectedNetworkInfoStr, networkInfoRoundTripped); - assertEquals(expectedRequest, mockOutputStream.toString(UTF_8)); - } - - private static String toJson(String json) { - return json.replace("'", "\"").replace("\\\"", "'"); - } - - private static ByteArrayInputStream toJsonIS(String json) { - return new ByteArrayInputStream(toJson(json).getBytes(UTF_8)); - } - - private static String readFromResourcesUnPrettified(String resourceName) { - try { - var path = Paths.get(BitcoindClientTest.class.getResource(resourceName).toURI()); - return new String(Files.readAllBytes(path), Charsets.UTF_8).replaceAll("(\\s+\\B|\\B\\s+|\\v)", ""); - } catch (Exception e) { - return ""; - } - } - - private static abstract class MyURLStreamHandler extends URLStreamHandler { - @Override - public abstract URLConnection openConnection(URL u, Proxy p); - } -} diff --git a/core/src/test/java/bisq/core/dao/node/full/rpc/BitcoindDaemonTest.java b/core/src/test/java/bisq/core/dao/node/full/rpc/BitcoindDaemonTest.java deleted file mode 100644 index 0cc9dc2065..0000000000 --- a/core/src/test/java/bisq/core/dao/node/full/rpc/BitcoindDaemonTest.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.full.rpc; - -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketException; - -import java.io.ByteArrayInputStream; -import java.io.IOException; - -import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import static org.mockito.Mockito.*; - -public class BitcoindDaemonTest { - private BitcoindDaemon daemon; - private int acceptAnotherCount; - private CountDownLatch errorHandlerLatch = new CountDownLatch(1); - private Consumer errorHandler = mock(ThrowableConsumer.class); - private BitcoindDaemon.BlockListener blockListener = mock(BitcoindDaemon.BlockListener.class); - private Socket socket = mock(Socket.class); - private volatile boolean socketClosed; - - @Before - public void setUp() throws Exception { - var serverSocket = mock(ServerSocket.class); - - when(serverSocket.accept()).then(invocation -> waitToAccept(() -> { - if (socketClosed) { - throw new SocketException(); - } - return socket; - })); - doAnswer((VoidAnswer) invocation -> { - socketClosed = true; - acceptAnother(1); - }).when(serverSocket).close(); - - doAnswer((VoidAnswer) invocation -> errorHandlerLatch.countDown()).when(errorHandler).accept(any()); - - daemon = new BitcoindDaemon(serverSocket, errorHandler); - daemon.setBlockListener(blockListener); - } - - @After - public void tearDown() { - daemon.shutdown(); - } - - @Test - public void testNoBlocksMissedDuringFloodOfIncomingBlocks() throws Exception { - var latch = new CountDownLatch(1); // to block all the daemon worker threads until shutdown, as if stuck - - doAnswer((VoidAnswer) invocation -> latch.await()).when(blockListener).blockDetected(any()); - when(socket.getInputStream()).then(invocation -> new ByteArrayInputStream("foo".getBytes())); - - acceptAnother(50); - waitUntilAllAccepted(); - - // Unblock all the daemon worker threads and shut down. - latch.countDown(); - daemon.shutdown(); - - verify(blockListener, times(50)).blockDetected("foo"); - } - - @Test - public void testBlockHashIsTrimmed() throws Exception { - when(socket.getInputStream()).then(invocation -> new ByteArrayInputStream("\r\nbar \n".getBytes())); - - acceptAnother(1); - waitUntilAllAccepted(); - daemon.shutdown(); - - verify(blockListener).blockDetected("bar"); - } - - @Test - public void testBrokenSocketRead() throws Exception { - when(socket.getInputStream()).thenThrow(IOException.class); - - acceptAnother(1); - errorHandlerLatch.await(5, TimeUnit.SECONDS); - - verify(errorHandler).accept(argThat(t -> t instanceof NotificationHandlerException && - t.getCause() instanceof IOException)); - } - - @Test - public void testRuntimeExceptionInBlockListener() throws Exception { - daemon.setBlockListener(blockHash -> { - throw new IndexOutOfBoundsException(); - }); - when(socket.getInputStream()).then(invocation -> new ByteArrayInputStream("foo".getBytes())); - - acceptAnother(1); - errorHandlerLatch.await(5, TimeUnit.SECONDS); - - verify(errorHandler).accept(argThat(t -> t instanceof NotificationHandlerException && - t.getCause() instanceof IndexOutOfBoundsException)); - } - - - @Test - public void testErrorInBlockListener() throws Exception { - synchronized (this) { - daemon.setBlockListener(blockHash -> { - throw new Error(); - }); - when(socket.getInputStream()).then(invocation -> new ByteArrayInputStream("foo".getBytes())); - acceptAnother(1); - } - errorHandlerLatch.await(5, TimeUnit.SECONDS); - - verify(errorHandler).accept(any(Error.class)); - } - - @Test(expected = NotificationHandlerException.class) - public void testUnknownHost() throws Exception { - new BitcoindDaemon("[", -1, errorHandler).shutdown(); - } - - private synchronized void acceptAnother(int n) { - acceptAnotherCount += n; - notifyAll(); - } - - private synchronized V waitToAccept(Callable onAccept) throws Exception { - while (acceptAnotherCount == 0) { - wait(); - } - var result = onAccept.call(); - acceptAnotherCount--; - notifyAll(); - return result; - } - - private synchronized void waitUntilAllAccepted() throws InterruptedException { - while (acceptAnotherCount > 0) { - wait(); - } - notifyAll(); - } - - private interface ThrowableConsumer extends Consumer { - } - - private interface VoidAnswer extends Answer { - void voidAnswer(InvocationOnMock invocation) throws Throwable; - - @Override - default Void answer(InvocationOnMock invocation) throws Throwable { - voidAnswer(invocation); - return null; - } - } -} diff --git a/core/src/test/java/bisq/core/dao/node/parser/GenesisTxParserTest.java b/core/src/test/java/bisq/core/dao/node/parser/GenesisTxParserTest.java deleted file mode 100644 index 62a591f3ab..0000000000 --- a/core/src/test/java/bisq/core/dao/node/parser/GenesisTxParserTest.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.node.parser; - -import bisq.core.dao.node.full.RawTx; -import bisq.core.dao.node.full.RawTxOutput; -import bisq.core.dao.node.parser.exceptions.InvalidGenesisTxException; -import bisq.core.dao.state.model.blockchain.TxInput; -import bisq.core.dao.state.model.blockchain.TxOutputType; -import bisq.core.dao.state.model.blockchain.TxType; - -import org.bitcoinj.core.Coin; - -import com.google.common.collect.ImmutableList; - -import java.util.Arrays; -import java.util.Date; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; - -public class GenesisTxParserTest { - - @Test - public void testIsGenesis() { - // fixme(chirhonul): Assert.assertEquals(2, 3); - - int blockHeight = 200; - String blockHash = "abc123"; - Coin genesisTotalSupply = Coin.parseCoin("2.5"); - long time = new Date().getTime(); - final List inputs = Arrays.asList( - new TxInput("tx0", 0, null), - new TxInput("tx1", 1, null) - ); - RawTxOutput output = new RawTxOutput( - 0, - genesisTotalSupply.value, - null, - null, - null, - null, - blockHeight - ); - RawTx rawTx = new RawTx( - "tx2", - blockHeight, - blockHash, - time, - ImmutableList.copyOf(inputs), - ImmutableList.copyOf(Arrays.asList(output)) - ); - - String genesisTxId = "genesisTxId"; - int genesisBlockHeight = 150; - - // With mismatch in block height and tx id, we should not get genesis tx back. - boolean result = GenesisTxParser.isGenesis(rawTx, genesisTxId, genesisBlockHeight); - boolean want = false; - Assert.assertEquals(want, result); - - // With correct block height but mismatch in tx id, we should still not get genesis tx back. - blockHeight = 150; - rawTx = new RawTx( - "tx2", - blockHeight, - blockHash, - time, - ImmutableList.copyOf(inputs), - ImmutableList.copyOf(Arrays.asList(output)) - ); - result = GenesisTxParser.isGenesis(rawTx, genesisTxId, genesisBlockHeight); - want = false; - Assert.assertEquals(want, result); - - } - - @Test - public void testGetGenesisTempTx() { - int blockHeight = 200; - String blockHash = "abc123"; - Coin genesisTotalSupply = Coin.parseCoin("2.5"); - long time = new Date().getTime(); - final List inputs = Arrays.asList( - new TxInput("tx0", 0, null), - new TxInput("tx1", 1, null) - ); - RawTxOutput output = new RawTxOutput( - 0, - genesisTotalSupply.value, - null, - null, - null, - null, - blockHeight - ); - - String genesisTxId = "genesisTxId"; - // With correct tx id and block height, we should find our genesis tx with correct tx and output type. - RawTx rawTx = new RawTx( - genesisTxId, - blockHeight, - blockHash, - time, - ImmutableList.copyOf(inputs), - ImmutableList.copyOf(Arrays.asList(output)) - ); - TempTx resultTempTx = GenesisTxParser.getGenesisTempTx(rawTx, genesisTotalSupply); - - TempTx tempTx = TempTx.fromRawTx(rawTx); - tempTx.setTxType(TxType.GENESIS); - for (int i = 0; i < tempTx.getTempTxOutputs().size(); ++i) { - tempTx.getTempTxOutputs().get(i).setTxOutputType(TxOutputType.GENESIS_OUTPUT); - } - TempTx wantTempTx = tempTx; - - Assert.assertEquals(wantTempTx, resultTempTx); - - // With correct tx id and block height, but too low sum of outputs (lower than genesisTotalSupply), we - // should see an exception raised. - output = new RawTxOutput( - 0, - genesisTotalSupply.value - 1, - null, - null, - null, - null, - blockHeight - ); - rawTx = new RawTx( - genesisTxId, - blockHeight, - blockHash, - time, - ImmutableList.copyOf(inputs), - ImmutableList.copyOf(Arrays.asList(output)) - ); - try { - GenesisTxParser.getGenesisTempTx(rawTx, genesisTotalSupply); - Assert.fail("Expected an InvalidGenesisTxException to be thrown when outputs are too low"); - } catch (InvalidGenesisTxException igtxe) { - String wantMessage = "Genesis tx is invalid; not using all available inputs. Remaining input value is 1 sat"; - Assert.assertTrue("Unexpected exception, want message starting with " + - "'" + wantMessage + "', got '" + igtxe.getMessage() + "'", igtxe.getMessage().startsWith(wantMessage)); - } - - // With correct tx id and block height, but too high sum of outputs (higher than from genesisTotalSupply), we - // should see an exception raised. - RawTxOutput output1 = new RawTxOutput( - 0, - genesisTotalSupply.value - 2, - null, - null, - null, - null, - blockHeight - ); - RawTxOutput output2 = new RawTxOutput( - 0, - 3, - null, - null, - null, - null, - blockHeight - ); - rawTx = new RawTx( - genesisTxId, - blockHeight, - blockHash, - time, - ImmutableList.copyOf(inputs), - ImmutableList.copyOf(Arrays.asList(output1, output2)) - ); - try { - GenesisTxParser.getGenesisTempTx(rawTx, genesisTotalSupply); - Assert.fail("Expected an InvalidGenesisTxException to be thrown when outputs are too high"); - } catch (InvalidGenesisTxException igtxe) { - String wantMessage = "Genesis tx is invalid; using more than available inputs. Remaining input value is 2 sat"; - Assert.assertTrue("Unexpected exception, want message starting with " + - "'" + wantMessage + "', got '" + igtxe.getMessage() + "'", igtxe.getMessage().startsWith(wantMessage)); - } - } -} diff --git a/core/src/test/java/bisq/core/dao/state/DaoStateServiceTest.java b/core/src/test/java/bisq/core/dao/state/DaoStateServiceTest.java deleted file mode 100644 index 96857e59bb..0000000000 --- a/core/src/test/java/bisq/core/dao/state/DaoStateServiceTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state; - -import bisq.core.dao.state.model.DaoState; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.util.coin.BsqFormatter; - -import org.bitcoinj.core.Coin; - -import org.junit.Assert; -import org.junit.Test; - -public class DaoStateServiceTest { - @Test - public void testIsBlockHashKnown() { - DaoStateService stateService = new DaoStateService( - new DaoState(), - new GenesisTxInfo("fakegenesistxid", 100, Coin.parseCoin("2.5").value), - new BsqFormatter()); - Assert.assertEquals( - "Unknown block should not exist.", - false, - stateService.isBlockHashKnown("fakeblockhash0") - ); - - Block block = new Block(0, 1534800000, "fakeblockhash0", null); - stateService.onNewBlockHeight(0); - stateService.onNewBlockWithEmptyTxs(block); - Assert.assertEquals( - "Block has to be genesis block to get added.", - false, - stateService.isBlockHashKnown("fakeblockhash0") - ); - - Assert.assertEquals( - "Block that was never added should still not exist.", - false, - stateService.isBlockHashKnown("fakeblockhash1") - ); - - block = new Block(1, 1534800001, "fakeblockhash1", null); - stateService.onNewBlockHeight(1); - stateService.onNewBlockWithEmptyTxs(block); - block = new Block(2, 1534800002, "fakeblockhash2", null); - stateService.onNewBlockHeight(2); - stateService.onNewBlockWithEmptyTxs(block); - block = new Block(3, 1534800003, "fakeblockhash3", null); - stateService.onNewBlockHeight(3); - stateService.onNewBlockWithEmptyTxs(block); - Assert.assertEquals( - "Block that was never added should still not exist after adding more blocks.", - false, - stateService.isBlockHashKnown("fakeblockhash4") - ); - } -} diff --git a/core/src/test/java/bisq/core/dao/state/DaoStateSnapshotServiceTest.java b/core/src/test/java/bisq/core/dao/state/DaoStateSnapshotServiceTest.java deleted file mode 100644 index a9e773878a..0000000000 --- a/core/src/test/java/bisq/core/dao/state/DaoStateSnapshotServiceTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.state; - -import bisq.core.dao.monitoring.DaoStateMonitoringService; -import bisq.core.dao.state.storage.DaoStateStorageService; - -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; - -public class DaoStateSnapshotServiceTest { - - private DaoStateSnapshotService daoStateSnapshotService; - - @Before - public void setup() { - daoStateSnapshotService = new DaoStateSnapshotService(mock(DaoStateService.class), - mock(GenesisTxInfo.class), - mock(DaoStateStorageService.class), - mock(DaoStateMonitoringService.class), - null); - } - - @Test - public void testGetSnapshotHeight() { - assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 0, 10)); - assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 100, 10)); - assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 102, 10)); - assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 119, 10)); - assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 120, 10)); - assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 121, 10)); - assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 130, 10)); - assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 139, 10)); - assertEquals(130, daoStateSnapshotService.getSnapshotHeight(102, 140, 10)); - assertEquals(130, daoStateSnapshotService.getSnapshotHeight(102, 141, 10)); - assertEquals(990, daoStateSnapshotService.getSnapshotHeight(102, 1000, 10)); - } - - @Test - public void testSnapshotHeight() { - assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 0, 10)); - assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 80, 10)); - assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 90, 10)); - assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 100, 10)); - assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 119, 10)); - assertTrue(daoStateSnapshotService.isSnapshotHeight(102, 120, 10)); - assertTrue(daoStateSnapshotService.isSnapshotHeight(102, 130, 10)); - assertTrue(daoStateSnapshotService.isSnapshotHeight(102, 140, 10)); - assertTrue(daoStateSnapshotService.isSnapshotHeight(102, 200, 10)); - assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 201, 10)); - assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 199, 10)); - } -} diff --git a/core/src/test/java/bisq/core/dao/voting/voteresult/VoteResultConsensusTest.java b/core/src/test/java/bisq/core/dao/voting/voteresult/VoteResultConsensusTest.java deleted file mode 100644 index c6d6de3c4f..0000000000 --- a/core/src/test/java/bisq/core/dao/voting/voteresult/VoteResultConsensusTest.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.dao.voting.voteresult; - -import bisq.core.dao.governance.merit.MeritConsensus; - -import lombok.extern.slf4j.Slf4j; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import static org.junit.Assert.assertEquals; - -@Slf4j -public class VoteResultConsensusTest { - @Before - public void setup() { - } - - @Rule - public final ExpectedException exception = ExpectedException.none(); - - @Test - public void testGetWeightedMeritAmount() { - int currentChainHeight; - int blocksPerYear = 50_000; // 144*365=51264; - currentChainHeight = 1_000_000; - - assertEquals("fresh issuance", 100000, MeritConsensus.getWeightedMeritAmount(100_000, 1_000_000, - currentChainHeight, blocksPerYear)); - assertEquals("0.5 year old issuance", 75000, MeritConsensus.getWeightedMeritAmount(100_000, 975_000, - currentChainHeight, blocksPerYear)); - assertEquals("1 year old issuance", 50000, MeritConsensus.getWeightedMeritAmount(100_000, 950_000, - currentChainHeight, blocksPerYear)); - assertEquals("1.5 year old issuance", 25000, MeritConsensus.getWeightedMeritAmount(100_000, 925_000, - currentChainHeight, blocksPerYear)); - assertEquals("2 year old issuance", 0, MeritConsensus.getWeightedMeritAmount(100_000, 900_000, - currentChainHeight, blocksPerYear)); - assertEquals("3 year old issuance", 0, MeritConsensus.getWeightedMeritAmount(100_000, 850_000, - currentChainHeight, blocksPerYear)); - - - assertEquals("1 block old issuance", 99999, MeritConsensus.getWeightedMeritAmount(100_000, 999_999, - currentChainHeight, blocksPerYear)); - assertEquals("2 block old issuance", 99998, MeritConsensus.getWeightedMeritAmount(100_000, 999_998, - currentChainHeight, blocksPerYear)); - assertEquals("10 blocks old issuance", 99990, MeritConsensus.getWeightedMeritAmount(100_000, 999_990, - currentChainHeight, blocksPerYear)); - assertEquals("100 blocks old issuance", 99900, MeritConsensus.getWeightedMeritAmount(100_000, 999_900, - currentChainHeight, blocksPerYear)); - assertEquals("99_999 blocks old issuance", 1, MeritConsensus.getWeightedMeritAmount(100_000, 900_001, - currentChainHeight, blocksPerYear)); - assertEquals("99_990 blocks old issuance", 10, MeritConsensus.getWeightedMeritAmount(100_000, 900_010, - currentChainHeight, blocksPerYear)); - assertEquals("100_001 blocks old issuance", 0, MeritConsensus.getWeightedMeritAmount(100_000, 899_999, - currentChainHeight, blocksPerYear)); - assertEquals("1_000_000 blocks old issuance", 0, MeritConsensus.getWeightedMeritAmount(100_000, 0, - currentChainHeight, blocksPerYear)); - } - - @Test - public void testInvalidChainHeight() { - exception.expect(IllegalArgumentException.class); - MeritConsensus.getWeightedMeritAmount(100_000, 2_000_000, 1_000_000, 1_000_000); - } - - @Test - public void testInvalidIssuanceHeight() { - exception.expect(IllegalArgumentException.class); - MeritConsensus.getWeightedMeritAmount(100_000, -1, 1_000_000, 1_000_000); - } - - @Test - public void testInvalidAmount() { - exception.expect(IllegalArgumentException.class); - MeritConsensus.getWeightedMeritAmount(-100_000, 1, 1_000_000, 1_000_000); - } - - @Test - public void testInvalidCurrentChainHeight() { - exception.expect(IllegalArgumentException.class); - MeritConsensus.getWeightedMeritAmount(100_000, 1, -1, 1_000_000); - } - - @Test - public void testInvalidBlockPerYear() { - exception.expect(IllegalArgumentException.class); - MeritConsensus.getWeightedMeritAmount(100_000, 1, 11, -1); - } -} diff --git a/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java b/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java index 4ed5c5d232..cf1de462b4 100644 --- a/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java +++ b/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java @@ -64,32 +64,12 @@ public class CurrencyUtilTest { public void testFindAsset() { MockAssetRegistry assetRegistry = new MockAssetRegistry(); - // test if code is matching - boolean daoTradingActivated = false; - // Test if BSQ on mainnet is failing - Assert.assertFalse(CurrencyUtil.findAsset(assetRegistry, "BSQ", - BaseCurrencyNetwork.XMR_MAINNET, daoTradingActivated).isPresent()); - - // on testnet/regtest it is allowed - assertEquals(CurrencyUtil.findAsset(assetRegistry, "BSQ", - BaseCurrencyNetwork.XMR_TESTNET, daoTradingActivated).get().getTickerSymbol(), "BSQ"); - - - daoTradingActivated = true; - // With daoTradingActivated we can request BSQ - assertEquals(CurrencyUtil.findAsset(assetRegistry, "BSQ", - BaseCurrencyNetwork.XMR_MAINNET, daoTradingActivated).get().getTickerSymbol(), "BSQ"); - - // Test if not matching ticker is failing - Assert.assertFalse(CurrencyUtil.findAsset(assetRegistry, "BSQ1", - BaseCurrencyNetwork.XMR_MAINNET, daoTradingActivated).isPresent()); - // Add a mock coin which has no mainnet version, needs to fail if we are on mainnet MockTestnetCoin.Testnet mockTestnetCoin = new MockTestnetCoin.Testnet(); try { assetRegistry.addAsset(mockTestnetCoin); CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN", - BaseCurrencyNetwork.XMR_MAINNET, daoTradingActivated); + BaseCurrencyNetwork.XMR_MAINNET); Assert.fail("Expected an IllegalArgumentException"); } catch (IllegalArgumentException e) { String wantMessage = "We are on mainnet and we could not find an asset with network type mainnet"; @@ -99,35 +79,22 @@ public class CurrencyUtilTest { // For testnet its ok assertEquals(CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN", - BaseCurrencyNetwork.XMR_TESTNET, daoTradingActivated).get().getTickerSymbol(), "MOCK_COIN"); + BaseCurrencyNetwork.XMR_TESTNET).get().getTickerSymbol(), "MOCK_COIN"); assertEquals(Coin.Network.TESTNET, mockTestnetCoin.getNetwork()); // For regtest its still found assertEquals(CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN", - BaseCurrencyNetwork.XMR_STAGENET, daoTradingActivated).get().getTickerSymbol(), "MOCK_COIN"); + BaseCurrencyNetwork.XMR_STAGENET).get().getTickerSymbol(), "MOCK_COIN"); // We test if we are not on mainnet to get the mainnet coin Coin ether = new Ether(); assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH", - BaseCurrencyNetwork.XMR_TESTNET, daoTradingActivated).get().getTickerSymbol(), "ETH"); + BaseCurrencyNetwork.XMR_TESTNET).get().getTickerSymbol(), "ETH"); assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH", - BaseCurrencyNetwork.XMR_STAGENET, daoTradingActivated).get().getTickerSymbol(), "ETH"); + BaseCurrencyNetwork.XMR_STAGENET).get().getTickerSymbol(), "ETH"); assertEquals(Coin.Network.MAINNET, ether.getNetwork()); - - // We test if network matches exactly if there are distinct network types defined like with BSQ - Coin bsq = (Coin) CurrencyUtil.findAsset(assetRegistry, "BSQ", BaseCurrencyNetwork.XMR_MAINNET, daoTradingActivated).get(); - assertEquals("BSQ", bsq.getTickerSymbol()); - assertEquals(Coin.Network.MAINNET, bsq.getNetwork()); - - bsq = (Coin) CurrencyUtil.findAsset(assetRegistry, "BSQ", BaseCurrencyNetwork.XMR_TESTNET, daoTradingActivated).get(); - assertEquals("BSQ", bsq.getTickerSymbol()); - assertEquals(Coin.Network.TESTNET, bsq.getNetwork()); - - bsq = (Coin) CurrencyUtil.findAsset(assetRegistry, "BSQ", BaseCurrencyNetwork.XMR_STAGENET, daoTradingActivated).get(); - assertEquals("BSQ", bsq.getTickerSymbol()); - assertEquals(Coin.Network.STAGENET, bsq.getNetwork()); - } + } @Test public void testGetNameAndCodeOfRemovedAsset() { diff --git a/core/src/test/java/bisq/core/offer/OfferMaker.java b/core/src/test/java/bisq/core/offer/OfferMaker.java index 5678cdbade..ca69814134 100644 --- a/core/src/test/java/bisq/core/offer/OfferMaker.java +++ b/core/src/test/java/bisq/core/offer/OfferMaker.java @@ -59,7 +59,6 @@ public class OfferMaker { 0L, 0L, 0L, - false, 0L, 0L, 0L, diff --git a/core/src/test/java/bisq/core/offer/OpenOfferManagerTest.java b/core/src/test/java/bisq/core/offer/OpenOfferManagerTest.java index d456e113df..fa9fb9d406 100644 --- a/core/src/test/java/bisq/core/offer/OpenOfferManagerTest.java +++ b/core/src/test/java/bisq/core/offer/OpenOfferManagerTest.java @@ -60,7 +60,6 @@ public class OpenOfferManagerTest { null, null, null, - null, offerBookService, null, null, @@ -70,8 +69,6 @@ public class OpenOfferManagerTest { null, null, null, - null, - null, persistenceManager, signedOfferPersistenceManager); @@ -109,7 +106,6 @@ public class OpenOfferManagerTest { null, null, null, - null, offerBookService, null, null, @@ -119,8 +115,6 @@ public class OpenOfferManagerTest { null, null, null, - null, - null, persistenceManager, signedOfferPersistenceManager); @@ -143,6 +137,7 @@ public class OpenOfferManagerTest { when(p2PService.getPeerManager()).thenReturn(mock(PeerManager.class)); + final OpenOfferManager manager = new OpenOfferManager(coreContext, null, null, @@ -151,7 +146,6 @@ public class OpenOfferManagerTest { null, null, null, - null, offerBookService, null, null, @@ -161,8 +155,6 @@ public class OpenOfferManagerTest { null, null, null, - null, - null, persistenceManager, signedOfferPersistenceManager); diff --git a/core/src/test/java/bisq/core/payment/TradeLimitsTest.java b/core/src/test/java/bisq/core/payment/TradeLimitsTest.java index 17e256b50f..e0f2a1a476 100644 --- a/core/src/test/java/bisq/core/payment/TradeLimitsTest.java +++ b/core/src/test/java/bisq/core/payment/TradeLimitsTest.java @@ -17,9 +17,6 @@ package bisq.core.payment; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.state.DaoStateService; - import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -28,23 +25,5 @@ import static org.mockito.Mockito.mock; public class TradeLimitsTest { @Test public void testGetFirstMonthRiskBasedTradeLimit() { - TradeLimits tradeLimits = new TradeLimits(mock(DaoStateService.class), mock(PeriodService.class)); - long expected, result; - - expected = 0; - result = tradeLimits.getFirstMonthRiskBasedTradeLimit(0, 1); - assertEquals(expected, result); - - expected = 25000000; - result = tradeLimits.getFirstMonthRiskBasedTradeLimit(100000000, 1); - assertEquals(expected, result); - - expected = 3130000; //0.03125 -> 0.0313 -> 0.0313 - result = tradeLimits.getFirstMonthRiskBasedTradeLimit(100000000, 8); - assertEquals(expected, result); - - expected = 6250000; - result = tradeLimits.getFirstMonthRiskBasedTradeLimit(200000000, 8); - assertEquals(expected, result); } } diff --git a/core/src/test/java/bisq/core/provider/mempool/TxValidatorTest.java b/core/src/test/java/bisq/core/provider/mempool/TxValidatorTest.java index 7b546af698..abea75e003 100644 --- a/core/src/test/java/bisq/core/provider/mempool/TxValidatorTest.java +++ b/core/src/test/java/bisq/core/provider/mempool/TxValidatorTest.java @@ -17,11 +17,7 @@ package bisq.core.provider.mempool; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.state.DaoStateService; import bisq.core.util.ParsingUtils; -import bisq.core.util.coin.BsqFormatter; - import com.google.gson.Gson; import org.apache.commons.io.IOUtils; @@ -72,85 +68,11 @@ public class TxValidatorTest { @Test public void testMakerTx() throws InterruptedException { String mempoolData, offerData; - - // paid the correct amount of BSQ fees - offerData = "msimscqb,0636bafb14890edfb95465e66e2b1e15915f7fb595f9b653b9129c15ef4c1c4b,1000000,10,0,662390"; - mempoolData = "{\"txid\":\"0636bafb14890edfb95465e66e2b1e15915f7fb595f9b653b9129c15ef4c1c4b\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":7899}},{\"vout\":2,\"prevout\":{\"value\":54877439}}],\"vout\":[{\"scriptpubkey_address\":\"1FCUu7hqKCSsGhVJaLbGEoCWdZRJRNqq8w\",\"value\":7889},{\"scriptpubkey_address\":\"bc1qkj5l4wxl00ufdx6ygcnrck9fz5u927gkwqcgey\",\"value\":1600000},{\"scriptpubkey_address\":\"bc1qkw4a8u9l5w9fhdh3ue9v7e7celk4jyudzg5gk5\",\"value\":53276799}],\"size\":405,\"weight\":1287,\"fee\":650,\"status\":{\"confirmed\":true,\"block_height\":663140}}"; - Assert.assertTrue(createTxValidator(offerData).parseJsonValidateMakerFeeTx(mempoolData, btcFeeReceivers).getResult()); - - // UNDERPAID expected 1.01 BSQ, actual fee paid 0.80 BSQ (USED 8.00 RATE INSTEAD OF 10.06 RATE) - // PASS due to leniency rule of accepting old DAO rate parameters: https://github.com/bisq-network/bisq/issues/5329#issuecomment-803223859 - offerData = "48067552,3b6009da764b71d79a4df8e2d8960b6919cae2e9bdccd5ef281e261fa9cd31b3,10000000,80,0,667656"; - mempoolData = "{\"txid\":\"3b6009da764b71d79a4df8e2d8960b6919cae2e9bdccd5ef281e261fa9cd31b3\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":9717}},{\"vout\":0,\"prevout\":{\"value\":4434912}},{\"vout\":2,\"prevout\":{\"value\":12809932}}],\"vout\":[{\"scriptpubkey_address\":\"1Nzqa4J7ck5bgz7QNXKtcjZExAvReozFo4\",\"value\":9637},{\"scriptpubkey_address\":\"bc1qhmmulf5prreqhccqy2wqpxxn6dcye7ame9dd57\",\"value\":11500000},{\"scriptpubkey_address\":\"bc1qx6hg8km2jdjc5ukhuedmkseu9wlsjtd8zeajpj\",\"value\":5721894}],\"size\":553,\"weight\":1879,\"fee\":23030,\"status\":{\"confirmed\":true,\"block_height\":667660}}"; - Assert.assertTrue(createTxValidator(offerData).parseJsonValidateMakerFeeTx(mempoolData, btcFeeReceivers).getResult()); - - // UNDERPAID Expected fee: 0.61 BSQ, actual fee paid: 0.35 BSQ (USED 5.75 RATE INSTEAD OF 10.06 RATE) - // PASS due to leniency rule of accepting old DAO rate parameters: https://github.com/bisq-network/bisq/issues/5329#issuecomment-803223859 - offerData = "am7DzIv,4cdea8872a7d96210f378e0221dc1aae8ee9abb282582afa7546890fb39b7189,6100000,35,0,668195"; - mempoolData = "{\"txid\":\"4cdea8872a7d96210f378e0221dc1aae8ee9abb282582afa7546890fb39b7189\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":23893}},{\"vout\":1,\"prevout\":{\"value\":1440000}},{\"vout\":2,\"prevout\":{\"value\":16390881}}],\"vout\":[{\"scriptpubkey_address\":\"1Kmrzq3WGCQsZw5kroEphuk1KgsEr65yB7\",\"value\":23858},{\"scriptpubkey_address\":\"bc1qyw5qql9m7rkse9mhcun225nrjpwycszsa5dpjg\",\"value\":7015000},{\"scriptpubkey_address\":\"bc1q90y3p6mg0pe3rvvzfeudq4mfxafgpc9rulruff\",\"value\":10774186}],\"size\":554,\"weight\":1559,\"fee\":41730,\"status\":{\"confirmed\":true,\"block_height\":668198}}"; - Assert.assertTrue(createTxValidator(offerData).parseJsonValidateMakerFeeTx(mempoolData, btcFeeReceivers).getResult()); - - // UNDERPAID expected 0.11 BSQ, actual fee paid 0.08 BSQ (USED 5.75 RATE INSTEAD OF 7.53) - // PASS due to leniency rule of accepting old DAO rate parameters: https://github.com/bisq-network/bisq/issues/5329#issuecomment-803223859 - offerData = "F1dzaFNQ,f72e263947c9dee6fbe7093fc85be34a149ef5bcfdd49b59b9cc3322fea8967b,1440000,8,0,670822, bsq paid too little"; - mempoolData = "{\"txid\":\"f72e263947c9dee6fbe7093fc85be34a149ef5bcfdd49b59b9cc3322fea8967b\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":15163}},{\"vout\":2,\"prevout\":{\"value\":6100000}}],\"vout\":[{\"scriptpubkey_address\":\"1MEsc2m4MSomNJWSr1p6fhnUQMyA3DRGrN\",\"value\":15155},{\"scriptpubkey_address\":\"bc1qztgwe9ry9a9puchjuscqdnv4v9lsm2ut0jtfec\",\"value\":2040000},{\"scriptpubkey_address\":\"bc1q0nstwxc0vqkj4x000xt328mfjapvlsd56nn70h\",\"value\":4048308}],\"size\":406,\"weight\":1291,\"fee\":11700,\"status\":{\"confirmed\":true,\"block_height\":670823}}"; - Assert.assertTrue(createTxValidator(offerData).parseJsonValidateMakerFeeTx(mempoolData, btcFeeReceivers).getResult()); } @Test public void testTakerTx() throws InterruptedException { String mempoolData, offerData; - - // The fee was more than what we expected: Expected BTC fee: 5000 sats , actual fee paid: 6000 sats - offerData = "00072328,3524364062c96ba0280621309e8b539d152154422294c2cf263a965dcde9a8ca,1000000,6000,1,614672"; - mempoolData = "{\"txid\":\"3524364062c96ba0280621309e8b539d152154422294c2cf263a965dcde9a8ca\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":1,\"prevout\":{\"value\":2971000}}],\"vout\":[{\"scriptpubkey_address\":\"3A8Zc1XioE2HRzYfbb5P8iemCS72M6vRJV\",\"value\":6000},{\"scriptpubkey_address\":\"1Hxu2X9Nr2fT3qEk9yjhiF54TJEz1Cxjoa\",\"value\":1607600},{\"scriptpubkey_address\":\"16VP6nHDDkmCMwaJj4PeyVHB88heDdVu9e\",\"value\":1353600}],\"size\":257,\"weight\":1028,\"fee\":3800,\"status\":{\"confirmed\":true,\"block_height\":614672}}"; - Assert.assertTrue(createTxValidator(offerData).parseJsonValidateTakerFeeTx(mempoolData, btcFeeReceivers).getResult()); - - // The fee matched what we expected - offerData = "00072328,12f658954890d38ce698355be0b27fdd68d092c7b1b7475381918db060f46166,6250000,188,0,615955"; - mempoolData = "{\"txid\":\"12f658954890d38ce698355be0b27fdd68d092c7b1b7475381918db060f46166\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":19980}},{\"vout\":2,\"prevout\":{\"value\":2086015}},{\"vout\":0,\"prevout\":{\"value\":1100000}},{\"vout\":2,\"prevout\":{\"value\":938200}}],\"vout\":[{\"scriptpubkey_address\":\"17qiF1TYgT1YvsCPJyXQoKMtBZ7YJBW9GH\",\"value\":19792},{\"scriptpubkey_address\":\"16aFKD5hvEjJgPme5yRNJT2rAPdTXzdQc2\",\"value\":3768432},{\"scriptpubkey_address\":\"1D5V3QW8f5n4PhwfPgNkW9eWZwNJFyVU8n\",\"value\":346755}],\"size\":701,\"weight\":2804,\"fee\":9216,\"status\":{\"confirmed\":true,\"block_height\":615955}}"; - Assert.assertTrue(createTxValidator(offerData).parseJsonValidateTakerFeeTx(mempoolData, btcFeeReceivers).getResult()); - - // The fee was more than what we expected: Expected BTC fee: 5000 sats , actual fee paid: 7000 sats - offerData = "bsqtrade,dfa4555ab78c657cad073e3f29c38c563d9dafc53afaa8c6af28510c734305c4,1000000,10,1,662390"; - mempoolData = "{\"txid\":\"dfa4555ab78c657cad073e3f29c38c563d9dafc53afaa8c6af28510c734305c4\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":678997}}],\"vout\":[{\"scriptpubkey_address\":\"3EfRGckBQQuk7cpU7SwatPv8kFD1vALkTU\",\"value\":7000},{\"scriptpubkey_address\":\"bc1qu6vey3e7flzg8gmhun05m43uc2vz0ay33kuu6r\",\"value\":647998}],\"size\":224,\"weight\":566,\"fee\":23999,\"status\":{\"confirmed\":true,\"block_height\":669720}}"; - Assert.assertTrue(createTxValidator(offerData).parseJsonValidateTakerFeeTx(mempoolData, btcFeeReceivers).getResult()); - - // The fee matched what we expected - offerData = "89284,e1269aad63b3d894f5133ad658960971ef5c0fce6a13ad10544dc50fa3360588,900000,9,0,666473"; - mempoolData = "{\"txid\":\"e1269aad63b3d894f5133ad658960971ef5c0fce6a13ad10544dc50fa3360588\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":72738}},{\"vout\":0,\"prevout\":{\"value\":1600000}}],\"vout\":[{\"scriptpubkey_address\":\"17Kh5Ype9yNomqRrqu2k1mdV5c6FcKfGwQ\",\"value\":72691},{\"scriptpubkey_address\":\"bc1qdr9zcw7gf2sehxkux4fmqujm5uguhaqz7l9lca\",\"value\":629016},{\"scriptpubkey_address\":\"bc1qgqrrqv8q6l5d3t52fe28ghuhz4xqrsyxlwn03z\",\"value\":956523}],\"size\":404,\"weight\":1286,\"fee\":14508,\"status\":{\"confirmed\":true,\"block_height\":672388}}"; - Assert.assertTrue(createTxValidator(offerData).parseJsonValidateTakerFeeTx(mempoolData, btcFeeReceivers).getResult()); - - // UNDERPAID: Expected fee: 7.04 BSQ, actual fee paid: 1.01 BSQ - offerData = "VOxRS,e99ea06aefc824fd45031447f7a0b56efb8117a09f9b8982e2c4da480a3a0e91,10000000,101,0,669129"; - mempoolData = "{\"txid\":\"e99ea06aefc824fd45031447f7a0b56efb8117a09f9b8982e2c4da480a3a0e91\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":16739}},{\"vout\":2,\"prevout\":{\"value\":113293809}}],\"vout\":[{\"scriptpubkey_address\":\"1F14nF6zoUfJkqZrFgdmK5VX5QVwEpAnKW\",\"value\":16638},{\"scriptpubkey_address\":\"bc1q80y688ev7u43vqy964yf7feqddvt2mkm8977cm\",\"value\":11500000},{\"scriptpubkey_address\":\"bc1q9whgyc2du9mrgnxz0nl0shwpw8ugrcae0j0w8p\",\"value\":101784485}],\"size\":406,\"weight\":1291,\"fee\":9425,\"status\":{\"confirmed\":true,\"block_height\":669134}}"; - Assert.assertFalse(createTxValidator(offerData).parseJsonValidateTakerFeeTx(mempoolData, btcFeeReceivers).getResult()); - - // UNDERPAID: Expected fee: 1029000 sats BTC, actual fee paid: 441000 sats BTC because they used the default rate of 0.003 should have been 0.007 per BTC - // after 1.6.0 we introduce additional leniency to allow the default rate (which is not stored in the DAO param change list) - offerData = "AKA,6779b7571f21a5a1af01d675bf032b8a3c571416d05345491018cbc2d016e888,147000000,441000,1,676543"; - mempoolData = "{'txid':'6779b7571f21a5a1af01d675bf032b8a3c571416d05345491018cbc2d016e888','version':1,'locktime':0,'vin':[{'txid':'94c36c0a9c5c99844ddfe17ef05a3ebbe94b14d76ee4bed7b00c7d45e681b441','vout':0,'prevout':{'scriptpubkey_address':'bc1qt5uprdzeh9g4el0k9cttn40qzagvpca9q0q6vl','value':177920825},'sequence':4294967295}],'vout':[{'scriptpubkey_address':'19BNi5EpZhgBBWAt5ka7xWpJpX2ZWJEYyq','value':441000},{'scriptpubkey_address':'bc1qxxcl9dz6usrx4z456g6fg8n3u9327hl458d6mx','value':177008388},{'scriptpubkey_address':'bc1qdq0894p2nmg04ceyqgapln6addfl80zy7z36jd','value':467243}],'size':256,'weight':697,'fee':4194,'status':{'confirmed':true,'block_height':676543}}"; - Assert.assertTrue(createTxValidator(offerData).parseJsonValidateTakerFeeTx(mempoolData, btcFeeReceivers).getResult()); - } - - @Test - public void testGoodOffers() throws InterruptedException { - Map goodOffers = loadJsonTestData("offerTestData.json"); - Map mempoolData = loadJsonTestData("txInfo.json"); - Assert.assertTrue(goodOffers.size() > 0); - Assert.assertTrue(mempoolData.size() > 0); - log.warn("TESTING GOOD OFFERS"); - testOfferSet(goodOffers, mempoolData, true); - } - - @Test - public void testBadOffers() throws InterruptedException { - Map badOffers = loadJsonTestData("badOfferTestData.json"); - Map mempoolData = loadJsonTestData("txInfo.json"); - Assert.assertTrue(badOffers.size() > 0); - Assert.assertTrue(mempoolData.size() > 0); - log.warn("TESTING BAD OFFERS"); - testOfferSet(badOffers, mempoolData, false); } private void testOfferSet(Map offers, Map mempoolData, boolean expectedResult) { @@ -163,7 +85,9 @@ public class TxValidatorTest { log.warn("{} was not found in the mempool", txValidator.getTxId()); Assert.assertFalse(expectedResult); // tx was not found in explorer } else { - txValidator.parseJsonValidateMakerFeeTx(jsonTxt, btcFeeReceivers); + //txValidator.parseJsonValidateMakerFeeTx(jsonTxt, btcFeeReceivers); + log.warn("expectedResult {}", expectedResult ); + log.warn("getResult {}", txValidator.getResult() ); Assert.assertTrue(expectedResult == txValidator.getResult()); } }); @@ -181,100 +105,16 @@ public class TxValidatorTest { } // initialize the TxValidator with offerData to be validated - // and mock the used DaoStateService private TxValidator createTxValidator(String offerData) { try { String[] y = offerData.split(","); String txId = y[1]; long amount = Long.parseLong(y[2]); - boolean isCurrencyForMakerFeeBtc = Long.parseLong(y[4]) > 0; - DaoStateService mockedDaoStateService = mock(DaoStateService.class); - - Answer mockGetFeeRate = invocation -> { - return mockedLookupFeeRate(invocation.getArgument(0), invocation.getArgument(1)); - }; - Answer mockGetParamValueAsCoin = invocation -> { - return mockedGetParamValueAsCoin(invocation.getArgument(0), invocation.getArgument(1)); - }; - Answer> mockGetParamChangeList = invocation -> { - return mockedGetParamChangeList(invocation.getArgument(0)); - }; - when(mockedDaoStateService.getParamValueAsCoin(Mockito.any(Param.class), Mockito.anyInt())).thenAnswer(mockGetFeeRate); - when(mockedDaoStateService.getParamValueAsCoin(Mockito.any(Param.class), Mockito.anyString())).thenAnswer(mockGetParamValueAsCoin); - when(mockedDaoStateService.getParamChangeList(Mockito.any())).thenAnswer(mockGetParamChangeList); - TxValidator txValidator = new TxValidator(mockedDaoStateService, txId, Coin.valueOf(amount), isCurrencyForMakerFeeBtc); + TxValidator txValidator = new TxValidator(txId, Coin.valueOf(amount)); return txValidator; } catch (RuntimeException ignore) { // If input format is not as expected we ignore entry } return null; } - - Coin mockedLookupFeeRate(Param param, int blockHeight) { - BsqFormatter bsqFormatter = new BsqFormatter(); - LinkedHashMap feeMap = mockedGetFeeRateMap(param); - for (Map.Entry entry : feeMap.entrySet()) { - if (blockHeight >= entry.getKey()) { - if (param.equals(Param.DEFAULT_MAKER_FEE_BTC) || param.equals(Param.DEFAULT_TAKER_FEE_BTC)) - return bsqFormatter.parseToBTC(entry.getValue()); - else - return ParsingUtils.parseToCoin(entry.getValue(), bsqFormatter); - } - } - if (param.equals(Param.DEFAULT_MAKER_FEE_BTC) || param.equals(Param.DEFAULT_TAKER_FEE_BTC)) - return bsqFormatter.parseToBTC(param.getDefaultValue()); - else - return ParsingUtils.parseToCoin(param.getDefaultValue(), bsqFormatter); - } - - private LinkedHashMap mockedGetFeeRateMap(Param param) { - LinkedHashMap feeMap = new LinkedHashMap<>(); - if (param == Param.DEFAULT_MAKER_FEE_BSQ) { - feeMap.put(674707L, "8.66"); // https://github.com/bisq-network/proposals/issues/318 - feeMap.put(670027L, "7.53"); - feeMap.put(660667L, "10.06"); - feeMap.put(655987L, "8.74"); - feeMap.put(641947L, "7.6"); - feeMap.put(632587L, "6.6"); - feeMap.put(623227L, "5.75"); - feeMap.put(599827L, "10.0"); - feeMap.put(590467L, "13.0"); - feeMap.put(585787L, "8.0"); - feeMap.put(581107L, "1.6"); - } else if (param == Param.DEFAULT_TAKER_FEE_BSQ) { - feeMap.put(674707L, "60.59"); // https://github.com/bisq-network/proposals/issues/318 - feeMap.put(670027L, "52.68"); - feeMap.put(660667L, "70.39"); - feeMap.put(655987L, "61.21"); - feeMap.put(641947L, "53.23"); - feeMap.put(632587L, "46.30"); - feeMap.put(623227L, "40.25"); - feeMap.put(599827L, "30.00"); - feeMap.put(590467L, "38.00"); - feeMap.put(585787L, "24.00"); - feeMap.put(581107L, "4.80"); - } else if (param == Param.DEFAULT_MAKER_FEE_BTC) { - feeMap.put(623227L, "0.0010"); - feeMap.put(585787L, "0.0020"); - } else if (param == Param.DEFAULT_TAKER_FEE_BTC) { - feeMap.put(623227L, "0.0070"); - feeMap.put(585787L, "0.0060"); - } - return feeMap; - } - - public Coin mockedGetParamValueAsCoin(Param param, String paramValue) { - BsqFormatter bsqFormatter = new BsqFormatter(); - return bsqFormatter.parseParamValueToCoin(param, paramValue); - } - - public List mockedGetParamChangeList(Param param) { - BsqFormatter bsqFormatter = new BsqFormatter(); - List retVal = new ArrayList(); - Map feeMap = mockedGetFeeRateMap(param); - for (Map.Entry entry : feeMap.entrySet()) { - retVal.add(ParsingUtils.parseToCoin(entry.getValue(), bsqFormatter)); - } - return retVal; - } } diff --git a/core/src/test/java/bisq/core/user/PreferencesTest.java b/core/src/test/java/bisq/core/user/PreferencesTest.java index 6b017a1b43..be54a4ea02 100644 --- a/core/src/test/java/bisq/core/user/PreferencesTest.java +++ b/core/src/test/java/bisq/core/user/PreferencesTest.java @@ -61,8 +61,7 @@ public class PreferencesTest { Config config = new Config(); LocalBitcoinNode localBitcoinNode = new LocalBitcoinNode(config); preferences = new Preferences( - persistenceManager, config, null, localBitcoinNode, null, null, Config.DEFAULT_FULL_DAO_NODE, - null, null, Config.UNSPECIFIED_PORT); + persistenceManager, config, null, localBitcoinNode, null); } @Test diff --git a/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java b/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java index 7d98e95923..fdf334af63 100644 --- a/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java +++ b/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java @@ -52,8 +52,6 @@ public class UserPayloadModelVOTest { Lists.newArrayList(), false, Lists.newArrayList(), - false, - null, null, Lists.newArrayList(), Lists.newArrayList(), diff --git a/core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java b/core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java deleted file mode 100644 index fc6a1f534d..0000000000 --- a/core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.core.util; - -import bisq.core.dao.DaoFacade; -import bisq.core.dao.governance.param.Param; -import bisq.core.filter.Filter; -import bisq.core.filter.FilterManager; - -import com.google.common.collect.Lists; -import com.google.common.primitives.Longs; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Random; - -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class FeeReceiverSelectorTest { - @Mock - private DaoFacade daoFacade; - @Mock - private FilterManager filterManager; - - @Test - public void testGetAddress() { - Random rnd = new Random(123); - when(filterManager.getFilter()).thenReturn(filterWithReceivers( - List.of("", "foo#0.001", "ill-formed", "bar#0.002", "baz#0.001", "partial#bad"))); - - Map selectionCounts = new HashMap<>(); - for (int i = 0; i < 400; i++) { - String address = FeeReceiverSelector.getAddress(daoFacade, filterManager, rnd); - selectionCounts.compute(address, (k, n) -> n != null ? n + 1 : 1); - } - - assertEquals(3, selectionCounts.size()); - - // Check within 2 std. of the expected values (95% confidence each): - assertEquals(100.0, selectionCounts.get("foo"), 18); - assertEquals(200.0, selectionCounts.get("bar"), 20); - assertEquals(100.0, selectionCounts.get("baz"), 18); - } - - @Test - public void testGetAddress_noValidReceivers_nullFilter() { - when(daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS)).thenReturn("default"); - - when(filterManager.getFilter()).thenReturn(null); - assertEquals("default", FeeReceiverSelector.getAddress(daoFacade, filterManager)); - } - - @Test - public void testGetAddress_noValidReceivers_filterWithNullList() { - when(daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS)).thenReturn("default"); - - when(filterManager.getFilter()).thenReturn(filterWithReceivers(null)); - assertEquals("default", FeeReceiverSelector.getAddress(daoFacade, filterManager)); - } - - @Test - public void testGetAddress_noValidReceivers_filterWithEmptyList() { - when(daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS)).thenReturn("default"); - - when(filterManager.getFilter()).thenReturn(filterWithReceivers(List.of())); - assertEquals("default", FeeReceiverSelector.getAddress(daoFacade, filterManager)); - } - - @Test - public void testGetAddress_noValidReceivers_filterWithIllFormedList() { - when(daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS)).thenReturn("default"); - - when(filterManager.getFilter()).thenReturn(filterWithReceivers(List.of("ill-formed"))); - assertEquals("default", FeeReceiverSelector.getAddress(daoFacade, filterManager)); - } - - @Test - public void testWeightedSelection() { - Random rnd = new Random(456); - - int[] selections = new int[3]; - for (int i = 0; i < 6000; i++) { - selections[FeeReceiverSelector.weightedSelection(Longs.asList(1, 2, 3), rnd)]++; - } - - // Check within 2 std. of the expected values (95% confidence each): - assertEquals(1000.0, selections[0], 58); - assertEquals(2000.0, selections[1], 74); - assertEquals(3000.0, selections[2], 78); - } - - private static Filter filterWithReceivers(List btcFeeReceiverAddresses) { - return new Filter(Lists.newArrayList(), - Lists.newArrayList(), - Lists.newArrayList(), - Lists.newArrayList(), - Lists.newArrayList(), - Lists.newArrayList(), - Lists.newArrayList(), - Lists.newArrayList(), - false, - Lists.newArrayList(), - false, - null, - null, - Lists.newArrayList(), - Lists.newArrayList(), - Lists.newArrayList(), - btcFeeReceiverAddresses, - null, - 0, - null, - null, - null, - null, - false, - Lists.newArrayList(), - new HashSet<>(), - false, - false); - } -} diff --git a/core/src/test/resources/bisq/core/provider/mempool/offerTestData.json b/core/src/test/resources/bisq/core/provider/mempool/offerTestData.json index 7b5b315529..658a41e185 100644 --- a/core/src/test/resources/bisq/core/provider/mempool/offerTestData.json +++ b/core/src/test/resources/bisq/core/provider/mempool/offerTestData.json @@ -1,7 +1,4 @@ { - "4cdea8872a7d96210f378e0221dc1aae8ee9abb282582afa7546890fb39b7189": "am7DzIv,4cdea8872a7d96210f378e0221dc1aae8ee9abb282582afa7546890fb39b7189,6100000,35,0,668195, underpaid but accepted due to use of different DAO parameter", - "ef1ea38b46402deb7df08c13a6dc379a65542a6940ac9d4ba436641ffd4bcb6e": "FQ0A7G,ef1ea38b46402deb7df08c13a6dc379a65542a6940ac9d4ba436641ffd4bcb6e,15970000,92,0,640438, underpaid but accepted due to use of different DAO parameter", - "051770f8d7f43a9b6ca10fefa6cdf4cb124a81eed26dc8af2e40f57d2589107b": "046698,051770f8d7f43a9b6ca10fefa6cdf4cb124a81eed26dc8af2e40f57d2589107b,15970000,92,0,667927, bsq fee underpaid using 5.75 rate for some weird reason", "e125fdbd09ee86c01e16e1f12a31507cfb8703ed1bd5a221461adf33cb3e00d9": "7213472,e125fdbd09ee86c01e16e1f12a31507cfb8703ed1bd5a221461adf33cb3e00d9,200000000,200000,1,578672, unknown_fee_receiver_1PUXU1MQ", "44b00de808d0145f9a948fe1b020c5d4173402ba0b5a5ba69124c67e371bca18": "aAPLmh98,44b00de808d0145f9a948fe1b020c5d4173402ba0b5a5ba69124c67e371bca18,140000000,140000,1,578629, unknown_fee_receiver_1PUXU1MQ", "654a7a34321b57be6a553052d1d9e0f1764dd2fab7b64c9422e9953e4d9d127d": "pwdbdku,654a7a34321b57be6a553052d1d9e0f1764dd2fab7b64c9422e9953e4d9d127d,24980000,238000,1,554947, unknown_fee_receiver_18GzH11", diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java index e61f9171b2..98db865c8f 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java @@ -153,7 +153,6 @@ class GrpcOffersService extends OffersImplBase { req.getBuyerSecurityDeposit(), req.getTriggerPrice(), req.getPaymentAccountId(), - req.getMakerFeeCurrencyCode(), offer -> { // This result handling consumer's accept operation will return // the new offer to the gRPC client after async placement is done. diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java index 069a837b00..2d1992f496 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java @@ -97,7 +97,6 @@ class GrpcTradesService extends TradesImplBase { log); coreApi.takeOffer(req.getOfferId(), req.getPaymentAccountId(), - req.getTakerFeeCurrencyCode(), trade -> { TradeInfo tradeInfo = toTradeInfo(trade); var reply = TakeOfferReply.newBuilder() diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcWalletsService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcWalletsService.java index 7619e7dc01..cb2af5334a 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcWalletsService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcWalletsService.java @@ -35,14 +35,10 @@ import bisq.proto.grpc.GetTransactionReply; import bisq.proto.grpc.GetTransactionRequest; import bisq.proto.grpc.GetTxFeeRateReply; import bisq.proto.grpc.GetTxFeeRateRequest; -import bisq.proto.grpc.GetUnusedBsqAddressReply; -import bisq.proto.grpc.GetUnusedBsqAddressRequest; import bisq.proto.grpc.LockWalletReply; import bisq.proto.grpc.LockWalletRequest; import bisq.proto.grpc.RemoveWalletPasswordReply; import bisq.proto.grpc.RemoveWalletPasswordRequest; -import bisq.proto.grpc.SendBsqReply; -import bisq.proto.grpc.SendBsqRequest; import bisq.proto.grpc.SendBtcReply; import bisq.proto.grpc.SendBtcRequest; import bisq.proto.grpc.SetTxFeeRatePreferenceReply; @@ -53,8 +49,6 @@ import bisq.proto.grpc.UnlockWalletReply; import bisq.proto.grpc.UnlockWalletRequest; import bisq.proto.grpc.UnsetTxFeeRatePreferenceReply; import bisq.proto.grpc.UnsetTxFeeRatePreferenceRequest; -import bisq.proto.grpc.VerifyBsqSentToAddressReply; -import bisq.proto.grpc.VerifyBsqSentToAddressRequest; import io.grpc.ServerInterceptor; import io.grpc.stub.StreamObserver; @@ -158,53 +152,6 @@ class GrpcWalletsService extends WalletsImplBase { } } - @Override - public void getUnusedBsqAddress(GetUnusedBsqAddressRequest req, - StreamObserver responseObserver) { - try { - String address = coreApi.getUnusedBsqAddress(); - var reply = GetUnusedBsqAddressReply.newBuilder() - .setAddress(address) - .build(); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - } catch (Throwable cause) { - exceptionHandler.handleException(log, cause, responseObserver); - } - } - - @Override - public void sendBsq(SendBsqRequest req, - StreamObserver responseObserver) { - try { - coreApi.sendBsq(req.getAddress(), - req.getAmount(), - req.getTxFeeRate(), - new TxBroadcaster.Callback() { - @Override - public void onSuccess(Transaction tx) { - log.info("Successfully published BSQ tx: id {}, output sum {} sats, fee {} sats, size {} bytes", - tx.getTxId().toString(), - tx.getOutputSum(), - tx.getFee(), - tx.getMessageSize()); - var reply = SendBsqReply.newBuilder() - .setTxInfo(toTxInfo(tx).toProtoMessage()) - .build(); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - } - - @Override - public void onFailure(TxBroadcastException ex) { - throw new IllegalStateException(ex); - } - }); - } catch (Throwable cause) { - exceptionHandler.handleException(log, cause, responseObserver); - } - } - @Override public void sendBtc(SendBtcRequest req, StreamObserver responseObserver) { @@ -243,21 +190,6 @@ class GrpcWalletsService extends WalletsImplBase { } } - @Override - public void verifyBsqSentToAddress(VerifyBsqSentToAddressRequest req, - StreamObserver responseObserver) { - try { - boolean isAmountReceived = coreApi.verifyBsqSentToAddress(req.getAddress(), req.getAmount()); - var reply = VerifyBsqSentToAddressReply.newBuilder() - .setIsAmountReceived(isAmountReceived) - .build(); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - } catch (Throwable cause) { - exceptionHandler.handleException(log, cause, responseObserver); - } - } - @Override public void getTxFeeRate(GetTxFeeRateRequest req, StreamObserver responseObserver) { @@ -389,8 +321,6 @@ class GrpcWalletsService extends WalletsImplBase { put(getGetBalancesMethod().getFullMethodName(), new GrpcCallRateMeter(10, SECONDS)); put(getGetAddressBalanceMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); put(getGetFundingAddressesMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); - put(getGetUnusedBsqAddressMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); - put(getSendBsqMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); put(getSendBtcMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); put(getGetTxFeeRateMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); put(getSetTxFeeRatePreferenceMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); diff --git a/desktop/src/main/java/bisq/desktop/app/BisqApp.java b/desktop/src/main/java/bisq/desktop/app/BisqApp.java index 31419d525a..48d6dd0150 100644 --- a/desktop/src/main/java/bisq/desktop/app/BisqApp.java +++ b/desktop/src/main/java/bisq/desktop/app/BisqApp.java @@ -23,7 +23,6 @@ import bisq.desktop.common.view.ViewLoader; import bisq.desktop.main.MainView; import bisq.desktop.main.debug.DebugView; import bisq.desktop.main.overlays.popups.Popup; -import bisq.desktop.main.overlays.windows.BsqEmptyWalletWindow; import bisq.desktop.main.overlays.windows.BtcEmptyWalletWindow; import bisq.desktop.main.overlays.windows.FilterWindow; import bisq.desktop.main.overlays.windows.ManualPayoutTxWindow; @@ -34,7 +33,6 @@ import bisq.desktop.util.ImageUtil; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.WalletsManager; -import bisq.core.dao.governance.voteresult.MissingDataRequestService; import bisq.core.locale.Res; import bisq.core.offer.OpenOffer; import bisq.core.offer.OpenOfferManager; @@ -313,16 +311,12 @@ public class BisqApp extends Application implements UncaughtExceptionHandler { } else { if (Utilities.isAltOrCtrlPressed(KeyCode.E, keyEvent)) { injector.getInstance(BtcEmptyWalletWindow.class).show(); - } else if (Utilities.isAltOrCtrlPressed(KeyCode.B, keyEvent)) { - injector.getInstance(BsqEmptyWalletWindow.class).show(); } else if (Utilities.isAltOrCtrlPressed(KeyCode.M, keyEvent)) { injector.getInstance(SendAlertMessageWindow.class).show(); } else if (Utilities.isAltOrCtrlPressed(KeyCode.F, keyEvent)) { injector.getInstance(FilterWindow.class).show(); - } else if (Utilities.isAltOrCtrlPressed(KeyCode.H, keyEvent)) { - log.warn("We re-published all proposalPayloads and blindVotePayloads to the P2P network."); - injector.getInstance(MissingDataRequestService.class).reRepublishAllGovernanceData(); - } else if (Utilities.isAltOrCtrlPressed(KeyCode.T, keyEvent)) { + } + else if (Utilities.isAltOrCtrlPressed(KeyCode.T, keyEvent)) { // Toggle between show tor logs and only show warnings. Helpful in case of connection problems String pattern = "org.berndpruenster.netlayer"; Level logLevel = ((Logger) LoggerFactory.getLogger(pattern)).getLevel(); diff --git a/desktop/src/main/java/bisq/desktop/components/BsqAddressTextField.java b/desktop/src/main/java/bisq/desktop/components/BsqAddressTextField.java deleted file mode 100644 index a74a4ae2e3..0000000000 --- a/desktop/src/main/java/bisq/desktop/components/BsqAddressTextField.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.components; - -import bisq.desktop.main.overlays.notifications.Notification; -import bisq.desktop.util.GUIUtil; - -import bisq.core.locale.Res; - -import bisq.common.util.Utilities; - -import org.bitcoinj.core.Coin; - -import de.jensd.fx.fontawesome.AwesomeDude; -import de.jensd.fx.fontawesome.AwesomeIcon; - -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.AnchorPane; - -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; - -public class BsqAddressTextField extends AnchorPane { - private final StringProperty address = new SimpleStringProperty(); - private final StringProperty paymentLabel = new SimpleStringProperty(); - private final ObjectProperty amountAsCoin = new SimpleObjectProperty<>(Coin.ZERO); - private boolean wasPrimaryButtonDown; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - public BsqAddressTextField() { - TextField textField = new BisqTextField(); - textField.setId("address-text-field"); - textField.setEditable(false); - textField.textProperty().bind(address); - String tooltipText = Res.get("addressTextField.copyToClipboard"); - textField.setTooltip(new Tooltip(tooltipText)); - - textField.setOnMousePressed(event -> wasPrimaryButtonDown = event.isPrimaryButtonDown()); - textField.setOnMouseReleased(event -> { - if (wasPrimaryButtonDown && address.get() != null && address.get().length() > 0) { - Utilities.copyToClipboard(address.get()); - Notification walletFundedNotification = new Notification() - .notification(Res.get("addressTextField.addressCopiedToClipboard")) - .hideCloseButton() - .autoClose(); - - walletFundedNotification.show(); - } - - wasPrimaryButtonDown = false; - }); - - textField.focusTraversableProperty().set(focusTraversableProperty().get()); - - Label copyIcon = new Label(); - copyIcon.setLayoutY(3); - copyIcon.getStyleClass().addAll("icon", "highlight"); - copyIcon.setTooltip(new Tooltip(Res.get("addressTextField.copyToClipboard"))); - AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY); - copyIcon.setOnMouseClicked(e -> GUIUtil.showFeeInfoBeforeExecute(() -> { - if (address.get() != null && address.get().length() > 0) - Utilities.copyToClipboard(address.get()); - })); - - AnchorPane.setRightAnchor(copyIcon, 5.0); - AnchorPane.setRightAnchor(textField, 30.0); - AnchorPane.setLeftAnchor(textField, 0.0); - - getChildren().addAll(textField, copyIcon); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Getters/Setters - /////////////////////////////////////////////////////////////////////////////////////////// - - public void setAddress(String address) { - this.address.set(address); - } - - public String getAddress() { - return address.get(); - } - - public StringProperty addressProperty() { - return address; - } - - public Coin getAmountAsCoin() { - return amountAsCoin.get(); - } - - public ObjectProperty amountAsCoinProperty() { - return amountAsCoin; - } - - public void setAmountAsCoin(Coin amountAsCoin) { - this.amountAsCoin.set(amountAsCoin); - } - - public String getPaymentLabel() { - return paymentLabel.get(); - } - - public StringProperty paymentLabelProperty() { - return paymentLabel; - } - - public void setPaymentLabel(String paymentLabel) { - this.paymentLabel.set(paymentLabel); - } -} diff --git a/desktop/src/main/java/bisq/desktop/components/SeparatedPhaseBars.java b/desktop/src/main/java/bisq/desktop/components/SeparatedPhaseBars.java deleted file mode 100644 index 6867df4b8a..0000000000 --- a/desktop/src/main/java/bisq/desktop/components/SeparatedPhaseBars.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.components; - -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.locale.Res; - -import com.jfoenix.controls.JFXProgressBar; - -import javafx.scene.control.Label; -import javafx.scene.control.ProgressBar; -import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; - -import javafx.geometry.Pos; - -import javafx.beans.property.DoubleProperty; -import javafx.beans.property.IntegerProperty; -import javafx.beans.property.SimpleDoubleProperty; -import javafx.beans.property.SimpleIntegerProperty; - -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class SeparatedPhaseBars extends VBox { - // Last day for creating github compensation request issue, as decided by general consensus - private static final double LAST_COMP_REQ_GH_ISSUE = (double) 18 / 25; - private double labelMinWidth = 150; - private double breakMinWidth = 20; - private int totalDuration; - private List items; - - public SeparatedPhaseBars(List items) { - this.items = items; - setSpacing(10); - - HBox titlesBars = new HBox(); - titlesBars.setSpacing(5); - getChildren().add(titlesBars); - - HBox progressBars = new HBox(); - progressBars.setSpacing(5); - getChildren().add(progressBars); - - items.forEach(item -> { - String text = item.phase.name().startsWith("BREAK") ? "" : Res.get("dao.phase.separatedPhaseBar." + item.phase); - Label titleLabel = new Label(text); - titleLabel.setEllipsisString(""); - titleLabel.setAlignment(Pos.CENTER); - item.setTitleLabel(titleLabel); - titlesBars.getChildren().addAll(titleLabel); - - JFXProgressBar progressBar = new JFXProgressBar(); - progressBar.setMinHeight(9); - progressBar.setMaxHeight(9); - progressBar.progressProperty().bind(item.progressProperty); - progressBar.setOpacity(item.isShowBlocks() ? 1 : 0.25); - if (item.phase.name().startsWith("PROPOSAL")) { - progressBar.setSecondaryProgress(LAST_COMP_REQ_GH_ISSUE); - } - progressBars.getChildren().add(progressBar); - item.setProgressBar(progressBar); - }); - - widthProperty().addListener((observable, oldValue, newValue) -> { - updateWidth((double) newValue); - }); - } - - public void updateWidth() { - updateWidth(getWidth()); - } - - private void updateWidth(double availableWidth) { - if (availableWidth > 0) { - totalDuration = items.stream().mapToInt(SeparatedPhaseBarsItem::getDuration).sum(); - if (totalDuration > 0) { - // We want to have a min. width for the breaks and for the phases which are important to the user but - // quite short (blind vote, vote reveal, result). If we display it correctly most of the space is - // consumed by the proposal phase. We apply a min and max width and adjust the available width so - // we have all phases displayed so that the text is fully readable. The proposal phase is shorter as - // it would be with correct display but we take that into account to have a better overall overview. - final double finalAvailableWidth = availableWidth; - AtomicReference adjustedAvailableWidth = new AtomicReference<>(availableWidth); - items.forEach(item -> { - double calculatedWidth = (double) item.duration / (double) totalDuration * finalAvailableWidth; - double minWidth = item.phase.name().startsWith("BREAK") ? breakMinWidth : labelMinWidth; - double maxWidth = item.phase.name().startsWith("BREAK") ? breakMinWidth : calculatedWidth; - if (calculatedWidth < minWidth) { - double missing = minWidth - calculatedWidth; - adjustedAvailableWidth.set(adjustedAvailableWidth.get() - missing); - } else if (calculatedWidth > maxWidth) { - double remaining = calculatedWidth - maxWidth; - adjustedAvailableWidth.set(adjustedAvailableWidth.get() + remaining); - } - }); - - items.forEach(item -> { - double calculatedWidth = (double) item.duration / (double) totalDuration * adjustedAvailableWidth.get(); - double minWidth = item.phase.name().startsWith("BREAK") ? breakMinWidth : labelMinWidth; - double maxWidth = item.phase.name().startsWith("BREAK") ? breakMinWidth : calculatedWidth; - double width = calculatedWidth; - if (calculatedWidth < minWidth) { - width = minWidth; - } else if (calculatedWidth > maxWidth) { - width = maxWidth; - } - item.getTitleLabel().setPrefWidth(width); - item.getProgressBar().setPrefWidth(width); - }); - } - } - } - - @Getter - public static class SeparatedPhaseBarsItem { - private final DaoPhase.Phase phase; - private final boolean showBlocks; - private final IntegerProperty startBlockProperty = new SimpleIntegerProperty(); - private final IntegerProperty lastBlockProperty = new SimpleIntegerProperty(); - private final DoubleProperty progressProperty = new SimpleDoubleProperty(); - private int duration; - @Setter - private javafx.scene.layout.VBox progressVBox; - @Setter - private Label titleLabel; - @Setter - private ProgressBar progressBar; - @Setter - private int indicatorBlock; - private ProgressBar indicatorBar; - - public SeparatedPhaseBarsItem(DaoPhase.Phase phase, boolean showBlocks) { - this.phase = phase; - this.showBlocks = showBlocks; - } - - public void setInActive() { - titleLabel.getStyleClass().add("separated-phase-bar-inactive"); - } - - public void setActive() { - titleLabel.getStyleClass().add("separated-phase-bar-active"); - } - - public void setPeriodRange(int firstBlock, int lastBlock, int duration) { - startBlockProperty.set(firstBlock); - lastBlockProperty.set(lastBlock); - this.duration = duration; - } - - } -} diff --git a/desktop/src/main/java/bisq/desktop/components/TxConfidenceListItem.java b/desktop/src/main/java/bisq/desktop/components/TxConfidenceListItem.java index e658cf9ee6..decd433021 100644 --- a/desktop/src/main/java/bisq/desktop/components/TxConfidenceListItem.java +++ b/desktop/src/main/java/bisq/desktop/components/TxConfidenceListItem.java @@ -21,7 +21,6 @@ import bisq.desktop.components.indicator.TxConfidenceIndicator; import bisq.desktop.util.GUIUtil; import bisq.core.btc.listeners.TxConfidenceListener; -import bisq.core.btc.wallet.BsqWalletService; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionConfidence; @@ -33,15 +32,12 @@ import lombok.Data; @Data public class TxConfidenceListItem { - protected final BsqWalletService bsqWalletService; protected final String txId; protected int confirmations = 0; protected TxConfidenceIndicator txConfidenceIndicator; protected TxConfidenceListener txConfidenceListener; - protected TxConfidenceListItem(Transaction transaction, - BsqWalletService bsqWalletService) { - this.bsqWalletService = bsqWalletService; + protected TxConfidenceListItem(Transaction transaction) { txId = transaction.getTxId().toString(); txConfidenceIndicator = new TxConfidenceIndicator(); @@ -57,12 +53,9 @@ public class TxConfidenceListItem { updateConfidence(confidence, tooltip); } }; - bsqWalletService.addTxConfidenceListener(txConfidenceListener); - updateConfidence(bsqWalletService.getConfidenceForTxId(txId), tooltip); } protected TxConfidenceListItem() { - this.bsqWalletService = null; this.txId = null; } @@ -74,7 +67,6 @@ public class TxConfidenceListItem { } public void cleanup() { - bsqWalletService.removeTxConfidenceListener(txConfidenceListener); } } diff --git a/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java b/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java index 84cea87e54..f1ad1f2e69 100644 --- a/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java +++ b/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java @@ -64,8 +64,6 @@ public class TxIdTextField extends AnchorPane { private final TxConfidenceIndicator txConfidenceIndicator; private final Label copyIcon, blockExplorerIcon, missingTxWarningIcon; private TxConfidenceListener txConfidenceListener; - @Setter - private boolean isBsq; /////////////////////////////////////////////////////////////////////////////////////////// @@ -170,9 +168,7 @@ public class TxIdTextField extends AnchorPane { private void openBlockExplorer(String txId) { if (preferences != null) { - BlockChainExplorer blockChainExplorer = isBsq ? - preferences.getBsqBlockChainExplorer() : - preferences.getBlockChainExplorer(); + BlockChainExplorer blockChainExplorer = preferences.getBlockChainExplorer(); GUIUtil.openWebPage(blockChainExplorer.txUrl + txId, false); } } diff --git a/desktop/src/main/java/bisq/desktop/components/chart/ChartView.java b/desktop/src/main/java/bisq/desktop/components/chart/ChartView.java deleted file mode 100644 index da278463d0..0000000000 --- a/desktop/src/main/java/bisq/desktop/components/chart/ChartView.java +++ /dev/null @@ -1,774 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.components.chart; - -import bisq.desktop.common.view.ActivatableViewAndModel; -import bisq.desktop.components.AutoTooltipSlideToggleButton; -import bisq.desktop.components.AutoTooltipToggleButton; - -import bisq.core.locale.Res; - -import bisq.common.UserThread; - -import javafx.stage.PopupWindow; -import javafx.stage.Stage; - -import javafx.scene.Node; -import javafx.scene.chart.Axis; -import javafx.scene.chart.LineChart; -import javafx.scene.chart.NumberAxis; -import javafx.scene.chart.XYChart; -import javafx.scene.control.Label; -import javafx.scene.control.SplitPane; -import javafx.scene.control.Toggle; -import javafx.scene.control.ToggleButton; -import javafx.scene.control.ToggleGroup; -import javafx.scene.control.Tooltip; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; -import javafx.scene.layout.Priority; -import javafx.scene.layout.Region; -import javafx.scene.layout.VBox; -import javafx.scene.text.Text; - -import javafx.geometry.Bounds; -import javafx.geometry.Insets; -import javafx.geometry.Pos; -import javafx.geometry.Side; - -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.beans.value.ChangeListener; - -import javafx.event.EventHandler; - -import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; - -import javafx.util.Duration; - -import java.time.temporal.TemporalAdjuster; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Stream; - -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -@Slf4j -public abstract class ChartView> extends ActivatableViewAndModel { - private Pane center; - private SplitPane timelineNavigation; - protected NumberAxis xAxis, yAxis; - protected LineChart chart; - private HBox timelineLabels, legendBox2; - private final ToggleGroup timeIntervalToggleGroup = new ToggleGroup(); - - protected final Set> activeSeries = new HashSet<>(); - protected final Map seriesIndexMap = new HashMap<>(); - protected final Map legendToggleBySeriesName = new HashMap<>(); - private final List dividerNodes = new ArrayList<>(); - private final List dividerNodesTooltips = new ArrayList<>(); - private ChangeListener widthListener; - private ChangeListener timeIntervalChangeListener; - private ListChangeListener nodeListChangeListener; - private int maxSeriesSize; - private boolean centerPanePressed; - private double x; - - @Setter - protected boolean isRadioButtonBehaviour; - @Setter - private int maxDataPointsForShowingSymbols = 100; - private ChangeListener yAxisWidthListener; - private EventHandler dividerMouseDraggedEventHandler; - private StringProperty fromProperty = new SimpleStringProperty(); - private StringProperty toProperty = new SimpleStringProperty(); - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - public ChartView(T model) { - super(model); - - root = new VBox(); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Lifecycle - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void initialize() { - // We need to call prepareInitialize as we are not using FXMLLoader - prepareInitialize(); - - maxSeriesSize = 0; - centerPanePressed = false; - x = 0; - - // Series - createSeries(); - - // Time interval - HBox timeIntervalBox = getTimeIntervalBox(); - - // chart - xAxis = getXAxis(); - yAxis = getYAxis(); - chart = getChart(); - - // Timeline navigation - addTimelineNavigation(); - - // Legend - HBox legendBox1 = initLegendsAndGetLegendBox(getSeriesForLegend1()); - - Collection> seriesForLegend2 = getSeriesForLegend2(); - if (seriesForLegend2 != null && !seriesForLegend2.isEmpty()) { - legendBox2 = initLegendsAndGetLegendBox(seriesForLegend2); - } - - // Set active series/legends - defineAndAddActiveSeries(); - - // Put all together - VBox timelineNavigationBox = new VBox(); - double paddingLeft = 15; - double paddingRight = 89; - // Y-axis width depends on data so we register a listener to get correct value - yAxisWidthListener = (observable, oldValue, newValue) -> { - double width = newValue.doubleValue(); - if (width > 0) { - double rightPadding = width + 14; - VBox.setMargin(timeIntervalBox, new Insets(0, rightPadding, 0, paddingLeft)); - VBox.setMargin(timelineNavigation, new Insets(0, rightPadding, 0, paddingLeft)); - VBox.setMargin(timelineLabels, new Insets(0, rightPadding, 0, paddingLeft)); - VBox.setMargin(legendBox1, new Insets(10, rightPadding, 0, paddingLeft)); - if (legendBox2 != null) { - VBox.setMargin(legendBox2, new Insets(-20, rightPadding, 0, paddingLeft)); - } - - if (model.getDividerPositions()[0] == 0 && model.getDividerPositions()[1] == 1) { - resetTimeNavigation(); - } - } - }; - - VBox.setMargin(timeIntervalBox, new Insets(0, paddingRight, 0, paddingLeft)); - VBox.setMargin(timelineNavigation, new Insets(0, paddingRight, 0, paddingLeft)); - VBox.setMargin(timelineLabels, new Insets(0, paddingRight, 0, paddingLeft)); - VBox.setMargin(legendBox1, new Insets(0, paddingRight, 0, paddingLeft)); - timelineNavigationBox.getChildren().addAll(timelineNavigation, timelineLabels, legendBox1); - if (legendBox2 != null) { - VBox.setMargin(legendBox2, new Insets(-20, paddingRight, 0, paddingLeft)); - timelineNavigationBox.getChildren().add(legendBox2); - } - root.getChildren().addAll(timeIntervalBox, chart, timelineNavigationBox); - - // Listeners - widthListener = (observable, oldValue, newValue) -> { - timelineNavigation.setDividerPosition(0, model.getDividerPositions()[0]); - timelineNavigation.setDividerPosition(1, model.getDividerPositions()[1]); - }; - - timeIntervalChangeListener = (observable, oldValue, newValue) -> { - if (newValue != null) { - onTimeIntervalChanged(newValue); - } - }; - - nodeListChangeListener = c -> { - while (c.next()) { - if (c.wasAdded()) { - c.getAddedSubList().stream() - .filter(node -> node instanceof Text) - .forEach(node -> node.getStyleClass().add("axis-tick-mark-text-node")); - } - } - }; - } - - @Override - public void activate() { - timelineNavigation.setDividerPositions(model.getDividerPositions()[0], model.getDividerPositions()[1]); - UserThread.execute(this::applyTimeLineNavigationLabels); - UserThread.execute(this::onTimelineChanged); - - TemporalAdjuster temporalAdjuster = model.getTemporalAdjuster(); - applyTemporalAdjuster(temporalAdjuster); - findTimeIntervalToggleByTemporalAdjuster(temporalAdjuster).ifPresent(timeIntervalToggleGroup::selectToggle); - - defineAndAddActiveSeries(); - applyData(); - initBoundsForTimelineNavigation(); - - updateChartAfterDataChange(); - - // Apply listeners and handlers - root.widthProperty().addListener(widthListener); - xAxis.getChildrenUnmodifiable().addListener(nodeListChangeListener); - yAxis.widthProperty().addListener(yAxisWidthListener); - timeIntervalToggleGroup.selectedToggleProperty().addListener(timeIntervalChangeListener); - - timelineNavigation.setOnMousePressed(this::onMousePressedSplitPane); - timelineNavigation.setOnMouseDragged(this::onMouseDragged); - center.setOnMousePressed(this::onMousePressedCenter); - center.setOnMouseReleased(this::onMouseReleasedCenter); - - addLegendToggleActionHandlers(getSeriesForLegend1()); - addLegendToggleActionHandlers(getSeriesForLegend2()); - addActionHandlersToDividers(); - } - - @Override - public void deactivate() { - root.widthProperty().removeListener(widthListener); - xAxis.getChildrenUnmodifiable().removeListener(nodeListChangeListener); - yAxis.widthProperty().removeListener(yAxisWidthListener); - timeIntervalToggleGroup.selectedToggleProperty().removeListener(timeIntervalChangeListener); - - timelineNavigation.setOnMousePressed(null); - timelineNavigation.setOnMouseDragged(null); - center.setOnMousePressed(null); - center.setOnMouseReleased(null); - - removeLegendToggleActionHandlers(getSeriesForLegend1()); - removeLegendToggleActionHandlers(getSeriesForLegend2()); - removeActionHandlersToDividers(); - - // clear data, reset states. We keep timeInterval state though - activeSeries.clear(); - chart.getData().clear(); - legendToggleBySeriesName.values().forEach(e -> e.setSelected(false)); - dividerNodes.clear(); - dividerNodesTooltips.clear(); - model.invalidateCache(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // TimeInterval/TemporalAdjuster - /////////////////////////////////////////////////////////////////////////////////////////// - - protected HBox getTimeIntervalBox() { - ToggleButton year = getTimeIntervalToggleButton(Res.get("time.year"), TemporalAdjusterModel.Interval.YEAR, - timeIntervalToggleGroup, "toggle-left"); - ToggleButton month = getTimeIntervalToggleButton(Res.get("time.month"), TemporalAdjusterModel.Interval.MONTH, - timeIntervalToggleGroup, "toggle-center"); - ToggleButton week = getTimeIntervalToggleButton(Res.get("time.week"), TemporalAdjusterModel.Interval.WEEK, - timeIntervalToggleGroup, "toggle-center"); - ToggleButton day = getTimeIntervalToggleButton(Res.get("time.day"), TemporalAdjusterModel.Interval.DAY, - timeIntervalToggleGroup, "toggle-center"); - HBox toggleBox = new HBox(); - toggleBox.setSpacing(0); - toggleBox.setAlignment(Pos.CENTER_LEFT); - Region spacer = new Region(); - HBox.setHgrow(spacer, Priority.ALWAYS); - toggleBox.getChildren().addAll(spacer, year, month, week, day); - return toggleBox; - } - - private ToggleButton getTimeIntervalToggleButton(String label, - TemporalAdjusterModel.Interval interval, - ToggleGroup toggleGroup, - String style) { - ToggleButton toggleButton = new AutoTooltipToggleButton(label); - toggleButton.setUserData(interval); - toggleButton.setToggleGroup(toggleGroup); - toggleButton.setId(style); - return toggleButton; - } - - protected void applyTemporalAdjuster(TemporalAdjuster temporalAdjuster) { - model.applyTemporalAdjuster(temporalAdjuster); - findTimeIntervalToggleByTemporalAdjuster(temporalAdjuster) - .map(e -> (TemporalAdjusterModel.Interval) e.getUserData()) - .ifPresent(model::setDateFormatPattern); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Chart - /////////////////////////////////////////////////////////////////////////////////////////// - - protected NumberAxis getXAxis() { - NumberAxis xAxis = new NumberAxis(); - xAxis.setForceZeroInRange(false); - xAxis.setAutoRanging(true); - xAxis.setTickLabelFormatter(model.getTimeAxisStringConverter()); - return xAxis; - } - - protected NumberAxis getYAxis() { - NumberAxis yAxis = new NumberAxis(); - yAxis.setForceZeroInRange(true); - yAxis.setSide(Side.RIGHT); - yAxis.setTickLabelFormatter(model.getYAxisStringConverter()); - return yAxis; - } - - // Add implementation if update of the y axis is required at series change - protected void onSetYAxisFormatter(XYChart.Series series) { - } - - protected LineChart getChart() { - LineChart chart = new LineChart<>(xAxis, yAxis); - chart.setAnimated(false); - chart.setLegendVisible(false); - chart.setMinHeight(200); - chart.setId("charts-dao"); - return chart; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Legend - /////////////////////////////////////////////////////////////////////////////////////////// - - protected HBox initLegendsAndGetLegendBox(Collection> collection) { - HBox hBox = new HBox(); - hBox.setSpacing(10); - collection.forEach(series -> { - AutoTooltipSlideToggleButton toggle = new AutoTooltipSlideToggleButton(); - toggle.setMinWidth(200); - toggle.setAlignment(Pos.TOP_LEFT); - String seriesId = getSeriesId(series); - legendToggleBySeriesName.put(seriesId, toggle); - toggle.setText(seriesId); - toggle.setId("charts-legend-toggle" + seriesIndexMap.get(seriesId)); - toggle.setSelected(false); - hBox.getChildren().add(toggle); - }); - Region spacer = new Region(); - HBox.setHgrow(spacer, Priority.ALWAYS); - hBox.getChildren().add(spacer); - return hBox; - } - - private void addLegendToggleActionHandlers(@Nullable Collection> collection) { - if (collection != null) { - collection.forEach(series -> - legendToggleBySeriesName.get(getSeriesId(series)).setOnAction(e -> onSelectLegendToggle(series))); - } - } - - private void removeLegendToggleActionHandlers(@Nullable Collection> collection) { - if (collection != null) { - collection.forEach(series -> - legendToggleBySeriesName.get(getSeriesId(series)).setOnAction(null)); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Timeline navigation - /////////////////////////////////////////////////////////////////////////////////////////// - - private void addTimelineNavigation() { - Pane left = new Pane(); - center = new Pane(); - center.setId("chart-navigation-center-pane"); - Pane right = new Pane(); - timelineNavigation = new SplitPane(left, center, right); - timelineNavigation.setDividerPositions(model.getDividerPositions()[0], model.getDividerPositions()[1]); - timelineNavigation.setMinHeight(25); - timelineLabels = new HBox(); - } - - // After initial chart data are created we apply the text from the x-axis ticks to our timeline navigation. - protected void applyTimeLineNavigationLabels() { - timelineLabels.getChildren().clear(); - ObservableList> tickMarks = xAxis.getTickMarks(); - int size = tickMarks.size(); - for (int i = 0; i < size; i++) { - Axis.TickMark tickMark = tickMarks.get(i); - Number xValue = tickMark.getValue(); - String xValueString; - if (xAxis.getTickLabelFormatter() != null) { - xValueString = xAxis.getTickLabelFormatter().toString(xValue); - } else { - xValueString = String.valueOf(xValue); - } - Label label = new Label(xValueString); - label.setMinHeight(30); - label.setId("chart-navigation-label"); - Region spacer = new Region(); - HBox.setHgrow(spacer, Priority.ALWAYS); - if (i < size - 1) { - timelineLabels.getChildren().addAll(label, spacer); - } else { - // After last label we don't add a spacer - timelineLabels.getChildren().add(label); - } - } - } - - private void onMousePressedSplitPane(MouseEvent e) { - x = e.getX(); - applyFromToDates(); - showDividerTooltips(); - } - - private void onMousePressedCenter(MouseEvent e) { - centerPanePressed = true; - applyFromToDates(); - showDividerTooltips(); - } - - private void onMouseReleasedCenter(MouseEvent e) { - centerPanePressed = false; - onTimelineChanged(); - hideDividerTooltips(); - } - - private void onMouseDragged(MouseEvent e) { - if (centerPanePressed) { - double newX = e.getX(); - double width = timelineNavigation.getWidth(); - double relativeDelta = (x - newX) / width; - double leftPos = timelineNavigation.getDividerPositions()[0] - relativeDelta; - double rightPos = timelineNavigation.getDividerPositions()[1] - relativeDelta; - - // Model might limit application of new values if we hit a boundary - model.onTimelineMouseDrag(leftPos, rightPos); - timelineNavigation.setDividerPositions(model.getDividerPositions()[0], model.getDividerPositions()[1]); - x = newX; - - applyFromToDates(); - showDividerTooltips(); - } - } - - private void addActionHandlersToDividers() { - // No API access to dividers ;-( only via css lookup hack (https://stackoverflow.com/questions/40707295/how-to-add-listener-to-divider-position?rq=1) - // Need to be done after added to scene and call requestLayout and applyCss. We keep it in a list atm - // and set action handler in activate. - timelineNavigation.requestLayout(); - timelineNavigation.applyCss(); - dividerMouseDraggedEventHandler = event -> { - applyFromToDates(); - showDividerTooltips(); - }; - - for (Node node : timelineNavigation.lookupAll(".split-pane-divider")) { - dividerNodes.add(node); - node.setOnMouseReleased(e -> { - hideDividerTooltips(); - onTimelineChanged(); - }); - node.addEventHandler(MouseEvent.MOUSE_DRAGGED, dividerMouseDraggedEventHandler); - - Tooltip tooltip = new Tooltip(""); - dividerNodesTooltips.add(tooltip); - tooltip.setShowDelay(Duration.millis(300)); - tooltip.setShowDuration(Duration.seconds(3)); - tooltip.textProperty().bind(dividerNodes.size() == 1 ? fromProperty : toProperty); - Tooltip.install(node, tooltip); - } - } - - private void removeActionHandlersToDividers() { - dividerNodes.forEach(node -> { - node.setOnMouseReleased(null); - node.removeEventHandler(MouseEvent.MOUSE_DRAGGED, dividerMouseDraggedEventHandler); - }); - for (int i = 0; i < dividerNodesTooltips.size(); i++) { - Tooltip tooltip = dividerNodesTooltips.get(i); - tooltip.textProperty().unbind(); - Tooltip.uninstall(dividerNodes.get(i), tooltip); - } - } - - private void resetTimeNavigation() { - timelineNavigation.setDividerPositions(0d, 1d); - model.onTimelineNavigationChanged(0, 1); - } - - private void showDividerTooltips() { - showDividerTooltip(0); - showDividerTooltip(1); - } - - private void hideDividerTooltips() { - dividerNodesTooltips.forEach(PopupWindow::hide); - } - - private void showDividerTooltip(int index) { - Node divider = dividerNodes.get(index); - Bounds bounds = divider.localToScene(divider.getBoundsInLocal()); - Tooltip tooltip = dividerNodesTooltips.get(index); - double xOffset = index == 0 ? -90 : 10; - Stage stage = (Stage) root.getScene().getWindow(); - tooltip.show(stage, stage.getX() + bounds.getMaxX() + xOffset, - stage.getY() + bounds.getMaxY() - 40); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Series - /////////////////////////////////////////////////////////////////////////////////////////// - - protected abstract void createSeries(); - - protected abstract Collection> getSeriesForLegend1(); - - // If a second legend is used this has to be overridden - protected Collection> getSeriesForLegend2() { - return null; - } - - protected abstract void defineAndAddActiveSeries(); - - protected void activateSeries(XYChart.Series series) { - if (activeSeries.contains(series)) { - return; - } - - chart.getData().add(series); - activeSeries.add(series); - legendToggleBySeriesName.get(getSeriesId(series)).setSelected(true); - updateChartAfterDataChange(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Data - /////////////////////////////////////////////////////////////////////////////////////////// - - protected abstract void applyData(); - - /** - * Implementations define which series will be used for setBoundsForTimelineNavigation - */ - protected abstract void initBoundsForTimelineNavigation(); - - /** - * @param data The series data which determines the min/max x values for the time line navigation. - * If not applicable initBoundsForTimelineNavigation requires custom implementation. - */ - protected void setBoundsForTimelineNavigation(ObservableList> data) { - model.initBounds(data); - xAxis.setLowerBound(model.getLowerBound().doubleValue()); - xAxis.setUpperBound(model.getUpperBound().doubleValue()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Handlers triggering a data/chart update - /////////////////////////////////////////////////////////////////////////////////////////// - - private void onTimeIntervalChanged(Toggle newValue) { - TemporalAdjusterModel.Interval interval = (TemporalAdjusterModel.Interval) newValue.getUserData(); - applyTemporalAdjuster(interval.getAdjuster()); - model.invalidateCache(); - applyData(); - updateChartAfterDataChange(); - } - - private void onTimelineChanged() { - updateTimeLinePositions(); - - model.invalidateCache(); - applyData(); - updateChartAfterDataChange(); - } - - private void updateTimeLinePositions() { - double leftPos = timelineNavigation.getDividerPositions()[0]; - double rightPos = timelineNavigation.getDividerPositions()[1]; - model.onTimelineNavigationChanged(leftPos, rightPos); - // We need to update as model might have adjusted the values - timelineNavigation.setDividerPositions(model.getDividerPositions()[0], model.getDividerPositions()[1]); - fromProperty.set(model.getTimeAxisStringConverter().toString(model.getFromDate()).replace("\n", " ")); - toProperty.set(model.getTimeAxisStringConverter().toString(model.getToDate()).replace("\n", " ")); - } - - private void applyFromToDates() { - double leftPos = timelineNavigation.getDividerPositions()[0]; - double rightPos = timelineNavigation.getDividerPositions()[1]; - model.applyFromToDates(leftPos, rightPos); - fromProperty.set(model.getTimeAxisStringConverter().toString(model.getFromDate()).replace("\n", " ")); - toProperty.set(model.getTimeAxisStringConverter().toString(model.getToDate()).replace("\n", " ")); - } - - private void onSelectLegendToggle(XYChart.Series series) { - boolean isSelected = legendToggleBySeriesName.get(getSeriesId(series)).isSelected(); - // If we have set that flag we deselect all other toggles - if (isRadioButtonBehaviour) { - new ArrayList<>(chart.getData()).stream() // We need to copy to a new list to avoid ConcurrentModificationException - .filter(activeSeries::contains) - .forEach(seriesToRemove -> { - chart.getData().remove(seriesToRemove); - String seriesId = getSeriesId(seriesToRemove); - activeSeries.remove(seriesToRemove); - legendToggleBySeriesName.get(seriesId).setSelected(false); - }); - } - - if (isSelected) { - chart.getData().add(series); - activeSeries.add(series); - //model.invalidateCache(); - applyData(); - - if (isRadioButtonBehaviour) { - // We support different y-axis formats only if isRadioButtonBehaviour is set, otherwise we would get - // mixed data on y-axis - onSetYAxisFormatter(series); - } - } else if (!isRadioButtonBehaviour) { // if isRadioButtonBehaviour we have removed it already via the code above - chart.getData().remove(series); - activeSeries.remove(series); - - } - updateChartAfterDataChange(); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Chart update after data change - /////////////////////////////////////////////////////////////////////////////////////////// - - // Update of the chart data can be triggered by: - // 1. activate() - // 2. TimeInterval toggle change - // 3. Timeline navigation change - // 4. Legend/series toggle change - - // Timeline navigation and legend/series toggles get reset at activate. - // Time interval toggle keeps its state at screen changes. - protected void updateChartAfterDataChange() { - // If a series got no data points after update we need to clear it from the chart - cleanupDanglingSeries(); - - // Hides symbols if too many data points are created - updateSymbolsVisibility(); - - // When series gets added/removed the JavaFx charts framework would try to apply styles by the index of - // addition, but we want to use a static color assignment which is synced with the legend color. - applySeriesStyles(); - - // Set tooltip on symbols - applyTooltip(); - } - - private void cleanupDanglingSeries() { - List> activeSeriesList = new ArrayList<>(activeSeries); - activeSeriesList.forEach(series -> { - ObservableList> seriesOnChart = chart.getData(); - if (series.getData().isEmpty()) { - seriesOnChart.remove(series); - } else if (!seriesOnChart.contains(series)) { - seriesOnChart.add(series); - } - }); - } - - private void updateSymbolsVisibility() { - maxDataPointsForShowingSymbols = 100; - long numDataPoints = chart.getData().stream() - .map(XYChart.Series::getData) - .mapToLong(List::size) - .max() - .orElse(0); - boolean prevValue = chart.getCreateSymbols(); - boolean newValue = numDataPoints < maxDataPointsForShowingSymbols; - if (prevValue != newValue) { - chart.setCreateSymbols(newValue); - } - } - - // The chart framework assigns the colored depending on the order it got added, but want to keep colors - // the same so they match with the legend toggle. - private void applySeriesStyles() { - for (int index = 0; index < chart.getData().size(); index++) { - XYChart.Series series = chart.getData().get(index); - int staticIndex = seriesIndexMap.get(getSeriesId(series)); - Set lines = getNodesForStyle(series.getNode(), ".default-color%d.chart-series-line"); - Stream symbols = series.getData().stream().map(XYChart.Data::getNode) - .flatMap(node -> getNodesForStyle(node, ".default-color%d.chart-line-symbol").stream()); - Stream.concat(lines.stream(), symbols).forEach(node -> { - removeStyles(node); - node.getStyleClass().add("default-color" + staticIndex); - }); - } - } - - private void applyTooltip() { - chart.getData().forEach(series -> { - series.getData().forEach(data -> { - Node node = data.getNode(); - if (node == null) { - return; - } - String xValue = model.getTooltipDateConverter(data.getXValue()); - String yValue = model.getYAxisStringConverter().toString(data.getYValue()); - Tooltip.install(node, new Tooltip(Res.get("dao.factsAndFigures.supply.chart.tradeFee.toolTip", yValue, xValue))); - }); - }); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Utils - /////////////////////////////////////////////////////////////////////////////////////////// - - private void removeStyles(Node node) { - for (int i = 0; i < getMaxSeriesSize(); i++) { - node.getStyleClass().remove("default-color" + i); - } - } - - private Set getNodesForStyle(Node node, String style) { - Set result = new HashSet<>(); - if (node != null) { - for (int i = 0; i < getMaxSeriesSize(); i++) { - result.addAll(node.lookupAll(String.format(style, i))); - } - } - return result; - } - - private int getMaxSeriesSize() { - maxSeriesSize = Math.max(maxSeriesSize, chart.getData().size()); - return maxSeriesSize; - } - - private Optional findTimeIntervalToggleByTemporalAdjuster(TemporalAdjuster adjuster) { - return timeIntervalToggleGroup.getToggles().stream() - .filter(toggle -> ((TemporalAdjusterModel.Interval) toggle.getUserData()).getAdjuster().equals(adjuster)) - .findAny(); - } - - // We use the name as id as there is no other suitable data inside series - protected String getSeriesId(XYChart.Series series) { - return series.getName(); - } -} diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java index 2933099ee1..87a7bac621 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java @@ -24,7 +24,6 @@ import bisq.desktop.util.FormBuilder; import bisq.desktop.util.Layout; import bisq.core.account.witness.AccountAgeWitnessService; -import bisq.core.dao.governance.asset.AssetService; import bisq.core.filter.FilterManager; import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; @@ -63,7 +62,6 @@ public class AssetsForm extends PaymentMethodForm { public static final String INSTANT_TRADE_NEWS = "instantTradeNews0.9.5"; private final AssetAccount assetAccount; private final AltCoinAddressValidator altCoinAddressValidator; - private final AssetService assetService; private final FilterManager filterManager; private InputTextField addressInputTextField; @@ -86,12 +84,10 @@ public class AssetsForm extends PaymentMethodForm { GridPane gridPane, int gridRow, CoinFormatter formatter, - AssetService assetService, FilterManager filterManager) { super(paymentAccount, accountAgeWitnessService, inputValidator, gridPane, gridRow, formatter); this.assetAccount = (AssetAccount) paymentAccount; this.altCoinAddressValidator = altCoinAddressValidator; - this.assetService = assetService; this.filterManager = filterManager; tradeInstant = paymentAccount instanceof InstantCryptoCurrencyAccount; @@ -216,7 +212,7 @@ public class AssetsForm extends PaymentMethodForm { currencyComboBox.setPromptText("")); ((AutocompleteComboBox) currencyComboBox).setAutocompleteItems( - CurrencyUtil.getActiveSortedCryptoCurrencies(assetService, filterManager)); + CurrencyUtil.getActiveSortedCryptoCurrencies(filterManager)); currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 10)); currencyComboBox.setConverter(new StringConverter<>() { diff --git a/desktop/src/main/java/bisq/desktop/main/MainView.java b/desktop/src/main/java/bisq/desktop/main/MainView.java index 926ac0d4c2..784ada67ba 100644 --- a/desktop/src/main/java/bisq/desktop/main/MainView.java +++ b/desktop/src/main/java/bisq/desktop/main/MainView.java @@ -40,7 +40,6 @@ import bisq.desktop.main.support.SupportView; import bisq.desktop.util.DisplayUtils; import bisq.desktop.util.Transitions; -import bisq.core.dao.monitoring.DaoStateMonitoringService; import bisq.core.locale.GlobalSettings; import bisq.core.locale.LanguageUtil; import bisq.core.locale.Res; @@ -108,8 +107,7 @@ import static javafx.scene.layout.AnchorPane.setTopAnchor; @FxmlView @Slf4j -public class MainView extends InitializableView - implements DaoStateMonitoringService.Listener { +public class MainView extends InitializableView { // If after 30 sec we have not got connected we show "open network settings" button private final static int SHOW_TOR_SETTINGS_DELAY_SEC = 90; @Setter @@ -153,19 +151,16 @@ public class MainView extends InitializableView private ProgressBar btcSyncIndicator, p2pNetworkProgressBar; private Label btcSplashInfo; private Popup p2PNetworkWarnMsgPopup, btcNetworkWarnMsgPopup; - private final DaoStateMonitoringService daoStateMonitoringService; @Inject public MainView(MainViewModel model, CachingViewLoader viewLoader, Navigation navigation, - Transitions transitions, - DaoStateMonitoringService daoStateMonitoringService) { + Transitions transitions) { super(model); this.viewLoader = viewLoader; this.navigation = navigation; MainView.transitions = transitions; - this.daoStateMonitoringService = daoStateMonitoringService; } @Override @@ -183,7 +178,6 @@ public class MainView extends InitializableView ToggleButton supportButton = new NavButton(SupportView.class, Res.get("mainView.menu.support")); ToggleButton settingsButton = new NavButton(SettingsView.class, Res.get("mainView.menu.settings")); ToggleButton accountButton = new NavButton(AccountView.class, Res.get("mainView.menu.account")); -// ToggleButton daoButton = new NavButton(DaoView.class, Res.get("mainView.menu.dao")); JFXBadge portfolioButtonWithBadge = new JFXBadge(portfolioButton); JFXBadge supportButtonWithBadge = new JFXBadge(supportButton); @@ -214,9 +208,6 @@ public class MainView extends InitializableView settingsButton.fire(); } else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT8, keyEvent)) { accountButton.fire(); -// } else if (Utilities.isAltOrCtrlPressed(KeyCode.DIGIT9, keyEvent)) { -// if (daoButton.isVisible()) -// daoButton.fire(); } }); } @@ -398,28 +389,10 @@ public class MainView extends InitializableView } }); - daoStateMonitoringService.addListener(this); - // Delay a bit to give time for rendering the splash screen UserThread.execute(() -> onApplicationStartedHandler.run()); } - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateMonitoringService.Listener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onChangeAfterBatchProcessing() { - } - - @Override - public void onCheckpointFail() { - new Popup().attention(Res.get("dao.monitor.daoState.checkpoint.popup")) - .useShutDownButton() - .show(); - } - - /////////////////////////////////////////////////////////////////////////////////////////// // Helpers /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/desktop/src/main/java/bisq/desktop/main/MainViewModel.java b/desktop/src/main/java/bisq/desktop/main/MainViewModel.java index d9e14837de..b30178e18e 100644 --- a/desktop/src/main/java/bisq/desktop/main/MainViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/MainViewModel.java @@ -31,7 +31,6 @@ import bisq.desktop.main.overlays.windows.UpdateRevolutAccountWindow; import bisq.desktop.main.overlays.windows.WalletPasswordWindow; import bisq.desktop.main.overlays.windows.downloadupdate.DisplayUpdateDownloadWindow; import bisq.desktop.main.presentation.AccountPresentation; -import bisq.desktop.main.presentation.DaoPresentation; import bisq.desktop.main.presentation.MarketPricePresentation; import bisq.desktop.main.presentation.SettingsPresentation; import bisq.desktop.main.shared.PriceFeedComboBoxItem; @@ -116,7 +115,6 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener { private final TradePresentation tradePresentation; private final SupportTicketsPresentation supportTicketsPresentation; private final MarketPricePresentation marketPricePresentation; - private final DaoPresentation daoPresentation; private final AccountPresentation accountPresentation; private final SettingsPresentation settingsPresentation; private final P2PService p2PService; @@ -162,7 +160,6 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener { TradePresentation tradePresentation, SupportTicketsPresentation supportTicketsPresentation, MarketPricePresentation marketPricePresentation, - DaoPresentation daoPresentation, AccountPresentation accountPresentation, SettingsPresentation settingsPresentation, P2PService p2PService, @@ -187,7 +184,6 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener { this.tradePresentation = tradePresentation; this.supportTicketsPresentation = supportTicketsPresentation; this.marketPricePresentation = marketPricePresentation; - this.daoPresentation = daoPresentation; this.accountPresentation = accountPresentation; this.settingsPresentation = settingsPresentation; this.p2PService = p2PService; @@ -265,7 +261,6 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener { setupBtcNumPeersWatcher(); marketPricePresentation.setup(); - daoPresentation.setup(); accountPresentation.setup(); settingsPresentation.setup(); @@ -335,7 +330,6 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener { .actionButtonText(Res.get("settings.net.reSyncSPVChainButton")) .onAction(() -> GUIUtil.reSyncSPVChain(preferences)) .show()); - bisqSetup.setVoteResultExceptionHandler(voteResultException -> log.warn(voteResultException.toString())); bisqSetup.setChainFileLockedExceptionHandler(msg -> new Popup().warning(msg) .useShutDownButton() @@ -368,8 +362,6 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener { .onClose(privateNotificationManager::removePrivateNotification) .useIUnderstandButton() .show()); - bisqSetup.setDaoErrorMessageHandler(errorMessage -> new Popup().error(errorMessage).show()); - bisqSetup.setDaoWarnMessageHandler(warnMessage -> new Popup().warning(warnMessage).show()); bisqSetup.setDisplaySecurityRecommendationHandler(key -> new Popup().headLine(Res.get("popup.securityRecommendation.headline")) .information(Res.get("popup.securityRecommendation.msg")) @@ -432,10 +424,6 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener { .show(); }); - bisqSetup.setDaoRequiresRestartHandler(() -> new Popup().warning("popup.warn.daoRequiresRestart") - .useShutDownButton() - .hideCloseButton() - .show()); corruptedStorageFileHandler.getFiles().ifPresent(files -> new Popup() .warning(Res.get("popup.warning.incompatibleDB", files.toString(), config.appDataDir)) @@ -447,7 +435,6 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener { .show()); bisqSetup.getBtcSyncProgress().addListener((observable, oldValue, newValue) -> updateBtcSyncProgress()); - daoPresentation.getBsqSyncProgress().addListener((observable, oldValue, newValue) -> updateBtcSyncProgress()); bisqSetup.setFilterWarningHandler(warning -> new Popup().warning(warning).show()); @@ -615,11 +602,7 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener { private void updateBtcSyncProgress() { final DoubleProperty btcSyncProgress = bisqSetup.getBtcSyncProgress(); - if (btcSyncProgress.doubleValue() < 1) { combinedSyncProgress.set(btcSyncProgress.doubleValue()); - } else { - combinedSyncProgress.set(daoPresentation.getBsqSyncProgress().doubleValue()); - } } private void setupInvalidOpenOffersHandler() { @@ -697,7 +680,7 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener { StringProperty getCombinedFooterInfo() { final StringProperty combinedInfo = new SimpleStringProperty(); - combinedInfo.bind(Bindings.concat(this.footerVersionInfo, " ", daoPresentation.getBsqInfo())); + combinedInfo.bind(Bindings.concat(this.footerVersionInfo, " ")); return combinedInfo; } @@ -775,14 +758,7 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener { return marketPricePresentation.getPriceFeedComboBoxItems(); } - // We keep daoPresentation and accountPresentation support even it is not used atm. But if we add a new feature and - // add a badge again it will be needed. - @SuppressWarnings({"unused"}) - public BooleanProperty getShowDaoUpdatesNotification() { - return daoPresentation.getShowDaoUpdatesNotification(); - } - - // We keep daoPresentation and accountPresentation support even it is not used atm. But if we add a new feature and + // We keep accountPresentation support even it is not used atm. But if we add a new feature and // add a badge again it will be needed. @SuppressWarnings("unused") public BooleanProperty getShowAccountUpdatesNotification() { diff --git a/desktop/src/main/java/bisq/desktop/main/PriceUtil.java b/desktop/src/main/java/bisq/desktop/main/PriceUtil.java index 7901c4689c..08c6d5b5d1 100644 --- a/desktop/src/main/java/bisq/desktop/main/PriceUtil.java +++ b/desktop/src/main/java/bisq/desktop/main/PriceUtil.java @@ -31,7 +31,6 @@ import bisq.core.provider.price.MarketPrice; import bisq.core.provider.price.PriceFeedService; import bisq.core.trade.statistics.TradeStatisticsManager; import bisq.core.user.Preferences; -import bisq.core.util.AveragePriceUtil; import bisq.core.util.FormattingUtils; import bisq.core.util.ParsingUtils; import bisq.core.util.validation.InputValidator; @@ -53,18 +52,12 @@ import static com.google.common.base.Preconditions.checkNotNull; @Singleton public class PriceUtil { private final PriceFeedService priceFeedService; - private final TradeStatisticsManager tradeStatisticsManager; - private final Preferences preferences; - @Nullable - private Price bsq30DayAveragePrice; @Inject public PriceUtil(PriceFeedService priceFeedService, TradeStatisticsManager tradeStatisticsManager, Preferences preferences) { this.priceFeedService = priceFeedService; - this.tradeStatisticsManager = tradeStatisticsManager; - this.preferences = preferences; } public static MonetaryValidator getPriceValidator(boolean isFiatCurrency) { @@ -117,19 +110,6 @@ public class PriceUtil { return Price.valueOf(currencyCode, roundedToLong); } - public void recalculateBsq30DayAveragePrice() { - bsq30DayAveragePrice = null; - bsq30DayAveragePrice = getBsq30DayAveragePrice(); - } - - public Price getBsq30DayAveragePrice() { - if (bsq30DayAveragePrice == null) { - bsq30DayAveragePrice = AveragePriceUtil.getAveragePriceTuple(preferences, - tradeStatisticsManager, 30).second; - } - return bsq30DayAveragePrice; - } - public boolean hasMarketPrice(Offer offer) { String currencyCode = offer.getCurrencyCode(); checkNotNull(priceFeedService, "priceFeed must not be null"); @@ -145,19 +125,9 @@ public class PriceUtil { } if (!hasMarketPrice(offer)) { - if (offer.getCurrencyCode().equals("BSQ")) { - Price bsq30DayAveragePrice = getBsq30DayAveragePrice(); - if (bsq30DayAveragePrice.isPositive()) { - double scaled = MathUtils.scaleDownByPowerOf10(bsq30DayAveragePrice.getValue(), 8); - return calculatePercentage(offer, scaled, direction); - } else { - return Optional.empty(); - } - } else { - log.trace("We don't have a market price. " + - "That case could only happen if you don't have a price feed."); - return Optional.empty(); - } + log.trace("We don't have a market price. " + + "That case could only happen if you don't have a price feed."); + return Optional.empty(); } String currencyCode = offer.getCurrencyCode(); diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsView.java b/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsView.java index 258dfb9dfa..9b7aa25229 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsView.java +++ b/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsView.java @@ -27,7 +27,6 @@ import bisq.desktop.util.FormBuilder; import bisq.desktop.util.Layout; import bisq.core.account.witness.AccountAgeWitnessService; -import bisq.core.dao.governance.asset.AssetService; import bisq.core.filter.FilterManager; import bisq.core.locale.CryptoCurrency; import bisq.core.locale.CurrencyUtil; @@ -75,7 +74,6 @@ public class AltCoinAccountsView extends PaymentAccountsView { private final WalletsManager walletsManager; private final BtcWalletService btcWalletService; - private final BsqWalletService bsqWalletService; private final CoinFormatter btcFormatter; - private final BsqFormatter bsqFormatter; private int gridRow = 0; private Button openDetailsButton; - private TextField btcTextField, bsqTextField; + private TextField btcTextField; private BalanceListener btcWalletBalanceListener; - private BalanceListener bsqWalletBalanceListener; /////////////////////////////////////////////////////////////////////////////////////////// @@ -75,14 +70,10 @@ public class WalletInfoView extends ActivatableView { @Inject private WalletInfoView(WalletsManager walletsManager, BtcWalletService btcWalletService, - BsqWalletService bsqWalletService, - @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter, - BsqFormatter bsqFormatter) { + @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter) { this.walletsManager = walletsManager; this.btcWalletService = btcWalletService; - this.bsqWalletService = bsqWalletService; this.btcFormatter = btcFormatter; - this.bsqFormatter = bsqFormatter; } @Override @@ -90,18 +81,15 @@ public class WalletInfoView extends ActivatableView { addTitledGroupBg(root, gridRow, 3, Res.get("account.menu.walletInfo.balance.headLine")); addMultilineLabel(root, gridRow, Res.get("account.menu.walletInfo.balance.info"), Layout.FIRST_ROW_DISTANCE, Double.MAX_VALUE); btcTextField = addTopLabelTextField(root, ++gridRow, "BTC", -Layout.FLOATING_LABEL_DISTANCE).second; - bsqTextField = addTopLabelTextField(root, ++gridRow, "BSQ", -Layout.FLOATING_LABEL_DISTANCE).second; addTitledGroupBg(root, ++gridRow, 3, Res.get("account.menu.walletInfo.xpub.headLine"), Layout.GROUP_DISTANCE); addXpubKeys(btcWalletService, "BTC", gridRow, Layout.FIRST_ROW_AND_GROUP_DISTANCE); ++gridRow; // update gridRow - addXpubKeys(bsqWalletService, "BSQ", ++gridRow, -Layout.FLOATING_LABEL_DISTANCE); addTitledGroupBg(root, ++gridRow, 4, Res.get("account.menu.walletInfo.path.headLine"), Layout.GROUP_DISTANCE); addMultilineLabel(root, gridRow, Res.get("account.menu.walletInfo.path.info"), Layout.FIRST_ROW_AND_GROUP_DISTANCE, Double.MAX_VALUE); addTopLabelTextField(root, ++gridRow, Res.get("account.menu.walletInfo.walletSelector", "BTC", "legacy"), "44'/0'/0'", -Layout.FLOATING_LABEL_DISTANCE); addTopLabelTextField(root, ++gridRow, Res.get("account.menu.walletInfo.walletSelector", "BTC", "segwit"), "44'/0'/1'", -Layout.FLOATING_LABEL_DISTANCE); - addTopLabelTextField(root, ++gridRow, Res.get("account.menu.walletInfo.walletSelector", "BSQ", ""), "44'/142'/0'", -Layout.FLOATING_LABEL_DISTANCE); openDetailsButton = addButtonAfterGroup(root, ++gridRow, Res.get("account.menu.walletInfo.openDetails")); @@ -111,21 +99,13 @@ public class WalletInfoView extends ActivatableView { updateBalances(btcWalletService); } }; - bsqWalletBalanceListener = new BalanceListener() { - @Override - public void onBalanceChanged(Coin balanceAsCoin, Transaction tx) { - updateBalances(bsqWalletService); - } - }; } @Override protected void activate() { btcWalletService.addBalanceListener(btcWalletBalanceListener); - bsqWalletService.addBalanceListener(bsqWalletBalanceListener); updateBalances(btcWalletService); - updateBalances(bsqWalletService); openDetailsButton.setOnAction(e -> { if (walletsManager.areWalletsAvailable()) { @@ -139,7 +119,6 @@ public class WalletInfoView extends ActivatableView { @Override protected void deactivate() { btcWalletService.removeBalanceListener(btcWalletBalanceListener); - bsqWalletService.removeBalanceListener(bsqWalletBalanceListener); openDetailsButton.setOnAction(null); } @@ -161,8 +140,6 @@ public class WalletInfoView extends ActivatableView { private void updateBalances(WalletService walletService) { if (walletService instanceof BtcWalletService) { btcTextField.setText(btcFormatter.formatCoinWithCode(walletService.getBalance(ESTIMATED_SPENDABLE))); - } else { - bsqTextField.setText(bsqFormatter.formatCoinWithCode(walletService.getBalance(ESTIMATED_SPENDABLE))); } } diff --git a/desktop/src/main/java/bisq/desktop/main/dao/DaoView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/DaoView.fxml deleted file mode 100644 index eccff618c9..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/DaoView.fxml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/DaoView.java b/desktop/src/main/java/bisq/desktop/main/dao/DaoView.java deleted file mode 100644 index 40b5f8082a..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/DaoView.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao; - -import bisq.desktop.Navigation; -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.CachingViewLoader; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.common.view.View; -import bisq.desktop.common.view.ViewLoader; -import bisq.desktop.main.MainView; -import bisq.desktop.main.dao.bonding.BondingView; -import bisq.desktop.main.dao.burnbsq.BurnBsqView; -import bisq.desktop.main.dao.economy.EconomyView; -import bisq.desktop.main.dao.governance.GovernanceView; -import bisq.desktop.main.dao.monitor.MonitorView; -import bisq.desktop.main.dao.news.NewsView; -import bisq.desktop.main.dao.wallet.BsqWalletView; -import bisq.desktop.main.dao.wallet.send.BsqSendView; -import bisq.desktop.main.overlays.popups.Popup; -import bisq.desktop.main.presentation.DaoPresentation; - -import bisq.core.dao.governance.votereveal.VoteRevealService; -import bisq.core.locale.Res; -import bisq.core.user.Preferences; - -import bisq.common.app.DevEnv; - -import javax.inject.Inject; - -import javafx.fxml.FXML; - -import javafx.scene.control.ScrollPane; -import javafx.scene.control.Tab; -import javafx.scene.control.TabPane; - -import javafx.beans.value.ChangeListener; - -@FxmlView -public class DaoView extends ActivatableView { - - @FXML - private Tab bsqWalletTab, proposalsTab, bondingTab, burnBsqTab, daoNewsTab, monitorTab, factsAndFiguresTab; - - private Navigation.Listener navigationListener; - private ChangeListener tabChangeListener; - - private final ViewLoader viewLoader; - private final Navigation navigation; - private Preferences preferences; - private Tab selectedTab; - private BsqWalletView bsqWalletView; - - @Inject - private DaoView(CachingViewLoader viewLoader, VoteRevealService voteRevealService, Navigation navigation, - Preferences preferences) { - this.viewLoader = viewLoader; - this.navigation = navigation; - this.preferences = preferences; - - voteRevealService.addVoteRevealTxPublishedListener(txId -> { - new Popup().headLine(Res.get("dao.voteReveal.txPublished.headLine")) - .feedback(Res.get("dao.voteReveal.txPublished", txId)) - .show(); - }); - } - - @Override - public void initialize() { - factsAndFiguresTab = new Tab(Res.get("dao.tab.factsAndFigures").toUpperCase()); - bsqWalletTab = new Tab(Res.get("dao.tab.bsqWallet").toUpperCase()); - proposalsTab = new Tab(Res.get("dao.tab.proposals").toUpperCase()); - bondingTab = new Tab(Res.get("dao.tab.bonding").toUpperCase()); - burnBsqTab = new Tab(Res.get("dao.tab.proofOfBurn").toUpperCase()); - monitorTab = new Tab(Res.get("dao.tab.monitor").toUpperCase()); - - factsAndFiguresTab.setClosable(false); - bsqWalletTab.setClosable(false); - proposalsTab.setClosable(false); - bondingTab.setClosable(false); - burnBsqTab.setClosable(false); - monitorTab.setClosable(false); - - if (!DevEnv.isDaoActivated()) { - factsAndFiguresTab.setDisable(true); - bsqWalletTab.setDisable(true); - proposalsTab.setDisable(true); - bondingTab.setDisable(true); - burnBsqTab.setDisable(true); - monitorTab.setDisable(true); - - daoNewsTab = new Tab(Res.get("dao.tab.news").toUpperCase()); - - root.getTabs().add(daoNewsTab); - } else { - root.getTabs().addAll(factsAndFiguresTab, bsqWalletTab, proposalsTab, bondingTab, burnBsqTab, monitorTab); - } - - navigationListener = (viewPath, data) -> { - if (viewPath.size() == 3 && viewPath.indexOf(DaoView.class) == 1) { - if (proposalsTab == null && viewPath.get(2).equals(EconomyView.class)) - navigation.navigateTo(MainView.class, DaoView.class, EconomyView.class); - else - loadView(viewPath.tip()); - } - }; - - tabChangeListener = (ov, oldValue, newValue) -> { - if (newValue == bsqWalletTab) { - Class selectedViewClass = bsqWalletView != null ? bsqWalletView.getSelectedViewClass() : null; - if (selectedViewClass == null) - navigation.navigateTo(MainView.class, DaoView.class, BsqWalletView.class, BsqSendView.class); - else - navigation.navigateTo(MainView.class, DaoView.class, BsqWalletView.class, selectedViewClass); - } else if (newValue == proposalsTab) { - navigation.navigateTo(MainView.class, DaoView.class, GovernanceView.class); - } else if (newValue == bondingTab) { - navigation.navigateTo(MainView.class, DaoView.class, BondingView.class); - } else if (newValue == burnBsqTab) { - navigation.navigateTo(MainView.class, DaoView.class, BurnBsqView.class); - } else if (newValue == factsAndFiguresTab) { - navigation.navigateTo(MainView.class, DaoView.class, EconomyView.class); - } else if (newValue == monitorTab) { - navigation.navigateTo(MainView.class, DaoView.class, MonitorView.class); - } - }; - } - - @Override - protected void activate() { - if (DevEnv.isDaoActivated()) { - - // Hide dao new badge if user saw this section - preferences.dontShowAgain(DaoPresentation.DAO_NEWS, true); - - navigation.addListener(navigationListener); - root.getSelectionModel().selectedItemProperty().addListener(tabChangeListener); - - if (navigation.getCurrentPath().size() == 2 && navigation.getCurrentPath().get(1) == DaoView.class) { - Tab selectedItem = root.getSelectionModel().getSelectedItem(); - if (selectedItem == bsqWalletTab) - navigation.navigateTo(MainView.class, DaoView.class, BsqWalletView.class); - else if (selectedItem == proposalsTab) - navigation.navigateTo(MainView.class, DaoView.class, GovernanceView.class); - else if (selectedItem == bondingTab) - navigation.navigateTo(MainView.class, DaoView.class, BondingView.class); - else if (selectedItem == burnBsqTab) - navigation.navigateTo(MainView.class, DaoView.class, BurnBsqView.class); - else if (selectedItem == factsAndFiguresTab) - navigation.navigateTo(MainView.class, DaoView.class, EconomyView.class); - else if (selectedItem == monitorTab) - navigation.navigateTo(MainView.class, DaoView.class, MonitorView.class); - } - } else { - loadView(NewsView.class); - } - } - - @Override - protected void deactivate() { - navigation.removeListener(navigationListener); - root.getSelectionModel().selectedItemProperty().removeListener(tabChangeListener); - } - - private void loadView(Class viewClass) { - - if (selectedTab != null && selectedTab.getContent() != null) { - if (selectedTab.getContent() instanceof ScrollPane) { - ((ScrollPane) selectedTab.getContent()).setContent(null); - } else { - selectedTab.setContent(null); - } - } - - View view = viewLoader.load(viewClass); - if (view instanceof BsqWalletView) { - selectedTab = bsqWalletTab; - bsqWalletView = (BsqWalletView) view; - } else if (view instanceof GovernanceView) { - selectedTab = proposalsTab; - } else if (view instanceof BondingView) { - selectedTab = bondingTab; - } else if (view instanceof BurnBsqView) { - selectedTab = burnBsqTab; - } else if (view instanceof MonitorView) { - selectedTab = monitorTab; - } else if (view instanceof NewsView) { - selectedTab = daoNewsTab; - } else if (view instanceof EconomyView) { - selectedTab = factsAndFiguresTab; - } - - selectedTab.setContent(view.getRoot()); - root.getSelectionModel().select(selectedTab); - } -} - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingView.fxml deleted file mode 100644 index a4f49d59d3..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingView.fxml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingView.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingView.java deleted file mode 100644 index cbeb133f9b..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingView.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.bonding; - -import bisq.desktop.Navigation; -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.CachingViewLoader; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.common.view.View; -import bisq.desktop.common.view.ViewLoader; -import bisq.desktop.common.view.ViewPath; -import bisq.desktop.components.MenuItem; -import bisq.desktop.main.MainView; -import bisq.desktop.main.dao.DaoView; -import bisq.desktop.main.dao.bonding.bonds.BondsView; -import bisq.desktop.main.dao.bonding.dashboard.BondingDashboardView; -import bisq.desktop.main.dao.bonding.reputation.MyReputationView; -import bisq.desktop.main.dao.bonding.roles.RolesView; - -import bisq.core.dao.governance.bond.Bond; -import bisq.core.locale.Res; - -import javax.inject.Inject; - -import javafx.fxml.FXML; - -import javafx.scene.control.ToggleGroup; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.VBox; - -import java.util.Arrays; -import java.util.List; - -import javax.annotation.Nullable; - -@FxmlView -public class BondingView extends ActivatableView { - - private final ViewLoader viewLoader; - private final Navigation navigation; - - private MenuItem dashboard, bondedRoles, reputation, bonds; - private Navigation.Listener listener; - - @FXML - private VBox leftVBox; - @FXML - private AnchorPane content; - - private Class selectedViewClass; - private ToggleGroup toggleGroup; - - @Inject - private BondingView(CachingViewLoader viewLoader, Navigation navigation) { - this.viewLoader = viewLoader; - this.navigation = navigation; - } - - @Override - public void initialize() { - listener = (viewPath, data) -> { - if (viewPath.size() != 4 || viewPath.indexOf(bisq.desktop.main.dao.bonding.BondingView.class) != 2) - return; - - selectedViewClass = viewPath.tip(); - loadView(selectedViewClass, data); - }; - - toggleGroup = new ToggleGroup(); - final List> baseNavPath = Arrays.asList(MainView.class, DaoView.class, bisq.desktop.main.dao.bonding.BondingView.class); - dashboard = new MenuItem(navigation, toggleGroup, Res.get("shared.dashboard"), - BondingDashboardView.class, baseNavPath); - bondedRoles = new MenuItem(navigation, toggleGroup, Res.get("dao.bond.menuItem.bondedRoles"), - RolesView.class, baseNavPath); - reputation = new MenuItem(navigation, toggleGroup, Res.get("dao.bond.menuItem.reputation"), - MyReputationView.class, baseNavPath); - bonds = new MenuItem(navigation, toggleGroup, Res.get("dao.bond.menuItem.bonds"), - BondsView.class, baseNavPath); - - leftVBox.getChildren().addAll(dashboard, bondedRoles, reputation, bonds); - } - - @Override - protected void activate() { - dashboard.activate(); - bondedRoles.activate(); - reputation.activate(); - bonds.activate(); - - navigation.addListener(listener); - ViewPath viewPath = navigation.getCurrentPath(); - if (viewPath.size() == 3 && viewPath.indexOf(BondingView.class) == 2 || - viewPath.size() == 2 && viewPath.indexOf(DaoView.class) == 1) { - if (selectedViewClass == null) - selectedViewClass = RolesView.class; - - loadView(selectedViewClass, null); - - } else if (viewPath.size() == 4 && viewPath.indexOf(BondingView.class) == 2) { - selectedViewClass = viewPath.get(3); - loadView(selectedViewClass, null); - } - } - - @SuppressWarnings("Duplicates") - @Override - protected void deactivate() { - navigation.removeListener(listener); - - dashboard.deactivate(); - bondedRoles.deactivate(); - reputation.deactivate(); - bonds.deactivate(); - } - - private void loadView(Class viewClass, @Nullable Object data) { - View view = viewLoader.load(viewClass); - content.getChildren().setAll(view.getRoot()); - - if (view instanceof BondingDashboardView) toggleGroup.selectToggle(dashboard); - else if (view instanceof RolesView) toggleGroup.selectToggle(bondedRoles); - else if (view instanceof MyReputationView) toggleGroup.selectToggle(reputation); - else if (view instanceof BondsView) { - toggleGroup.selectToggle(bonds); - if (data instanceof Bond) - ((BondsView) view).setSelectedBond((Bond) data); - } - - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java deleted file mode 100644 index bd9743c018..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.bonding; - -import bisq.desktop.Navigation; -import bisq.desktop.main.MainView; -import bisq.desktop.main.funds.FundsView; -import bisq.desktop.main.funds.deposit.DepositView; -import bisq.desktop.main.overlays.popups.Popup; -import bisq.desktop.util.GUIUtil; - -import bisq.core.btc.setup.WalletsSetup; -import bisq.core.dao.DaoFacade; -import bisq.core.dao.governance.bond.lockup.LockupReason; -import bisq.core.dao.governance.bond.reputation.MyReputation; -import bisq.core.dao.governance.bond.reputation.MyReputationListService; -import bisq.core.dao.governance.bond.role.BondedRolesRepository; -import bisq.core.dao.state.model.blockchain.TxOutput; -import bisq.core.dao.state.model.governance.Role; -import bisq.core.dao.state.model.governance.RoleProposal; -import bisq.core.locale.Res; -import bisq.core.util.coin.BsqFormatter; -import bisq.core.util.coin.CoinUtil; -import bisq.core.util.FormattingUtils; - -import bisq.network.p2p.P2PService; - -import bisq.common.app.DevEnv; -import bisq.common.util.Tuple2; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import java.util.Optional; -import java.util.function.Consumer; - -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.base.Preconditions.checkArgument; - -@Slf4j -@Singleton -public class BondingViewUtils { - private final P2PService p2PService; - private final MyReputationListService myReputationListService; - private final BondedRolesRepository bondedRolesRepository; - private final WalletsSetup walletsSetup; - private final DaoFacade daoFacade; - private final Navigation navigation; - private final BsqFormatter bsqFormatter; - - @Inject - public BondingViewUtils(P2PService p2PService, - MyReputationListService myReputationListService, - BondedRolesRepository bondedRolesRepository, - WalletsSetup walletsSetup, - DaoFacade daoFacade, - Navigation navigation, - BsqFormatter bsqFormatter) { - this.p2PService = p2PService; - this.myReputationListService = myReputationListService; - this.bondedRolesRepository = bondedRolesRepository; - this.walletsSetup = walletsSetup; - this.daoFacade = daoFacade; - this.navigation = navigation; - this.bsqFormatter = bsqFormatter; - } - - public void lockupBondForBondedRole(Role role, Consumer resultHandler) { - Optional roleProposal = getAcceptedBondedRoleProposal(role); - checkArgument(roleProposal.isPresent(), "roleProposal must be present"); - - long requiredBond = daoFacade.getRequiredBond(roleProposal); - Coin lockupAmount = Coin.valueOf(requiredBond); - int lockupTime = roleProposal.get().getUnlockTime(); - if (!bondedRolesRepository.isBondedAssetAlreadyInBond(role)) { - lockupBond(role.getHash(), lockupAmount, lockupTime, LockupReason.BONDED_ROLE, resultHandler); - } else { - handleError(new RuntimeException("The role has been used already for a lockup tx.")); - } - } - - public void lockupBondForReputation(Coin lockupAmount, int lockupTime, byte[] salt, Consumer resultHandler) { - MyReputation myReputation = new MyReputation(salt); - lockupBond(myReputation.getHash(), lockupAmount, lockupTime, LockupReason.REPUTATION, resultHandler); - myReputationListService.addReputation(myReputation); - } - - private void lockupBond(byte[] hash, Coin lockupAmount, int lockupTime, LockupReason lockupReason, - Consumer resultHandler) { - if (GUIUtil.isReadyForTxBroadcastOrShowPopup(p2PService, walletsSetup)) { - if (!DevEnv.isDevMode()) { - try { - Tuple2 miningFeeAndTxVsize = daoFacade.getLockupTxMiningFeeAndTxVsize(lockupAmount, lockupTime, lockupReason, hash); - Coin miningFee = miningFeeAndTxVsize.first; - int txVsize = miningFeeAndTxVsize.second; - String duration = FormattingUtils.formatDurationAsWords(lockupTime * 10 * 60 * 1000L, false, false); - new Popup().headLine(Res.get("dao.bond.reputation.lockup.headline")) - .confirmation(Res.get("dao.bond.reputation.lockup.details", - bsqFormatter.formatCoinWithCode(lockupAmount), - lockupTime, - duration, - bsqFormatter.formatBTCWithCode(miningFee), - CoinUtil.getFeePerVbyte(miningFee, txVsize), - txVsize / 1000d - )) - .actionButtonText(Res.get("shared.yes")) - .onAction(() -> publishLockupTx(lockupAmount, lockupTime, lockupReason, hash, resultHandler)) - .closeButtonText(Res.get("shared.cancel")) - .show(); - } catch (Throwable e) { - log.error(e.toString()); - e.printStackTrace(); - new Popup().warning(e.getMessage()).show(); - } - } else { - publishLockupTx(lockupAmount, lockupTime, lockupReason, hash, resultHandler); - } - } - } - - private void publishLockupTx(Coin lockupAmount, int lockupTime, LockupReason lockupReason, byte[] hash, Consumer resultHandler) { - daoFacade.publishLockupTx(lockupAmount, - lockupTime, - lockupReason, - hash, - txId -> { - if (!DevEnv.isDevMode()) - new Popup().feedback(Res.get("dao.tx.published.success")).show(); - - if (resultHandler != null) - resultHandler.accept(txId); - }, - this::handleError - ); - } - - public Optional getAcceptedBondedRoleProposal(Role role) { - return bondedRolesRepository.getAcceptedBondedRoleProposal(role); - } - - public void unLock(String lockupTxId, Consumer resultHandler) { - if (GUIUtil.isReadyForTxBroadcastOrShowPopup(p2PService, walletsSetup)) { - Optional lockupTxOutput = daoFacade.getLockupTxOutput(lockupTxId); - checkArgument(lockupTxOutput.isPresent(), "Lockup output must be present. TxId=" + lockupTxId); - Coin unlockAmount = Coin.valueOf(lockupTxOutput.get().getValue()); - Optional opLockTime = daoFacade.getLockTime(lockupTxId); - int lockTime = opLockTime.orElse(-1); - - try { - if (!DevEnv.isDevMode()) { - Tuple2 miningFeeAndTxVsize = daoFacade.getUnlockTxMiningFeeAndTxVsize(lockupTxId); - Coin miningFee = miningFeeAndTxVsize.first; - int txVsize = miningFeeAndTxVsize.second; - String duration = FormattingUtils.formatDurationAsWords(lockTime * 10 * 60 * 1000L, false, false); - new Popup().headLine(Res.get("dao.bond.reputation.unlock.headline")) - .confirmation(Res.get("dao.bond.reputation.unlock.details", - bsqFormatter.formatCoinWithCode(unlockAmount), - lockTime, - duration, - bsqFormatter.formatBTCWithCode(miningFee), - CoinUtil.getFeePerVbyte(miningFee, txVsize), - txVsize / 1000d - )) - .actionButtonText(Res.get("shared.yes")) - .onAction(() -> publishUnlockTx(lockupTxId, resultHandler)) - .closeButtonText(Res.get("shared.cancel")) - .show(); - } else { - publishUnlockTx(lockupTxId, resultHandler); - } - } catch (Throwable t) { - log.error(t.toString()); - t.printStackTrace(); - new Popup().warning(t.getMessage()).show(); - } - } - log.info("unlock tx: {}", lockupTxId); - } - - private void publishUnlockTx(String lockupTxId, Consumer resultHandler) { - daoFacade.publishUnlockTx(lockupTxId, - txId -> { - if (!DevEnv.isDevMode()) - new Popup().confirmation(Res.get("dao.tx.published.success")).show(); - - if (resultHandler != null) - resultHandler.accept(txId); - }, - errorMessage -> new Popup().warning(errorMessage.toString()).show() - ); - } - - private void handleError(Throwable throwable) { - if (throwable instanceof InsufficientMoneyException) { - final Coin missingCoin = ((InsufficientMoneyException) throwable).missing; - final String missing = missingCoin != null ? missingCoin.toFriendlyString() : "null"; - new Popup().warning(Res.get("popup.warning.insufficientBtcFundsForBsqTx", missing)) - .actionButtonTextWithGoTo("navigation.funds.depositFunds") - .onAction(() -> navigation.navigateTo(MainView.class, FundsView.class, DepositView.class)) - .show(); - } else { - log.error(throwable.toString()); - throwable.printStackTrace(); - new Popup().warning(throwable.toString()).show(); - } - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/bonds/BondListItem.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/bonds/BondListItem.java deleted file mode 100644 index 6164a6a340..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/bonds/BondListItem.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.bonding.bonds; - -import bisq.desktop.util.DisplayUtils; - -import bisq.core.dao.governance.bond.Bond; -import bisq.core.dao.governance.bond.BondState; -import bisq.core.dao.governance.bond.role.BondedRole; -import bisq.core.locale.Res; -import bisq.core.util.coin.BsqFormatter; - -import bisq.common.util.Utilities; - -import org.bitcoinj.core.Coin; - -import java.util.Date; - -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -@Value -@Slf4j -class BondListItem { - private final Bond bond; - private final String bondType; - private final String lockupTxId; - private final String amount; - private final String lockupDateString; - private final String lockTime; - private final String bondDetails; - private final BondState bondState; - private final String bondStateString; - - BondListItem(Bond bond, BsqFormatter bsqFormatter) { - this.bond = bond; - - amount = bsqFormatter.formatCoin(Coin.valueOf(bond.getAmount())); - lockTime = Integer.toString(bond.getLockTime()); - if (bond instanceof BondedRole) { - bondType = Res.get("dao.bond.bondedRoles"); - bondDetails = bond.getBondedAsset().getDisplayString(); - } else { - bondType = Res.get("dao.bond.bondedReputation"); - bondDetails = Utilities.bytesAsHexString(bond.getBondedAsset().getHash()); - } - lockupTxId = bond.getLockupTxId(); - lockupDateString = bond.getLockupDate() > 0 ? DisplayUtils.formatDateTime(new Date(bond.getLockupDate())) : "-"; - bondState = bond.getBondState(); - bondStateString = Res.get("dao.bond.bondState." + bond.getBondState().name()); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/bonds/BondsView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/bonding/bonds/BondsView.fxml deleted file mode 100644 index 7da6f9ec12..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/bonds/BondsView.fxml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/bonds/BondsView.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/bonds/BondsView.java deleted file mode 100644 index 9a32224107..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/bonds/BondsView.java +++ /dev/null @@ -1,349 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.bonding.bonds; - -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.components.AutoTooltipTableColumn; -import bisq.desktop.components.ExternalHyperlink; -import bisq.desktop.components.HyperlinkWithIcon; -import bisq.desktop.components.InfoAutoTooltipLabel; -import bisq.desktop.util.FormBuilder; -import bisq.desktop.util.GUIUtil; - -import bisq.core.dao.governance.bond.Bond; -import bisq.core.dao.governance.bond.reputation.BondedReputation; -import bisq.core.dao.governance.bond.reputation.BondedReputationRepository; -import bisq.core.dao.governance.bond.role.BondedRole; -import bisq.core.dao.governance.bond.role.BondedRolesRepository; -import bisq.core.locale.Res; -import bisq.core.user.Preferences; -import bisq.core.util.coin.BsqFormatter; - -import javax.inject.Inject; - -import de.jensd.fx.fontawesome.AwesomeIcon; - -import javafx.scene.control.ContentDisplay; -import javafx.scene.control.TableCell; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Priority; - -import javafx.beans.property.ReadOnlyObjectWrapper; - -import javafx.collections.FXCollections; -import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; -import javafx.collections.transformation.SortedList; - -import javafx.util.Callback; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; - -@FxmlView -public class BondsView extends ActivatableView { - private TableView tableView; - - private final BsqFormatter bsqFormatter; - private final BondedRolesRepository bondedRolesRepository; - private final BondedReputationRepository bondedReputationRepository; - private final Preferences preferences; - - private int gridRow = 0; - - private final ObservableList observableList = FXCollections.observableArrayList(); - private final SortedList sortedList = new SortedList<>(observableList); - - private ListChangeListener bondedRolesListener; - private ListChangeListener bondedReputationListener; - - private Bond selectedBond; - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, lifecycle - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - private BondsView(BsqFormatter bsqFormatter, - BondedRolesRepository bondedRolesRepository, - BondedReputationRepository bondedReputationRepository, - Preferences preferences) { - this.bsqFormatter = bsqFormatter; - this.bondedRolesRepository = bondedRolesRepository; - this.bondedReputationRepository = bondedReputationRepository; - this.preferences = preferences; - } - - @Override - public void initialize() { - tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.bond.allBonds.header"), "last"); - tableView.setItems(sortedList); - GridPane.setVgrow(tableView, Priority.ALWAYS); - addColumns(); - - bondedReputationListener = c -> updateList(); - bondedRolesListener = c -> updateList(); - } - - @Override - protected void activate() { - sortedList.comparatorProperty().bind(tableView.comparatorProperty()); - bondedReputationRepository.getBonds().addListener(bondedReputationListener); - bondedRolesRepository.getBonds().addListener(bondedRolesListener); - updateList(); - GUIUtil.setFitToRowsForTableView(tableView, 37, 28, 2, 30); - } - - @Override - protected void deactivate() { - sortedList.comparatorProperty().unbind(); - bondedReputationRepository.getBonds().removeListener(bondedReputationListener); - bondedRolesRepository.getBonds().removeListener(bondedRolesListener); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void setSelectedBond(Bond bond) { - // Set the selected bond if it's found in the tableView, which listens to sortedList. - // If this is called before the sortedList has been populated the selected bond is stored and - // we try to apply again after the next update. - tableView.getItems().stream() - .filter(item -> item.getBond() == bond) - .findFirst() - .ifPresentOrElse(item -> tableView.getSelectionModel().select(item), - () -> this.selectedBond = bond); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void updateList() { - List combined = new ArrayList<>(bondedReputationRepository.getBonds()); - combined.addAll(bondedRolesRepository.getBonds()); - observableList.setAll(combined.stream() - .map(bond -> new BondListItem(bond, bsqFormatter)) - .sorted(Comparator.comparing(BondListItem::getLockupDateString).reversed()) - .collect(Collectors.toList())); - GUIUtil.setFitToRowsForTableView(tableView, 37, 28, 2, 30); - if (selectedBond != null) { - Bond bond = selectedBond; - selectedBond = null; - setSelectedBond(bond); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Table columns - /////////////////////////////////////////////////////////////////////////////////////////// - - private void addColumns() { - TableColumn column; - - column = new AutoTooltipTableColumn<>(Res.get("shared.amountWithCur", "BSQ")); - column.setMinWidth(80); - column.getStyleClass().add("first-column"); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final BondListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getAmount()); - } else - setText(""); - } - }; - } - }); - column.setComparator(Comparator.comparing(e -> e.getBond().getAmount())); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.lockTime")); - column.setMinWidth(40); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final BondListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getLockTime()); - } else - setText(""); - } - }; - } - }); - column.setComparator(Comparator.comparing(e -> e.getBond().getLockTime())); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.bondState")); - column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setMinWidth(80); - column.setCellFactory( - new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final BondListItem item, boolean empty) { - super.updateItem(item, empty); - - if (item != null && !empty) { - setText(item.getBondStateString()); - } else - setText(""); - } - }; - } - }); - column.setComparator(Comparator.comparing(BondListItem::getBondStateString)); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.bondType")); - column.setMinWidth(100); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - - @Override - public void updateItem(final BondListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getBondType()); - } else - setText(""); - } - }; - } - }); - column.setComparator(Comparator.comparing(BondListItem::getBondType)); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.details")); - column.setMinWidth(100); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - private InfoAutoTooltipLabel infoTextField; - - @Override - public void updateItem(final BondListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - String info = Res.get("shared.id") + ": " + item.getBond().getBondedAsset().getUid(); - - if (item.getBond() instanceof BondedRole) { - info = item.getBondDetails() + "\n" + info; - } - - infoTextField = new InfoAutoTooltipLabel(item.getBondDetails(), - AwesomeIcon.INFO_SIGN, - ContentDisplay.LEFT, - info, - 350 - ); - setGraphic(infoTextField); - } else - setGraphic(null); - } - }; - } - }); - column.setComparator(Comparator.comparing(BondListItem::getBondDetails)); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.lockupDate")); - column.setMinWidth(140); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final BondListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getLockupDateString()); - } else - setText(""); - } - }; - } - }); - column.setComparator(Comparator.comparing(e -> e.getBond().getLockupDate())); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.lockupTxId")); - column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setMinWidth(60); - column.getStyleClass().add("last-column"); - column.setCellFactory( - new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - private HyperlinkWithIcon hyperlinkWithIcon; - - @Override - public void updateItem(final BondListItem item, boolean empty) { - super.updateItem(item, empty); - - if (item != null && !empty) { - String lockupTxId = item.getLockupTxId(); - hyperlinkWithIcon = new ExternalHyperlink(lockupTxId); - hyperlinkWithIcon.setOnAction(event -> GUIUtil.openTxInBsqBlockExplorer(lockupTxId, preferences)); - hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", lockupTxId))); - if (item.getLockupDateString().equals("-")) hyperlinkWithIcon.hideIcon(); - setGraphic(hyperlinkWithIcon); - } else { - setGraphic(null); - if (hyperlinkWithIcon != null) - hyperlinkWithIcon.setOnAction(null); - } - } - }; - } - }); - column.setComparator(Comparator.comparing(BondListItem::getLockupTxId)); - tableView.getColumns().add(column); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/dashboard/BondingDashboardView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/bonding/dashboard/BondingDashboardView.fxml deleted file mode 100644 index 31a852c68f..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/dashboard/BondingDashboardView.fxml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/dashboard/BondingDashboardView.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/dashboard/BondingDashboardView.java deleted file mode 100644 index 32054288fd..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/dashboard/BondingDashboardView.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.bonding.dashboard; - -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.main.dao.wallet.BsqBalanceUtil; - -import javax.inject.Inject; - -import javafx.scene.layout.GridPane; - -@FxmlView -public class BondingDashboardView extends ActivatableView { - private final BsqBalanceUtil bsqBalanceUtil; - - private int gridRow = 0; - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, lifecycle - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - private BondingDashboardView(BsqBalanceUtil bsqBalanceUtil) { - this.bsqBalanceUtil = bsqBalanceUtil; - } - - public void initialize() { - gridRow = bsqBalanceUtil.addGroup(root, gridRow); - gridRow = bsqBalanceUtil.addBondBalanceGroup(root, gridRow, "last"); - } - - @Override - protected void activate() { - bsqBalanceUtil.activate(); - } - - @Override - protected void deactivate() { - bsqBalanceUtil.deactivate(); - } -} - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/reputation/MyReputationListItem.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/reputation/MyReputationListItem.java deleted file mode 100644 index d3e75f1010..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/reputation/MyReputationListItem.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.bonding.reputation; - -import bisq.desktop.util.DisplayUtils; - -import bisq.core.dao.governance.bond.BondState; -import bisq.core.dao.governance.bond.reputation.MyBondedReputation; -import bisq.core.dao.governance.bond.reputation.MyReputation; -import bisq.core.locale.Res; -import bisq.core.util.coin.BsqFormatter; - -import bisq.common.util.Utilities; - -import org.bitcoinj.core.Coin; - -import java.util.Date; - -import lombok.Value; - -@Value -class MyReputationListItem { - private final MyBondedReputation myBondedReputation; - private final String hash, salt; - private final String txId; - private final String amount; - private final String lockupDateString; - private final String lockTime; - - private final String buttonText; - private final boolean showButton; - private final BondState bondState; - private final String bondStateString; - private final String lockupTxId; - private final Date lockupDate; - - MyReputationListItem(MyBondedReputation myBondedReputation, - BsqFormatter bsqFormatter) { - this.myBondedReputation = myBondedReputation; - - MyReputation myReputation = myBondedReputation.getBondedAsset(); - hash = Utilities.bytesAsHexString(myReputation.getHash()); - salt = Utilities.bytesAsHexString(myReputation.getSalt()); - txId = myBondedReputation.getLockupTxId(); - amount = bsqFormatter.formatCoin(Coin.valueOf(myBondedReputation.getAmount())); - lockupDate = new Date(myBondedReputation.getLockupDate()); - lockupDateString = DisplayUtils.formatDateTime(lockupDate); - lockTime = Integer.toString(myBondedReputation.getLockTime()); - lockupTxId = myBondedReputation.getLockupTxId(); - bondState = myBondedReputation.getBondState(); - bondStateString = Res.get("dao.bond.bondState." + myBondedReputation.getBondState().name()); - showButton = myBondedReputation.getBondState() == BondState.LOCKUP_TX_CONFIRMED; - buttonText = Res.get("dao.bond.table.button.unlock"); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/reputation/MyReputationView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/bonding/reputation/MyReputationView.fxml deleted file mode 100644 index f0a618ed72..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/reputation/MyReputationView.fxml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/reputation/MyReputationView.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/reputation/MyReputationView.java deleted file mode 100644 index ae051a2410..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/reputation/MyReputationView.java +++ /dev/null @@ -1,494 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.bonding.reputation; - -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.components.AutoTooltipButton; -import bisq.desktop.components.AutoTooltipTableColumn; -import bisq.desktop.components.ExternalHyperlink; -import bisq.desktop.components.HyperlinkWithIcon; -import bisq.desktop.components.InputTextField; -import bisq.desktop.main.dao.bonding.BondingViewUtils; -import bisq.desktop.util.FormBuilder; -import bisq.desktop.util.GUIUtil; -import bisq.desktop.util.Layout; -import bisq.desktop.util.validation.BsqValidator; - -import bisq.core.btc.listeners.BsqBalanceListener; -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.dao.DaoFacade; -import bisq.core.dao.governance.bond.BondConsensus; -import bisq.core.dao.governance.bond.BondState; -import bisq.core.dao.governance.bond.reputation.MyBondedReputation; -import bisq.core.locale.Res; -import bisq.core.user.Preferences; -import bisq.core.util.ParsingUtils; -import bisq.core.util.coin.BsqFormatter; -import bisq.core.util.validation.HexStringValidator; -import bisq.core.util.validation.IntegerValidator; - -import bisq.common.crypto.Hash; -import bisq.common.util.Utilities; - -import org.bitcoinj.core.Coin; - -import javax.inject.Inject; - -import com.google.common.base.Charsets; - -import javafx.scene.control.Button; -import javafx.scene.control.TableCell; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Priority; - -import javafx.beans.property.ReadOnlyObjectWrapper; -import javafx.beans.value.ChangeListener; - -import javafx.collections.FXCollections; -import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; -import javafx.collections.transformation.SortedList; - -import javafx.util.Callback; - -import java.util.Comparator; -import java.util.UUID; -import java.util.stream.Collectors; - -import static bisq.desktop.util.FormBuilder.addButtonAfterGroup; -import static bisq.desktop.util.FormBuilder.addInputTextField; -import static bisq.desktop.util.FormBuilder.addTitledGroupBg; - -@FxmlView -public class MyReputationView extends ActivatableView implements BsqBalanceListener { - private InputTextField amountInputTextField, timeInputTextField, saltInputTextField; - private Button lockupButton; - private TableView tableView; - - private final BsqFormatter bsqFormatter; - private final BsqWalletService bsqWalletService; - private final BondingViewUtils bondingViewUtils; - private final HexStringValidator hexStringValidator; - private final BsqValidator bsqValidator; - private final DaoFacade daoFacade; - private final Preferences preferences; - - private final IntegerValidator timeInputTextFieldValidator; - - private final ObservableList observableList = FXCollections.observableArrayList(); - private final SortedList sortedList = new SortedList<>(observableList); - - private int gridRow = 0; - - private ChangeListener amountFocusOutListener, timeFocusOutListener, saltFocusOutListener; - private ChangeListener amountInputTextFieldListener, timeInputTextFieldListener, saltInputTextFieldListener; - private ListChangeListener myBondedReputationsChangeListener; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, lifecycle - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - private MyReputationView(BsqFormatter bsqFormatter, - BsqWalletService bsqWalletService, - BondingViewUtils bondingViewUtils, - HexStringValidator hexStringValidator, - BsqValidator bsqValidator, - DaoFacade daoFacade, - Preferences preferences) { - this.bsqFormatter = bsqFormatter; - this.bsqWalletService = bsqWalletService; - this.bondingViewUtils = bondingViewUtils; - this.hexStringValidator = hexStringValidator; - this.bsqValidator = bsqValidator; - this.daoFacade = daoFacade; - this.preferences = preferences; - - timeInputTextFieldValidator = new IntegerValidator(); - timeInputTextFieldValidator.setMinValue(BondConsensus.getMinLockTime()); - timeInputTextFieldValidator.setMaxValue(BondConsensus.getMaxLockTime()); - } - - @Override - public void initialize() { - addTitledGroupBg(root, gridRow, 3, Res.get("dao.bond.reputation.header")); - - amountInputTextField = addInputTextField(root, gridRow, Res.get("dao.bond.reputation.amount"), - Layout.FIRST_ROW_DISTANCE); - amountInputTextField.setValidator(bsqValidator); - - timeInputTextField = FormBuilder.addInputTextField(root, ++gridRow, Res.get("dao.bond.reputation.time")); - timeInputTextField.setValidator(timeInputTextFieldValidator); - - saltInputTextField = FormBuilder.addInputTextField(root, ++gridRow, Res.get("dao.bond.reputation.salt")); - saltInputTextField.setValidator(hexStringValidator); - - lockupButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.bond.reputation.lockupButton")); - - tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.bond.reputation.table.header"), 20, "last"); - createColumns(); - tableView.setItems(sortedList); - GridPane.setVgrow(tableView, Priority.ALWAYS); - - createListeners(); - } - - @Override - protected void activate() { - amountInputTextField.textProperty().addListener(amountInputTextFieldListener); - amountInputTextField.focusedProperty().addListener(amountFocusOutListener); - - timeInputTextField.textProperty().addListener(timeInputTextFieldListener); - timeInputTextField.focusedProperty().addListener(timeFocusOutListener); - - saltInputTextField.textProperty().addListener(saltInputTextFieldListener); - saltInputTextField.focusedProperty().addListener(saltFocusOutListener); - - sortedList.comparatorProperty().bind(tableView.comparatorProperty()); - - daoFacade.getMyBondedReputations().addListener(myBondedReputationsChangeListener); - bsqWalletService.addBsqBalanceListener(this); - - lockupButton.setOnAction((event) -> { - Coin lockupAmount = ParsingUtils.parseToCoin(amountInputTextField.getText(), bsqFormatter); - int lockupTime = Integer.parseInt(timeInputTextField.getText()); - byte[] salt = Utilities.decodeFromHex(saltInputTextField.getText()); - bondingViewUtils.lockupBondForReputation(lockupAmount, - lockupTime, - salt, - txId -> { - }); - amountInputTextField.setText(""); - timeInputTextField.setText(""); - setNewRandomSalt(); - }); - - - amountInputTextField.resetValidation(); - timeInputTextField.resetValidation(); - - setNewRandomSalt(); - - updateList(); - GUIUtil.setFitToRowsForTableView(tableView, 41, 28, 2, 30); - } - - @Override - protected void deactivate() { - amountInputTextField.textProperty().removeListener(amountInputTextFieldListener); - amountInputTextField.focusedProperty().removeListener(amountFocusOutListener); - - timeInputTextField.textProperty().removeListener(timeInputTextFieldListener); - timeInputTextField.focusedProperty().removeListener(timeFocusOutListener); - - saltInputTextField.textProperty().removeListener(saltInputTextFieldListener); - saltInputTextField.focusedProperty().removeListener(saltFocusOutListener); - - daoFacade.getMyBondedReputations().removeListener(myBondedReputationsChangeListener); - bsqWalletService.removeBsqBalanceListener(this); - - sortedList.comparatorProperty().unbind(); - - lockupButton.setOnAction(null); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // BsqBalanceListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onUpdateBalances(Coin availableConfirmedBalance, - Coin availableNonBsqBalance, - Coin unverifiedBalance, - Coin unconfirmedChangeBalance, - Coin lockedForVotingBalance, - Coin lockupBondsBalance, - Coin unlockingBondsBalance) { - bsqValidator.setAvailableBalance(availableConfirmedBalance); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void createListeners() { - amountFocusOutListener = (observable, oldValue, newValue) -> { - if (!newValue) { - updateButtonState(); - } - }; - timeFocusOutListener = (observable, oldValue, newValue) -> { - if (!newValue) { - updateButtonState(); - } - }; - saltFocusOutListener = (observable, oldValue, newValue) -> { - if (!newValue) { - updateButtonState(); - } - }; - - amountInputTextFieldListener = (observable, oldValue, newValue) -> updateButtonState(); - timeInputTextFieldListener = (observable, oldValue, newValue) -> updateButtonState(); - saltInputTextFieldListener = (observable, oldValue, newValue) -> updateButtonState(); - - myBondedReputationsChangeListener = c -> updateList(); - } - - private void updateList() { - observableList.setAll(daoFacade.getMyBondedReputations().stream() - .map(myBondedReputation -> new MyReputationListItem(myBondedReputation, bsqFormatter)) - .sorted(Comparator.comparing(MyReputationListItem::getLockupDateString).reversed()) - .collect(Collectors.toList())); - GUIUtil.setFitToRowsForTableView(tableView, 41, 28, 2, 30); - } - - private void setNewRandomSalt() { - byte[] randomBytes = UUID.randomUUID().toString().getBytes(Charsets.UTF_8); - // We want to limit it to 20 bytes - byte[] hashOfRandomBytes = Hash.getSha256Ripemd160hash(randomBytes); - // bytesAsHexString results in 40 chars - String bytesAsHexString = Utilities.bytesAsHexString(hashOfRandomBytes); - saltInputTextField.setText(bytesAsHexString); - saltInputTextField.resetValidation(); - } - - private void updateButtonState() { - boolean isValid = bsqValidator.validate(amountInputTextField.getText()).isValid && - timeInputTextFieldValidator.validate(timeInputTextField.getText()).isValid && - hexStringValidator.validate(saltInputTextField.getText()).isValid; - lockupButton.setDisable(!isValid); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Table columns - /////////////////////////////////////////////////////////////////////////////////////////// - - private void createColumns() { - TableColumn column; - - column = new AutoTooltipTableColumn<>(Res.get("shared.amountWithCur", "BSQ")); - column.setMinWidth(120); - column.setMaxWidth(column.getMinWidth()); - column.getStyleClass().add("first-column"); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final MyReputationListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getAmount()); - } else - setText(""); - } - }; - } - }); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.lockTime")); - column.setMinWidth(60); - column.setMaxWidth(column.getMinWidth()); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final MyReputationListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getLockTime()); - } else - setText(""); - } - }; - } - }); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.bondState")); - column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setMinWidth(120); - column.setCellFactory( - new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(MyReputationListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getBondStateString()); - } else - setText(""); - } - }; - } - }); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.lockupDate")); - column.setMinWidth(140); - column.setMaxWidth(column.getMinWidth()); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - - @Override - public void updateItem(final MyReputationListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getLockupDateString()); - } else - setText(""); - } - }; - } - }); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.lockupTxId")); - column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setMinWidth(80); - column.setCellFactory( - new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - private HyperlinkWithIcon hyperlinkWithIcon; - - @Override - public void updateItem(final MyReputationListItem item, boolean empty) { - super.updateItem(item, empty); - //noinspection Duplicates - if (item != null && !empty) { - String transactionId = item.getTxId(); - hyperlinkWithIcon = new ExternalHyperlink(transactionId); - hyperlinkWithIcon.setOnAction(event -> GUIUtil.openTxInBsqBlockExplorer(item.getTxId(), preferences)); - hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", transactionId))); - setGraphic(hyperlinkWithIcon); - } else { - setGraphic(null); - if (hyperlinkWithIcon != null) - hyperlinkWithIcon.setOnAction(null); - } - } - }; - } - }); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.reputation.salt")); - column.setMinWidth(80); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final MyReputationListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getSalt()); - } else - setText(""); - } - }; - } - }); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.reputation.hash")); - column.setMinWidth(80); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final MyReputationListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getHash()); - } else - setText(""); - } - }; - } - }); - tableView.getColumns().add(column); - - column = new TableColumn<>(); - column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setMinWidth(60); - column.getStyleClass().add("last-column"); - column.setCellFactory( - new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - AutoTooltipButton button; - - @Override - public void updateItem(final MyReputationListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty && item.isShowButton()) { - button = new AutoTooltipButton(item.getButtonText()); - button.setOnAction(e -> { - if (item.getBondState() == BondState.LOCKUP_TX_CONFIRMED) { - bondingViewUtils.unLock(item.getLockupTxId(), - txId -> { - }); - } - }); - setGraphic(button); - } else { - setGraphic(null); - if (button != null) { - button.setOnAction(null); - button = null; - } - } - } - }; - } - }); - tableView.getColumns().add(column); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RoleDetailsWindow.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RoleDetailsWindow.java deleted file mode 100644 index b23e6066d4..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RoleDetailsWindow.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.bonding.roles; - -import bisq.desktop.main.overlays.Overlay; -import bisq.desktop.util.DisplayUtils; -import bisq.desktop.util.FormBuilder; - -import bisq.core.dao.DaoFacade; -import bisq.core.dao.state.model.governance.BondedRoleType; -import bisq.core.dao.state.model.governance.RoleProposal; -import bisq.core.locale.Res; -import bisq.core.util.coin.BsqFormatter; - -import org.bitcoinj.core.Coin; - -import javafx.geometry.Insets; - -import java.util.Optional; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -class RoleDetailsWindow extends Overlay { - private final BondedRoleType bondedRoleType; - private final Optional roleProposal; - private final DaoFacade daoFacade; - private final BsqFormatter bsqFormatter; - - - RoleDetailsWindow(BondedRoleType bondedRoleType, Optional roleProposal, DaoFacade daoFacade, - BsqFormatter bsqFormatter) { - this.bondedRoleType = bondedRoleType; - this.roleProposal = roleProposal; - this.daoFacade = daoFacade; - this.bsqFormatter = bsqFormatter; - - width = 968; - type = Type.Confirmation; - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Public API - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void show() { - headLine = Res.get("dao.bond.details.header"); - - createGridPane(); - addHeadLine(); - addContent(); - addButtons(); - applyStyles(); - display(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Protected - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void createGridPane() { - super.createGridPane(); - gridPane.setPadding(new Insets(70, 80, 60, 80)); - gridPane.getStyleClass().add("grid-pane"); - } - - private void addContent() { - FormBuilder.addTopLabelTextField(gridPane, ++rowIndex, Res.get("dao.bond.details.role"), - bondedRoleType.getDisplayString()); - - long requiredBond = daoFacade.getRequiredBond(roleProposal); - int unlockTime = roleProposal.map(RoleProposal::getUnlockTime).orElse(bondedRoleType.getUnlockTimeInBlocks()); - FormBuilder.addTopLabelTextField(gridPane, ++rowIndex, Res.get("dao.bond.details.requiredBond"), - bsqFormatter.formatCoinWithCode(Coin.valueOf(requiredBond))); - - FormBuilder.addTopLabelTextField(gridPane, ++rowIndex, Res.get("dao.bond.details.unlockTime"), - Res.get("dao.bond.details.blocks", unlockTime)); - - FormBuilder.addTopLabelHyperlinkWithIcon(gridPane, ++rowIndex, Res.get("dao.bond.details.link"), - bondedRoleType.getLink(), bondedRoleType.getLink(), 0); - - FormBuilder.addTopLabelTextField(gridPane, ++rowIndex, Res.get("dao.bond.details.isSingleton"), - DisplayUtils.booleanToYesNo(bondedRoleType.isAllowMultipleHolders())); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RolesListItem.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RolesListItem.java deleted file mode 100644 index 40bb7f668b..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RolesListItem.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.bonding.roles; - -import bisq.core.dao.DaoFacade; -import bisq.core.dao.governance.bond.BondState; -import bisq.core.dao.governance.bond.role.BondedRole; -import bisq.core.dao.state.model.governance.Role; -import bisq.core.locale.Res; - -import java.util.Date; - -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@Value -class RolesListItem { - private final BondedRole bondedRole; - private final Role role; - private final String buttonText; - private final boolean isButtonVisible; - private final BondState bondState; - private final String bondStateString; - private final String lockupTxId; - private final Date lockupDate; - - RolesListItem(BondedRole bondedRole, - DaoFacade daoFacade) { - this.bondedRole = bondedRole; - - role = bondedRole.getBondedAsset(); - boolean isMyRole = daoFacade.isMyRole(role); - bondState = bondedRole.getBondState(); - lockupTxId = bondedRole.getLockupTxId(); - lockupDate = new Date(bondedRole.getLockupDate()); - bondStateString = Res.get("dao.bond.bondState." + bondedRole.getBondState().name()); - - boolean showLockup = bondedRole.getBondState() == BondState.READY_FOR_LOCKUP; - boolean showRevoke = bondedRole.getBondState() == BondState.LOCKUP_TX_CONFIRMED; - if (showLockup) { - buttonText = Res.get("dao.bond.table.button.lockup"); - } else if (showRevoke) { - buttonText = Res.get("dao.bond.table.button.revoke"); - } else { - buttonText = ""; - } - - isButtonVisible = isMyRole && (showLockup || showRevoke); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RolesView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RolesView.fxml deleted file mode 100644 index 456483f9e6..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RolesView.fxml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RolesView.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RolesView.java deleted file mode 100644 index febf166e44..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/roles/RolesView.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.bonding.roles; - -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.components.AutoTooltipButton; -import bisq.desktop.components.AutoTooltipTableColumn; -import bisq.desktop.components.ExternalHyperlink; -import bisq.desktop.components.HyperlinkWithIcon; -import bisq.desktop.main.dao.bonding.BondingViewUtils; -import bisq.desktop.util.FormBuilder; -import bisq.desktop.util.GUIUtil; - -import bisq.core.dao.DaoFacade; -import bisq.core.dao.governance.bond.BondState; -import bisq.core.dao.governance.bond.role.BondedRole; -import bisq.core.dao.state.model.governance.BondedRoleType; -import bisq.core.dao.state.model.governance.RoleProposal; -import bisq.core.locale.Res; -import bisq.core.user.Preferences; -import bisq.core.util.coin.BsqFormatter; - -import javax.inject.Inject; - -import javafx.scene.control.Hyperlink; -import javafx.scene.control.Label; -import javafx.scene.control.TableCell; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Priority; - -import javafx.beans.property.ReadOnlyObjectWrapper; - -import javafx.collections.FXCollections; -import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; -import javafx.collections.transformation.SortedList; - -import javafx.util.Callback; - -import java.util.Comparator; -import java.util.Optional; -import java.util.stream.Collectors; - -@FxmlView -public class RolesView extends ActivatableView { - private TableView tableView; - - private final BondingViewUtils bondingViewUtils; - private final BsqFormatter bsqFormatter; - private final DaoFacade daoFacade; - private final Preferences preferences; - - private final ObservableList observableList = FXCollections.observableArrayList(); - private final SortedList sortedList = new SortedList<>(observableList); - - private ListChangeListener bondedRoleListChangeListener; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, lifecycle - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - private RolesView(BsqFormatter bsqFormatter, - BondingViewUtils bondingViewUtils, - DaoFacade daoFacade, - Preferences preferences) { - this.bsqFormatter = bsqFormatter; - this.bondingViewUtils = bondingViewUtils; - this.daoFacade = daoFacade; - this.preferences = preferences; - } - - @Override - public void initialize() { - int gridRow = 0; - tableView = FormBuilder.addTableViewWithHeader(root, gridRow, Res.get("dao.bond.bondedRoles"), "last"); - createColumns(); - tableView.setItems(sortedList); - GridPane.setVgrow(tableView, Priority.ALWAYS); - bondedRoleListChangeListener = c -> updateList(); - } - - @Override - protected void activate() { - sortedList.comparatorProperty().bind(tableView.comparatorProperty()); - daoFacade.getBondedRoles().addListener(bondedRoleListChangeListener); - updateList(); - GUIUtil.setFitToRowsForTableView(tableView, 41, 28, 2, 30); - } - - @Override - protected void deactivate() { - sortedList.comparatorProperty().unbind(); - daoFacade.getBondedRoles().removeListener(bondedRoleListChangeListener); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void updateList() { - observableList.setAll(daoFacade.getAcceptedBondedRoles().stream() - .map(bond -> new RolesListItem(bond, daoFacade)) - .sorted(Comparator.comparing(RolesListItem::getLockupDate).reversed()) - .collect(Collectors.toList())); - GUIUtil.setFitToRowsForTableView(tableView, 41, 28, 2, 30); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Table columns - /////////////////////////////////////////////////////////////////////////////////////////// - - private void createColumns() { - TableColumn column; - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.name")); - column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setMinWidth(80); - column.getStyleClass().add("first-column"); - column.setCellFactory( - new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final RolesListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getRole().getName()); - } else - setText(""); - } - }; - } - }); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.link")); - column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setMinWidth(60); - column.setCellFactory( - new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - private HyperlinkWithIcon hyperlinkWithIcon; - - @Override - public void updateItem(final RolesListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - String link = item.getRole().getLink(); - hyperlinkWithIcon = new ExternalHyperlink(link); - hyperlinkWithIcon.setOnAction(event -> GUIUtil.openWebPage(link)); - hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("shared.openURL", link))); - setGraphic(hyperlinkWithIcon); - } else { - setGraphic(null); - if (hyperlinkWithIcon != null) - hyperlinkWithIcon.setOnAction(null); - } - } - }; - } - }); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.bondType")); - column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setMinWidth(80); - column.setCellFactory( - new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - private Hyperlink hyperlink; - - @Override - public void updateItem(final RolesListItem item, boolean empty) { - super.updateItem(item, empty); - - if (item != null && !empty) { - BondedRoleType bondedRoleType = item.getRole().getBondedRoleType(); - String type = bondedRoleType.getDisplayString(); - hyperlink = new Hyperlink(type); - hyperlink.setOnAction(event -> { - Optional roleProposal = bondingViewUtils.getAcceptedBondedRoleProposal(item.getRole()); - new RoleDetailsWindow(bondedRoleType, roleProposal, daoFacade, bsqFormatter).show(); - }); - hyperlink.setTooltip(new Tooltip(Res.get("tooltip.openPopupForDetails", type))); - setGraphic(hyperlink); - } else { - setGraphic(null); - if (hyperlink != null) - hyperlink.setOnAction(null); - } - } - }; - } - }); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.lockupTxId")); - column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setMinWidth(80); - column.setCellFactory( - new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - private HyperlinkWithIcon hyperlinkWithIcon; - private Label label; - - @Override - public void updateItem(final RolesListItem item, boolean empty) { - super.updateItem(item, empty); - - if (item != null && !empty) { - String transactionId = item.getBondedRole().getLockupTxId(); - if (transactionId != null) { - hyperlinkWithIcon = new ExternalHyperlink(transactionId); - hyperlinkWithIcon.setOnAction(event -> GUIUtil.openTxInBsqBlockExplorer(transactionId, preferences)); - hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", transactionId))); - setGraphic(hyperlinkWithIcon); - } else { - label = new Label("-"); - setGraphic(label); - } - } else { - setGraphic(null); - if (hyperlinkWithIcon != null) - hyperlinkWithIcon.setOnAction(null); - if (label != null) - label = null; - } - } - }; - } - }); - tableView.getColumns().add(column); - - column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.bondState")); - column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setMinWidth(120); - column.setCellFactory( - new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final RolesListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getBondStateString()); - } else - setText(""); - } - }; - } - }); - tableView.getColumns().add(column); - - column = new TableColumn<>(); - column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setMinWidth(80); - column.getStyleClass().add("last-column"); - column.setCellFactory( - new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - AutoTooltipButton button; - - @Override - public void updateItem(final RolesListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty && item.isButtonVisible()) { - if (button == null) { - button = new AutoTooltipButton(item.getButtonText()); - button.setMinWidth(70); - button.setOnAction(e -> { - if (item.getBondState() == BondState.READY_FOR_LOCKUP) { - bondingViewUtils.lockupBondForBondedRole(item.getRole(), - txId -> { - }); - } else if (item.getBondState() == BondState.LOCKUP_TX_CONFIRMED) { - bondingViewUtils.unLock(item.getLockupTxId(), - txId -> { - }); - } - }); - setGraphic(button); - } - } else { - setGraphic(null); - if (button != null) { - button.setOnAction(null); - button = null; - } - } - } - }; - } - }); - tableView.getColumns().add(column); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/BurnBsqView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/BurnBsqView.fxml deleted file mode 100644 index 3a5230561f..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/BurnBsqView.fxml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/BurnBsqView.java b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/BurnBsqView.java deleted file mode 100644 index 882da4aedf..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/BurnBsqView.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.burnbsq; - -import bisq.desktop.Navigation; -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.CachingViewLoader; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.common.view.View; -import bisq.desktop.common.view.ViewLoader; -import bisq.desktop.common.view.ViewPath; -import bisq.desktop.components.MenuItem; -import bisq.desktop.main.MainView; -import bisq.desktop.main.dao.DaoView; -import bisq.desktop.main.dao.burnbsq.assetfee.AssetFeeView; -import bisq.desktop.main.dao.burnbsq.proofofburn.ProofOfBurnView; - -import bisq.core.locale.Res; - -import javax.inject.Inject; - -import javafx.fxml.FXML; - -import javafx.scene.control.ToggleGroup; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.VBox; - -import java.util.Arrays; -import java.util.List; - -@FxmlView -public class BurnBsqView extends ActivatableView { - - private final ViewLoader viewLoader; - private final Navigation navigation; - - private MenuItem assetFee, proofOfBurn; - private Navigation.Listener listener; - - @FXML - private VBox leftVBox; - @FXML - private AnchorPane content; - - private Class selectedViewClass; - private ToggleGroup toggleGroup; - - @Inject - private BurnBsqView(CachingViewLoader viewLoader, Navigation navigation) { - this.viewLoader = viewLoader; - this.navigation = navigation; - } - - @Override - public void initialize() { - listener = (viewPath, data) -> { - if (viewPath.size() != 4 || viewPath.indexOf(BurnBsqView.class) != 2) - return; - - selectedViewClass = viewPath.tip(); - loadView(selectedViewClass); - }; - - toggleGroup = new ToggleGroup(); - final List> baseNavPath = Arrays.asList(MainView.class, DaoView.class, BurnBsqView.class); - assetFee = new MenuItem(navigation, toggleGroup, Res.get("dao.burnBsq.menuItem.assetFee"), - AssetFeeView.class, baseNavPath); - proofOfBurn = new MenuItem(navigation, toggleGroup, Res.get("dao.burnBsq.menuItem.proofOfBurn"), - ProofOfBurnView.class, baseNavPath); - - leftVBox.getChildren().addAll(assetFee, proofOfBurn); - } - - @Override - protected void activate() { - assetFee.activate(); - proofOfBurn.activate(); - - navigation.addListener(listener); - ViewPath viewPath = navigation.getCurrentPath(); - if (viewPath.size() == 3 && viewPath.indexOf(BurnBsqView.class) == 2 || - viewPath.size() == 2 && viewPath.indexOf(DaoView.class) == 1) { - if (selectedViewClass == null) - selectedViewClass = AssetFeeView.class; - - loadView(selectedViewClass); - - } else if (viewPath.size() == 4 && viewPath.indexOf(BurnBsqView.class) == 2) { - selectedViewClass = viewPath.get(3); - loadView(selectedViewClass); - } - } - - @SuppressWarnings("Duplicates") - @Override - protected void deactivate() { - navigation.removeListener(listener); - - assetFee.deactivate(); - proofOfBurn.deactivate(); - } - - private void loadView(Class viewClass) { - View view = viewLoader.load(viewClass); - content.getChildren().setAll(view.getRoot()); - - if (view instanceof AssetFeeView) toggleGroup.selectToggle(assetFee); - else if (view instanceof ProofOfBurnView) toggleGroup.selectToggle(proofOfBurn); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/assetfee/AssetFeeView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/assetfee/AssetFeeView.fxml deleted file mode 100644 index bbb0b8ae4d..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/assetfee/AssetFeeView.fxml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/assetfee/AssetFeeView.java b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/assetfee/AssetFeeView.java deleted file mode 100644 index 0d95572770..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/assetfee/AssetFeeView.java +++ /dev/null @@ -1,471 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.burnbsq.assetfee; - -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.components.AutoTooltipTableColumn; -import bisq.desktop.components.InputTextField; -import bisq.desktop.main.overlays.popups.Popup; -import bisq.desktop.util.FormBuilder; -import bisq.desktop.util.GUIUtil; -import bisq.desktop.util.Layout; -import bisq.desktop.util.validation.BsqValidator; - -import bisq.core.btc.listeners.BsqBalanceListener; -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.dao.governance.asset.AssetService; -import bisq.core.dao.governance.asset.StatefulAsset; -import bisq.core.dao.governance.proposal.TxException; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.locale.CurrencyUtil; -import bisq.core.locale.Res; -import bisq.core.util.FormattingUtils; -import bisq.core.util.ParsingUtils; -import bisq.core.util.coin.BsqFormatter; -import bisq.core.util.coin.CoinFormatter; - -import bisq.common.UserThread; -import bisq.common.app.DevEnv; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.Transaction; - -import javax.inject.Inject; -import javax.inject.Named; - -import javafx.scene.control.Button; -import javafx.scene.control.ComboBox; -import javafx.scene.control.TableCell; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.TextField; -import javafx.scene.layout.GridPane; - -import javafx.beans.property.ReadOnlyObjectWrapper; -import javafx.beans.value.ChangeListener; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.collections.transformation.SortedList; - -import javafx.util.Callback; -import javafx.util.StringConverter; - -import java.util.Comparator; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import javax.annotation.Nullable; - -import static bisq.desktop.util.FormBuilder.addButtonAfterGroup; -import static bisq.desktop.util.FormBuilder.addInputTextField; -import static bisq.desktop.util.FormBuilder.addTitledGroupBg; - -@FxmlView -public class AssetFeeView extends ActivatableView implements BsqBalanceListener, DaoStateListener { - private ComboBox assetComboBox; - private InputTextField feeAmountInputTextField; - private TextField trialPeriodTextField; - private Button payFeeButton; - private TableView tableView; - - private final BsqFormatter bsqFormatter; - private final BsqWalletService bsqWalletService; - private final BsqValidator bsqValidator; - private final AssetService assetService; - private final DaoStateService daoStateService; - private final CoinFormatter btcFormatter; - - private final ObservableList observableList = FXCollections.observableArrayList(); - private final SortedList sortedList = new SortedList<>(observableList); - - private int gridRow = 0; - - private ChangeListener amountFocusOutListener; - private ChangeListener amountInputTextFieldListener; - @Nullable - private StatefulAsset selectedAsset; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, lifecycle - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public AssetFeeView(BsqFormatter bsqFormatter, - BsqWalletService bsqWalletService, - BsqValidator bsqValidator, - AssetService assetService, - DaoStateService daoStateService, - @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter) { - this.bsqFormatter = bsqFormatter; - this.bsqWalletService = bsqWalletService; - this.bsqValidator = bsqValidator; - this.assetService = assetService; - this.daoStateService = daoStateService; - this.btcFormatter = btcFormatter; - } - - @Override - public void initialize() { - addTitledGroupBg(root, gridRow, 3, Res.get("dao.burnBsq.header")); - - assetComboBox = FormBuilder.addComboBox(root, gridRow, - Res.get("dao.burnBsq.selectAsset"), Layout.FIRST_ROW_DISTANCE); - assetComboBox.setConverter(new StringConverter<>() { - @Override - public String toString(StatefulAsset statefulAsset) { - return CurrencyUtil.getNameAndCode(statefulAsset.getAsset().getTickerSymbol()); - } - - @Override - public StatefulAsset fromString(String string) { - return null; - } - }); - - feeAmountInputTextField = addInputTextField(root, ++gridRow, Res.get("dao.burnBsq.fee")); - feeAmountInputTextField.setValidator(bsqValidator); - - trialPeriodTextField = FormBuilder.addTopLabelTextField(root, ++gridRow, Res.get("dao.burnBsq.trialPeriod")).second; - - payFeeButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.burnBsq.payFee")); - - tableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.burnBsq.allAssets"), 20, "last"); - createColumns(); - tableView.setItems(sortedList); - - createListeners(); - } - - @Override - protected void activate() { - assetComboBox.setOnAction(e -> { - selectedAsset = assetComboBox.getSelectionModel().getSelectedItem(); - }); - - feeAmountInputTextField.textProperty().addListener(amountInputTextFieldListener); - feeAmountInputTextField.focusedProperty().addListener(amountFocusOutListener); - - sortedList.comparatorProperty().bind(tableView.comparatorProperty()); - - daoStateService.addDaoStateListener(this); - - bsqWalletService.addBsqBalanceListener(this); - - assetService.updateAssetStates(); - updateList(); - - onUpdateAvailableConfirmedBalance(bsqWalletService.getAvailableConfirmedBalance()); - - payFeeButton.setOnAction((event) -> { - Coin listingFee = getListingFee(); - long days = getDays(); - // We don't allow shorter periods as it would allow an attacker to try to deactivate other coins by making a - // small fee payment to reduce the trial period and look back period. - // Still not a perfect solution but should be good enough for now. - long minDays = 30; - if (days >= minDays) { - try { - Transaction transaction = assetService.payFee(selectedAsset, listingFee.value); - Coin miningFee = transaction.getFee(); - int txVsize = transaction.getVsize(); - - if (!DevEnv.isDevMode()) { - GUIUtil.showBsqFeeInfoPopup(listingFee, miningFee, txVsize, bsqFormatter, btcFormatter, - Res.get("dao.burnBsq.assetFee"), () -> doPublishFeeTx(transaction)); - } else { - doPublishFeeTx(transaction); - } - } catch (InsufficientMoneyException | TxException e) { - e.printStackTrace(); - new Popup().error(e.toString()).show(); - } - } else { - new Popup().warning(Res.get("dao.burnBsq.assets.toFewDays", minDays)).show(); - } - }); - - - GUIUtil.setFitToRowsForTableView(tableView, 41, 28, 2, 100); - updateButtonState(); - - feeAmountInputTextField.resetValidation(); - } - - @Override - protected void deactivate() { - assetComboBox.setOnAction(null); - - feeAmountInputTextField.textProperty().removeListener(amountInputTextFieldListener); - feeAmountInputTextField.focusedProperty().removeListener(amountFocusOutListener); - - daoStateService.removeDaoStateListener(this); - - bsqWalletService.removeBsqBalanceListener(this); - - sortedList.comparatorProperty().unbind(); - - payFeeButton.setOnAction(null); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // BsqBalanceListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onUpdateBalances(Coin availableConfirmedBalance, - Coin availableNonBsqBalance, - Coin unverifiedBalance, - Coin unconfirmedChangeBalance, - Coin lockedForVotingBalance, - Coin lockupBondsBalance, - Coin unlockingBondsBalance) { - - onUpdateAvailableConfirmedBalance(availableConfirmedBalance); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - // Delay a bit to reduce load at onParseBlockCompleteAfterBatchProcessing event - UserThread.runAfter(() -> { - assetService.updateAssetStates(); - updateList(); - }, 300, TimeUnit.MILLISECONDS); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void createListeners() { - amountFocusOutListener = (observable, oldValue, newValue) -> { - if (!newValue) { - updateButtonState(); - } - }; - - amountInputTextFieldListener = (observable, oldValue, newValue) -> { - trialPeriodTextField.setText(Res.get("dao.burnBsq.assets.days", getDays())); - updateButtonState(); - }; - } - - private void onUpdateAvailableConfirmedBalance(Coin availableConfirmedBalance) { - bsqValidator.setAvailableBalance(availableConfirmedBalance); - updateButtonState(); - } - - private long getDays() { - return getListingFee().value / assetService.getFeePerDay().value; - } - - // We only update on new BSQ blocks and at view activation. We do not update at each trade statistics change as - // that would cause too much CPU load. The assetService.updateAssetStates() call takes about 22 ms. - private void updateList() { - // Here we exclude the assets which have been removed by voting. Paying a fee would not change the state. - List statefulAssets = assetService.getStatefulAssets(); - ObservableList nonRemovedStatefulAssets = FXCollections.observableArrayList(statefulAssets.stream() - .filter(e -> !e.wasRemovedByVoting()) - .collect(Collectors.toList())); - assetComboBox.setItems(nonRemovedStatefulAssets); - - // In the table we want to show all including removed assets. - observableList.setAll(statefulAssets.stream() - .map(statefulAsset -> new AssetListItem(statefulAsset, bsqFormatter)) - .collect(Collectors.toList())); - GUIUtil.setFitToRowsForTableView(tableView, 41, 28, 2, 100); - } - - private void updateButtonState() { - boolean isValid = bsqValidator.validate(feeAmountInputTextField.getText()).isValid && - selectedAsset != null; - payFeeButton.setDisable(!isValid); - } - - private Coin getListingFee() { - return ParsingUtils.parseToCoin(feeAmountInputTextField.getText(), bsqFormatter); - } - - private void doPublishFeeTx(Transaction transaction) { - assetService.publishTransaction(transaction, - () -> { - assetComboBox.getSelectionModel().clearSelection(); - if (!DevEnv.isDevMode()) - new Popup().confirmation(Res.get("dao.tx.published.success")).show(); - }, - errorMessage -> new Popup().warning(errorMessage).show()); - - feeAmountInputTextField.clear(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Table columns - /////////////////////////////////////////////////////////////////////////////////////////// - - private void createColumns() { - TableColumn column; - - column = new AutoTooltipTableColumn<>(Res.get("dao.burnBsq.assets.nameAndCode")); - column.setMinWidth(120); - column.getStyleClass().add("first-column"); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final AssetListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getNameAndCode()); - } else - setText(""); - } - }; - } - }); - tableView.getColumns().add(column); - column.setComparator(Comparator.comparing(AssetListItem::getNameAndCode)); - - column = new AutoTooltipTableColumn<>(Res.get("dao.burnBsq.assets.state")); - column.setMinWidth(120); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final AssetListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getAssetStateString()); - } else - setText(""); - } - }; - } - }); - tableView.getColumns().add(column); - column.setComparator(Comparator.comparing(AssetListItem::getAssetStateString)); - - column = new AutoTooltipTableColumn<>(Res.get("dao.burnBsq.assets.tradeVolume")); - column.setMinWidth(120); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final AssetListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getTradedVolumeAsString()); - } else - setText(""); - } - }; - } - }); - tableView.getColumns().add(column); - column.setComparator(Comparator.comparing(AssetListItem::getTradedVolume)); - - column = new AutoTooltipTableColumn<>(Res.get("dao.burnBsq.assets.lookBackPeriod")); - column.setMinWidth(120); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final AssetListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getLookBackPeriodInDays()); - } else - setText(""); - } - }; - } - }); - tableView.getColumns().add(column); - column.setComparator(Comparator.comparing(AssetListItem::getLookBackPeriodInDays)); - - column = new AutoTooltipTableColumn<>(Res.get("dao.burnBsq.assets.trialFee")); - column.setMinWidth(120); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final AssetListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getFeeOfTrialPeriodAsString()); - } else - setText(""); - } - }; - } - }); - tableView.getColumns().add(column); - column.setComparator(Comparator.comparing(AssetListItem::getFeeOfTrialPeriod)); - - column = new AutoTooltipTableColumn<>(Res.get("dao.burnBsq.assets.totalFee")); - column.setMinWidth(120); - column.getStyleClass().add("last-column"); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final AssetListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getTotalFeesPaidAsString()); - } else - setText(""); - } - }; - } - }); - tableView.getColumns().add(column); - column.setComparator(Comparator.comparing(AssetListItem::getTotalFeesPaid)); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/assetfee/AssetListItem.java b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/assetfee/AssetListItem.java deleted file mode 100644 index 3f49e0edab..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/assetfee/AssetListItem.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.burnbsq.assetfee; - -import bisq.core.dao.governance.asset.StatefulAsset; -import bisq.core.locale.Res; -import bisq.core.util.coin.BsqFormatter; - -import lombok.Value; - -@Value -class AssetListItem { - private final StatefulAsset statefulAsset; - private final String tickerSymbol; - private final String assetStateString; - private final int trialPeriodInBlocks; - private final String nameAndCode; - private final long totalFeesPaid; - private final String totalFeesPaidAsString; - private final long feeOfTrialPeriod; - private final String feeOfTrialPeriodAsString; - private final String tradedVolumeAsString; - private final String lookBackPeriodInDays; - private final long tradedVolume; - - AssetListItem(StatefulAsset statefulAsset, - BsqFormatter bsqFormatter) { - this.statefulAsset = statefulAsset; - - tickerSymbol = statefulAsset.getTickerSymbol(); - nameAndCode = statefulAsset.getNameAndCode(); - assetStateString = Res.get("dao.assetState." + statefulAsset.getAssetState()); - feeOfTrialPeriod = statefulAsset.getFeeOfTrialPeriod(); - feeOfTrialPeriodAsString = bsqFormatter.formatCoinWithCode(feeOfTrialPeriod); - totalFeesPaid = statefulAsset.getTotalFeesPaid(); - totalFeesPaidAsString = bsqFormatter.formatCoinWithCode(totalFeesPaid); - trialPeriodInBlocks = (int) totalFeesPaid * 144; - tradedVolume = statefulAsset.getTradeVolume(); - tradedVolumeAsString = bsqFormatter.formatBTCWithCode(tradedVolume); - lookBackPeriodInDays = Res.get("dao.burnBsq.assets.days", statefulAsset.getLookBackPeriodInDays()); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/MyProofOfBurnListItem.java b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/MyProofOfBurnListItem.java deleted file mode 100644 index 0fe3afe189..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/MyProofOfBurnListItem.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.burnbsq.proofofburn; - -import bisq.desktop.util.DisplayUtils; - -import bisq.core.dao.governance.proofofburn.MyProofOfBurn; -import bisq.core.dao.governance.proofofburn.ProofOfBurnService; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.locale.Res; -import bisq.core.util.coin.BsqFormatter; - -import bisq.common.util.Utilities; - -import org.bitcoinj.core.Coin; - -import java.util.Date; -import java.util.Optional; - -import lombok.Value; - -@Value -class MyProofOfBurnListItem { - private final MyProofOfBurn myProofOfBurn; - private final long amount; - private final String amountAsString; - private final String txId; - private final String hashAsHex; - private final String preImage; - private final String pubKey; - private final Date date; - private final String dateAsString; - - MyProofOfBurnListItem(MyProofOfBurn myProofOfBurn, ProofOfBurnService proofOfBurnService, BsqFormatter bsqFormatter) { - this.myProofOfBurn = myProofOfBurn; - - preImage = myProofOfBurn.getPreImage(); - Optional optionalTx = proofOfBurnService.getTx(myProofOfBurn.getTxId()); - if (optionalTx.isPresent()) { - Tx tx = optionalTx.get(); - date = new Date(tx.getTime()); - dateAsString = DisplayUtils.formatDateTime(date); - amount = proofOfBurnService.getAmount(tx); - amountAsString = bsqFormatter.formatCoinWithCode(Coin.valueOf(amount)); - txId = tx.getId(); - hashAsHex = Utilities.bytesAsHexString(proofOfBurnService.getHashFromOpReturnData(tx)); - pubKey = Utilities.bytesAsHexString(proofOfBurnService.getPubKey(txId)); - } else { - amount = 0; - amountAsString = Res.get("shared.na"); - txId = Res.get("shared.na"); - hashAsHex = Res.get("shared.na"); - pubKey = Res.get("shared.na"); - dateAsString = Res.get("shared.na"); - date = new Date(0); - } - } - -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnListItem.java b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnListItem.java deleted file mode 100644 index 5dfe0fbdf8..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnListItem.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.burnbsq.proofofburn; - -import bisq.desktop.util.DisplayUtils; - -import bisq.core.dao.governance.proofofburn.ProofOfBurnService; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.util.coin.BsqFormatter; - -import bisq.common.util.Utilities; - -import org.bitcoinj.core.Coin; - -import java.util.Date; - -import lombok.Value; - -@Value -class ProofOfBurnListItem { - private final long amount; - private final String amountAsString; - private final String txId; - private final String hashAsHex; - private final String pubKey; - private final Date date; - private final String dateAsString; - - ProofOfBurnListItem(Tx tx, ProofOfBurnService proofOfBurnService, BsqFormatter bsqFormatter) { - amount = proofOfBurnService.getAmount(tx); - amountAsString = bsqFormatter.formatCoinWithCode(Coin.valueOf(amount)); - txId = tx.getId(); - hashAsHex = Utilities.bytesAsHexString(proofOfBurnService.getHashFromOpReturnData(tx)); - pubKey = Utilities.bytesAsHexString(proofOfBurnService.getPubKey(txId)); - date = new Date(tx.getTime()); - dateAsString = DisplayUtils.formatDateTime(date); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnSignatureWindow.java b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnSignatureWindow.java deleted file mode 100644 index d62a428817..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnSignatureWindow.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.burnbsq.proofofburn; - -import bisq.desktop.components.InputTextField; -import bisq.desktop.main.overlays.Overlay; -import bisq.desktop.util.FormBuilder; - -import bisq.core.dao.governance.proofofburn.ProofOfBurnService; -import bisq.core.locale.Res; - -import bisq.common.util.Tuple3; -import bisq.common.util.Utilities; - -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.layout.ColumnConstraints; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.VBox; - -import javafx.geometry.Insets; - -import java.util.Optional; - -import static bisq.desktop.util.FormBuilder.addInputTextField; - -class ProofOfBurnSignatureWindow extends Overlay { - private final ProofOfBurnService proofOfBurnService; - private final String proofOfBurnTxId; - private final String pubKey; - - private TextField sigTextField; - private VBox sigTextFieldBox; - - ProofOfBurnSignatureWindow(ProofOfBurnService proofOfBurnService, String proofOfBurnTxId) { - this.proofOfBurnService = proofOfBurnService; - this.proofOfBurnTxId = proofOfBurnTxId; - this.pubKey = proofOfBurnService.getPubKeyAsHex(proofOfBurnTxId); - type = Type.Attention; - } - - public void show() { - if (headLine == null) - headLine = Res.get("dao.proofOfBurn.signature.window.title"); - - width = 800; - createGridPane(); - addHeadLine(); - addContent(); - addButtons(); - applyStyles(); - display(); - } - - @SuppressWarnings("Duplicates") - @Override - protected void createGridPane() { - gridPane = new GridPane(); - gridPane.setHgap(5); - gridPane.setVgap(5); - gridPane.setPadding(new Insets(64, 64, 64, 64)); - gridPane.setPrefWidth(width); - - ColumnConstraints columnConstraints = new ColumnConstraints(); - columnConstraints.setPercentWidth(100); - gridPane.getColumnConstraints().add(columnConstraints); - } - - private void addContent() { - FormBuilder.addTopLabelTextField(gridPane, rowIndex, Res.get("dao.proofOfBurn.pubKey"), pubKey, 40); - - InputTextField messageInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("dao.proofOfBurn.message")); - - Button signButton = FormBuilder.addButton(gridPane, ++rowIndex, Res.get("dao.proofOfBurn.sign"), 10); - signButton.setOnAction(e -> { - proofOfBurnService.sign(proofOfBurnTxId, messageInputTextField.getText()).ifPresent(sig -> { - sigTextFieldBox.setVisible(true); - sigTextField.setText(sig); - }); - }); - Tuple3 tuple = FormBuilder.addTopLabelTextField(gridPane, ++rowIndex, Res.get("dao.proofOfBurn.sig")); - sigTextFieldBox = tuple.third; - sigTextField = tuple.second; - sigTextFieldBox.setVisible(false); - - actionHandlerOptional = Optional.of(() -> { - Utilities.copyToClipboard(sigTextField.getText()); - }); - actionButtonText = Res.get("dao.proofOfBurn.copySig"); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnVerificationWindow.java b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnVerificationWindow.java deleted file mode 100644 index 5737854f0b..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnVerificationWindow.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.burnbsq.proofofburn; - -import bisq.desktop.components.InputTextField; -import bisq.desktop.main.overlays.Overlay; -import bisq.desktop.util.FormBuilder; - -import bisq.core.dao.governance.proofofburn.ProofOfBurnService; -import bisq.core.locale.Res; - -import bisq.common.util.Tuple3; - -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.layout.ColumnConstraints; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.VBox; - -import javafx.geometry.Insets; - -import java.security.SignatureException; - -import static bisq.desktop.util.FormBuilder.addInputTextField; - -class ProofOfBurnVerificationWindow extends Overlay { - private final ProofOfBurnService proofOfBurnService; - private final String pubKey; - - private TextField verificationResultTextField; - private VBox verificationResultBox; - - ProofOfBurnVerificationWindow(ProofOfBurnService proofOfBurnService, String proofOfBurnTxId) { - this.proofOfBurnService = proofOfBurnService; - this.pubKey = proofOfBurnService.getPubKeyAsHex(proofOfBurnTxId); - type = Type.Attention; - } - - @SuppressWarnings("Duplicates") - @Override - protected void createGridPane() { - gridPane = new GridPane(); - gridPane.setHgap(5); - gridPane.setVgap(5); - gridPane.setPadding(new Insets(64, 64, 64, 64)); - gridPane.setPrefWidth(width); - - ColumnConstraints columnConstraints = new ColumnConstraints(); - columnConstraints.setPercentWidth(100); - gridPane.getColumnConstraints().add(columnConstraints); - } - - public void show() { - if (headLine == null) - headLine = Res.get("dao.proofOfBurn.verify.window.title"); - - width = 800; - createGridPane(); - addHeadLine(); - addContent(); - addButtons(); - applyStyles(); - display(); - } - - private void addContent() { - FormBuilder.addTopLabelTextField(gridPane, rowIndex, Res.get("dao.proofOfBurn.pubKey"), pubKey, 40); - InputTextField messageInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("dao.proofOfBurn.message")); - InputTextField signatureInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("dao.proofOfBurn.sig")); - Button verifyButton = FormBuilder.addButton(gridPane, ++rowIndex, Res.get("dao.proofOfBurn.verify"), 10); - - verifyButton.setOnAction(e -> { - try { - verificationResultBox.setVisible(true); - proofOfBurnService.verify(messageInputTextField.getText(), pubKey, signatureInputTextField.getText()); - verificationResultTextField.setText(Res.get("dao.proofOfBurn.verificationResult.ok")); - } catch (SignatureException e1) { - verificationResultTextField.setText(Res.get("dao.proofOfBurn.verificationResult.failed")); - } - }); - - Tuple3 tuple = FormBuilder.addTopLabelTextField(gridPane, ++rowIndex, Res.get("dao.proofOfBurn.sig")); - verificationResultBox = tuple.third; - verificationResultTextField = tuple.second; - verificationResultBox.setVisible(false); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnView.fxml deleted file mode 100644 index 498a89f2a6..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnView.fxml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnView.java b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnView.java deleted file mode 100644 index d1dbc15d75..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/proofofburn/ProofOfBurnView.java +++ /dev/null @@ -1,652 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.burnbsq.proofofburn; - -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.components.AutoTooltipButton; -import bisq.desktop.components.AutoTooltipTableColumn; -import bisq.desktop.components.ExternalHyperlink; -import bisq.desktop.components.HyperlinkWithIcon; -import bisq.desktop.components.InputTextField; -import bisq.desktop.main.overlays.popups.Popup; -import bisq.desktop.util.FormBuilder; -import bisq.desktop.util.GUIUtil; -import bisq.desktop.util.Layout; -import bisq.desktop.util.validation.BsqValidator; - -import bisq.core.btc.listeners.BsqBalanceListener; -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.dao.governance.proofofburn.MyProofOfBurnListService; -import bisq.core.dao.governance.proofofburn.ProofOfBurnService; -import bisq.core.dao.governance.proposal.TxException; -import bisq.core.locale.Res; -import bisq.core.user.Preferences; -import bisq.core.util.FormattingUtils; -import bisq.core.util.ParsingUtils; -import bisq.core.util.coin.BsqFormatter; -import bisq.core.util.coin.CoinFormatter; -import bisq.core.util.validation.InputValidator; - -import bisq.common.app.DevEnv; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.Transaction; - -import javax.inject.Inject; -import javax.inject.Named; - -import javafx.scene.control.Button; -import javafx.scene.control.TableCell; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.TextField; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.GridPane; - -import javafx.beans.InvalidationListener; -import javafx.beans.property.ReadOnlyObjectWrapper; -import javafx.beans.value.ChangeListener; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.collections.transformation.SortedList; - -import javafx.util.Callback; - -import java.util.Comparator; -import java.util.stream.Collectors; - -import static bisq.desktop.util.FormBuilder.addButtonAfterGroup; -import static bisq.desktop.util.FormBuilder.addInputTextField; -import static bisq.desktop.util.FormBuilder.addTitledGroupBg; -import static bisq.desktop.util.FormBuilder.addTopLabelTextField; - -@FxmlView -public class ProofOfBurnView extends ActivatableView implements BsqBalanceListener { - private final ProofOfBurnService proofOfBurnService; - private final MyProofOfBurnListService myProofOfBurnListService; - private final Preferences preferences; - private final CoinFormatter btcFormatter; - private final BsqFormatter bsqFormatter; - private final BsqWalletService bsqWalletService; - private final BsqValidator bsqValidator; - - private InputTextField amountInputTextField, preImageTextField; - private TextField hashTextField; - private Button burnButton; - private TableView myItemsTableView; - private TableView allTxsTableView; - - private final ObservableList myItemsObservableList = FXCollections.observableArrayList(); - private final SortedList myItemsSortedList = new SortedList<>(myItemsObservableList); - - private final ObservableList allItemsObservableList = FXCollections.observableArrayList(); - private final SortedList allItemsSortedList = new SortedList<>(allItemsObservableList); - - private int gridRow = 0; - - private ChangeListener amountFocusOutListener, preImageFocusOutListener; - private ChangeListener amountInputTextFieldListener, preImageInputTextFieldListener; - private InvalidationListener updateListener; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, lifecycle - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - private ProofOfBurnView(BsqFormatter bsqFormatter, - BsqWalletService bsqWalletService, - BsqValidator bsqValidator, - ProofOfBurnService proofOfBurnService, - MyProofOfBurnListService myProofOfBurnListService, - Preferences preferences, - @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter) { - this.bsqFormatter = bsqFormatter; - this.bsqWalletService = bsqWalletService; - this.bsqValidator = bsqValidator; - this.proofOfBurnService = proofOfBurnService; - this.myProofOfBurnListService = myProofOfBurnListService; - this.preferences = preferences; - this.btcFormatter = btcFormatter; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, lifecycle - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void initialize() { - addTitledGroupBg(root, gridRow, 4, Res.get("dao.proofOfBurn.header")); - amountInputTextField = addInputTextField(root, ++gridRow, Res.get("dao.proofOfBurn.amount"), Layout.FIRST_ROW_DISTANCE); - preImageTextField = addInputTextField(root, ++gridRow, Res.get("dao.proofOfBurn.preImage")); - hashTextField = addTopLabelTextField(root, ++gridRow, Res.get("dao.proofOfBurn.hash")).second; - burnButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.proofOfBurn.burn")); - - myItemsTableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.proofOfBurn.myItems"), 30); - createColumnsForMyItems(); - myItemsTableView.setItems(myItemsSortedList); - - allTxsTableView = FormBuilder.addTableViewWithHeader(root, ++gridRow, Res.get("dao.proofOfBurn.allTxs"), 30, "last"); - createColumnsForAllTxs(); - allTxsTableView.setItems(allItemsSortedList); - - createListeners(); - } - - @Override - protected void activate() { - amountInputTextField.textProperty().addListener(amountInputTextFieldListener); - amountInputTextField.focusedProperty().addListener(amountFocusOutListener); - - preImageTextField.textProperty().addListener(preImageInputTextFieldListener); - preImageTextField.focusedProperty().addListener(preImageFocusOutListener); - - allItemsSortedList.comparatorProperty().bind(allTxsTableView.comparatorProperty()); - - proofOfBurnService.getUpdateFlag().addListener(updateListener); - bsqWalletService.addBsqBalanceListener(this); - onUpdateAvailableConfirmedBalance(bsqWalletService.getAvailableConfirmedBalance()); - - burnButton.setOnAction((event) -> { - Coin amount = getAmountFee(); - try { - String preImageAsString = preImageTextField.getText(); - Transaction transaction = proofOfBurnService.burn(preImageAsString, amount.value); - Coin miningFee = transaction.getFee(); - int txVsize = transaction.getVsize(); - - if (!DevEnv.isDevMode()) { - GUIUtil.showBsqFeeInfoPopup(amount, miningFee, txVsize, bsqFormatter, btcFormatter, - Res.get("dao.proofOfBurn.header"), () -> doPublishFeeTx(transaction, preImageAsString)); - } else { - doPublishFeeTx(transaction, preImageAsString); - } - } catch (InsufficientMoneyException | TxException e) { - e.printStackTrace(); - new Popup().error(e.toString()).show(); - } - }); - - amountInputTextField.setValidator(bsqValidator); - preImageTextField.setValidator(new InputValidator()); - - updateList(); - GUIUtil.setFitToRowsForTableView(myItemsTableView, 41, 28, 4, 6); - GUIUtil.setFitToRowsForTableView(allTxsTableView, 41, 28, 2, 10); - updateButtonState(); - } - - @Override - protected void deactivate() { - amountInputTextField.textProperty().removeListener(amountInputTextFieldListener); - amountInputTextField.focusedProperty().removeListener(amountFocusOutListener); - - amountInputTextField.textProperty().removeListener(amountInputTextFieldListener); - amountInputTextField.focusedProperty().removeListener(amountFocusOutListener); - - allItemsSortedList.comparatorProperty().unbind(); - - proofOfBurnService.getUpdateFlag().removeListener(updateListener); - bsqWalletService.removeBsqBalanceListener(this); - - burnButton.setOnAction(null); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // BsqBalanceListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onUpdateBalances(Coin availableConfirmedBalance, - Coin availableNonBsqBalance, - Coin unverifiedBalance, - Coin unconfirmedChangeBalance, - Coin lockedForVotingBalance, - Coin lockupBondsBalance, - Coin unlockingBondsBalance) { - onUpdateAvailableConfirmedBalance(availableConfirmedBalance); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void createListeners() { - amountFocusOutListener = (observable, oldValue, newValue) -> { - if (!newValue) { - updateButtonState(); - } - }; - - amountInputTextFieldListener = (observable, oldValue, newValue) -> { - updateButtonState(); - }; - preImageFocusOutListener = (observable, oldValue, newValue) -> { - if (!newValue) { - updateButtonState(); - } - }; - - preImageInputTextFieldListener = (observable, oldValue, newValue) -> { - hashTextField.setText(proofOfBurnService.getHashAsString(newValue)); - updateButtonState(); - }; - - updateListener = observable -> updateList(); - } - - private void onUpdateAvailableConfirmedBalance(Coin availableConfirmedBalance) { - bsqValidator.setAvailableBalance(availableConfirmedBalance); - updateButtonState(); - } - - private void updateList() { - myItemsObservableList.setAll(myProofOfBurnListService.getMyProofOfBurnList().stream() - .map(myProofOfBurn -> new MyProofOfBurnListItem(myProofOfBurn, proofOfBurnService, bsqFormatter)) - .sorted(Comparator.comparing(MyProofOfBurnListItem::getDate).reversed()) - .collect(Collectors.toList())); - GUIUtil.setFitToRowsForTableView(myItemsTableView, 41, 28, 4, 6); - - allItemsObservableList.setAll(proofOfBurnService.getProofOfBurnTxList().stream() - .map(tx -> new ProofOfBurnListItem(tx, proofOfBurnService, bsqFormatter)) - .collect(Collectors.toList())); - GUIUtil.setFitToRowsForTableView(allTxsTableView, 41, 28, 2, 10); - } - - private void updateButtonState() { - boolean isValid = bsqValidator.validate(amountInputTextField.getText()).isValid && - preImageTextField.validate(); - burnButton.setDisable(!isValid); - } - - private Coin getAmountFee() { - return ParsingUtils.parseToCoin(amountInputTextField.getText(), bsqFormatter); - } - - private void doPublishFeeTx(Transaction transaction, String preImageAsString) { - proofOfBurnService.publishTransaction(transaction, preImageAsString, - () -> { - if (!DevEnv.isDevMode()) - new Popup().confirmation(Res.get("dao.tx.published.success")).show(); - }, - errorMessage -> new Popup().warning(errorMessage).show()); - - amountInputTextField.clear(); - preImageTextField.clear(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Table columns - /////////////////////////////////////////////////////////////////////////////////////////// - - - private void createColumnsForMyItems() { - TableColumn column; - - column = new AutoTooltipTableColumn<>(Res.get("dao.proofOfBurn.amount")); - column.setMinWidth(80); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.getStyleClass().add("first-column"); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final MyProofOfBurnListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getAmountAsString()); - } else - setText(""); - } - }; - } - }); - myItemsTableView.getColumns().add(column); - column.setComparator(Comparator.comparing(MyProofOfBurnListItem::getAmount)); - - column = new AutoTooltipTableColumn<>(Res.get("dao.proofOfBurn.date")); - column.setMinWidth(120); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final MyProofOfBurnListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getDateAsString()); - } else - setText(""); - } - }; - } - }); - myItemsTableView.getColumns().add(column); - column.setComparator(Comparator.comparing(MyProofOfBurnListItem::getDate)); - - column = new AutoTooltipTableColumn<>(Res.get("dao.proofOfBurn.preImage")); - column.setMinWidth(80); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final MyProofOfBurnListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getPreImage()); - } else - setText(""); - } - }; - } - }); - myItemsTableView.getColumns().add(column); - column.setComparator(Comparator.comparing(MyProofOfBurnListItem::getPreImage)); - - column = new AutoTooltipTableColumn<>(Res.get("dao.proofOfBurn.hash")); - column.setMinWidth(80); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final MyProofOfBurnListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getHashAsHex()); - } else - setText(""); - } - }; - } - }); - myItemsTableView.getColumns().add(column); - column.setComparator(Comparator.comparing(MyProofOfBurnListItem::getHashAsHex)); - - column = new AutoTooltipTableColumn<>(Res.get("dao.proofOfBurn.txs")); - column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setMinWidth(80); - column.setCellFactory( - new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - private HyperlinkWithIcon hyperlinkWithIcon; - - @Override - public void updateItem(final MyProofOfBurnListItem item, boolean empty) { - super.updateItem(item, empty); - //noinspection Duplicates - if (item != null && !empty) { - String transactionId = item.getTxId(); - hyperlinkWithIcon = new ExternalHyperlink(transactionId); - hyperlinkWithIcon.setOnAction(event -> GUIUtil.openTxInBsqBlockExplorer(item.getTxId(), preferences)); - hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", transactionId))); - setGraphic(hyperlinkWithIcon); - } else { - setGraphic(null); - if (hyperlinkWithIcon != null) - hyperlinkWithIcon.setOnAction(null); - } - } - }; - } - }); - myItemsTableView.getColumns().add(column); - column.setComparator(Comparator.comparing(MyProofOfBurnListItem::getTxId)); - - column = new AutoTooltipTableColumn<>(Res.get("dao.proofOfBurn.pubKey")); - column.setMinWidth(80); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final MyProofOfBurnListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getPubKey()); - } else - setText(""); - } - }; - } - }); - myItemsTableView.getColumns().add(column); - column.setComparator(Comparator.comparing(MyProofOfBurnListItem::getPubKey)); - - column = new AutoTooltipTableColumn<>(""); - column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setMinWidth(60); - column.getStyleClass().add("last-column"); - column.setCellFactory( - new Callback<>() { - - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - Button button; - - @Override - public void updateItem(final MyProofOfBurnListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - if (button == null) { - button = new AutoTooltipButton(Res.get("dao.proofOfBurn.sign")); - setGraphic(button); - } - button.setOnAction(e -> new ProofOfBurnSignatureWindow(proofOfBurnService, item.getTxId()).show()); - } else { - setGraphic(null); - if (button != null) { - button.setOnAction(null); - button = null; - } - } - } - }; - } - }); - myItemsTableView.getColumns().add(column); - column.setSortable(false); - } - - private void createColumnsForAllTxs() { - TableColumn column; - - column = new AutoTooltipTableColumn<>(Res.get("dao.proofOfBurn.amount")); - column.setMinWidth(80); - column.getStyleClass().add("first-column"); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final ProofOfBurnListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getAmountAsString()); - } else - setText(""); - } - }; - } - }); - allTxsTableView.getColumns().add(column); - column.setComparator(Comparator.comparing(ProofOfBurnListItem::getAmount)); - - column = new AutoTooltipTableColumn<>(Res.get("dao.proofOfBurn.date")); - column.setMinWidth(120); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final ProofOfBurnListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getDateAsString()); - } else - setText(""); - } - }; - } - }); - allTxsTableView.getColumns().add(column); - column.setComparator(Comparator.comparing(ProofOfBurnListItem::getDate)); - - - column = new AutoTooltipTableColumn<>(Res.get("dao.proofOfBurn.hash")); - column.setMinWidth(80); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final ProofOfBurnListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getHashAsHex()); - } else - setText(""); - } - }; - } - }); - allTxsTableView.getColumns().add(column); - column.setComparator(Comparator.comparing(ProofOfBurnListItem::getHashAsHex)); - - column = new AutoTooltipTableColumn<>(Res.get("dao.proofOfBurn.txs")); - column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setMinWidth(80); - column.setCellFactory( - new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - private HyperlinkWithIcon hyperlinkWithIcon; - - @Override - public void updateItem(final ProofOfBurnListItem item, boolean empty) { - super.updateItem(item, empty); - //noinspection Duplicates - if (item != null && !empty) { - String transactionId = item.getTxId(); - hyperlinkWithIcon = new ExternalHyperlink(transactionId); - hyperlinkWithIcon.setOnAction(event -> GUIUtil.openTxInBsqBlockExplorer(item.getTxId(), preferences)); - hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", transactionId))); - setGraphic(hyperlinkWithIcon); - } else { - setGraphic(null); - if (hyperlinkWithIcon != null) - hyperlinkWithIcon.setOnAction(null); - } - } - }; - } - }); - allTxsTableView.getColumns().add(column); - column.setComparator(Comparator.comparing(ProofOfBurnListItem::getTxId)); - - - column = new AutoTooltipTableColumn<>(Res.get("dao.proofOfBurn.pubKey")); - column.setMinWidth(80); - column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback<>() { - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - @Override - public void updateItem(final ProofOfBurnListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - setText(item.getPubKey()); - } else - setText(""); - } - }; - } - }); - allTxsTableView.getColumns().add(column); - column.setComparator(Comparator.comparing(ProofOfBurnListItem::getPubKey)); - - - column = new AutoTooltipTableColumn<>(""); - column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setMinWidth(80); - column.getStyleClass().add("last-column"); - column.setCellFactory( - new Callback<>() { - - @Override - public TableCell call(TableColumn column) { - return new TableCell<>() { - Button button; - - @Override - public void updateItem(final ProofOfBurnListItem item, boolean empty) { - super.updateItem(item, empty); - if (item != null && !empty) { - if (button == null) { - button = new AutoTooltipButton(Res.get("dao.proofOfBurn.verify")); - setGraphic(button); - } - button.setOnAction(e -> new ProofOfBurnVerificationWindow(proofOfBurnService, item.getTxId()).show()); - } else { - setGraphic(null); - if (button != null) { - button.setOnAction(null); - button = null; - } - } - } - }; - } - }); - allTxsTableView.getColumns().add(column); - column.setSortable(false); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/EconomyView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/economy/EconomyView.fxml deleted file mode 100644 index 1f1c5c527b..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/EconomyView.fxml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/EconomyView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/EconomyView.java deleted file mode 100644 index f21e6b62d3..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/EconomyView.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more supply. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.economy; - -import bisq.desktop.Navigation; -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.CachingViewLoader; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.common.view.View; -import bisq.desktop.common.view.ViewLoader; -import bisq.desktop.common.view.ViewPath; -import bisq.desktop.components.MenuItem; -import bisq.desktop.main.MainView; -import bisq.desktop.main.dao.DaoView; -import bisq.desktop.main.dao.economy.dashboard.BsqDashboardView; -import bisq.desktop.main.dao.economy.supply.SupplyView; -import bisq.desktop.main.dao.economy.transactions.BSQTransactionsView; - -import bisq.core.locale.Res; - -import bisq.common.app.DevEnv; - -import javax.inject.Inject; - -import javafx.fxml.FXML; - -import javafx.scene.control.ToggleGroup; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.VBox; - -import java.util.Arrays; -import java.util.List; - -@FxmlView -public class EconomyView extends ActivatableView { - - private final ViewLoader viewLoader; - private final Navigation navigation; - - private MenuItem dashboard, supply, transactions; - private Navigation.Listener listener; - - @FXML - private VBox leftVBox; - @FXML - private AnchorPane content; - - private Class selectedViewClass; - private ToggleGroup toggleGroup; - - @Inject - private EconomyView(CachingViewLoader viewLoader, Navigation navigation) { - this.viewLoader = viewLoader; - this.navigation = navigation; - } - - @Override - public void initialize() { - listener = (viewPath, data) -> { - if (viewPath.size() != 4 || viewPath.indexOf(EconomyView.class) != 2) - return; - - selectedViewClass = viewPath.tip(); - loadView(selectedViewClass); - }; - - toggleGroup = new ToggleGroup(); - List> baseNavPath = Arrays.asList(MainView.class, DaoView.class, EconomyView.class); - dashboard = new MenuItem(navigation, toggleGroup, Res.get("shared.dashboard"), BsqDashboardView.class, baseNavPath); - supply = new MenuItem(navigation, toggleGroup, Res.get("dao.factsAndFigures.menuItem.supply"), SupplyView.class, baseNavPath); - transactions = new MenuItem(navigation, toggleGroup, Res.get("dao.factsAndFigures.menuItem.transactions"), BSQTransactionsView.class, baseNavPath); - - leftVBox.getChildren().addAll(dashboard, supply, transactions); - - if (!DevEnv.isDaoActivated()) { - dashboard.setDisable(true); - supply.setDisable(true); - transactions.setDisable(true); - } - } - - @Override - protected void activate() { - dashboard.activate(); - supply.activate(); - transactions.activate(); - - navigation.addListener(listener); - ViewPath viewPath = navigation.getCurrentPath(); - if (viewPath.size() == 3 && viewPath.indexOf(EconomyView.class) == 2 || - viewPath.size() == 2 && viewPath.indexOf(DaoView.class) == 1) { - if (selectedViewClass == null) - selectedViewClass = BsqDashboardView.class; - - loadView(selectedViewClass); - } else if (viewPath.size() == 4 && viewPath.indexOf(EconomyView.class) == 2) { - selectedViewClass = viewPath.get(3); - loadView(selectedViewClass); - } - } - - @SuppressWarnings("Duplicates") - @Override - protected void deactivate() { - navigation.removeListener(listener); - - dashboard.deactivate(); - supply.deactivate(); - transactions.deactivate(); - } - - private void loadView(Class viewClass) { - View view = viewLoader.load(viewClass); - content.getChildren().setAll(view.getRoot()); - - if (view instanceof BsqDashboardView) toggleGroup.selectToggle(dashboard); - else if (view instanceof SupplyView) toggleGroup.selectToggle(supply); - else if (view instanceof BSQTransactionsView) toggleGroup.selectToggle(transactions); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.fxml deleted file mode 100644 index 3a541a621a..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.fxml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java deleted file mode 100644 index b4d4c9dd89..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java +++ /dev/null @@ -1,366 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.economy.dashboard; - -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.components.TextFieldWithIcon; -import bisq.desktop.components.TitledGroupBg; -import bisq.desktop.main.dao.economy.dashboard.price.PriceChartView; -import bisq.desktop.main.dao.economy.dashboard.volume.VolumeChartView; -import bisq.desktop.util.Layout; - -import bisq.core.dao.DaoFacade; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.governance.IssuanceType; -import bisq.core.locale.GlobalSettings; -import bisq.core.locale.Res; -import bisq.core.monetary.Price; -import bisq.core.provider.price.PriceFeedService; -import bisq.core.trade.statistics.TradeStatisticsManager; -import bisq.core.user.Preferences; -import bisq.core.util.AveragePriceUtil; -import bisq.core.util.FormattingUtils; -import bisq.core.util.coin.BsqFormatter; - -import bisq.common.util.MathUtils; -import bisq.common.util.Tuple2; -import bisq.common.util.Tuple3; - -import org.bitcoinj.core.Coin; - -import javax.inject.Inject; - -import de.jensd.fx.fontawesome.AwesomeIcon; - -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.VBox; - -import javafx.geometry.Insets; - -import javafx.beans.binding.Bindings; -import javafx.beans.value.ChangeListener; - -import javafx.collections.ObservableList; - -import java.text.DecimalFormat; - -import java.util.Optional; - -import static bisq.desktop.util.FormBuilder.addLabelWithSubText; -import static bisq.desktop.util.FormBuilder.addTitledGroupBg; -import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField; -import static bisq.desktop.util.FormBuilder.addTopLabelTextFieldWithIcon; - - -@FxmlView -public class BsqDashboardView extends ActivatableView implements DaoStateListener { - - private final PriceChartView priceChartView; - private final VolumeChartView volumeChartView; - private final DaoFacade daoFacade; - private final TradeStatisticsManager tradeStatisticsManager; - private final PriceFeedService priceFeedService; - private final Preferences preferences; - private final BsqFormatter bsqFormatter; - - private TextField avgPrice90TextField, avgUSDPrice90TextField, marketCapTextField, availableAmountTextField, - usdVolumeTextField, btcVolumeTextField, averageBsqUsdPriceTextField, averageBsqBtcPriceTextField; - private TextFieldWithIcon avgPrice30TextField, avgUSDPrice30TextField; - private Label marketPriceLabel; - - private ChangeListener priceChangeListener; - private int gridRow = 0; - private Coin availableAmount; - private Price avg30DayUSDPrice; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, lifecycle - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public BsqDashboardView(PriceChartView priceChartView, - VolumeChartView volumeChartView, - DaoFacade daoFacade, - TradeStatisticsManager tradeStatisticsManager, - PriceFeedService priceFeedService, - Preferences preferences, - BsqFormatter bsqFormatter) { - this.priceChartView = priceChartView; - this.volumeChartView = volumeChartView; - this.daoFacade = daoFacade; - this.tradeStatisticsManager = tradeStatisticsManager; - this.priceFeedService = priceFeedService; - this.preferences = preferences; - this.bsqFormatter = bsqFormatter; - } - - @Override - public void initialize() { - createTextFields(); - createPriceChart(); - createTradeChart(); - - priceChangeListener = (observable, oldValue, newValue) -> { - updatePrice(); - updateAveragePriceFields(avgPrice90TextField, avgPrice30TextField, false); - updateAveragePriceFields(avgUSDPrice90TextField, avgUSDPrice30TextField, true); - updateMarketCap(); - }; - } - - @Override - protected void activate() { - daoFacade.addBsqStateListener(this); - priceFeedService.updateCounterProperty().addListener(priceChangeListener); - - updateWithBsqBlockChainData(); - updatePrice(); - updateAveragePriceFields(avgPrice90TextField, avgPrice30TextField, false); - updateAveragePriceFields(avgUSDPrice90TextField, avgUSDPrice30TextField, true); - updateMarketCap(); - - averageBsqUsdPriceTextField.textProperty().bind(Bindings.createStringBinding( - () -> { - DecimalFormat priceFormat = (DecimalFormat) DecimalFormat.getNumberInstance(GlobalSettings.getLocale()); - priceFormat.setMaximumFractionDigits(4); - return priceFormat.format(priceChartView.averageBsqUsdPriceProperty().get()) + " BSQ/USD"; - }, - priceChartView.averageBsqUsdPriceProperty())); - averageBsqBtcPriceTextField.textProperty().bind(Bindings.createStringBinding( - () -> { - DecimalFormat priceFormat = (DecimalFormat) DecimalFormat.getNumberInstance(GlobalSettings.getLocale()); - priceFormat.setMaximumFractionDigits(8); - /* yAxisFormatter = value -> { - value = MathUtils.scaleDownByPowerOf10(value.longValue(), 8); - return priceFormat.format(value) + " BSQ/BTC"; - };*/ - - double scaled = MathUtils.scaleDownByPowerOf10(priceChartView.averageBsqBtcPriceProperty().get(), 8); - return priceFormat.format(scaled) + " BSQ/BTC"; - }, - priceChartView.averageBsqBtcPriceProperty())); - - usdVolumeTextField.textProperty().bind(Bindings.createStringBinding( - () -> { - DecimalFormat volumeFormat = (DecimalFormat) DecimalFormat.getNumberInstance(GlobalSettings.getLocale()); - volumeFormat.setMaximumFractionDigits(0); - double scaled = MathUtils.scaleDownByPowerOf10(volumeChartView.usdVolumeProperty().get(), 4); - return volumeFormat.format(scaled) + " USD"; - }, - volumeChartView.usdVolumeProperty())); - btcVolumeTextField.textProperty().bind(Bindings.createStringBinding( - () -> { - DecimalFormat volumeFormat = (DecimalFormat) DecimalFormat.getNumberInstance(GlobalSettings.getLocale()); - volumeFormat.setMaximumFractionDigits(4); - double scaled = MathUtils.scaleDownByPowerOf10(volumeChartView.btcVolumeProperty().get(), 8); - return volumeFormat.format(scaled) + " BTC"; - }, - volumeChartView.btcVolumeProperty())); - } - - - @Override - protected void deactivate() { - daoFacade.removeBsqStateListener(this); - priceFeedService.updateCounterProperty().removeListener(priceChangeListener); - - averageBsqUsdPriceTextField.textProperty().unbind(); - averageBsqBtcPriceTextField.textProperty().unbind(); - usdVolumeTextField.textProperty().unbind(); - btcVolumeTextField.textProperty().unbind(); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - updateWithBsqBlockChainData(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Build UI - /////////////////////////////////////////////////////////////////////////////////////////// - - private void createTextFields() { - Tuple3 marketPriceBox = addLabelWithSubText(root, gridRow++, "", ""); - marketPriceLabel = marketPriceBox.first; - marketPriceLabel.getStyleClass().add("dao-kpi-big"); - - marketPriceBox.second.getStyleClass().add("dao-kpi-subtext"); - - avgUSDPrice90TextField = addTopLabelReadOnlyTextField(root, ++gridRow, - Res.get("dao.factsAndFigures.dashboard.avgUSDPrice90"), -20).second; - - avgUSDPrice30TextField = addTopLabelTextFieldWithIcon(root, gridRow, 1, - Res.get("dao.factsAndFigures.dashboard.avgUSDPrice30"), -35).second; - AnchorPane.setRightAnchor(avgUSDPrice30TextField.getIconLabel(), 10d); - - avgPrice90TextField = addTopLabelReadOnlyTextField(root, ++gridRow, - Res.get("dao.factsAndFigures.dashboard.avgPrice90")).second; - - avgPrice30TextField = addTopLabelTextFieldWithIcon(root, gridRow, 1, - Res.get("dao.factsAndFigures.dashboard.avgPrice30"), -15).second; - AnchorPane.setRightAnchor(avgPrice30TextField.getIconLabel(), 10d); - - marketCapTextField = addTopLabelReadOnlyTextField(root, ++gridRow, - Res.get("dao.factsAndFigures.dashboard.marketCap")).second; - - availableAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, 1, - Res.get("dao.factsAndFigures.dashboard.availableAmount")).second; - } - - private void createPriceChart() { - TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 2, - Res.get("dao.factsAndFigures.supply.priceChat"), Layout.FLOATING_LABEL_DISTANCE); - titledGroupBg.getStyleClass().add("last"); - - priceChartView.initialize(); - VBox chartContainer = priceChartView.getRoot(); - - AnchorPane chartPane = new AnchorPane(); - chartPane.getStyleClass().add("chart-pane"); - AnchorPane.setTopAnchor(chartContainer, 15d); - AnchorPane.setBottomAnchor(chartContainer, 0d); - AnchorPane.setLeftAnchor(chartContainer, 25d); - AnchorPane.setRightAnchor(chartContainer, 10d); - GridPane.setColumnSpan(chartPane, 2); - GridPane.setRowIndex(chartPane, ++gridRow); - GridPane.setMargin(chartPane, new Insets(Layout.COMPACT_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE, 0, 0, 0)); - chartPane.getChildren().add(chartContainer); - - root.getChildren().add(chartPane); - - averageBsqUsdPriceTextField = addTopLabelReadOnlyTextField(root, ++gridRow, - Res.get("dao.factsAndFigures.dashboard.averageBsqUsdPriceFromSelection")).second; - averageBsqBtcPriceTextField = addTopLabelReadOnlyTextField(root, gridRow, 1, - Res.get("dao.factsAndFigures.dashboard.averageBsqBtcPriceFromSelection")).second; - - } - - private void createTradeChart() { - TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 2, - Res.get("dao.factsAndFigures.supply.volumeChat"), Layout.FLOATING_LABEL_DISTANCE); - titledGroupBg.getStyleClass().add("last"); // hides separator as we add a second TitledGroupBg - - volumeChartView.initialize(); - VBox chartContainer = volumeChartView.getRoot(); - - AnchorPane chartPane = new AnchorPane(); - chartPane.getStyleClass().add("chart-pane"); - AnchorPane.setTopAnchor(chartContainer, 15d); - AnchorPane.setBottomAnchor(chartContainer, 0d); - AnchorPane.setLeftAnchor(chartContainer, 25d); - AnchorPane.setRightAnchor(chartContainer, 10d); - GridPane.setColumnSpan(chartPane, 2); - GridPane.setRowIndex(chartPane, ++gridRow); - GridPane.setMargin(chartPane, new Insets(Layout.COMPACT_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE, 0, 0, 0)); - chartPane.getChildren().add(chartContainer); - - root.getChildren().add(chartPane); - - usdVolumeTextField = addTopLabelReadOnlyTextField(root, ++gridRow, - Res.get("dao.factsAndFigures.dashboard.volumeUsd")).second; - btcVolumeTextField = addTopLabelReadOnlyTextField(root, gridRow, 1, - Res.get("dao.factsAndFigures.dashboard.volumeBtc")).second; - } - - private void updateWithBsqBlockChainData() { - Coin issuedAmountFromGenesis = daoFacade.getGenesisTotalSupply(); - Coin issuedAmountFromCompRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.COMPENSATION)); - Coin issuedAmountFromReimbursementRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT)); - Coin totalConfiscatedAmount = Coin.valueOf(daoFacade.getTotalAmountOfConfiscatedTxOutputs()); - Coin totalAmountOfBurntBsq = Coin.valueOf(daoFacade.getTotalAmountOfBurntBsq()); - - availableAmount = issuedAmountFromGenesis - .add(issuedAmountFromCompRequests) - .add(issuedAmountFromReimbursementRequests) - .subtract(totalAmountOfBurntBsq) - .subtract(totalConfiscatedAmount); - - availableAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(availableAmount)); - } - - private void updatePrice() { - Optional optionalBsqPrice = priceFeedService.getBsqPrice(); - if (optionalBsqPrice.isPresent()) { - Price bsqPrice = optionalBsqPrice.get(); - marketPriceLabel.setText(FormattingUtils.formatPrice(bsqPrice) + " BSQ/BTC"); - } else { - marketPriceLabel.setText(Res.get("shared.na")); - } - } - - private void updateMarketCap() { - if (avg30DayUSDPrice != null) { - marketCapTextField.setText(bsqFormatter.formatMarketCap(avg30DayUSDPrice, availableAmount)); - } else { - marketCapTextField.setText(Res.get("shared.na")); - } - } - - private void updateAveragePriceFields(TextField field90, TextFieldWithIcon field30, boolean isUSDField) { - long average90 = updateAveragePriceField(field90, 90, isUSDField); - long average30 = updateAveragePriceField(field30.getTextField(), 30, isUSDField); - boolean trendUp = average30 > average90; - boolean trendDown = average30 < average90; - - Label iconLabel = field30.getIconLabel(); - ObservableList styleClass = iconLabel.getStyleClass(); - if (trendUp) { - field30.setVisible(true); - field30.setIcon(AwesomeIcon.CIRCLE_ARROW_UP); - styleClass.remove("price-trend-down"); - styleClass.add("price-trend-up"); - } else if (trendDown) { - field30.setVisible(true); - field30.setIcon(AwesomeIcon.CIRCLE_ARROW_DOWN); - styleClass.remove("price-trend-up"); - styleClass.add("price-trend-down"); - } else { - iconLabel.setVisible(false); - } - } - - private long updateAveragePriceField(TextField textField, int days, boolean isUSDField) { - Tuple2 tuple = AveragePriceUtil.getAveragePriceTuple(preferences, tradeStatisticsManager, days); - Price usdPrice = tuple.first; - Price bsqPrice = tuple.second; - - if (isUSDField) { - textField.setText(usdPrice + " BSQ/USD"); - if (days == 30) { - avg30DayUSDPrice = usdPrice; - } - } else { - textField.setText(bsqPrice + " BSQ/BTC"); - } - - Price average = isUSDField ? usdPrice : bsqPrice; - return average.getValue(); - } -} - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/price/PriceChartDataModel.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/price/PriceChartDataModel.java deleted file mode 100644 index 49b2a0a7b4..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/price/PriceChartDataModel.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.economy.dashboard.price; - -import bisq.desktop.components.chart.ChartDataModel; - -import bisq.core.trade.statistics.TradeStatistics3; -import bisq.core.trade.statistics.TradeStatisticsManager; - -import bisq.common.util.MathUtils; - -import javax.inject.Inject; - -import java.time.Instant; - -import java.util.AbstractMap; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class PriceChartDataModel extends ChartDataModel { - private final TradeStatisticsManager tradeStatisticsManager; - private Map bsqUsdPriceByInterval, bsqBtcPriceByInterval, btcUsdPriceByInterval; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public PriceChartDataModel(TradeStatisticsManager tradeStatisticsManager) { - super(); - - this.tradeStatisticsManager = tradeStatisticsManager; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Data - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void invalidateCache() { - bsqUsdPriceByInterval = null; - bsqBtcPriceByInterval = null; - btcUsdPriceByInterval = null; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Average price from timeline selection - /////////////////////////////////////////////////////////////////////////////////////////// - - double averageBsqUsdPrice() { - return getAveragePriceFromDateFilter(tradeStatistics -> tradeStatistics.getCurrency().equals("BSQ") || - tradeStatistics.getCurrency().equals("USD"), - PriceChartDataModel::getAverageBsqUsdPrice); - } - - double averageBsqBtcPrice() { - return getAveragePriceFromDateFilter(tradeStatistics -> tradeStatistics.getCurrency().equals("BSQ"), - PriceChartDataModel::getAverageBsqBtcPrice); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Chart data - /////////////////////////////////////////////////////////////////////////////////////////// - - Map getBsqUsdPriceByInterval() { - if (bsqUsdPriceByInterval != null) { - return bsqUsdPriceByInterval; - } - bsqUsdPriceByInterval = getPriceByInterval(tradeStatistics -> tradeStatistics.getCurrency().equals("BSQ") || - tradeStatistics.getCurrency().equals("USD"), - PriceChartDataModel::getAverageBsqUsdPrice); - return bsqUsdPriceByInterval; - } - - Map getBsqBtcPriceByInterval() { - if (bsqBtcPriceByInterval != null) { - return bsqBtcPriceByInterval; - } - - bsqBtcPriceByInterval = getPriceByInterval(tradeStatistics -> tradeStatistics.getCurrency().equals("BSQ"), - PriceChartDataModel::getAverageBsqBtcPrice); - return bsqBtcPriceByInterval; - } - - Map getBtcUsdPriceByInterval() { - if (btcUsdPriceByInterval != null) { - return btcUsdPriceByInterval; - } - - btcUsdPriceByInterval = getPriceByInterval(tradeStatistics -> tradeStatistics.getCurrency().equals("USD"), - PriceChartDataModel::getAverageBtcUsdPrice); - return btcUsdPriceByInterval; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Average price functions - /////////////////////////////////////////////////////////////////////////////////////////// - - private static double getAverageBsqUsdPrice(List list) { - double sumBsq = 0; - double sumBtcFromBsqTrades = 0; - double sumBtcFromUsdTrades = 0; - double sumUsd = 0; - for (TradeStatistics3 tradeStatistics : list) { - if (tradeStatistics.getCurrency().equals("BSQ")) { - sumBsq += getBsqAmount(tradeStatistics); - sumBtcFromBsqTrades += getBtcAmount(tradeStatistics); - } else if (tradeStatistics.getCurrency().equals("USD")) { - sumUsd += getUsdAmount(tradeStatistics); - sumBtcFromUsdTrades += getBtcAmount(tradeStatistics); - } - } - if (sumBsq == 0 || sumBtcFromBsqTrades == 0 || sumBtcFromUsdTrades == 0 || sumUsd == 0) { - return 0d; - } - double averageUsdPrice = sumUsd / sumBtcFromUsdTrades; - return sumBtcFromBsqTrades * averageUsdPrice / sumBsq; - } - - private static double getAverageBsqBtcPrice(List list) { - double sumBsq = 0; - double sumBtc = 0; - for (TradeStatistics3 tradeStatistics : list) { - sumBsq += getBsqAmount(tradeStatistics); - sumBtc += getBtcAmount(tradeStatistics); - } - if (sumBsq == 0 || sumBtc == 0) { - return 0d; - } - return MathUtils.scaleUpByPowerOf10(sumBtc / sumBsq, 8); - } - - private static double getAverageBtcUsdPrice(List list) { - double sumUsd = 0; - double sumBtc = 0; - for (TradeStatistics3 tradeStatistics : list) { - sumUsd += getUsdAmount(tradeStatistics); - sumBtc += getBtcAmount(tradeStatistics); - } - if (sumUsd == 0 || sumBtc == 0) { - return 0d; - } - return sumUsd / sumBtc; - } - - private static long getBtcAmount(TradeStatistics3 tradeStatistics) { - return tradeStatistics.getAmount(); - } - - private static double getUsdAmount(TradeStatistics3 tradeStatistics) { - return MathUtils.scaleUpByPowerOf10(tradeStatistics.getTradeVolume().getValue(), 4); - } - - private static long getBsqAmount(TradeStatistics3 tradeStatistics) { - return tradeStatistics.getTradeVolume().getValue(); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Aggregated collection data by interval - /////////////////////////////////////////////////////////////////////////////////////////// - - private Map getPriceByInterval(Predicate collectionFilter, - Function, Double> getAveragePriceFunction) { - return getPriceByInterval(tradeStatisticsManager.getObservableTradeStatisticsSet(), - collectionFilter, - tradeStatistics -> toTimeInterval(Instant.ofEpochMilli(tradeStatistics.getDateAsLong())), - dateFilter, - getAveragePriceFunction); - } - - private Map getPriceByInterval(Collection collection, - Predicate collectionFilter, - Function groupByDateFunction, - Predicate dateFilter, - Function, Double> getAveragePriceFunction) { - return collection.stream() - .filter(collectionFilter) - .collect(Collectors.groupingBy(groupByDateFunction)) - .entrySet() - .stream() - .filter(entry -> dateFilter.test(entry.getKey())) - .map(entry -> new AbstractMap.SimpleEntry<>( - entry.getKey(), - getAveragePriceFunction.apply(entry.getValue()))) - .filter(e -> e.getValue() > 0d) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - - private double getAveragePriceFromDateFilter(Predicate collectionFilter, - Function, Double> getAveragePriceFunction) { - return getAveragePriceFunction.apply(tradeStatisticsManager.getObservableTradeStatisticsSet().stream() - .filter(collectionFilter) - .filter(tradeStatistics -> dateFilter.test(tradeStatistics.getDateAsLong() / 1000)) - .collect(Collectors.toList())); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/price/PriceChartView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/price/PriceChartView.java deleted file mode 100644 index 0245b028a0..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/price/PriceChartView.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.economy.dashboard.price; - -import bisq.desktop.components.chart.ChartView; - -import bisq.core.locale.Res; - -import javax.inject.Inject; - -import javafx.scene.chart.XYChart; - -import javafx.beans.property.DoubleProperty; -import javafx.beans.property.ReadOnlyDoubleProperty; -import javafx.beans.property.SimpleDoubleProperty; - -import java.util.Collection; -import java.util.List; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class PriceChartView extends ChartView { - private XYChart.Series seriesBsqUsdPrice, seriesBsqBtcPrice, seriesBtcUsdPrice; - private DoubleProperty averageBsqUsdPriceProperty = new SimpleDoubleProperty(); - private DoubleProperty averageBsqBtcPriceProperty = new SimpleDoubleProperty(); - - @Inject - public PriceChartView(PriceChartViewModel model) { - super(model); - - setRadioButtonBehaviour(true); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public ReadOnlyDoubleProperty averageBsqUsdPriceProperty() { - return averageBsqUsdPriceProperty; - } - - public ReadOnlyDoubleProperty averageBsqBtcPriceProperty() { - return averageBsqBtcPriceProperty; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Chart - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void onSetYAxisFormatter(XYChart.Series series) { - if (series == seriesBsqUsdPrice) { - model.setBsqUsdPriceFormatter(); - } else if (series == seriesBsqBtcPrice) { - model.setBsqBtcPriceFormatter(); - } else { - model.setBtcUsdPriceFormatter(); - } - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Legend - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected Collection> getSeriesForLegend1() { - return List.of(seriesBsqUsdPrice, seriesBsqBtcPrice, seriesBtcUsdPrice); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Timeline navigation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void initBoundsForTimelineNavigation() { - setBoundsForTimelineNavigation(seriesBsqUsdPrice.getData()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Series - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void createSeries() { - seriesBsqUsdPrice = new XYChart.Series<>(); - seriesBsqUsdPrice.setName(Res.get("dao.factsAndFigures.supply.bsqUsdPrice")); - seriesIndexMap.put(getSeriesId(seriesBsqUsdPrice), 0); - - seriesBsqBtcPrice = new XYChart.Series<>(); - seriesBsqBtcPrice.setName(Res.get("dao.factsAndFigures.supply.bsqBtcPrice")); - seriesIndexMap.put(getSeriesId(seriesBsqBtcPrice), 1); - - seriesBtcUsdPrice = new XYChart.Series<>(); - seriesBtcUsdPrice.setName(Res.get("dao.factsAndFigures.supply.btcUsdPrice")); - seriesIndexMap.put(getSeriesId(seriesBtcUsdPrice), 2); - } - - @Override - protected void defineAndAddActiveSeries() { - activateSeries(seriesBsqUsdPrice); - onSetYAxisFormatter(seriesBsqUsdPrice); - } - - @Override - protected void activateSeries(XYChart.Series series) { - super.activateSeries(series); - - String seriesId = getSeriesId(series); - if (seriesId.equals(getSeriesId(seriesBsqUsdPrice))) { - seriesBsqUsdPrice.getData().setAll(model.getBsqUsdPriceChartData()); - } else if (seriesId.equals(getSeriesId(seriesBsqBtcPrice))) { - seriesBsqBtcPrice.getData().setAll(model.getBsqBtcPriceChartData()); - } else if (seriesId.equals(getSeriesId(seriesBtcUsdPrice))) { - seriesBtcUsdPrice.getData().setAll(model.getBtcUsdPriceChartData()); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Data - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void applyData() { - if (activeSeries.contains(seriesBsqUsdPrice)) { - seriesBsqUsdPrice.getData().setAll(model.getBsqUsdPriceChartData()); - } - if (activeSeries.contains(seriesBsqBtcPrice)) { - seriesBsqBtcPrice.getData().setAll(model.getBsqBtcPriceChartData()); - } - if (activeSeries.contains(seriesBtcUsdPrice)) { - seriesBtcUsdPrice.getData().setAll(model.getBtcUsdPriceChartData()); - } - - averageBsqBtcPriceProperty.set(model.averageBsqBtcPrice()); - averageBsqUsdPriceProperty.set(model.averageBsqUsdPrice()); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/price/PriceChartViewModel.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/price/PriceChartViewModel.java deleted file mode 100644 index 6074a279d3..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/price/PriceChartViewModel.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.economy.dashboard.price; - -import bisq.desktop.components.chart.ChartViewModel; - -import bisq.core.locale.GlobalSettings; - -import bisq.common.util.MathUtils; - -import javax.inject.Inject; - -import javafx.scene.chart.XYChart; - -import javafx.util.StringConverter; - -import java.text.DecimalFormat; - -import java.util.List; -import java.util.function.Function; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class PriceChartViewModel extends ChartViewModel { - private Function yAxisFormatter = value -> value + " BSQ/USD"; - private final DecimalFormat priceFormat; - - @Inject - public PriceChartViewModel(PriceChartDataModel dataModel) { - super(dataModel); - - priceFormat = (DecimalFormat) DecimalFormat.getNumberInstance(GlobalSettings.getLocale()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Average price from timeline selection - /////////////////////////////////////////////////////////////////////////////////////////// - - double averageBsqUsdPrice() { - return dataModel.averageBsqUsdPrice(); - } - - double averageBsqBtcPrice() { - return dataModel.averageBsqBtcPrice(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Chart data - /////////////////////////////////////////////////////////////////////////////////////////// - - List> getBsqUsdPriceChartData() { - return toChartDoubleData(dataModel.getBsqUsdPriceByInterval()); - } - - List> getBsqBtcPriceChartData() { - return toChartDoubleData(dataModel.getBsqBtcPriceByInterval()); - } - - List> getBtcUsdPriceChartData() { - return toChartDoubleData(dataModel.getBtcUsdPriceByInterval()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Formatters/Converters - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected StringConverter getYAxisStringConverter() { - return new StringConverter<>() { - @Override - public String toString(Number value) { - return yAxisFormatter.apply(value); - } - - @Override - public Number fromString(String string) { - return null; - } - }; - } - - void setBsqUsdPriceFormatter() { - priceFormat.setMaximumFractionDigits(4); - yAxisFormatter = value -> priceFormat.format(value) + " BSQ/USD"; - } - - void setBsqBtcPriceFormatter() { - priceFormat.setMaximumFractionDigits(8); - yAxisFormatter = value -> { - value = MathUtils.scaleDownByPowerOf10(value.longValue(), 8); - return priceFormat.format(value) + " BSQ/BTC"; - }; - } - - void setBtcUsdPriceFormatter() { - priceFormat.setMaximumFractionDigits(0); - yAxisFormatter = value -> priceFormat.format(value) + " BTC/USD"; - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/volume/VolumeChartDataModel.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/volume/VolumeChartDataModel.java deleted file mode 100644 index e147ce6977..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/volume/VolumeChartDataModel.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.economy.dashboard.volume; - -import bisq.desktop.components.chart.ChartDataModel; - -import bisq.core.trade.statistics.TradeStatistics3; -import bisq.core.trade.statistics.TradeStatisticsManager; - -import javax.inject.Inject; - -import java.time.Instant; - -import java.util.AbstractMap; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class VolumeChartDataModel extends ChartDataModel { - private final TradeStatisticsManager tradeStatisticsManager; - private Map usdVolumeByInterval, btcVolumeByInterval; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public VolumeChartDataModel(TradeStatisticsManager tradeStatisticsManager) { - super(); - - this.tradeStatisticsManager = tradeStatisticsManager; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Total amounts - /////////////////////////////////////////////////////////////////////////////////////////// - - long getUsdVolume() { - return getUsdVolumeByInterval().values().stream() - .mapToLong(e -> e) - .sum(); - } - - long getBtcVolume() { - return getBtcVolumeByInterval().values().stream() - .mapToLong(e -> e) - .sum(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Data - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void invalidateCache() { - usdVolumeByInterval = null; - btcVolumeByInterval = null; - } - - public Map getUsdVolumeByInterval() { - if (usdVolumeByInterval != null) { - return usdVolumeByInterval; - } - - usdVolumeByInterval = getVolumeByInterval(VolumeChartDataModel::getVolumeInUsd); - return usdVolumeByInterval; - } - - public Map getBtcVolumeByInterval() { - if (btcVolumeByInterval != null) { - return btcVolumeByInterval; - } - - btcVolumeByInterval = getVolumeByInterval(VolumeChartDataModel::getVolumeInBtc); - return btcVolumeByInterval; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Get volume functions - /////////////////////////////////////////////////////////////////////////////////////////// - - private static long getVolumeInUsd(List list) { - double sumBtcFromAllTrades = 0; - double sumBtcFromUsdTrades = 0; - double sumUsd = 0; - for (TradeStatistics3 tradeStatistics : list) { - long amount = tradeStatistics.getAmount(); - if (tradeStatistics.getCurrency().equals("USD")) { - sumUsd += tradeStatistics.getTradeVolume().getValue(); - sumBtcFromUsdTrades += amount; - } - sumBtcFromAllTrades += amount; - } - if (sumBtcFromAllTrades == 0 || sumBtcFromUsdTrades == 0 || sumUsd == 0) { - return 0L; - } - double averageUsdPrice = sumUsd / sumBtcFromUsdTrades; - // We truncate to 4 decimals - return (long) (sumBtcFromAllTrades * averageUsdPrice); - } - - private static long getVolumeInBtc(List list) { - return list.stream().mapToLong(TradeStatistics3::getAmount).sum(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Aggregated collection data by interval - /////////////////////////////////////////////////////////////////////////////////////////// - - private Map getVolumeByInterval(Function, Long> getVolumeFunction) { - return getVolumeByInterval(tradeStatisticsManager.getObservableTradeStatisticsSet(), - tradeStatistics -> toTimeInterval(Instant.ofEpochMilli(tradeStatistics.getDateAsLong())), - dateFilter, - getVolumeFunction); - } - - private Map getVolumeByInterval(Collection collection, - Function groupByDateFunction, - Predicate dateFilter, - Function, Long> getVolumeFunction) { - return collection.stream() - .collect(Collectors.groupingBy(groupByDateFunction)) - .entrySet() - .stream() - .filter(entry -> dateFilter.test(entry.getKey())) - .map(entry -> new AbstractMap.SimpleEntry<>( - entry.getKey(), - getVolumeFunction.apply(entry.getValue()))) - .filter(e -> e.getValue() > 0L) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/volume/VolumeChartView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/volume/VolumeChartView.java deleted file mode 100644 index 2dff0a73ea..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/volume/VolumeChartView.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.economy.dashboard.volume; - -import bisq.desktop.components.chart.ChartView; - -import bisq.core.locale.Res; - -import javax.inject.Inject; - -import javafx.scene.chart.XYChart; - -import javafx.beans.property.LongProperty; -import javafx.beans.property.ReadOnlyLongProperty; -import javafx.beans.property.SimpleLongProperty; - -import java.util.Collection; -import java.util.List; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class VolumeChartView extends ChartView { - private XYChart.Series seriesUsdVolume, seriesBtcVolume; - - private LongProperty usdVolumeProperty = new SimpleLongProperty(); - private LongProperty btcVolumeProperty = new SimpleLongProperty(); - - @Inject - public VolumeChartView(VolumeChartViewModel model) { - super(model); - - setRadioButtonBehaviour(true); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public ReadOnlyLongProperty usdVolumeProperty() { - return usdVolumeProperty; - } - - public ReadOnlyLongProperty btcVolumeProperty() { - return btcVolumeProperty; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Chart - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void onSetYAxisFormatter(XYChart.Series series) { - if (series == seriesUsdVolume) { - model.setUsdVolumeFormatter(); - } else { - model.setBtcVolumeFormatter(); - } - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Legend - /////////////////////////////////////////////////////////////////////////////////////////// - - - @Override - protected Collection> getSeriesForLegend1() { - return List.of(seriesUsdVolume, seriesBtcVolume); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Timeline navigation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void initBoundsForTimelineNavigation() { - setBoundsForTimelineNavigation(seriesUsdVolume.getData()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Series - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void createSeries() { - seriesUsdVolume = new XYChart.Series<>(); - seriesUsdVolume.setName(Res.get("dao.factsAndFigures.supply.tradeVolumeInUsd")); - seriesIndexMap.put(getSeriesId(seriesUsdVolume), 0); - - seriesBtcVolume = new XYChart.Series<>(); - seriesBtcVolume.setName(Res.get("dao.factsAndFigures.supply.tradeVolumeInBtc")); - seriesIndexMap.put(getSeriesId(seriesBtcVolume), 1); - } - - @Override - protected void defineAndAddActiveSeries() { - activateSeries(seriesUsdVolume); - onSetYAxisFormatter(seriesUsdVolume); - } - - @Override - protected void activateSeries(XYChart.Series series) { - super.activateSeries(series); - - if (getSeriesId(series).equals(getSeriesId(seriesUsdVolume))) { - seriesUsdVolume.getData().setAll(model.getUsdVolumeChartData()); - } else if (getSeriesId(series).equals(getSeriesId(seriesBtcVolume))) { - seriesBtcVolume.getData().setAll(model.getBtcVolumeChartData()); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Data - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void applyData() { - if (activeSeries.contains(seriesUsdVolume)) { - seriesUsdVolume.getData().setAll(model.getUsdVolumeChartData()); - } - if (activeSeries.contains(seriesBtcVolume)) { - seriesBtcVolume.getData().setAll(model.getBtcVolumeChartData()); - } - - usdVolumeProperty.set(model.getUsdVolume()); - btcVolumeProperty.set(model.getBtcVolume()); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/volume/VolumeChartViewModel.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/volume/VolumeChartViewModel.java deleted file mode 100644 index 142d2f3912..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/volume/VolumeChartViewModel.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.economy.dashboard.volume; - -import bisq.desktop.components.chart.ChartViewModel; - -import bisq.core.locale.GlobalSettings; - -import bisq.common.util.MathUtils; - -import javax.inject.Inject; - -import javafx.scene.chart.XYChart; - -import javafx.util.StringConverter; - -import java.text.DecimalFormat; - -import java.util.List; -import java.util.function.Function; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class VolumeChartViewModel extends ChartViewModel { - private Function yAxisFormatter = value -> value + " USD"; - private final DecimalFormat volumeFormat; - - - @Inject - public VolumeChartViewModel(VolumeChartDataModel dataModel) { - super(dataModel); - - volumeFormat = (DecimalFormat) DecimalFormat.getNumberInstance(GlobalSettings.getLocale()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Total amounts - /////////////////////////////////////////////////////////////////////////////////////////// - - long getUsdVolume() { - return dataModel.getUsdVolume(); - } - - long getBtcVolume() { - return dataModel.getBtcVolume(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Chart data - /////////////////////////////////////////////////////////////////////////////////////////// - - List> getUsdVolumeChartData() { - return toChartLongData(dataModel.getUsdVolumeByInterval()); - } - - List> getBtcVolumeChartData() { - return toChartLongData(dataModel.getBtcVolumeByInterval()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Formatters/Converters - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected StringConverter getYAxisStringConverter() { - return new StringConverter<>() { - @Override - public String toString(Number value) { - return yAxisFormatter.apply(value); - } - - @Override - public Number fromString(String string) { - return null; - } - }; - } - - void setUsdVolumeFormatter() { - volumeFormat.setMaximumFractionDigits(0); - yAxisFormatter = value -> volumeFormat.format(MathUtils.scaleDownByPowerOf10(value.longValue(), 4)) + " USD"; - } - - void setBtcVolumeFormatter() { - volumeFormat.setMaximumFractionDigits(4); - yAxisFormatter = value -> volumeFormat.format(MathUtils.scaleDownByPowerOf10(value.longValue(), 8)) + " BTC"; - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/SupplyView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/SupplyView.fxml deleted file mode 100644 index 561c3c2121..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/SupplyView.fxml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/SupplyView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/SupplyView.java deleted file mode 100644 index 7c8816942b..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/SupplyView.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.economy.supply; - -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.components.TitledGroupBg; -import bisq.desktop.main.dao.economy.supply.dao.DaoChartView; -import bisq.desktop.util.Layout; - -import bisq.core.dao.DaoFacade; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.locale.Res; -import bisq.core.util.coin.BsqFormatter; - -import bisq.common.util.Tuple3; - -import org.bitcoinj.core.Coin; - -import javax.inject.Inject; - -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.VBox; - -import javafx.geometry.Insets; - -import javafx.beans.binding.Bindings; -import javafx.beans.property.ReadOnlyLongProperty; - -import static bisq.desktop.util.FormBuilder.addTitledGroupBg; -import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField; - -@FxmlView -public class SupplyView extends ActivatableView implements DaoStateListener { - private final DaoFacade daoFacade; - private final DaoChartView daoChartView; - private final BsqFormatter bsqFormatter; - - private TextField genesisIssueAmountTextField, compensationAmountTextField, reimbursementAmountTextField, - bsqTradeFeeAmountTextField, totalLockedUpAmountTextField, totalUnlockingAmountTextField, - totalUnlockedAmountTextField, totalConfiscatedAmountTextField, proofOfBurnAmountTextField; - private int gridRow = 0; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, lifecycle - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - private SupplyView(DaoFacade daoFacade, - DaoChartView daoChartView, - BsqFormatter bsqFormatter) { - this.daoFacade = daoFacade; - this.daoChartView = daoChartView; - this.bsqFormatter = bsqFormatter; - } - - @Override - public void initialize() { - createDaoChart(); - createIssuedAndBurnedFields(); - createLockedBsqFields(); - } - - @Override - protected void activate() { - daoFacade.addBsqStateListener(this); - - compensationAmountTextField.textProperty().bind(Bindings.createStringBinding( - () -> getFormattedValue(daoChartView.compensationAmountProperty()), - daoChartView.compensationAmountProperty())); - reimbursementAmountTextField.textProperty().bind(Bindings.createStringBinding( - () -> getFormattedValue(daoChartView.reimbursementAmountProperty()), - daoChartView.reimbursementAmountProperty())); - bsqTradeFeeAmountTextField.textProperty().bind(Bindings.createStringBinding( - () -> getFormattedValue(daoChartView.bsqTradeFeeAmountProperty()), - daoChartView.bsqTradeFeeAmountProperty())); - proofOfBurnAmountTextField.textProperty().bind(Bindings.createStringBinding( - () -> getFormattedValue(daoChartView.proofOfBurnAmountProperty()), - daoChartView.proofOfBurnAmountProperty())); - - Coin issuedAmountFromGenesis = daoFacade.getGenesisTotalSupply(); - genesisIssueAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(issuedAmountFromGenesis)); - updateWithBsqBlockChainData(); - } - - @Override - protected void deactivate() { - daoFacade.removeBsqStateListener(this); - - compensationAmountTextField.textProperty().unbind(); - reimbursementAmountTextField.textProperty().unbind(); - bsqTradeFeeAmountTextField.textProperty().unbind(); - proofOfBurnAmountTextField.textProperty().unbind(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - updateWithBsqBlockChainData(); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Build UI - /////////////////////////////////////////////////////////////////////////////////////////// - - private void createDaoChart() { - TitledGroupBg titledGroupBg = addTitledGroupBg(root, gridRow, 2, Res.get("dao.factsAndFigures.supply.issuedVsBurnt")); - titledGroupBg.getStyleClass().add("last"); // hides separator as we add a second TitledGroupBg - - daoChartView.initialize(); - VBox chartContainer = daoChartView.getRoot(); - - AnchorPane chartPane = new AnchorPane(); - chartPane.getStyleClass().add("chart-pane"); - AnchorPane.setTopAnchor(chartContainer, 15d); - AnchorPane.setBottomAnchor(chartContainer, 0d); - AnchorPane.setLeftAnchor(chartContainer, 25d); - AnchorPane.setRightAnchor(chartContainer, 10d); - GridPane.setColumnSpan(chartPane, 2); - GridPane.setRowIndex(chartPane, ++gridRow); - GridPane.setMargin(chartPane, new Insets(Layout.FIRST_ROW_DISTANCE, 0, 0, 0)); - chartPane.getChildren().add(chartContainer); - - root.getChildren().add(chartPane); - } - - private void createIssuedAndBurnedFields() { - TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 3, Res.get("dao.factsAndFigures.supply.issued"), Layout.FLOATING_LABEL_DISTANCE); - titledGroupBg.getStyleClass().add("last"); // hides separator as we add a second TitledGroupBg - - Tuple3 genesisAmountTuple = addTopLabelReadOnlyTextField(root, gridRow, - Res.get("dao.factsAndFigures.supply.genesisIssueAmount"), Layout.COMPACT_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE); - genesisIssueAmountTextField = genesisAmountTuple.second; - GridPane.setColumnSpan(genesisAmountTuple.third, 2); - - compensationAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, - Res.get("dao.factsAndFigures.supply.compRequestIssueAmount")).second; - reimbursementAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, 1, - Res.get("dao.factsAndFigures.supply.reimbursementAmount")).second; - - addTitledGroupBg(root, ++gridRow, 1, Res.get("dao.factsAndFigures.supply.burnt"), Layout.GROUP_DISTANCE_WITHOUT_SEPARATOR); - - bsqTradeFeeAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, - Res.get("dao.factsAndFigures.supply.bsqTradeFee"), Layout.COMPACT_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE).second; - - proofOfBurnAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, 1, - Res.get("dao.factsAndFigures.supply.proofOfBurn"), Layout.COMPACT_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE).second; - } - - private void createLockedBsqFields() { - TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 2, Res.get("dao.factsAndFigures.supply.locked"), Layout.GROUP_DISTANCE); - titledGroupBg.getStyleClass().add("last"); - - totalLockedUpAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, - Res.get("dao.factsAndFigures.supply.totalLockedUpAmount"), - Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - totalUnlockingAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, 1, - Res.get("dao.factsAndFigures.supply.totalUnlockingAmount"), - Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - - totalUnlockedAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow, - Res.get("dao.factsAndFigures.supply.totalUnlockedAmount")).second; - totalConfiscatedAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, 1, - Res.get("dao.factsAndFigures.supply.totalConfiscatedAmount")).second; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Data - /////////////////////////////////////////////////////////////////////////////////////////// - - private void updateWithBsqBlockChainData() { - Coin totalLockedUpAmount = Coin.valueOf(daoFacade.getTotalLockupAmount()); - totalLockedUpAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalLockedUpAmount)); - - Coin totalUnlockingAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockingTxOutputs()); - totalUnlockingAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockingAmount)); - - Coin totalUnlockedAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockedTxOutputs()); - totalUnlockedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockedAmount)); - - Coin totalConfiscatedAmount = Coin.valueOf(daoFacade.getTotalAmountOfConfiscatedTxOutputs()); - totalConfiscatedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalConfiscatedAmount)); - } - - private String getFormattedValue(ReadOnlyLongProperty property) { - return bsqFormatter.formatAmountWithGroupSeparatorAndCode(Coin.valueOf(property.get())); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/dao/DaoChartDataModel.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/dao/DaoChartDataModel.java deleted file mode 100644 index 52b90f72ba..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/dao/DaoChartDataModel.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.economy.supply.dao; - -import bisq.desktop.components.chart.ChartDataModel; - -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.governance.Issuance; -import bisq.core.dao.state.model.governance.IssuanceType; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import java.time.Instant; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@Singleton -public class DaoChartDataModel extends ChartDataModel { - private final DaoStateService daoStateService; - private final Function blockTimeOfIssuanceFunction; - private Map totalIssuedByInterval, compensationByInterval, reimbursementByInterval, - totalBurnedByInterval, bsqTradeFeeByInterval, proofOfBurnByInterval; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public DaoChartDataModel(DaoStateService daoStateService) { - super(); - - this.daoStateService = daoStateService; - - // TODO getBlockTime is the bottleneck. Add a lookup map to daoState to fix that in a dedicated PR. - blockTimeOfIssuanceFunction = memoize(issuance -> { - int height = daoStateService.getStartHeightOfCurrentCycle(issuance.getChainHeight()).orElse(0); - return daoStateService.getBlockTime(height); - }); - } - - @Override - protected void invalidateCache() { - totalIssuedByInterval = null; - compensationByInterval = null; - reimbursementByInterval = null; - totalBurnedByInterval = null; - bsqTradeFeeByInterval = null; - proofOfBurnByInterval = null; - - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Total amounts - /////////////////////////////////////////////////////////////////////////////////////////// - - long getCompensationAmount() { - return getCompensationByInterval().values().stream() - .mapToLong(e -> e) - .sum(); - } - - long getReimbursementAmount() { - return getReimbursementByInterval().values().stream() - .mapToLong(e -> e) - .sum(); - } - - long getBsqTradeFeeAmount() { - return getBsqTradeFeeByInterval().values().stream() - .mapToLong(e -> e) - .sum(); - } - - long getProofOfBurnAmount() { - return getProofOfBurnByInterval().values().stream() - .mapToLong(e -> e) - .sum(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Data for chart - /////////////////////////////////////////////////////////////////////////////////////////// - - Map getTotalIssuedByInterval() { - if (totalIssuedByInterval != null) { - return totalIssuedByInterval; - } - - Map compensationMap = getCompensationByInterval(); - Map reimbursementMap = getReimbursementByInterval(); - totalIssuedByInterval = getMergedMap(compensationMap, reimbursementMap, Long::sum); - return totalIssuedByInterval; - } - - Map getCompensationByInterval() { - if (compensationByInterval != null) { - return compensationByInterval; - } - - Set issuanceSetForType = daoStateService.getIssuanceSetForType(IssuanceType.COMPENSATION); - Map issuedBsqByInterval = getIssuedBsqByInterval(issuanceSetForType, getDateFilter()); - Map historicalIssuanceByInterval = getHistoricalIssuedBsqByInterval(DaoEconomyHistoricalData.COMPENSATIONS_BY_CYCLE_DATE, getDateFilter()); - compensationByInterval = getMergedMap(issuedBsqByInterval, historicalIssuanceByInterval, (daoDataValue, staticDataValue) -> staticDataValue); - return compensationByInterval; - } - - Map getReimbursementByInterval() { - if (reimbursementByInterval != null) { - return reimbursementByInterval; - } - - Map issuedBsqByInterval = getIssuedBsqByInterval(daoStateService.getIssuanceSetForType(IssuanceType.REIMBURSEMENT), getDateFilter()); - Map historicalIssuanceByInterval = getHistoricalIssuedBsqByInterval(DaoEconomyHistoricalData.REIMBURSEMENTS_BY_CYCLE_DATE, getDateFilter()); - reimbursementByInterval = getMergedMap(issuedBsqByInterval, historicalIssuanceByInterval, (daoDataValue, staticDataValue) -> staticDataValue); - return reimbursementByInterval; - } - - Map getTotalBurnedByInterval() { - if (totalBurnedByInterval != null) { - return totalBurnedByInterval; - } - - Map tradeFee = getBsqTradeFeeByInterval(); - Map proofOfBurn = getProofOfBurnByInterval(); - totalBurnedByInterval = getMergedMap(tradeFee, proofOfBurn, Long::sum); - return totalBurnedByInterval; - } - - Map getBsqTradeFeeByInterval() { - if (bsqTradeFeeByInterval != null) { - return bsqTradeFeeByInterval; - } - - bsqTradeFeeByInterval = getBurntBsqByInterval(daoStateService.getTradeFeeTxs(), getDateFilter()); - return bsqTradeFeeByInterval; - } - - Map getProofOfBurnByInterval() { - if (proofOfBurnByInterval != null) { - return proofOfBurnByInterval; - } - - proofOfBurnByInterval = getBurntBsqByInterval(daoStateService.getProofOfBurnTxs(), getDateFilter()); - return proofOfBurnByInterval; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Aggregated collection data by interval - /////////////////////////////////////////////////////////////////////////////////////////// - - private Map getIssuedBsqByInterval(Set issuanceSet, Predicate dateFilter) { - return issuanceSet.stream() - .collect(Collectors.groupingBy(issuance -> - toTimeInterval(Instant.ofEpochMilli(blockTimeOfIssuanceFunction.apply(issuance))))) - .entrySet() - .stream() - .filter(entry -> dateFilter.test(entry.getKey())) - .collect(Collectors.toMap(Map.Entry::getKey, - entry -> entry.getValue().stream() - .mapToLong(Issuance::getAmount) - .sum())); - } - - private Map getHistoricalIssuedBsqByInterval(Map historicalData, - Predicate dateFilter) { - - return historicalData.entrySet().stream() - .filter(e -> dateFilter.test(e.getKey())) - .collect(Collectors.toMap(e -> toTimeInterval(Instant.ofEpochSecond(e.getKey())), - Map.Entry::getValue, - (a, b) -> a + b)); - } - - private Map getBurntBsqByInterval(Collection txs, Predicate dateFilter) { - return txs.stream() - .collect(Collectors.groupingBy(tx -> toTimeInterval(Instant.ofEpochMilli(tx.getTime())))) - .entrySet() - .stream() - .filter(entry -> dateFilter.test(entry.getKey())) - .collect(Collectors.toMap(Map.Entry::getKey, - entry -> entry.getValue().stream() - .mapToLong(Tx::getBurntBsq) - .sum())); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Utils - /////////////////////////////////////////////////////////////////////////////////////////// - - private static Function memoize(Function fn) { - Map map = new ConcurrentHashMap<>(); - return x -> map.computeIfAbsent(x, fn); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Historical data - /////////////////////////////////////////////////////////////////////////////////////////// - - // We did not use the reimbursement requests initially (but the compensation requests) because the limits - // have been too low. Over time it got mixed in compensation requests and reimbursement requests. - // To reflect that we use static data derived from the Github data. For new data we do not need that anymore - // as we have clearly separated that now. In case we have duplicate data for a months we use the static data. - private static class DaoEconomyHistoricalData { - // Key is start date of the cycle in epoch seconds, value is reimbursement amount - public final static Map REIMBURSEMENTS_BY_CYCLE_DATE = new HashMap<>(); - public final static Map COMPENSATIONS_BY_CYCLE_DATE = new HashMap<>(); - - static { - REIMBURSEMENTS_BY_CYCLE_DATE.put(1571349571L, 60760L); - REIMBURSEMENTS_BY_CYCLE_DATE.put(1574180991L, 2621000L); - REIMBURSEMENTS_BY_CYCLE_DATE.put(1576966522L, 4769100L); - REIMBURSEMENTS_BY_CYCLE_DATE.put(1579613568L, 0L); - REIMBURSEMENTS_BY_CYCLE_DATE.put(1582399054L, 9186600L); - REIMBURSEMENTS_BY_CYCLE_DATE.put(1585342220L, 12089400L); - REIMBURSEMENTS_BY_CYCLE_DATE.put(1588025030L, 5420700L); - REIMBURSEMENTS_BY_CYCLE_DATE.put(1591004931L, 9138760L); - REIMBURSEMENTS_BY_CYCLE_DATE.put(1593654027L, 10821807L); - REIMBURSEMENTS_BY_CYCLE_DATE.put(1596407074L, 2160157L); - REIMBURSEMENTS_BY_CYCLE_DATE.put(1599175867L, 8769408L); - REIMBURSEMENTS_BY_CYCLE_DATE.put(1601861442L, 4956585L); - REIMBURSEMENTS_BY_CYCLE_DATE.put(1604845863L, 2121664L); - - COMPENSATIONS_BY_CYCLE_DATE.put(1555340856L, 6931863L); - COMPENSATIONS_BY_CYCLE_DATE.put(1558083590L, 2287000L); - COMPENSATIONS_BY_CYCLE_DATE.put(1560771266L, 2273000L); - COMPENSATIONS_BY_CYCLE_DATE.put(1563347672L, 2943772L); - COMPENSATIONS_BY_CYCLE_DATE.put(1566009595L, 10040170L); - COMPENSATIONS_BY_CYCLE_DATE.put(1568643566L, 8685115L); - COMPENSATIONS_BY_CYCLE_DATE.put(1571349571L, 7315879L); - COMPENSATIONS_BY_CYCLE_DATE.put(1574180991L, 12508300L); - COMPENSATIONS_BY_CYCLE_DATE.put(1576966522L, 5884500L); - COMPENSATIONS_BY_CYCLE_DATE.put(1579613568L, 8206000L); - COMPENSATIONS_BY_CYCLE_DATE.put(1582399054L, 3518364L); - COMPENSATIONS_BY_CYCLE_DATE.put(1585342220L, 6231700L); - COMPENSATIONS_BY_CYCLE_DATE.put(1588025030L, 4391400L); - COMPENSATIONS_BY_CYCLE_DATE.put(1591004931L, 3636463L); - COMPENSATIONS_BY_CYCLE_DATE.put(1593654027L, 6156631L); - COMPENSATIONS_BY_CYCLE_DATE.put(1596407074L, 5838368L); - COMPENSATIONS_BY_CYCLE_DATE.put(1599175867L, 6086442L); - COMPENSATIONS_BY_CYCLE_DATE.put(1601861442L, 5615973L); - COMPENSATIONS_BY_CYCLE_DATE.put(1604845863L, 7782667L); - } - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/dao/DaoChartView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/dao/DaoChartView.java deleted file mode 100644 index 8cf2e7cc88..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/dao/DaoChartView.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.economy.supply.dao; - -import bisq.desktop.components.chart.ChartView; - -import bisq.core.locale.Res; - -import javax.inject.Inject; - -import javafx.scene.chart.XYChart; - -import javafx.beans.property.LongProperty; -import javafx.beans.property.ReadOnlyLongProperty; -import javafx.beans.property.SimpleLongProperty; - -import java.util.Collection; -import java.util.List; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class DaoChartView extends ChartView { - private LongProperty compensationAmountProperty = new SimpleLongProperty(); - private LongProperty reimbursementAmountProperty = new SimpleLongProperty(); - private LongProperty bsqTradeFeeAmountProperty = new SimpleLongProperty(); - private LongProperty proofOfBurnAmountProperty = new SimpleLongProperty(); - - private XYChart.Series seriesBsqTradeFee, seriesProofOfBurn, seriesCompensation, - seriesReimbursement, seriesTotalIssued, seriesTotalBurned; - - - @Inject - public DaoChartView(DaoChartViewModel model) { - super(model); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API Total amounts - /////////////////////////////////////////////////////////////////////////////////////////// - - public ReadOnlyLongProperty compensationAmountProperty() { - return compensationAmountProperty; - } - - public ReadOnlyLongProperty reimbursementAmountProperty() { - return reimbursementAmountProperty; - } - - public ReadOnlyLongProperty bsqTradeFeeAmountProperty() { - return bsqTradeFeeAmountProperty; - } - - public ReadOnlyLongProperty proofOfBurnAmountProperty() { - return proofOfBurnAmountProperty; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Legend - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected Collection> getSeriesForLegend1() { - return List.of(seriesTotalIssued, seriesCompensation, seriesReimbursement); - } - - @Override - protected Collection> getSeriesForLegend2() { - return List.of(seriesTotalBurned, seriesBsqTradeFee, seriesProofOfBurn); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Timeline navigation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void initBoundsForTimelineNavigation() { - setBoundsForTimelineNavigation(seriesTotalBurned.getData()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Series - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void createSeries() { - seriesTotalIssued = new XYChart.Series<>(); - seriesTotalIssued.setName(Res.get("dao.factsAndFigures.supply.totalIssued")); - seriesIndexMap.put(getSeriesId(seriesTotalIssued), 0); - - seriesTotalBurned = new XYChart.Series<>(); - seriesTotalBurned.setName(Res.get("dao.factsAndFigures.supply.totalBurned")); - seriesIndexMap.put(getSeriesId(seriesTotalBurned), 1); - - seriesCompensation = new XYChart.Series<>(); - seriesCompensation.setName(Res.get("dao.factsAndFigures.supply.compReq")); - seriesIndexMap.put(getSeriesId(seriesCompensation), 2); - - seriesReimbursement = new XYChart.Series<>(); - seriesReimbursement.setName(Res.get("dao.factsAndFigures.supply.reimbursement")); - seriesIndexMap.put(getSeriesId(seriesReimbursement), 3); - - seriesBsqTradeFee = new XYChart.Series<>(); - seriesBsqTradeFee.setName(Res.get("dao.factsAndFigures.supply.bsqTradeFee")); - seriesIndexMap.put(getSeriesId(seriesBsqTradeFee), 4); - - seriesProofOfBurn = new XYChart.Series<>(); - seriesProofOfBurn.setName(Res.get("dao.factsAndFigures.supply.proofOfBurn")); - seriesIndexMap.put(getSeriesId(seriesProofOfBurn), 5); - } - - @Override - protected void defineAndAddActiveSeries() { - activateSeries(seriesTotalIssued); - activateSeries(seriesTotalBurned); - } - - @Override - protected void activateSeries(XYChart.Series series) { - super.activateSeries(series); - - if (getSeriesId(series).equals(getSeriesId(seriesTotalIssued))) { - applyTotalIssued(); - } else if (getSeriesId(series).equals(getSeriesId(seriesCompensation))) { - applyCompensation(); - } else if (getSeriesId(series).equals(getSeriesId(seriesReimbursement))) { - applyReimbursement(); - } else if (getSeriesId(series).equals(getSeriesId(seriesTotalBurned))) { - applyTotalBurned(); - } else if (getSeriesId(series).equals(getSeriesId(seriesBsqTradeFee))) { - applyBsqTradeFee(); - } else if (getSeriesId(series).equals(getSeriesId(seriesProofOfBurn))) { - applyProofOfBurn(); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Data - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected void applyData() { - if (activeSeries.contains(seriesTotalIssued)) { - applyTotalIssued(); - } - if (activeSeries.contains(seriesCompensation)) { - applyCompensation(); - } - if (activeSeries.contains(seriesReimbursement)) { - applyReimbursement(); - } - if (activeSeries.contains(seriesTotalBurned)) { - applyTotalBurned(); - } - if (activeSeries.contains(seriesBsqTradeFee)) { - applyBsqTradeFee(); - } - if (activeSeries.contains(seriesProofOfBurn)) { - applyProofOfBurn(); - } - - compensationAmountProperty.set(model.getCompensationAmount()); - reimbursementAmountProperty.set(model.getReimbursementAmount()); - bsqTradeFeeAmountProperty.set(model.getBsqTradeFeeAmount()); - proofOfBurnAmountProperty.set(model.getProofOfBurnAmount()); - } - - private void applyTotalIssued() { - seriesTotalIssued.getData().setAll(model.getTotalIssuedChartData()); - } - - private void applyCompensation() { - seriesCompensation.getData().setAll(model.getCompensationChartData()); - } - - private void applyReimbursement() { - seriesReimbursement.getData().setAll(model.getReimbursementChartData()); - } - - private void applyTotalBurned() { - seriesTotalBurned.getData().setAll(model.getTotalBurnedChartData()); - } - - private void applyBsqTradeFee() { - seriesBsqTradeFee.getData().setAll(model.getBsqTradeFeeChartData()); - } - - private void applyProofOfBurn() { - seriesProofOfBurn.getData().setAll(model.getProofOfBurnChartData()); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/dao/DaoChartViewModel.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/dao/DaoChartViewModel.java deleted file mode 100644 index 6acca876a1..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/dao/DaoChartViewModel.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.economy.supply.dao; - -import bisq.desktop.components.chart.ChartViewModel; - -import bisq.core.locale.GlobalSettings; -import bisq.core.util.coin.BsqFormatter; - -import javax.inject.Inject; - -import javafx.scene.chart.XYChart; - -import javafx.util.StringConverter; - -import java.text.DecimalFormat; - -import java.util.List; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class DaoChartViewModel extends ChartViewModel { - private final DecimalFormat priceFormat; - private final BsqFormatter bsqFormatter; - - - @Inject - public DaoChartViewModel(DaoChartDataModel dataModel, BsqFormatter bsqFormatter) { - super(dataModel); - - this.bsqFormatter = bsqFormatter; - priceFormat = (DecimalFormat) DecimalFormat.getNumberInstance(GlobalSettings.getLocale()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Chart data - /////////////////////////////////////////////////////////////////////////////////////////// - - List> getTotalIssuedChartData() { - return toChartData(dataModel.getTotalIssuedByInterval()); - } - - List> getCompensationChartData() { - return toChartData(dataModel.getCompensationByInterval()); - } - - List> getReimbursementChartData() { - return toChartData(dataModel.getReimbursementByInterval()); - } - - List> getTotalBurnedChartData() { - return toChartData(dataModel.getTotalBurnedByInterval()); - } - - List> getBsqTradeFeeChartData() { - return toChartData(dataModel.getBsqTradeFeeByInterval()); - } - - List> getProofOfBurnChartData() { - return toChartData(dataModel.getProofOfBurnByInterval()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Formatters/Converters - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - protected StringConverter getYAxisStringConverter() { - return new StringConverter<>() { - @Override - public String toString(Number value) { - return priceFormat.format(Double.parseDouble(bsqFormatter.formatBSQSatoshis(value.longValue()))) + " BSQ"; - } - - @Override - public Number fromString(String string) { - return null; - } - }; - } - - @Override - protected String getTooltipValueConverter(Number value) { - return bsqFormatter.formatBSQSatoshisWithCode(value.longValue()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoChartDataModel delegates - /////////////////////////////////////////////////////////////////////////////////////////// - - long getCompensationAmount() { - return dataModel.getCompensationAmount(); - } - - long getReimbursementAmount() { - return dataModel.getReimbursementAmount(); - } - - long getBsqTradeFeeAmount() { - return dataModel.getBsqTradeFeeAmount(); - } - - long getProofOfBurnAmount() { - return dataModel.getProofOfBurnAmount(); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.fxml deleted file mode 100644 index bb89c7c4b2..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.fxml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.java deleted file mode 100644 index 36cd962320..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.economy.transactions; - -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.components.HyperlinkWithIcon; -import bisq.desktop.components.TitledGroupBg; -import bisq.desktop.util.Layout; - -import bisq.core.dao.DaoFacade; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.governance.IssuanceType; -import bisq.core.locale.Res; -import bisq.core.user.Preferences; - -import bisq.common.util.Tuple3; - -import javax.inject.Inject; - -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.VBox; - -import static bisq.desktop.util.FormBuilder.addTitledGroupBg; -import static bisq.desktop.util.FormBuilder.addTopLabelHyperlinkWithIcon; -import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField; - -@FxmlView -public class BSQTransactionsView extends ActivatableView implements DaoStateListener { - - private final DaoFacade daoFacade; - private final Preferences preferences; - - private int gridRow = 0; - private TextField allTxTextField, burntFeeTxsTextField, - utxoTextField, compensationIssuanceTxTextField, - reimbursementIssuanceTxTextField, invalidTxsTextField, irregularTxsTextField; - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, lifecycle - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - private BSQTransactionsView(DaoFacade daoFacade, - Preferences preferences) { - this.daoFacade = daoFacade; - this.preferences = preferences; - } - - @Override - public void initialize() { - addTitledGroupBg(root, gridRow, 2, Res.get("dao.factsAndFigures.transactions.genesis")); - String genTxHeight = String.valueOf(daoFacade.getGenesisBlockHeight()); - String genesisTxId = daoFacade.getGenesisTxId(); - String url = preferences.getBsqBlockChainExplorer().txUrl + genesisTxId; - - GridPane.setColumnSpan(addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.factsAndFigures.transactions.genesisBlockHeight"), - genTxHeight, Layout.FIRST_ROW_DISTANCE).third, 2); - - // TODO use addTopLabelTxIdTextField - Tuple3 tuple = addTopLabelHyperlinkWithIcon(root, ++gridRow, - Res.get("dao.factsAndFigures.transactions.genesisTxId"), genesisTxId, url, 0); - HyperlinkWithIcon hyperlinkWithIcon = tuple.second; - hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", genesisTxId))); - - GridPane.setColumnSpan(tuple.third, 2); - - - int startRow = ++gridRow; - - TitledGroupBg titledGroupBg = addTitledGroupBg(root, gridRow, 3, Res.get("dao.factsAndFigures.transactions.txDetails"), Layout.GROUP_DISTANCE); - titledGroupBg.getStyleClass().add("last"); - - allTxTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.factsAndFigures.transactions.allTx"), - genTxHeight, Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - utxoTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.factsAndFigures.transactions.utxo")).second; - compensationIssuanceTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow, - Res.get("dao.factsAndFigures.transactions.compensationIssuanceTx")).second; - reimbursementIssuanceTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow, - Res.get("dao.factsAndFigures.transactions.reimbursementIssuanceTx")).second; - - int columnIndex = 1; - gridRow = startRow; - - titledGroupBg = addTitledGroupBg(root, startRow, columnIndex, 3, "", Layout.GROUP_DISTANCE); - titledGroupBg.getStyleClass().add("last"); - - burntFeeTxsTextField = addTopLabelReadOnlyTextField(root, gridRow, columnIndex, - Res.get("dao.factsAndFigures.transactions.burntTx"), - Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - invalidTxsTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, - Res.get("dao.factsAndFigures.transactions.invalidTx")).second; - irregularTxsTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, - Res.get("dao.factsAndFigures.transactions.irregularTx")).second; - gridRow++; - - } - - @Override - protected void activate() { - daoFacade.addBsqStateListener(this); - - updateWithBsqBlockChainData(); - } - - @Override - protected void deactivate() { - daoFacade.removeBsqStateListener(this); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - updateWithBsqBlockChainData(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void updateWithBsqBlockChainData() { - allTxTextField.setText(String.valueOf(daoFacade.getNumTxs())); - utxoTextField.setText(String.valueOf(daoFacade.getUnspentTxOutputs().size())); - compensationIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.COMPENSATION))); - reimbursementIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.REIMBURSEMENT))); - burntFeeTxsTextField.setText(String.valueOf(daoFacade.getBurntFeeTxs().size())); - invalidTxsTextField.setText(String.valueOf(daoFacade.getInvalidTxs().size())); - irregularTxsTextField.setText(String.valueOf(daoFacade.getIrregularTxs().size())); - } -} - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/GovernanceView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/governance/GovernanceView.fxml deleted file mode 100644 index 11370e0ee4..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/GovernanceView.fxml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/GovernanceView.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/GovernanceView.java deleted file mode 100644 index 6d0c6eaa56..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/GovernanceView.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.governance; - -import bisq.desktop.Navigation; -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.CachingViewLoader; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.common.view.View; -import bisq.desktop.common.view.ViewLoader; -import bisq.desktop.common.view.ViewPath; -import bisq.desktop.components.MenuItem; -import bisq.desktop.main.MainView; -import bisq.desktop.main.dao.DaoView; -import bisq.desktop.main.dao.governance.dashboard.GovernanceDashboardView; -import bisq.desktop.main.dao.governance.make.MakeProposalView; -import bisq.desktop.main.dao.governance.proposals.ProposalsView; -import bisq.desktop.main.dao.governance.result.VoteResultView; - -import bisq.core.dao.DaoFacade; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.locale.Res; - -import javax.inject.Inject; - -import javafx.fxml.FXML; - -import javafx.scene.control.ToggleGroup; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.VBox; - -import javafx.beans.value.ChangeListener; - -import java.util.Arrays; -import java.util.List; - -@FxmlView -public class GovernanceView extends ActivatableView implements DaoStateListener { - private final ViewLoader viewLoader; - private final Navigation navigation; - private final DaoFacade daoFacade; - private final DaoStateService daoStateService; - - private MenuItem dashboard, make, open, result; - private Navigation.Listener navigationListener; - - @FXML - private VBox leftVBox; - @FXML - private AnchorPane content; - - private Class selectedViewClass; - private ChangeListener phaseChangeListener; - private ToggleGroup toggleGroup; - - @Inject - private GovernanceView(CachingViewLoader viewLoader, Navigation navigation, DaoFacade daoFacade, - DaoStateService daoStateService) { - this.viewLoader = viewLoader; - this.navigation = navigation; - this.daoFacade = daoFacade; - this.daoStateService = daoStateService; - } - - @Override - public void initialize() { - navigationListener = (viewPath, data) -> { - if (viewPath.size() != 4 || viewPath.indexOf(GovernanceView.class) != 2) - return; - - selectedViewClass = viewPath.tip(); - loadView(selectedViewClass); - }; - - phaseChangeListener = (observable, oldValue, newValue) -> { - if (newValue == DaoPhase.Phase.BLIND_VOTE) - open.setLabelText(Res.get("dao.proposal.menuItem.vote")); - else - open.setLabelText(Res.get("dao.proposal.menuItem.browse")); - }; - - toggleGroup = new ToggleGroup(); - List> baseNavPath = Arrays.asList(MainView.class, DaoView.class, GovernanceView.class); - dashboard = new MenuItem(navigation, toggleGroup, Res.get("shared.dashboard"), - GovernanceDashboardView.class, baseNavPath); - make = new MenuItem(navigation, toggleGroup, Res.get("dao.proposal.menuItem.make"), - MakeProposalView.class, baseNavPath); - open = new MenuItem(navigation, toggleGroup, Res.get("dao.proposal.menuItem.browse"), - ProposalsView.class, baseNavPath); - result = new MenuItem(navigation, toggleGroup, Res.get("dao.proposal.menuItem.result"), - VoteResultView.class, baseNavPath); - - leftVBox.getChildren().addAll(dashboard, make, open, result); - } - - @Override - protected void activate() { - if (daoStateService.isParseBlockChainComplete()) { - daoFacade.phaseProperty().addListener(phaseChangeListener); - } else { - daoStateService.addDaoStateListener(this); - } - - - dashboard.activate(); - make.activate(); - open.activate(); - result.activate(); - - navigation.addListener(navigationListener); - ViewPath viewPath = navigation.getCurrentPath(); - if (viewPath.size() == 3 && viewPath.indexOf(GovernanceView.class) == 2 || - viewPath.size() == 2 && viewPath.indexOf(DaoView.class) == 1) { - if (selectedViewClass == null) - selectedViewClass = MakeProposalView.class; - - loadView(selectedViewClass); - - } else if (viewPath.size() == 4 && viewPath.indexOf(GovernanceView.class) == 2) { - selectedViewClass = viewPath.get(3); - loadView(selectedViewClass); - } - } - - @SuppressWarnings("Duplicates") - @Override - protected void deactivate() { - daoFacade.phaseProperty().removeListener(phaseChangeListener); - daoStateService.removeDaoStateListener(this); - - navigation.removeListener(navigationListener); - - dashboard.deactivate(); - make.deactivate(); - open.deactivate(); - result.deactivate(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockChainComplete() { - daoFacade.phaseProperty().addListener(phaseChangeListener); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void loadView(Class viewClass) { - View view = viewLoader.load(viewClass); - content.getChildren().setAll(view.getRoot()); - - if (view instanceof GovernanceDashboardView) toggleGroup.selectToggle(dashboard); - else if (view instanceof MakeProposalView) toggleGroup.selectToggle(make); - else if (view instanceof ProposalsView) toggleGroup.selectToggle(open); - else if (view instanceof VoteResultView) toggleGroup.selectToggle(result); - } -} - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/PhasesView.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/PhasesView.java deleted file mode 100644 index 20b33ddaf6..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/PhasesView.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.governance; - -import bisq.desktop.components.SeparatedPhaseBars; -import bisq.desktop.util.Layout; - -import bisq.core.dao.DaoFacade; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.locale.Res; - -import javax.inject.Inject; - -import javafx.scene.layout.GridPane; - -import javafx.geometry.Insets; - -import java.util.Arrays; -import java.util.List; - -import lombok.extern.slf4j.Slf4j; - -import static bisq.desktop.util.FormBuilder.addTitledGroupBg; - -@Slf4j -public class PhasesView implements DaoStateListener { - private final DaoFacade daoFacade; - private final PeriodService periodService; - private SeparatedPhaseBars separatedPhaseBars; - private List phaseBarsItems; - - @Inject - private PhasesView(DaoFacade daoFacade, PeriodService periodService) { - this.daoFacade = daoFacade; - this.periodService = periodService; - } - - public int addGroup(GridPane gridPane, int gridRow) { - addTitledGroupBg(gridPane, gridRow, 1, Res.get("dao.cycle.headline")); - separatedPhaseBars = createSeparatedPhaseBars(); - GridPane.setMargin(separatedPhaseBars, new Insets(Layout.FIRST_ROW_DISTANCE + 5, 0, 0, 0)); - GridPane.setRowIndex(separatedPhaseBars, gridRow); - gridPane.getChildren().add(separatedPhaseBars); - return gridRow; - } - - public void activate() { - daoFacade.addBsqStateListener(this); - - applyData(daoFacade.getChainHeight()); - } - - public void deactivate() { - daoFacade.removeBsqStateListener(this); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - applyData(block.getHeight()); - - phaseBarsItems.forEach(item -> { - DaoPhase.Phase phase = item.getPhase(); - // Last block is considered for the break as we must not publish a tx there (would get confirmed in next - // block which would be a break). Only at result phase we don't have that situation ans show the last block - // as valid block in the phase. - if (periodService.isInPhaseButNotLastBlock(phase) || - (phase == DaoPhase.Phase.RESULT && periodService.isInPhase(block.getHeight(), phase))) { - item.setActive(); - } else { - item.setInActive(); - } - }); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private SeparatedPhaseBars createSeparatedPhaseBars() { - phaseBarsItems = Arrays.asList( - new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.PROPOSAL, true), - new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.BREAK1, false), - new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.BLIND_VOTE, true), - new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.BREAK2, false), - new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.VOTE_REVEAL, true), - new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.BREAK3, false), - new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.RESULT, false)); - return new SeparatedPhaseBars(phaseBarsItems); - } - - private void applyData(int height) { - if (height > 0) { - phaseBarsItems.forEach(item -> { - int firstBlock = daoFacade.getFirstBlockOfPhaseForDisplay(height, item.getPhase()); - int lastBlock = daoFacade.getLastBlockOfPhaseForDisplay(height, item.getPhase()); - int duration = daoFacade.getDurationForPhaseForDisplay(item.getPhase()); - item.setPeriodRange(firstBlock, lastBlock, duration); - double progress = 0; - if (height >= firstBlock && height <= lastBlock) { - progress = (double) (height - firstBlock + 1) / (double) duration; - } else if (height < firstBlock) { - progress = 0; - } else if (height > lastBlock) { - progress = 1; - } - - item.getProgressProperty().set(progress); - }); - separatedPhaseBars.updateWidth(); - } - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/ProposalDisplay.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/ProposalDisplay.java deleted file mode 100644 index 3368ac3b3f..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/ProposalDisplay.java +++ /dev/null @@ -1,711 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.governance; - -import bisq.desktop.Navigation; -import bisq.desktop.components.HyperlinkWithIcon; -import bisq.desktop.components.InputTextField; -import bisq.desktop.components.TitledGroupBg; -import bisq.desktop.main.MainView; -import bisq.desktop.main.dao.DaoView; -import bisq.desktop.main.dao.bonding.BondingView; -import bisq.desktop.main.dao.bonding.bonds.BondsView; -import bisq.desktop.util.FormBuilder; -import bisq.desktop.util.GUIUtil; -import bisq.desktop.util.Layout; -import bisq.desktop.util.validation.BsqValidator; - -import bisq.core.dao.DaoFacade; -import bisq.core.dao.governance.bond.Bond; -import bisq.core.dao.governance.bond.role.BondedRole; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.proposal.ProposalType; -import bisq.core.dao.governance.proposal.param.ChangeParamInputValidator; -import bisq.core.dao.governance.proposal.param.ChangeParamValidator; -import bisq.core.dao.state.model.blockchain.BaseTx; -import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.governance.Ballot; -import bisq.core.dao.state.model.governance.BondedRoleType; -import bisq.core.dao.state.model.governance.ChangeParamProposal; -import bisq.core.dao.state.model.governance.CompensationProposal; -import bisq.core.dao.state.model.governance.ConfiscateBondProposal; -import bisq.core.dao.state.model.governance.EvaluatedProposal; -import bisq.core.dao.state.model.governance.GenericProposal; -import bisq.core.dao.state.model.governance.Proposal; -import bisq.core.dao.state.model.governance.ProposalVoteResult; -import bisq.core.dao.state.model.governance.ReimbursementProposal; -import bisq.core.dao.state.model.governance.RemoveAssetProposal; -import bisq.core.dao.state.model.governance.Role; -import bisq.core.dao.state.model.governance.RoleProposal; -import bisq.core.dao.state.model.governance.Vote; -import bisq.core.locale.CurrencyUtil; -import bisq.core.locale.Res; -import bisq.core.user.Preferences; -import bisq.core.util.coin.BsqFormatter; -import bisq.core.util.validation.InputValidator; -import bisq.core.util.validation.RegexValidator; - -import bisq.asset.Asset; - -import bisq.common.config.BaseCurrencyNetwork; -import bisq.common.util.Tuple3; - -import org.bitcoinj.core.Coin; - -import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; -import javafx.scene.control.ScrollPane; -import javafx.scene.control.TextField; -import javafx.scene.control.TextInputControl; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.ColumnConstraints; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.VBox; - -import javafx.beans.value.ChangeListener; - -import javafx.collections.FXCollections; - -import javafx.util.StringConverter; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -import static bisq.desktop.util.FormBuilder.*; -import static com.google.common.base.Preconditions.checkNotNull; - -@SuppressWarnings({"ConstantConditions", "StatementWithEmptyBody"}) -@Slf4j -public class ProposalDisplay { - private final GridPane gridPane; - private final BsqFormatter bsqFormatter; - private final DaoFacade daoFacade; - - // Nullable because if we are in result view mode (readonly) we don't need to set the input validator) - @Nullable - private final ChangeParamValidator changeParamValidator; - private final Navigation navigation; - private final Preferences preferences; - - @Nullable - private TextField proposalFeeTextField, comboBoxValueTextField, requiredBondForRoleTextField; - private TextField proposalTypeTextField, myVoteTextField, voteResultTextField; - public InputTextField nameTextField; - public InputTextField linkInputTextField; - @Nullable - public InputTextField requestedBsqTextField, paramValueTextField; - @Nullable - public ComboBox paramComboBox; - @Nullable - public ComboBox confiscateBondComboBox; - @Nullable - public ComboBox bondedRoleTypeComboBox; - @Nullable - public ComboBox assetComboBox; - - @Getter - private int gridRow; - private HyperlinkWithIcon linkHyperlinkWithIcon; - private HyperlinkWithIcon txHyperlinkWithIcon; - private int gridRowStartIndex; - private final List inputChangedListeners = new ArrayList<>(); - @Getter - private List inputControls = new ArrayList<>(); - @Getter - private List> comboBoxes = new ArrayList<>(); - private final ChangeListener focusOutListener; - private final ChangeListener inputListener; - private ChangeListener paramChangeListener; - private ChangeListener requiredBondForRoleListener; - private TitledGroupBg myVoteTitledGroup; - private VBox linkWithIconContainer, comboBoxValueContainer, myVoteBox, voteResultBox; - private int votingBoxRowSpan; - - private Optional navigateHandlerOptional = Optional.empty(); - - public ProposalDisplay(GridPane gridPane, - BsqFormatter bsqFormatter, - DaoFacade daoFacade, - @Nullable ChangeParamValidator changeParamValidator, - Navigation navigation, - @Nullable Preferences preferences) { - this.gridPane = gridPane; - this.bsqFormatter = bsqFormatter; - this.daoFacade = daoFacade; - this.changeParamValidator = changeParamValidator; - this.navigation = navigation; - this.preferences = preferences; - - // focusOutListener = observable -> inputChangedListeners.forEach(Runnable::run); - - focusOutListener = (observable, oldValue, newValue) -> { - if (oldValue && !newValue) - inputChangedListeners.forEach(Runnable::run); - }; - inputListener = (observable, oldValue, newValue) -> inputChangedListeners.forEach(Runnable::run); - } - - public void addInputChangedListener(Runnable listener) { - inputChangedListeners.add(listener); - } - - public void removeInputChangedListener(Runnable listener) { - inputChangedListeners.remove(listener); - } - - public void createAllFields(String title, int gridRowStartIndex, double top, ProposalType proposalType, - boolean isMakeProposalScreen) { - createAllFields(title, gridRowStartIndex, top, proposalType, isMakeProposalScreen, null); - } - - public void createAllFields(String title, int gridRowStartIndex, double top, ProposalType proposalType, - boolean isMakeProposalScreen, String titledGroupStyle) { - removeAllFields(); - this.gridRowStartIndex = gridRowStartIndex; - this.gridRow = gridRowStartIndex; - int titledGroupBgRowSpan = 5; - - switch (proposalType) { - case COMPENSATION_REQUEST: - case REIMBURSEMENT_REQUEST: - case CONFISCATE_BOND: - case REMOVE_ASSET: - break; - case CHANGE_PARAM: - case BONDED_ROLE: - titledGroupBgRowSpan = 6; - break; - case GENERIC: - titledGroupBgRowSpan = 4; - break; - } - - TitledGroupBg titledGroupBg = addTitledGroupBg(gridPane, gridRow, titledGroupBgRowSpan, title, top); - - if (titledGroupStyle != null) titledGroupBg.getStyleClass().add(titledGroupStyle); - - double proposalTypeTop; - - if (top == Layout.GROUP_DISTANCE_WITHOUT_SEPARATOR) { - proposalTypeTop = Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE_WITHOUT_SEPARATOR; - } else if (top == Layout.GROUP_DISTANCE) { - proposalTypeTop = Layout.FIRST_ROW_AND_GROUP_DISTANCE; - } else if (top == 0) { - proposalTypeTop = Layout.FIRST_ROW_DISTANCE; - } else { - proposalTypeTop = Layout.FIRST_ROW_DISTANCE + top; - } - - proposalTypeTextField = addTopLabelTextField(gridPane, gridRow, - Res.get("dao.proposal.display.type"), proposalType.getDisplayName(), proposalTypeTop).second; - - nameTextField = addInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.name")); - if (isMakeProposalScreen) - nameTextField.setValidator(new InputValidator()); - inputControls.add(nameTextField); - - linkInputTextField = addInputTextField(gridPane, ++gridRow, - Res.get("dao.proposal.display.link")); - linkInputTextField.setPromptText(Res.get("dao.proposal.display.link.prompt")); - if (isMakeProposalScreen) { - RegexValidator validator = new RegexValidator(); - if (proposalType == ProposalType.COMPENSATION_REQUEST) { - validator.setPattern("https://bisq.network/dao-compensation/\\d+"); - linkInputTextField.setText("https://bisq.network/dao-compensation/#"); - } else if (proposalType == ProposalType.REIMBURSEMENT_REQUEST) { - validator.setPattern("https://bisq.network/dao-reimbursement/\\d+"); - linkInputTextField.setText("https://bisq.network/dao-reimbursement/#"); - } else { - validator.setPattern("https://bisq.network/dao-proposals/\\d+"); - linkInputTextField.setText("https://bisq.network/dao-proposals/#"); - } - linkInputTextField.setValidator(validator); - } - inputControls.add(linkInputTextField); - - Tuple3 tuple = addTopLabelHyperlinkWithIcon(gridPane, gridRow, - Res.get("dao.proposal.display.link"), "", "", 0); - linkHyperlinkWithIcon = tuple.second; - linkWithIconContainer = tuple.third; - // TODO HyperlinkWithIcon does not scale automatically (button base, -> make anchorpane as base) - linkHyperlinkWithIcon.prefWidthProperty().bind(nameTextField.widthProperty()); - - linkWithIconContainer.setVisible(false); - linkWithIconContainer.setManaged(false); - - if (!isMakeProposalScreen) { - Tuple3 uidTuple = addTopLabelHyperlinkWithIcon(gridPane, ++gridRow, - Res.get("dao.proposal.display.txId"), "", "", 0); - txHyperlinkWithIcon = uidTuple.second; - // TODO HyperlinkWithIcon does not scale automatically (button base, -> make anchorPane as base) - txHyperlinkWithIcon.prefWidthProperty().bind(nameTextField.widthProperty()); - } - - int comboBoxValueTextFieldIndex = -1; - switch (proposalType) { - case COMPENSATION_REQUEST: - case REIMBURSEMENT_REQUEST: - requestedBsqTextField = addInputTextField(gridPane, ++gridRow, - Res.get("dao.proposal.display.requestedBsq")); - checkNotNull(requestedBsqTextField, "requestedBsqTextField must not be null"); - inputControls.add(requestedBsqTextField); - - if (isMakeProposalScreen) { - BsqValidator bsqValidator = new BsqValidator(bsqFormatter); - if (proposalType == ProposalType.COMPENSATION_REQUEST) { - bsqValidator.setMinValue(daoFacade.getMinCompensationRequestAmount()); - bsqValidator.setMaxValue(daoFacade.getMaxCompensationRequestAmount()); - } else if (proposalType == ProposalType.REIMBURSEMENT_REQUEST) { - bsqValidator.setMinValue(daoFacade.getMinReimbursementRequestAmount()); - bsqValidator.setMaxValue(daoFacade.getMaxReimbursementRequestAmount()); - } - requestedBsqTextField.setValidator(bsqValidator); - } - break; - case CHANGE_PARAM: - checkNotNull(gridPane, "gridPane must not be null"); - paramComboBox = FormBuilder.addComboBox(gridPane, ++gridRow, - Res.get("dao.proposal.display.paramComboBox.label")); - comboBoxValueTextFieldIndex = gridRow; - checkNotNull(paramComboBox, "paramComboBox must not be null"); - List list = Arrays.stream(Param.values()) - .filter(e -> e != Param.UNDEFINED && e != Param.PHASE_UNDEFINED) - .collect(Collectors.toList()); - paramComboBox.setItems(FXCollections.observableArrayList(list)); - paramComboBox.setConverter(new StringConverter<>() { - @Override - public String toString(Param param) { - return param != null ? param.getDisplayString() : ""; - } - - @Override - public Param fromString(String string) { - return null; - } - }); - comboBoxes.add(paramComboBox); - paramValueTextField = addInputTextField(gridPane, ++gridRow, - Res.get("dao.proposal.display.paramValue")); - - inputControls.add(paramValueTextField); - - paramChangeListener = (observable, oldValue, newValue) -> { - if (newValue != null) { - paramValueTextField.clear(); - String currentValue = bsqFormatter.formatParamValue(newValue, daoFacade.getParamValue(newValue)); - paramValueTextField.setPromptText(Res.get("dao.param.currentValue", currentValue)); - if (changeParamValidator != null && isMakeProposalScreen) { - ChangeParamInputValidator validator = new ChangeParamInputValidator(newValue, changeParamValidator); - paramValueTextField.setValidator(validator); - } - } - }; - paramComboBox.getSelectionModel().selectedItemProperty().addListener(paramChangeListener); - break; - case BONDED_ROLE: - bondedRoleTypeComboBox = FormBuilder.addComboBox(gridPane, ++gridRow, - Res.get("dao.proposal.display.bondedRoleComboBox.label")); - comboBoxValueTextFieldIndex = gridRow; - checkNotNull(bondedRoleTypeComboBox, "bondedRoleTypeComboBox must not be null"); - List bondedRoleTypes = Arrays.stream(BondedRoleType.values()) - .filter(e -> e != BondedRoleType.UNDEFINED) - .collect(Collectors.toList()); - bondedRoleTypeComboBox.setItems(FXCollections.observableArrayList(bondedRoleTypes)); - bondedRoleTypeComboBox.setConverter(new StringConverter<>() { - @Override - public String toString(BondedRoleType bondedRoleType) { - return bondedRoleType != null ? bondedRoleType.getDisplayString() : ""; - } - - @Override - public BondedRoleType fromString(String string) { - return null; - } - }); - comboBoxes.add(bondedRoleTypeComboBox); - requiredBondForRoleTextField = addTopLabelReadOnlyTextField(gridPane, ++gridRow, - Res.get("dao.proposal.display.requiredBondForRole.label")).second; - - requiredBondForRoleListener = (observable, oldValue, newValue) -> { - if (newValue != null) { - requiredBondForRoleTextField.setText(bsqFormatter.formatCoinWithCode(Coin.valueOf(daoFacade.getRequiredBond(newValue)))); - } - }; - bondedRoleTypeComboBox.getSelectionModel().selectedItemProperty().addListener(requiredBondForRoleListener); - - break; - case CONFISCATE_BOND: - confiscateBondComboBox = FormBuilder.addComboBox(gridPane, ++gridRow, - Res.get("dao.proposal.display.confiscateBondComboBox.label")); - comboBoxValueTextFieldIndex = gridRow; - checkNotNull(confiscateBondComboBox, "confiscateBondComboBox must not be null"); - - confiscateBondComboBox.setItems(FXCollections.observableArrayList(daoFacade.getAllActiveBonds())); - confiscateBondComboBox.setConverter(new StringConverter<>() { - @Override - public String toString(Bond bond) { - String details = " (" + Res.get("dao.bond.table.column.lockupTxId") + ": " + bond.getLockupTxId() + ")"; - if (bond instanceof BondedRole) { - return bond.getBondedAsset().getDisplayString() + details; - } else { - return Res.get("dao.bond.bondedReputation") + details; - } - } - - @Override - public Bond fromString(String string) { - return null; - } - }); - comboBoxes.add(confiscateBondComboBox); - break; - case GENERIC: - break; - case REMOVE_ASSET: - assetComboBox = FormBuilder.addComboBox(gridPane, ++gridRow, - Res.get("dao.proposal.display.assetComboBox.label")); - comboBoxValueTextFieldIndex = gridRow; - checkNotNull(assetComboBox, "assetComboBox must not be null"); - List assetList = CurrencyUtil.getSortedAssetStream() - .filter(e -> !e.getTickerSymbol().equals("BSQ")) - .collect(Collectors.toList()); - assetComboBox.setItems(FXCollections.observableArrayList(assetList)); - assetComboBox.setConverter(new StringConverter<>() { - @Override - public String toString(Asset asset) { - return asset != null ? CurrencyUtil.getNameAndCode(asset.getTickerSymbol()) : ""; - } - - @Override - public Asset fromString(String string) { - return null; - } - }); - comboBoxes.add(assetComboBox); - break; - } - - if (comboBoxValueTextFieldIndex > -1) { - Tuple3 tuple3 = addTopLabelReadOnlyTextField(gridPane, comboBoxValueTextFieldIndex, - Res.get("dao.proposal.display.option")); - comboBoxValueTextField = tuple3.second; - comboBoxValueContainer = tuple3.third; - comboBoxValueContainer.setVisible(false); - comboBoxValueContainer.setManaged(false); - } - - if (isMakeProposalScreen) { - proposalFeeTextField = addTopLabelTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.proposalFee")).second; - proposalFeeTextField.setText(bsqFormatter.formatCoinWithCode(daoFacade.getProposalFee(daoFacade.getChainHeight()))); - } - - votingBoxRowSpan = 4; - - myVoteTitledGroup = addTitledGroupBg(gridPane, ++gridRow, 4, Res.get("dao.proposal.myVote.title"), Layout.COMPACT_FIRST_ROW_DISTANCE); - - Tuple3 tuple3 = addTopLabelTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.myVote"), Layout.COMPACT_FIRST_ROW_DISTANCE); - - myVoteBox = tuple3.third; - setMyVoteBoxVisibility(false); - - myVoteTextField = tuple3.second; - - tuple3 = addTopLabelReadOnlyTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.voteResult")); - - voteResultBox = tuple3.third; - voteResultBox.setVisible(false); - voteResultBox.setManaged(false); - - voteResultTextField = tuple3.second; - - addListeners(); - } - - public void applyBallot(@Nullable Ballot ballot) { - String myVote = Res.get("dao.proposal.display.myVote.ignored"); - boolean isNotNull = ballot != null; - Vote vote = isNotNull ? ballot.getVote() : null; - if (vote != null) { - myVote = vote.isAccepted() ? Res.get("dao.proposal.display.myVote.accepted") : - Res.get("dao.proposal.display.myVote.rejected"); - } - myVoteTextField.setText(myVote); - - setMyVoteBoxVisibility(isNotNull); - } - - public void applyEvaluatedProposal(@Nullable EvaluatedProposal evaluatedProposal) { - - boolean isEvaluatedProposalNotNull = evaluatedProposal != null; - if (isEvaluatedProposalNotNull) { - String result = evaluatedProposal.isAccepted() ? Res.get("dao.proposal.voteResult.success") : - Res.get("dao.proposal.voteResult.failed"); - ProposalVoteResult proposalVoteResult = evaluatedProposal.getProposalVoteResult(); - String threshold = (proposalVoteResult.getThreshold() / 100D) + "%"; - String requiredThreshold = (daoFacade.getRequiredThreshold(evaluatedProposal.getProposal()) * 100D) + "%"; - String quorum = bsqFormatter.formatCoinWithCode(Coin.valueOf(proposalVoteResult.getQuorum())); - String requiredQuorum = bsqFormatter.formatCoinWithCode(daoFacade.getRequiredQuorum(evaluatedProposal.getProposal())); - String summary = Res.get("dao.proposal.voteResult.summary", result, - threshold, requiredThreshold, quorum, requiredQuorum); - voteResultTextField.setText(summary); - } - voteResultBox.setVisible(isEvaluatedProposalNotNull); - voteResultBox.setManaged(isEvaluatedProposalNotNull); - } - - public void applyBallotAndVoteWeight(@Nullable Ballot ballot, long merit, long stake) { - applyBallotAndVoteWeight(ballot, merit, stake, true); - } - - public void applyBallotAndVoteWeight(@Nullable Ballot ballot, long merit, long stake, boolean ballotIncluded) { - boolean ballotIsNotNull = ballot != null; - boolean hasVoted = stake > 0; - if (hasVoted) { - String myVote = Res.get("dao.proposal.display.myVote.ignored"); - Vote vote = ballotIsNotNull ? ballot.getVote() : null; - if (vote != null) { - myVote = vote.isAccepted() ? Res.get("dao.proposal.display.myVote.accepted") : - Res.get("dao.proposal.display.myVote.rejected"); - } - - String voteIncluded = ballotIncluded ? "" : " - " + Res.get("dao.proposal.display.myVote.unCounted"); - String meritString = bsqFormatter.formatCoinWithCode(Coin.valueOf(merit)); - String stakeString = bsqFormatter.formatCoinWithCode(Coin.valueOf(stake)); - String weight = bsqFormatter.formatCoinWithCode(Coin.valueOf(merit + stake)); - String myVoteSummary = Res.get("dao.proposal.myVote.summary", myVote, - weight, meritString, stakeString, voteIncluded); - myVoteTextField.setText(myVoteSummary); - - GridPane.setRowSpan(myVoteTitledGroup, votingBoxRowSpan - 1); - } - - boolean show = ballotIsNotNull && hasVoted; - setMyVoteBoxVisibility(show); - } - - public void setIsVoteIncludedInResult(boolean isVoteIncludedInResult) { - if (!isVoteIncludedInResult && myVoteTextField != null && !myVoteTextField.getText().isEmpty()) { - String text = myVoteTextField.getText(); - myVoteTextField.setText(Res.get("dao.proposal.myVote.invalid") + " - " + text); - myVoteTextField.getStyleClass().add("error-text"); - } - } - - public void applyProposalPayload(Proposal proposal) { - proposalTypeTextField.setText(proposal.getType().getDisplayName()); - nameTextField.setText(proposal.getName()); - linkInputTextField.setVisible(false); - linkInputTextField.setManaged(false); - if (linkWithIconContainer != null) { - linkWithIconContainer.setVisible(true); - linkWithIconContainer.setManaged(true); - linkHyperlinkWithIcon.setText(proposal.getLink()); - linkHyperlinkWithIcon.setOnAction(e -> GUIUtil.openWebPage(proposal.getLink())); - } - - if (txHyperlinkWithIcon != null) { - txHyperlinkWithIcon.setText(proposal.getTxId()); - txHyperlinkWithIcon.setOnAction(e -> - GUIUtil.openTxInBsqBlockExplorer(proposal.getTxId(), preferences)); - } - - if (proposal instanceof CompensationProposal) { - CompensationProposal compensationProposal = (CompensationProposal) proposal; - checkNotNull(requestedBsqTextField, "requestedBsqTextField must not be null"); - requestedBsqTextField.setText(bsqFormatter.formatCoinWithCode(compensationProposal.getRequestedBsq())); - } else if (proposal instanceof ReimbursementProposal) { - ReimbursementProposal reimbursementProposal = (ReimbursementProposal) proposal; - checkNotNull(requestedBsqTextField, "requestedBsqTextField must not be null"); - requestedBsqTextField.setText(bsqFormatter.formatCoinWithCode(reimbursementProposal.getRequestedBsq())); - } else if (proposal instanceof ChangeParamProposal) { - ChangeParamProposal changeParamProposal = (ChangeParamProposal) proposal; - checkNotNull(paramComboBox, "paramComboBox must not be null"); - paramComboBox.getSelectionModel().select(changeParamProposal.getParam()); - comboBoxValueTextField.setText(paramComboBox.getConverter().toString(changeParamProposal.getParam())); - checkNotNull(paramValueTextField, "paramValueTextField must not be null"); - paramValueTextField.setText(bsqFormatter.formatParamValue(changeParamProposal.getParam(), changeParamProposal.getParamValue())); - String currentValue = bsqFormatter.formatParamValue(changeParamProposal.getParam(), - daoFacade.getParamValue(changeParamProposal.getParam())); - int height = daoFacade.getTx(changeParamProposal.getTxId()) - .map(BaseTx::getBlockHeight) - .orElse(daoFacade.getGenesisBlockHeight()); - String valueAtProposal = bsqFormatter.formatParamValue(changeParamProposal.getParam(), - daoFacade.getParamValue(changeParamProposal.getParam(), height)); - paramValueTextField.setPromptText(Res.get("dao.param.currentAndPastValue", currentValue, valueAtProposal)); - } else if (proposal instanceof RoleProposal) { - RoleProposal roleProposal = (RoleProposal) proposal; - checkNotNull(bondedRoleTypeComboBox, "bondedRoleComboBox must not be null"); - Role role = roleProposal.getRole(); - bondedRoleTypeComboBox.getSelectionModel().select(role.getBondedRoleType()); - comboBoxValueTextField.setText(bondedRoleTypeComboBox.getConverter().toString(role.getBondedRoleType())); - requiredBondForRoleTextField.setText(bsqFormatter.formatCoin(Coin.valueOf(daoFacade.getRequiredBond(roleProposal)))); - // TODO maybe show also unlock time? - } else if (proposal instanceof ConfiscateBondProposal) { - ConfiscateBondProposal confiscateBondProposal = (ConfiscateBondProposal) proposal; - checkNotNull(confiscateBondComboBox, "confiscateBondComboBox must not be null"); - daoFacade.getBondByLockupTxId(confiscateBondProposal.getLockupTxId()) - .ifPresent(bond -> { - confiscateBondComboBox.getSelectionModel().select(bond); - comboBoxValueTextField.setText(confiscateBondComboBox.getConverter().toString(bond)); - comboBoxValueTextField.setOnMouseClicked(e -> { - navigateHandlerOptional.ifPresent(Runnable::run); - navigation.navigateToWithData(bond, MainView.class, DaoView.class, BondingView.class, - BondsView.class); - }); - - comboBoxValueTextField.getStyleClass().addAll("hyperlink", "force-underline", "show-hand"); - }); - } else if (proposal instanceof GenericProposal) { - // do nothing - } else if (proposal instanceof RemoveAssetProposal) { - RemoveAssetProposal removeAssetProposal = (RemoveAssetProposal) proposal; - checkNotNull(assetComboBox, "assetComboBox must not be null"); - CurrencyUtil.findAsset(removeAssetProposal.getTickerSymbol(), BaseCurrencyNetwork.XMR_MAINNET) - .ifPresent(asset -> { - assetComboBox.getSelectionModel().select(asset); - comboBoxValueTextField.setText(assetComboBox.getConverter().toString(asset)); - }); - } - int chainHeight = daoFacade.getTx(proposal.getTxId()).map(Tx::getBlockHeight).orElse(daoFacade.getChainHeight()); - if (proposalFeeTextField != null) - proposalFeeTextField.setText(bsqFormatter.formatCoinWithCode(daoFacade.getProposalFee(chainHeight))); - } - - private void addListeners() { - inputControls.stream() - .filter(Objects::nonNull).forEach(inputControl -> { - inputControl.textProperty().addListener(inputListener); - inputControl.focusedProperty().addListener(focusOutListener); - }); - comboBoxes.stream() - .filter(Objects::nonNull) - .forEach(comboBox -> comboBox.getSelectionModel().selectedItemProperty().addListener(inputListener)); - } - - public void removeListeners() { - inputControls.stream() - .filter(Objects::nonNull).forEach(inputControl -> { - inputControl.textProperty().removeListener(inputListener); - inputControl.focusedProperty().removeListener(focusOutListener); - }); - comboBoxes.stream() - .filter(Objects::nonNull) - .forEach(comboBox -> comboBox.getSelectionModel().selectedItemProperty().removeListener(inputListener)); - - if (paramComboBox != null && paramChangeListener != null) - paramComboBox.getSelectionModel().selectedItemProperty().removeListener(paramChangeListener); - - if (bondedRoleTypeComboBox != null && requiredBondForRoleListener != null) - bondedRoleTypeComboBox.getSelectionModel().selectedItemProperty().removeListener(requiredBondForRoleListener); - } - - public void clearForm() { - inputControls.stream().filter(Objects::nonNull).forEach(TextInputControl::clear); - - if (linkHyperlinkWithIcon != null) - linkHyperlinkWithIcon.clear(); - - comboBoxes.stream().filter(Objects::nonNull).forEach(comboBox -> comboBox.getSelectionModel().clearSelection()); - } - - public void setEditable(boolean isEditable) { - inputControls.stream().filter(Objects::nonNull).forEach(e -> e.setEditable(isEditable)); - comboBoxes.stream().filter(Objects::nonNull).forEach(comboBox -> { - comboBox.setVisible(isEditable); - comboBox.setManaged(isEditable); - - if (comboBoxValueContainer != null) { - comboBoxValueContainer.setVisible(!isEditable); - comboBoxValueContainer.setManaged(!isEditable); - } - }); - - linkInputTextField.setVisible(true); - linkInputTextField.setManaged(true); - - if (linkWithIconContainer != null) { - linkWithIconContainer.setVisible(false); - linkWithIconContainer.setManaged(false); - linkHyperlinkWithIcon.setOnAction(null); - } - } - - public void removeAllFields() { - if (gridRow > 0) { - clearForm(); - GUIUtil.removeChildrenFromGridPaneRows(gridPane, gridRowStartIndex, gridRow); - gridRow = gridRowStartIndex; - } - - if (linkHyperlinkWithIcon != null) - linkHyperlinkWithIcon.prefWidthProperty().unbind(); - - inputControls.clear(); - comboBoxes.clear(); - } - - public void onNavigate(Runnable navigateHandler) { - navigateHandlerOptional = Optional.of(navigateHandler); - } - - public int incrementAndGetGridRow() { - return ++gridRow; - } - - @SuppressWarnings("Duplicates") - public ScrollPane getView() { - ScrollPane scrollPane = new ScrollPane(); - scrollPane.setFitToWidth(true); - scrollPane.setFitToHeight(true); - - AnchorPane anchorPane = new AnchorPane(); - scrollPane.setContent(anchorPane); - - gridPane.setHgap(5); - gridPane.setVgap(5); - - ColumnConstraints columnConstraints1 = new ColumnConstraints(); - columnConstraints1.setPercentWidth(100); - - gridPane.getColumnConstraints().addAll(columnConstraints1); - - AnchorPane.setBottomAnchor(gridPane, 10d); - AnchorPane.setRightAnchor(gridPane, 10d); - AnchorPane.setLeftAnchor(gridPane, 10d); - AnchorPane.setTopAnchor(gridPane, 10d); - anchorPane.getChildren().add(gridPane); - - return scrollPane; - } - - private void setMyVoteBoxVisibility(boolean visibility) { - myVoteTitledGroup.setVisible(visibility); - myVoteTitledGroup.setManaged(visibility); - myVoteBox.setVisible(visibility); - myVoteBox.setManaged(visibility); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/dashboard/GovernanceDashboardView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/governance/dashboard/GovernanceDashboardView.fxml deleted file mode 100644 index a71c978980..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/dashboard/GovernanceDashboardView.fxml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/dashboard/GovernanceDashboardView.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/dashboard/GovernanceDashboardView.java deleted file mode 100644 index d6926cb24d..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/dashboard/GovernanceDashboardView.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.governance.dashboard; - -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.components.TitledGroupBg; -import bisq.desktop.main.dao.governance.PhasesView; -import bisq.desktop.util.Layout; - -import bisq.core.dao.DaoFacade; -import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.presentation.DaoUtil; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.locale.Res; - -import javax.inject.Inject; - -import javafx.scene.control.TextField; -import javafx.scene.layout.GridPane; - -import static bisq.desktop.util.FormBuilder.addTitledGroupBg; -import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField; - -// We use here ChainHeightListener because we are interested in period changes not in the result of a completed -// block. The event from the ChainHeightListener is sent before parsing starts. -// The event from the ChainHeightListener would notify after parsing a new block. -@FxmlView -public class GovernanceDashboardView extends ActivatableView implements DaoStateListener { - private final DaoFacade daoFacade; - private final PeriodService periodService; - private final PhasesView phasesView; - - private int gridRow = 0; - private TextField currentPhaseTextField, currentBlockHeightTextField, proposalTextField, blindVoteTextField, voteRevealTextField, voteResultTextField; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, lifecycle - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - public GovernanceDashboardView(DaoFacade daoFacade, PeriodService periodService, PhasesView phasesView) { - this.daoFacade = daoFacade; - this.periodService = periodService; - this.phasesView = phasesView; - } - - @Override - public void initialize() { - gridRow = phasesView.addGroup(root, gridRow); - - TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 6, Res.get("dao.cycle.overview.headline"), Layout.GROUP_DISTANCE); - titledGroupBg.getStyleClass().add("last"); - currentBlockHeightTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.cycle.currentBlockHeight"), - Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - currentPhaseTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.cycle.currentPhase")).second; - proposalTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.cycle.proposal")).second; - blindVoteTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.cycle.blindVote")).second; - voteRevealTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.cycle.voteReveal")).second; - voteResultTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.cycle.voteResult")).second; - } - - @Override - protected void activate() { - super.activate(); - - phasesView.activate(); - - daoFacade.addBsqStateListener(this); - - applyData(daoFacade.getChainHeight()); - } - - @Override - protected void deactivate() { - super.deactivate(); - - phasesView.deactivate(); - - daoFacade.removeBsqStateListener(this); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - applyData(block.getHeight()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void applyData(int height) { - currentBlockHeightTextField.setText(String.valueOf(daoFacade.getChainHeight())); - DaoPhase.Phase phase = daoFacade.phaseProperty().get(); - // If we are in last block of proposal, blindVote or voteReveal phase we show following break. - if (!periodService.isInPhaseButNotLastBlock(phase) && - (phase == DaoPhase.Phase.PROPOSAL || phase == DaoPhase.Phase.BLIND_VOTE || phase == DaoPhase.Phase.VOTE_REVEAL)) { - phase = periodService.getPhaseForHeight(height + 1); - } - currentPhaseTextField.setText(Res.get("dao.phase." + phase.name())); - proposalTextField.setText(DaoUtil.getPhaseDuration(height, DaoPhase.Phase.PROPOSAL, daoFacade)); - blindVoteTextField.setText(DaoUtil.getPhaseDuration(height, DaoPhase.Phase.BLIND_VOTE, daoFacade)); - voteRevealTextField.setText(DaoUtil.getPhaseDuration(height, DaoPhase.Phase.VOTE_REVEAL, daoFacade)); - voteResultTextField.setText(DaoUtil.getPhaseDuration(height, DaoPhase.Phase.RESULT, daoFacade)); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/make/MakeProposalView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/governance/make/MakeProposalView.fxml deleted file mode 100644 index d5d6c8d287..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/make/MakeProposalView.fxml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/make/MakeProposalView.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/make/MakeProposalView.java deleted file mode 100644 index 5b1a467968..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/make/MakeProposalView.java +++ /dev/null @@ -1,525 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.governance.make; - -import bisq.desktop.Navigation; -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.components.BusyAnimation; -import bisq.desktop.components.InputTextField; -import bisq.desktop.components.TitledGroupBg; -import bisq.desktop.main.dao.governance.PhasesView; -import bisq.desktop.main.dao.governance.ProposalDisplay; -import bisq.desktop.main.overlays.popups.Popup; -import bisq.desktop.util.GUIUtil; -import bisq.desktop.util.Layout; - -import bisq.core.btc.exceptions.InsufficientBsqException; -import bisq.core.btc.setup.WalletsSetup; -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.dao.DaoFacade; -import bisq.core.dao.governance.bond.Bond; -import bisq.core.dao.governance.param.Param; -import bisq.core.dao.governance.proposal.IssuanceProposal; -import bisq.core.dao.governance.proposal.ProposalType; -import bisq.core.dao.governance.proposal.ProposalValidationException; -import bisq.core.dao.governance.proposal.ProposalWithTransaction; -import bisq.core.dao.governance.proposal.TxException; -import bisq.core.dao.governance.proposal.param.ChangeParamValidator; -import bisq.core.dao.governance.voteresult.VoteResultException; -import bisq.core.dao.presentation.DaoUtil; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.governance.BondedRoleType; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.dao.state.model.governance.Proposal; -import bisq.core.dao.state.model.governance.Role; -import bisq.core.locale.Res; -import bisq.core.util.FormattingUtils; -import bisq.core.util.coin.BsqFormatter; -import bisq.core.util.ParsingUtils; -import bisq.core.util.coin.CoinFormatter; - -import bisq.asset.Asset; - -import bisq.network.p2p.P2PService; - -import bisq.common.app.DevEnv; -import bisq.common.util.Tuple3; -import bisq.common.util.Tuple4; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.Transaction; - -import javax.inject.Inject; -import javax.inject.Named; - -import javafx.scene.control.Button; -import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; - -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.beans.value.ChangeListener; - -import javafx.collections.FXCollections; - -import javafx.util.StringConverter; - -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; - -import javax.annotation.Nullable; - -import static bisq.desktop.util.FormBuilder.addButtonBusyAnimationLabelAfterGroup; -import static bisq.desktop.util.FormBuilder.addComboBox; -import static bisq.desktop.util.FormBuilder.addTitledGroupBg; -import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField; -import static com.google.common.base.Preconditions.checkNotNull; - -@FxmlView -public class MakeProposalView extends ActivatableView implements DaoStateListener { - private final DaoFacade daoFacade; - private final WalletsSetup walletsSetup; - private final P2PService p2PService; - private final PhasesView phasesView; - private final ChangeParamValidator changeParamValidator; - private final CoinFormatter btcFormatter; - private final BsqFormatter bsqFormatter; - private final Navigation navigation; - private final BsqWalletService bsqWalletService; - - @Nullable - private ProposalDisplay proposalDisplay; - private Button makeProposalButton; - private ComboBox proposalTypeComboBox; - private ChangeListener proposalTypeChangeListener; - private TextField nextProposalTextField; - private TitledGroupBg proposalTitledGroup; - private VBox nextProposalBox; - private BusyAnimation busyAnimation; - private Label busyLabel; - - private final BooleanProperty isProposalPhase = new SimpleBooleanProperty(false); - private final StringProperty proposalGroupTitle = new SimpleStringProperty(Res.get("dao.proposal.create.phase.inactive")); - - @Nullable - private ProposalType selectedProposalType; - private int gridRow; - private int alwaysVisibleGridRowIndex; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, lifecycle - /////////////////////////////////////////////////////////////////////////////////////////// - - @Inject - private MakeProposalView(DaoFacade daoFacade, - WalletsSetup walletsSetup, - P2PService p2PService, - BsqWalletService bsqWalletService, - PhasesView phasesView, - ChangeParamValidator changeParamValidator, - @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter, - BsqFormatter bsqFormatter, - Navigation navigation) { - this.daoFacade = daoFacade; - this.walletsSetup = walletsSetup; - this.p2PService = p2PService; - this.bsqWalletService = bsqWalletService; - this.phasesView = phasesView; - this.changeParamValidator = changeParamValidator; - this.btcFormatter = btcFormatter; - this.bsqFormatter = bsqFormatter; - this.navigation = navigation; - } - - @Override - public void initialize() { - gridRow = phasesView.addGroup(root, gridRow); - - proposalTitledGroup = addTitledGroupBg(root, ++gridRow, 2, proposalGroupTitle.get(), Layout.GROUP_DISTANCE); - proposalTitledGroup.getStyleClass().add("last"); - final Tuple3 nextProposalPhaseTuple = addTopLabelReadOnlyTextField(root, gridRow, - Res.get("dao.cycle.proposal.next"), - Layout.FIRST_ROW_AND_GROUP_DISTANCE); - nextProposalBox = nextProposalPhaseTuple.third; - nextProposalTextField = nextProposalPhaseTuple.second; - proposalTypeComboBox = addComboBox(root, gridRow, - Res.get("dao.proposal.create.proposalType"), Layout.FIRST_ROW_AND_GROUP_DISTANCE); - proposalTypeComboBox.setMaxWidth(300); - proposalTypeComboBox.setConverter(new StringConverter<>() { - @Override - public String toString(ProposalType proposalType) { - return proposalType.getDisplayName(); - } - - @Override - public ProposalType fromString(String string) { - return null; - } - }); - proposalTypeChangeListener = (observable, oldValue, newValue) -> { - selectedProposalType = newValue; - removeProposalDisplay(); - addProposalDisplay(); - }; - alwaysVisibleGridRowIndex = gridRow + 1; - - List proposalTypes = Arrays.stream(ProposalType.values()) - .filter(e -> e != ProposalType.UNDEFINED) - .collect(Collectors.toList()); - proposalTypeComboBox.setItems(FXCollections.observableArrayList(proposalTypes)); - } - - @Override - protected void activate() { - addBindings(); - - phasesView.activate(); - - daoFacade.addBsqStateListener(this); - - proposalTypeComboBox.getSelectionModel().selectedItemProperty().addListener(proposalTypeChangeListener); - if (makeProposalButton != null) - setMakeProposalButtonHandler(); - - Optional blockAtChainHeight = daoFacade.getBlockAtChainHeight(); - - blockAtChainHeight.ifPresent(this::onParseBlockCompleteAfterBatchProcessing); - } - - @Override - protected void deactivate() { - removeBindings(); - - phasesView.deactivate(); - - daoFacade.removeBsqStateListener(this); - - proposalTypeComboBox.getSelectionModel().selectedItemProperty().removeListener(proposalTypeChangeListener); - if (makeProposalButton != null) - makeProposalButton.setOnAction(null); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Bindings, Listeners - /////////////////////////////////////////////////////////////////////////////////////////// - - private void addBindings() { - proposalTypeComboBox.managedProperty().bind(isProposalPhase); - proposalTypeComboBox.visibleProperty().bind(isProposalPhase); - nextProposalBox.managedProperty().bind(isProposalPhase.not()); - nextProposalBox.visibleProperty().bind(isProposalPhase.not()); - proposalTitledGroup.textProperty().bind(proposalGroupTitle); - } - - private void removeBindings() { - proposalTypeComboBox.managedProperty().unbind(); - proposalTypeComboBox.visibleProperty().unbind(); - nextProposalBox.managedProperty().unbind(); - nextProposalBox.visibleProperty().unbind(); - proposalTitledGroup.textProperty().unbind(); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // DaoStateListener - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onParseBlockCompleteAfterBatchProcessing(Block block) { - isProposalPhase.set(daoFacade.isInPhaseButNotLastBlock(DaoPhase.Phase.PROPOSAL)); - if (isProposalPhase.get()) { - proposalGroupTitle.set(Res.get("dao.proposal.create.selectProposalType")); - } else { - proposalGroupTitle.set(Res.get("dao.proposal.create.phase.inactive")); - proposalTypeComboBox.getSelectionModel().clearSelection(); - updateTimeUntilNextProposalPhase(block.getHeight()); - } - - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private void updateTimeUntilNextProposalPhase(int height) { - nextProposalTextField.setText(DaoUtil.getNextPhaseDuration(height, DaoPhase.Phase.PROPOSAL, daoFacade)); - } - - private void publishMyProposal(ProposalType type) { - try { - ProposalWithTransaction proposalWithTransaction = getProposalWithTransaction(type); - if (proposalWithTransaction == null) - return; - - Proposal proposal = proposalWithTransaction.getProposal(); - Transaction transaction = proposalWithTransaction.getTransaction(); - Coin miningFee = transaction.getFee(); - int txVsize = transaction.getVsize(); - Coin fee = daoFacade.getProposalFee(daoFacade.getChainHeight()); - - if (type.equals(ProposalType.BONDED_ROLE)) { - checkNotNull(proposalDisplay, "proposalDisplay must not be null"); - checkNotNull(proposalDisplay.bondedRoleTypeComboBox, "proposalDisplay.bondedRoleTypeComboBox must not be null"); - BondedRoleType bondedRoleType = proposalDisplay.bondedRoleTypeComboBox.getSelectionModel().getSelectedItem(); - long requiredBond = daoFacade.getRequiredBond(bondedRoleType); - long availableBalance = bsqWalletService.getAvailableConfirmedBalance().value; - - if (requiredBond > availableBalance) { - long missing = requiredBond - availableBalance; - new Popup().warning(Res.get("dao.proposal.create.missingBsqFundsForBond", - bsqFormatter.formatCoinWithCode(missing))) - .actionButtonText(Res.get("dao.proposal.create.publish")) - .onAction(() -> showFeeInfoAndPublishMyProposal(proposal, transaction, miningFee, txVsize, fee)) - .show(); - } else { - showFeeInfoAndPublishMyProposal(proposal, transaction, miningFee, txVsize, fee); - } - } else { - showFeeInfoAndPublishMyProposal(proposal, transaction, miningFee, txVsize, fee); - } - } catch (InsufficientMoneyException e) { - if (e instanceof InsufficientBsqException) { - new Popup().warning(Res.get("dao.proposal.create.missingBsqFunds", - bsqFormatter.formatCoinWithCode(e.missing))).show(); - } else { - if (type.equals(ProposalType.COMPENSATION_REQUEST) || type.equals(ProposalType.REIMBURSEMENT_REQUEST)) { - new Popup().warning(Res.get("dao.proposal.create.missingIssuanceFunds", - 100, - btcFormatter.formatCoinWithCode(e.missing))).show(); - } else { - new Popup().warning(Res.get("dao.proposal.create.missingMinerFeeFunds", - btcFormatter.formatCoinWithCode(e.missing))).show(); - } - } - } catch (ProposalValidationException e) { - String message; - if (e.getMinRequestAmount() != null) { - message = Res.get("validation.bsq.amountBelowMinAmount", - bsqFormatter.formatCoinWithCode(e.getMinRequestAmount())); - } else { - message = e.getMessage(); - } - new Popup().warning(message).show(); - } catch (IllegalArgumentException e) { - log.error(e.toString()); - e.printStackTrace(); - new Popup().warning(e.getMessage()).show(); - } catch (Throwable e) { - log.error(e.toString()); - e.printStackTrace(); - new Popup().warning(e.toString()).show(); - } - } - - private void showFeeInfoAndPublishMyProposal(Proposal proposal, Transaction transaction, Coin miningFee, int txVsize, Coin fee) { - if (!DevEnv.isDevMode()) { - Coin btcForIssuance = null; - - if (proposal instanceof IssuanceProposal) btcForIssuance = ((IssuanceProposal) proposal).getRequestedBsq(); - - GUIUtil.showBsqFeeInfoPopup(fee, miningFee, btcForIssuance, txVsize, bsqFormatter, btcFormatter, - Res.get("dao.proposal"), () -> doPublishMyProposal(proposal, transaction)); - } else { - doPublishMyProposal(proposal, transaction); - } - } - - private void doPublishMyProposal(Proposal proposal, Transaction transaction) { - //TODO it still happens that the user can click twice. Not clear why that can happen. Maybe we get updateButtonState - // called in between which re-enables the button? - makeProposalButton.setDisable(true); - busyLabel.setVisible(true); - busyAnimation.play(); - - daoFacade.publishMyProposal(proposal, - transaction, - () -> { - if (!DevEnv.isDevMode()) - new Popup().feedback(Res.get("dao.tx.published.success")).show(); - - if (proposalDisplay != null) - proposalDisplay.clearForm(); - proposalTypeComboBox.getSelectionModel().clearSelection(); - busyAnimation.stop(); - busyLabel.setVisible(false); - makeProposalButton.setDisable(false); - }, - errorMessage -> { - new Popup().warning(errorMessage).show(); - busyAnimation.stop(); - busyLabel.setVisible(false); - makeProposalButton.setDisable(false); - }); - - } - - @Nullable - private ProposalWithTransaction getProposalWithTransaction(ProposalType proposalType) - throws InsufficientMoneyException, ProposalValidationException, TxException, VoteResultException.ValidationException { - - checkNotNull(proposalDisplay, "proposalDisplay must not be null"); - - String link = proposalDisplay.linkInputTextField.getText(); - String name = proposalDisplay.nameTextField.getText(); - switch (proposalType) { - case COMPENSATION_REQUEST: - checkNotNull(proposalDisplay.requestedBsqTextField, - "proposalDisplay.requestedBsqTextField must not be null"); - return daoFacade.getCompensationProposalWithTransaction(name, - link, - ParsingUtils.parseToCoin(proposalDisplay.requestedBsqTextField.getText(), bsqFormatter)); - case REIMBURSEMENT_REQUEST: - checkNotNull(proposalDisplay.requestedBsqTextField, - "proposalDisplay.requestedBsqTextField must not be null"); - return daoFacade.getReimbursementProposalWithTransaction(name, - link, - ParsingUtils.parseToCoin(proposalDisplay.requestedBsqTextField.getText(), bsqFormatter)); - case CHANGE_PARAM: - checkNotNull(proposalDisplay.paramComboBox, - "proposalDisplay.paramComboBox must not be null"); - checkNotNull(proposalDisplay.paramValueTextField, - "proposalDisplay.paramValueTextField must not be null"); - Param selectedParam = proposalDisplay.paramComboBox.getSelectionModel().getSelectedItem(); - if (selectedParam == null) - throw new ProposalValidationException("selectedParam is null"); - String paramValueAsString = proposalDisplay.paramValueTextField.getText(); - if (paramValueAsString == null || paramValueAsString.isEmpty()) - throw new ProposalValidationException("paramValue is null or empty"); - - try { - String paramValue = bsqFormatter.parseParamValueToString(selectedParam, paramValueAsString); - proposalDisplay.paramValueTextField.setText(paramValue); - log.info("Change param: paramValue={}, paramValueAsString={}", paramValue, paramValueAsString); - - changeParamValidator.validateParamValue(selectedParam, paramValue); - return daoFacade.getParamProposalWithTransaction(name, - link, - selectedParam, - paramValue); - } catch (Throwable e) { - new Popup().warning(e.getMessage()).show(); - return null; - } - case BONDED_ROLE: - checkNotNull(proposalDisplay.bondedRoleTypeComboBox, - "proposalDisplay.bondedRoleTypeComboBox must not be null"); - Role role = new Role(name, - link, - proposalDisplay.bondedRoleTypeComboBox.getSelectionModel().getSelectedItem()); - return daoFacade.getBondedRoleProposalWithTransaction(role); - case CONFISCATE_BOND: - checkNotNull(proposalDisplay.confiscateBondComboBox, - "proposalDisplay.confiscateBondComboBox must not be null"); - Bond bond = proposalDisplay.confiscateBondComboBox.getSelectionModel().getSelectedItem(); - - if (!bond.isActive()) - throw new VoteResultException.ValidationException("Bond is not locked and can't be confiscated"); - - return daoFacade.getConfiscateBondProposalWithTransaction(name, link, bond.getLockupTxId()); - case GENERIC: - return daoFacade.getGenericProposalWithTransaction(name, link); - case REMOVE_ASSET: - checkNotNull(proposalDisplay.assetComboBox, - "proposalDisplay.assetComboBox must not be null"); - Asset asset = proposalDisplay.assetComboBox.getSelectionModel().getSelectedItem(); - return daoFacade.getRemoveAssetProposalWithTransaction(name, link, asset); - default: - final String msg = "Undefined ProposalType " + selectedProposalType; - log.error(msg); - throw new RuntimeException(msg); - } - } - - private void addProposalDisplay() { - if (selectedProposalType != null) { - proposalDisplay = new ProposalDisplay(root, bsqFormatter, daoFacade, changeParamValidator, navigation, null); - - proposalDisplay.createAllFields(Res.get("dao.proposal.create.new"), alwaysVisibleGridRowIndex, Layout.GROUP_DISTANCE_WITHOUT_SEPARATOR, - selectedProposalType, true); - - final Tuple4 makeProposalTuple = addButtonBusyAnimationLabelAfterGroup(root, - proposalDisplay.getGridRow(), 0, Res.get("dao.proposal.create.button")); - makeProposalButton = makeProposalTuple.first; - - busyAnimation = makeProposalTuple.second; - busyLabel = makeProposalTuple.third; - busyLabel.setVisible(false); - busyLabel.setText(Res.get("dao.proposal.create.publishing")); - - setMakeProposalButtonHandler(); - proposalDisplay.addInputChangedListener(this::updateButtonState); - updateButtonState(); - } - } - - private void removeProposalDisplay() { - if (proposalDisplay != null) { - proposalDisplay.removeAllFields(); - GUIUtil.removeChildrenFromGridPaneRows(root, alwaysVisibleGridRowIndex, proposalDisplay.getGridRow()); - proposalDisplay.removeInputChangedListener(this::updateButtonState); - proposalDisplay.removeListeners(); - proposalDisplay = null; - } - } - - private void setMakeProposalButtonHandler() { - makeProposalButton.setOnAction(event -> { - if (GUIUtil.isReadyForTxBroadcastOrShowPopup(p2PService, walletsSetup)) { - publishMyProposal(selectedProposalType); - } - }); - } - - private void updateButtonState() { - AtomicBoolean inputsValid = new AtomicBoolean(true); - if (proposalDisplay != null) { - proposalDisplay.getInputControls().stream() - .filter(Objects::nonNull).forEach(e -> { - if (e instanceof InputTextField) { - InputTextField inputTextField = (InputTextField) e; - inputsValid.set(inputsValid.get() && - inputTextField.getValidator() != null && - inputTextField.getValidator().validate(e.getText()).isValid); - } - }); - proposalDisplay.getComboBoxes().stream() - .filter(Objects::nonNull).forEach(comboBox -> inputsValid.set(inputsValid.get() && - comboBox.getSelectionModel().getSelectedItem() != null)); - - InputTextField linkInputTextField = proposalDisplay.linkInputTextField; - inputsValid.set(inputsValid.get() && - linkInputTextField.getValidator().validate(linkInputTextField.getText()).isValid); - } - - makeProposalButton.setDisable(!inputsValid.get()); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsListItem.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsListItem.java deleted file mode 100644 index 78cb1a0a73..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsListItem.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.governance.proposals; - -import bisq.desktop.util.FormBuilder; - -import bisq.core.dao.DaoFacade; -import bisq.core.dao.state.model.governance.Ballot; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.dao.state.model.governance.Proposal; -import bisq.core.dao.state.model.governance.Vote; -import bisq.core.locale.Res; -import bisq.core.util.coin.BsqFormatter; - -import de.jensd.fx.fontawesome.AwesomeIcon; - -import com.jfoenix.controls.JFXButton; - -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; - -import javafx.beans.value.ChangeListener; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.ToString; -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -@ToString -@Slf4j -@EqualsAndHashCode -//TODO merge with vote result ProposalListItem -public class ProposalsListItem { - - enum IconButtonType { - REMOVE_PROPOSAL(Res.get("dao.proposal.table.icon.tooltip.removeProposal")), - ACCEPT(Res.get("dao.proposal.display.myVote.accepted")), - REJECT(Res.get("dao.proposal.display.myVote.rejected")), - IGNORE(Res.get("dao.proposal.display.myVote.ignored")); - @Getter - private String title; - - IconButtonType(String title) { - this.title = title; - } - } - - @Getter - private final Proposal proposal; - private final DaoFacade daoFacade; - private final BsqFormatter bsqFormatter; - - @Getter - @Nullable - private Ballot ballot; - - @Getter - private JFXButton iconButton; - - private ChangeListener phaseChangeListener; - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, lifecycle - /////////////////////////////////////////////////////////////////////////////////////////// - - ProposalsListItem(Proposal proposal, - DaoFacade daoFacade, - BsqFormatter bsqFormatter) { - this.proposal = proposal; - this.daoFacade = daoFacade; - this.bsqFormatter = bsqFormatter; - - init(); - } - - ProposalsListItem(Ballot ballot, - DaoFacade daoFacade, - BsqFormatter bsqFormatter) { - this.ballot = ballot; - this.proposal = ballot.getProposal(); - this.daoFacade = daoFacade; - this.bsqFormatter = bsqFormatter; - - init(); - } - - private void init() { - phaseChangeListener = (observable, oldValue, newValue) -> onPhaseChanged(newValue); - - daoFacade.phaseProperty().addListener(phaseChangeListener); - - onPhaseChanged(daoFacade.phaseProperty().get()); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void cleanup() { - daoFacade.phaseProperty().removeListener(phaseChangeListener); - } - - public void onPhaseChanged(DaoPhase.Phase phase) { - //noinspection IfCanBeSwitch - Label icon; - if (phase == DaoPhase.Phase.PROPOSAL) { - icon = FormBuilder.getIcon(AwesomeIcon.TRASH); - - icon.getStyleClass().addAll("icon", "dao-remove-proposal-icon"); - iconButton = new JFXButton("", icon); - boolean isMyProposal = daoFacade.isMyProposal(proposal); - if (isMyProposal) { - iconButton.setUserData(IconButtonType.REMOVE_PROPOSAL); - } - iconButton.setVisible(isMyProposal); - iconButton.setManaged(isMyProposal); - iconButton.getStyleClass().add("hidden-icon-button"); - iconButton.setTooltip(new Tooltip(Res.get("dao.proposal.table.icon.tooltip.removeProposal"))); - } else if (iconButton != null) { - iconButton.setVisible(true); - iconButton.setManaged(true); - } - - // ballot - if (ballot != null) { - Vote vote = ballot.getVote(); - - if (vote != null) { - if ((vote).isAccepted()) { - icon = FormBuilder.getIcon(AwesomeIcon.THUMBS_UP); - icon.getStyleClass().addAll("icon", "dao-accepted-icon"); - iconButton = new JFXButton("", icon); - iconButton.setUserData(IconButtonType.ACCEPT); - } else { - icon = FormBuilder.getIcon(AwesomeIcon.THUMBS_DOWN); - icon.getStyleClass().addAll("icon", "dao-rejected-icon"); - iconButton = new JFXButton("", icon); - iconButton.setUserData(IconButtonType.REJECT); - } - } else { - icon = FormBuilder.getIcon(AwesomeIcon.MINUS); - icon.getStyleClass().addAll("icon", "dao-ignored-icon"); - iconButton = new JFXButton("", icon); - iconButton.setUserData(IconButtonType.IGNORE); - } - iconButton.setTooltip(new Tooltip(Res.get("dao.proposal.table.icon.tooltip.changeVote", - ((IconButtonType) iconButton.getUserData()).getTitle(), - getNext(((IconButtonType) iconButton.getUserData())) - ))); - iconButton.getStyleClass().add("hidden-icon-button"); - iconButton.layout(); - } - } - - public String getProposalTypeAsString() { - return Res.get("dao.proposal.type." + proposal.getType().name()); - } - - private String getNext(IconButtonType iconButtonType) { - switch (iconButtonType) { - case ACCEPT: - return IconButtonType.REJECT.getTitle(); - case REJECT: - return IconButtonType.IGNORE.getTitle(); - default: - return IconButtonType.ACCEPT.getTitle(); - } - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.fxml b/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.fxml deleted file mode 100644 index c056ac3b60..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.fxml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.java deleted file mode 100644 index d8763a94c6..0000000000 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.java +++ /dev/null @@ -1,893 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.main.dao.governance.proposals; - -import bisq.desktop.common.view.ActivatableView; -import bisq.desktop.common.view.FxmlView; -import bisq.desktop.components.AutoTooltipLabel; -import bisq.desktop.components.AutoTooltipTableColumn; -import bisq.desktop.components.BusyAnimation; -import bisq.desktop.components.HyperlinkWithIcon; -import bisq.desktop.components.InputTextField; -import bisq.desktop.components.TableGroupHeadline; -import bisq.desktop.components.TitledGroupBg; -import bisq.desktop.components.TxIdTextField; -import bisq.desktop.main.dao.governance.PhasesView; -import bisq.desktop.main.overlays.popups.Popup; -import bisq.desktop.main.overlays.windows.SelectProposalWindow; -import bisq.desktop.util.DisplayUtils; -import bisq.desktop.util.GUIUtil; -import bisq.desktop.util.Layout; -import bisq.desktop.util.validation.BsqValidator; - -import bisq.core.btc.exceptions.TransactionVerificationException; -import bisq.core.btc.exceptions.WalletException; -import bisq.core.btc.listeners.BsqBalanceListener; -import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.dao.DaoFacade; -import bisq.core.dao.governance.blindvote.BlindVoteConsensus; -import bisq.core.dao.governance.blindvote.MyBlindVoteListService; -import bisq.core.dao.governance.myvote.MyVote; -import bisq.core.dao.state.DaoStateListener; -import bisq.core.dao.state.DaoStateService; -import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.governance.Ballot; -import bisq.core.dao.state.model.governance.DaoPhase; -import bisq.core.dao.state.model.governance.EvaluatedProposal; -import bisq.core.dao.state.model.governance.Proposal; -import bisq.core.dao.state.model.governance.Vote; -import bisq.core.locale.Res; -import bisq.core.user.Preferences; -import bisq.core.util.FormattingUtils; -import bisq.core.util.ParsingUtils; -import bisq.core.util.coin.BsqFormatter; -import bisq.core.util.coin.CoinFormatter; -import bisq.core.util.validation.InputValidator; - -import bisq.common.UserThread; -import bisq.common.app.DevEnv; -import bisq.common.util.Tuple2; -import bisq.common.util.Tuple3; -import bisq.common.util.Tuple4; - -import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; - -import javax.inject.Inject; -import javax.inject.Named; - -import com.jfoenix.controls.JFXButton; - -import javafx.scene.Node; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.TableCell; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.TextField; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Priority; -import javafx.scene.layout.VBox; - -import javafx.geometry.Insets; - -import org.fxmisc.easybind.EasyBind; -import org.fxmisc.easybind.Subscription; - -import javafx.beans.property.ReadOnlyObjectWrapper; -import javafx.beans.value.ChangeListener; - -import javafx.collections.FXCollections; -import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; -import javafx.collections.transformation.SortedList; - -import javafx.util.Callback; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; - -import javax.annotation.Nullable; - -import static bisq.desktop.util.FormBuilder.*; -import static bisq.desktop.util.Layout.INITIAL_WINDOW_HEIGHT; - -@FxmlView -public class ProposalsView extends ActivatableView implements BsqBalanceListener, DaoStateListener { - private final DaoFacade daoFacade; - private final BsqWalletService bsqWalletService; - private final PhasesView phasesView; - private final DaoStateService daoStateService; - private final MyBlindVoteListService myBlindVoteListService; - private final Preferences preferences; - private final BsqFormatter bsqFormatter; - private final CoinFormatter btcFormatter; - private final SelectProposalWindow selectProposalWindow; - - private final ObservableList listItems = FXCollections.observableArrayList(); - private final SortedList sortedList = new SortedList<>(listItems); - private final List