mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 09:52:23 +01:00
Merge branch 'master' of github.com:bisq-network/bisq into release/v1.3.9
# Conflicts: # build.gradle # desktop/package/linux/Dockerfile # desktop/package/linux/package.sh # desktop/package/linux/release.sh # desktop/package/macosx/create_app.sh # desktop/package/macosx/finalize.sh # desktop/package/macosx/insert_snapshot_version.sh # desktop/package/windows/package.bat # desktop/package/windows/release.bat # relay/src/main/resources/version.txt
This commit is contained in:
commit
81df879881
@ -48,14 +48,14 @@
|
||||
run ./bisq-cli --password="xyz" getversion
|
||||
[ "$status" -eq 0 ]
|
||||
echo "actual output: $output" >&2
|
||||
[ "$output" = "1.3.7" ]
|
||||
[ "$output" = "1.3.8" ]
|
||||
}
|
||||
|
||||
@test "test getversion" {
|
||||
run ./bisq-cli --password=xyz getversion
|
||||
[ "$status" -eq 0 ]
|
||||
echo "actual output: $output" >&2
|
||||
[ "$output" = "1.3.7" ]
|
||||
[ "$output" = "1.3.8" ]
|
||||
}
|
||||
|
||||
@test "test setwalletpassword \"a b c\"" {
|
||||
|
@ -27,6 +27,8 @@ import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import java.io.File;
|
||||
@ -169,7 +171,7 @@ public class ApiTestConfig {
|
||||
ArgumentAcceptingOptionSpec<String> bitcoinRegtestHostOpt =
|
||||
parser.accepts(BITCOIN_REGTEST_HOST, "Bitcoin Core regtest host")
|
||||
.withRequiredArg()
|
||||
.ofType(String.class).defaultsTo("localhost");
|
||||
.ofType(String.class).defaultsTo(InetAddress.getLoopbackAddress().getHostAddress());
|
||||
|
||||
ArgumentAcceptingOptionSpec<Integer> bitcoinRpcPortOpt =
|
||||
parser.accepts(BITCOIN_RPC_PORT, "Bitcoin Core rpc port (non-default)")
|
||||
|
@ -17,16 +17,20 @@
|
||||
|
||||
package bisq.apitest;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.config.ApiTestConfig;
|
||||
import bisq.apitest.config.BisqAppConfig;
|
||||
import bisq.apitest.method.BitcoinCliHelper;
|
||||
import bisq.cli.GrpcStubs;
|
||||
|
||||
@ -57,34 +61,41 @@ import bisq.cli.GrpcStubs;
|
||||
*/
|
||||
public class ApiTestCase {
|
||||
|
||||
// The gRPC service stubs are used by method & scenario tests, but not e2e tests.
|
||||
protected static GrpcStubs grpcStubs;
|
||||
|
||||
protected static Scaffold scaffold;
|
||||
protected static ApiTestConfig config;
|
||||
protected static BitcoinCliHelper bitcoinCli;
|
||||
|
||||
// gRPC service stubs are used by method & scenario tests, but not e2e tests.
|
||||
private static final Map<BisqAppConfig, GrpcStubs> grpcStubsCache = new HashMap<>();
|
||||
|
||||
public static void setUpScaffold(String supportingApps)
|
||||
throws InterruptedException, ExecutionException, IOException {
|
||||
scaffold = new Scaffold(supportingApps).setUp();
|
||||
config = scaffold.config;
|
||||
bitcoinCli = new BitcoinCliHelper((config));
|
||||
// For now, all grpc requests are sent to the alicedaemon, but this will need to
|
||||
// be made configurable for new test cases that call arb or bob node daemons.
|
||||
grpcStubs = new GrpcStubs("localhost", alicedaemon.apiPort, config.apiPassword);
|
||||
}
|
||||
|
||||
public static void setUpScaffold(String[] params)
|
||||
throws InterruptedException, ExecutionException, IOException {
|
||||
scaffold = new Scaffold(params).setUp();
|
||||
config = scaffold.config;
|
||||
grpcStubs = new GrpcStubs("localhost", alicedaemon.apiPort, config.apiPassword);
|
||||
}
|
||||
|
||||
public static void tearDownScaffold() {
|
||||
scaffold.tearDown();
|
||||
}
|
||||
|
||||
protected static GrpcStubs grpcStubs(BisqAppConfig bisqAppConfig) {
|
||||
if (grpcStubsCache.containsKey(bisqAppConfig)) {
|
||||
return grpcStubsCache.get(bisqAppConfig);
|
||||
} else {
|
||||
GrpcStubs stubs = new GrpcStubs(InetAddress.getLoopbackAddress().getHostAddress(),
|
||||
bisqAppConfig.apiPort, config.apiPassword);
|
||||
grpcStubsCache.put(bisqAppConfig, stubs);
|
||||
return stubs;
|
||||
}
|
||||
}
|
||||
|
||||
protected void sleep(long ms) {
|
||||
try {
|
||||
MILLISECONDS.sleep(ms);
|
||||
|
@ -27,6 +27,7 @@ import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
@ -57,7 +58,8 @@ public class GetBalanceTest extends MethodTest {
|
||||
public void testGetBalance() {
|
||||
// All tests depend on the DAO / regtest environment, and Alice's wallet is
|
||||
// initialized with 10 BTC during the scaffolding setup.
|
||||
var balance = grpcStubs.walletsService.getBalance(GetBalanceRequest.newBuilder().build()).getBalance();
|
||||
var balance = grpcStubs(alicedaemon).walletsService
|
||||
.getBalance(GetBalanceRequest.newBuilder().build()).getBalance();
|
||||
assertEquals(1000000000, balance);
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,8 @@ public class GetVersionTest extends MethodTest {
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testGetVersion() {
|
||||
var version = grpcStubs.versionService.getVersion(GetVersionRequest.newBuilder().build()).getVersion();
|
||||
var version = grpcStubs(alicedaemon).versionService
|
||||
.getVersion(GetVersionRequest.newBuilder().build()).getVersion();
|
||||
assertEquals(VERSION, version);
|
||||
}
|
||||
|
||||
|
@ -20,13 +20,17 @@ package bisq.apitest.method;
|
||||
import bisq.proto.grpc.GetBalanceRequest;
|
||||
import bisq.proto.grpc.GetFundingAddressesRequest;
|
||||
import bisq.proto.grpc.LockWalletRequest;
|
||||
import bisq.proto.grpc.RegisterDisputeAgentRequest;
|
||||
import bisq.proto.grpc.RemoveWalletPasswordRequest;
|
||||
import bisq.proto.grpc.SetWalletPasswordRequest;
|
||||
import bisq.proto.grpc.UnlockWalletRequest;
|
||||
|
||||
import static bisq.common.app.DevEnv.DEV_PRIVILEGE_PRIV_KEY;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.ApiTestCase;
|
||||
import bisq.apitest.config.BisqAppConfig;
|
||||
|
||||
public class MethodTest extends ApiTestCase {
|
||||
|
||||
@ -60,24 +64,31 @@ public class MethodTest extends ApiTestCase {
|
||||
return GetFundingAddressesRequest.newBuilder().build();
|
||||
}
|
||||
|
||||
protected final RegisterDisputeAgentRequest createRegisterDisputeAgentRequest(String disputeAgentType) {
|
||||
return RegisterDisputeAgentRequest.newBuilder()
|
||||
.setDisputeAgentType(disputeAgentType)
|
||||
.setRegistrationKey(DEV_PRIVILEGE_PRIV_KEY).build();
|
||||
}
|
||||
|
||||
// Convenience methods for calling frequently used & thoroughly tested gRPC services.
|
||||
|
||||
protected final long getBalance() {
|
||||
return grpcStubs.walletsService.getBalance(createBalanceRequest()).getBalance();
|
||||
protected final long getBalance(BisqAppConfig bisqAppConfig) {
|
||||
return grpcStubs(bisqAppConfig).walletsService.getBalance(createBalanceRequest()).getBalance();
|
||||
}
|
||||
|
||||
protected final void unlockWallet(String password, long timeout) {
|
||||
protected final void unlockWallet(BisqAppConfig bisqAppConfig, String password, long timeout) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
grpcStubs.walletsService.unlockWallet(createUnlockWalletRequest(password, timeout));
|
||||
grpcStubs(bisqAppConfig).walletsService.unlockWallet(createUnlockWalletRequest(password, timeout));
|
||||
}
|
||||
|
||||
protected final void lockWallet() {
|
||||
protected final void lockWallet(BisqAppConfig bisqAppConfig) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
grpcStubs.walletsService.lockWallet(createLockWalletRequest());
|
||||
grpcStubs(bisqAppConfig).walletsService.lockWallet(createLockWalletRequest());
|
||||
}
|
||||
|
||||
protected final String getUnusedBtcAddress() {
|
||||
return grpcStubs.walletsService.getFundingAddresses(createGetFundingAddressesRequest())
|
||||
protected final String getUnusedBtcAddress(BisqAppConfig bisqAppConfig) {
|
||||
//noinspection OptionalGetWithoutIsPresent
|
||||
return grpcStubs(bisqAppConfig).walletsService.getFundingAddresses(createGetFundingAddressesRequest())
|
||||
.getAddressBalanceInfoList()
|
||||
.stream()
|
||||
.filter(a -> a.getBalance() == 0 && a.getNumConfirmations() == 0)
|
||||
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest.method;
|
||||
|
||||
import bisq.proto.grpc.RegisterDisputeAgentRequest;
|
||||
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.config.BisqAppConfig.arbdaemon;
|
||||
import static bisq.common.app.DevEnv.DEV_PRIVILEGE_PRIV_KEY;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
@Slf4j
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
public class RegisterDisputeAgentsTest extends MethodTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
try {
|
||||
setUpScaffold("bitcoind,seednode,arbdaemon");
|
||||
} catch (Exception ex) {
|
||||
fail(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testRegisterArbitratorShouldThrowException() {
|
||||
var req =
|
||||
createRegisterDisputeAgentRequest("arbitrator");
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req));
|
||||
assertEquals("INVALID_ARGUMENT: arbitrators must be registered in a Bisq UI",
|
||||
exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testInvalidDisputeAgentTypeArgShouldThrowException() {
|
||||
var req =
|
||||
createRegisterDisputeAgentRequest("badagent");
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req));
|
||||
assertEquals("INVALID_ARGUMENT: unknown dispute agent type badagent",
|
||||
exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void testInvalidRegistrationKeyArgShouldThrowException() {
|
||||
var req = RegisterDisputeAgentRequest.newBuilder()
|
||||
.setDisputeAgentType("refundagent")
|
||||
.setRegistrationKey("invalid" + DEV_PRIVILEGE_PRIV_KEY).build();
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req));
|
||||
assertEquals("INVALID_ARGUMENT: invalid registration key",
|
||||
exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testRegisterMediator() {
|
||||
var req =
|
||||
createRegisterDisputeAgentRequest("mediator");
|
||||
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
public void testRegisterRefundAgent() {
|
||||
var req =
|
||||
createRegisterDisputeAgentRequest("refundagent");
|
||||
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void tearDown() {
|
||||
tearDownScaffold();
|
||||
}
|
||||
}
|
@ -36,13 +36,13 @@ public class WalletProtectionTest extends MethodTest {
|
||||
@Order(1)
|
||||
public void testSetWalletPassword() {
|
||||
var request = createSetWalletPasswordRequest("first-password");
|
||||
grpcStubs.walletsService.setWalletPassword(request);
|
||||
grpcStubs(alicedaemon).walletsService.setWalletPassword(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testGetBalanceOnEncryptedWalletShouldThrowException() {
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, this::getBalance);
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> getBalance(alicedaemon));
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@ -50,11 +50,10 @@ public class WalletProtectionTest extends MethodTest {
|
||||
@Order(3)
|
||||
public void testUnlockWalletFor4Seconds() {
|
||||
var request = createUnlockWalletRequest("first-password", 4);
|
||||
grpcStubs.walletsService.unlockWallet(request);
|
||||
getBalance(); // should not throw 'wallet locked' exception
|
||||
|
||||
grpcStubs(alicedaemon).walletsService.unlockWallet(request);
|
||||
getBalance(alicedaemon); // should not throw 'wallet locked' exception
|
||||
sleep(4500); // let unlock timeout expire
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, this::getBalance);
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> getBalance(alicedaemon));
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@ -62,20 +61,19 @@ public class WalletProtectionTest extends MethodTest {
|
||||
@Order(4)
|
||||
public void testGetBalanceAfterUnlockTimeExpiryShouldThrowException() {
|
||||
var request = createUnlockWalletRequest("first-password", 3);
|
||||
grpcStubs.walletsService.unlockWallet(request);
|
||||
grpcStubs(alicedaemon).walletsService.unlockWallet(request);
|
||||
sleep(4000); // let unlock timeout expire
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, this::getBalance);
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> getBalance(alicedaemon));
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
public void testLockWalletBeforeUnlockTimeoutExpiry() {
|
||||
unlockWallet("first-password", 60);
|
||||
unlockWallet(alicedaemon, "first-password", 60);
|
||||
var request = createLockWalletRequest();
|
||||
grpcStubs.walletsService.lockWallet(request);
|
||||
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, this::getBalance);
|
||||
grpcStubs(alicedaemon).walletsService.lockWallet(request);
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> getBalance(alicedaemon));
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@ -83,40 +81,39 @@ public class WalletProtectionTest extends MethodTest {
|
||||
@Order(6)
|
||||
public void testLockWalletWhenWalletAlreadyLockedShouldThrowException() {
|
||||
var request = createLockWalletRequest();
|
||||
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
grpcStubs.walletsService.lockWallet(request));
|
||||
grpcStubs(alicedaemon).walletsService.lockWallet(request));
|
||||
assertEquals("UNKNOWN: wallet is already locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(7)
|
||||
public void testUnlockWalletTimeoutOverride() {
|
||||
unlockWallet("first-password", 2);
|
||||
unlockWallet(alicedaemon, "first-password", 2);
|
||||
sleep(500); // override unlock timeout after 0.5s
|
||||
unlockWallet("first-password", 6);
|
||||
unlockWallet(alicedaemon, "first-password", 6);
|
||||
sleep(5000);
|
||||
getBalance(); // getbalance 5s after resetting unlock timeout to 6s
|
||||
getBalance(alicedaemon); // getbalance 5s after resetting unlock timeout to 6s
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(8)
|
||||
public void testSetNewWalletPassword() {
|
||||
var request = createSetWalletPasswordRequest("first-password", "second-password");
|
||||
grpcStubs.walletsService.setWalletPassword(request);
|
||||
|
||||
unlockWallet("second-password", 2);
|
||||
getBalance();
|
||||
var request = createSetWalletPasswordRequest(
|
||||
"first-password", "second-password");
|
||||
grpcStubs(alicedaemon).walletsService.setWalletPassword(request);
|
||||
unlockWallet(alicedaemon, "second-password", 2);
|
||||
getBalance(alicedaemon);
|
||||
sleep(2500); // allow time for wallet save
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(9)
|
||||
public void testSetNewWalletPasswordWithIncorrectNewPasswordShouldThrowException() {
|
||||
var request = createSetWalletPasswordRequest("bad old password", "irrelevant");
|
||||
|
||||
var request = createSetWalletPasswordRequest(
|
||||
"bad old password", "irrelevant");
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
grpcStubs.walletsService.setWalletPassword(request));
|
||||
grpcStubs(alicedaemon).walletsService.setWalletPassword(request));
|
||||
assertEquals("UNKNOWN: incorrect old password", exception.getMessage());
|
||||
}
|
||||
|
||||
@ -124,8 +121,8 @@ public class WalletProtectionTest extends MethodTest {
|
||||
@Order(10)
|
||||
public void testRemoveNewWalletPassword() {
|
||||
var request = createRemoveWalletPasswordRequest("second-password");
|
||||
grpcStubs.walletsService.removeWalletPassword(request);
|
||||
getBalance(); // should not throw 'wallet locked' exception
|
||||
grpcStubs(alicedaemon).walletsService.removeWalletPassword(request);
|
||||
getBalance(alicedaemon); // should not throw 'wallet locked' exception
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
|
@ -26,6 +26,7 @@ import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
@ -48,16 +49,17 @@ public class FundWalletScenarioTest extends ScenarioTest {
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testFundWallet() {
|
||||
long balance = getBalance(); // bisq wallet was initialized with 10 btc
|
||||
// bisq wallet was initialized with 10 btc
|
||||
long balance = getBalance(alicedaemon);
|
||||
assertEquals(1000000000, balance);
|
||||
|
||||
String unusedAddress = getUnusedBtcAddress();
|
||||
String unusedAddress = getUnusedBtcAddress(alicedaemon);
|
||||
bitcoinCli.sendToAddress(unusedAddress, "2.5");
|
||||
|
||||
bitcoinCli.generateBlocks(1);
|
||||
sleep(1500);
|
||||
|
||||
balance = getBalance();
|
||||
balance = getBalance(alicedaemon);
|
||||
assertEquals(1250000000L, balance); // new balance is 12.5 btc
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ configure(subprojects) {
|
||||
loggingVersion = '1.2'
|
||||
lombokVersion = '1.18.2'
|
||||
mockitoVersion = '3.0.0'
|
||||
netlayerVersion = '0.6.7'
|
||||
netlayerVersion = '0.6.8'
|
||||
protobufVersion = '3.10.0'
|
||||
protocVersion = protobufVersion
|
||||
pushyVersion = '0.13.2'
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
package bisq.cli;
|
||||
|
||||
import bisq.proto.grpc.DisputeAgentsGrpc;
|
||||
import bisq.proto.grpc.GetVersionGrpc;
|
||||
import bisq.proto.grpc.OffersGrpc;
|
||||
import bisq.proto.grpc.PaymentAccountsGrpc;
|
||||
@ -29,6 +30,7 @@ import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
public class GrpcStubs {
|
||||
|
||||
public final DisputeAgentsGrpc.DisputeAgentsBlockingStub disputeAgentsService;
|
||||
public final GetVersionGrpc.GetVersionBlockingStub versionService;
|
||||
public final OffersGrpc.OffersBlockingStub offersService;
|
||||
public final PaymentAccountsGrpc.PaymentAccountsBlockingStub paymentAccountsService;
|
||||
@ -46,6 +48,7 @@ public class GrpcStubs {
|
||||
}
|
||||
}));
|
||||
|
||||
this.disputeAgentsService = DisputeAgentsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.versionService = GetVersionGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.offersService = OffersGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.paymentAccountsService = PaymentAccountsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
|
@ -102,7 +102,9 @@ public class FileUtil {
|
||||
deleteDirectory(file, null, true);
|
||||
}
|
||||
|
||||
public static void deleteDirectory(File file, @Nullable File exclude, boolean ignoreLockedFiles) throws IOException {
|
||||
public static void deleteDirectory(File file,
|
||||
@Nullable File exclude,
|
||||
boolean ignoreLockedFiles) throws IOException {
|
||||
boolean excludeFileFound = false;
|
||||
if (file.isDirectory()) {
|
||||
File[] files = file.listFiles();
|
||||
@ -156,7 +158,8 @@ public class FileUtil {
|
||||
return !file.canWrite();
|
||||
}
|
||||
|
||||
public static void resourceToFile(String resourcePath, File destinationFile) throws ResourceNotFoundException, IOException {
|
||||
public static void resourceToFile(String resourcePath,
|
||||
File destinationFile) throws ResourceNotFoundException, IOException {
|
||||
try (InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath)) {
|
||||
if (inputStream == null) {
|
||||
throw new ResourceNotFoundException(resourcePath);
|
||||
@ -182,6 +185,20 @@ public class FileUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyFile(File origin, File target) throws IOException {
|
||||
if (!origin.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Files.copy(origin, target);
|
||||
} catch (IOException e) {
|
||||
log.error("Copy file failed", e);
|
||||
throw new IOException("Failed to copy " + origin + " to " + target);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void copyDirectory(File source, File destination) throws IOException {
|
||||
FileUtils.copyDirectory(source, destination);
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ package bisq.core.account.witness;
|
||||
import bisq.core.account.sign.SignedWitness;
|
||||
import bisq.core.account.sign.SignedWitnessService;
|
||||
import bisq.core.filter.FilterManager;
|
||||
import bisq.core.filter.PaymentAccountFilter;
|
||||
import bisq.core.locale.CurrencyUtil;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.offer.Offer;
|
||||
@ -718,10 +717,8 @@ public class AccountAgeWitnessService {
|
||||
filterManager.isCurrencyBanned(dispute.getContract().getOfferPayload().getCurrencyCode()) ||
|
||||
filterManager.isPaymentMethodBanned(
|
||||
PaymentMethod.getPaymentMethodById(dispute.getContract().getPaymentMethodId())) ||
|
||||
filterManager.arePeersPaymentAccountDataBanned(dispute.getContract().getBuyerPaymentAccountPayload(),
|
||||
new PaymentAccountFilter[1]) ||
|
||||
filterManager.arePeersPaymentAccountDataBanned(dispute.getContract().getSellerPaymentAccountPayload(),
|
||||
new PaymentAccountFilter[1]) ||
|
||||
filterManager.arePeersPaymentAccountDataBanned(dispute.getContract().getBuyerPaymentAccountPayload()) ||
|
||||
filterManager.arePeersPaymentAccountDataBanned(dispute.getContract().getSellerPaymentAccountPayload()) ||
|
||||
filterManager.isWitnessSignerPubKeyBanned(
|
||||
Utils.HEX.encode(dispute.getContract().getBuyerPubKeyRing().getSignaturePubKeyBytes())) ||
|
||||
filterManager.isWitnessSignerPubKeyBanned(
|
||||
|
@ -47,26 +47,38 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@Slf4j
|
||||
public class CoreApi {
|
||||
|
||||
private final CoreDisputeAgentsService coreDisputeAgentsService;
|
||||
private final CoreOffersService coreOffersService;
|
||||
private final CorePaymentAccountsService paymentAccountsService;
|
||||
private final CoreWalletsService walletsService;
|
||||
private final TradeStatisticsManager tradeStatisticsManager;
|
||||
|
||||
@Inject
|
||||
public CoreApi(CoreOffersService coreOffersService,
|
||||
public CoreApi(CoreDisputeAgentsService coreDisputeAgentsService,
|
||||
CoreOffersService coreOffersService,
|
||||
CorePaymentAccountsService paymentAccountsService,
|
||||
CoreWalletsService walletsService,
|
||||
TradeStatisticsManager tradeStatisticsManager) {
|
||||
this.coreDisputeAgentsService = coreDisputeAgentsService;
|
||||
this.coreOffersService = coreOffersService;
|
||||
this.paymentAccountsService = paymentAccountsService;
|
||||
this.walletsService = walletsService;
|
||||
this.tradeStatisticsManager = tradeStatisticsManager;
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameReturnValue")
|
||||
public String getVersion() {
|
||||
return Version.VERSION;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Dispute Agents
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void registerDisputeAgent(String disputeAgentType, String registrationKey) {
|
||||
coreDisputeAgentsService.registerDisputeAgent(disputeAgentType, registrationKey);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Offers
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
144
core/src/main/java/bisq/core/api/CoreDisputeAgentsService.java
Normal file
144
core/src/main/java/bisq/core/api/CoreDisputeAgentsService.java
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.api;
|
||||
|
||||
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
||||
import bisq.core.support.dispute.mediation.mediator.MediatorManager;
|
||||
import bisq.core.support.dispute.refund.refundagent.RefundAgent;
|
||||
import bisq.core.support.dispute.refund.refundagent.RefundAgentManager;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
import bisq.network.p2p.P2PService;
|
||||
|
||||
import bisq.common.config.Config;
|
||||
import bisq.common.crypto.KeyRing;
|
||||
|
||||
import org.bitcoinj.core.ECKey;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.common.app.DevEnv.DEV_PRIVILEGE_PRIV_KEY;
|
||||
import static java.net.InetAddress.getLoopbackAddress;
|
||||
|
||||
@Slf4j
|
||||
class CoreDisputeAgentsService {
|
||||
|
||||
private final Config config;
|
||||
private final KeyRing keyRing;
|
||||
private final MediatorManager mediatorManager;
|
||||
private final RefundAgentManager refundAgentManager;
|
||||
private final P2PService p2PService;
|
||||
private final NodeAddress nodeAddress;
|
||||
private final List<String> languageCodes;
|
||||
|
||||
@Inject
|
||||
public CoreDisputeAgentsService(Config config,
|
||||
KeyRing keyRing,
|
||||
MediatorManager mediatorManager,
|
||||
RefundAgentManager refundAgentManager,
|
||||
P2PService p2PService) {
|
||||
this.config = config;
|
||||
this.keyRing = keyRing;
|
||||
this.mediatorManager = mediatorManager;
|
||||
this.refundAgentManager = refundAgentManager;
|
||||
this.p2PService = p2PService;
|
||||
this.nodeAddress = new NodeAddress(getLoopbackAddress().getHostAddress(), config.nodePort);
|
||||
this.languageCodes = Arrays.asList("de", "en", "es", "fr");
|
||||
}
|
||||
|
||||
public void registerDisputeAgent(String disputeAgentType, String registrationKey) {
|
||||
if (!p2PService.isBootstrapped())
|
||||
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");
|
||||
|
||||
if (!registrationKey.equals(DEV_PRIVILEGE_PRIV_KEY))
|
||||
throw new IllegalArgumentException("invalid registration key");
|
||||
|
||||
ECKey ecKey;
|
||||
String signature;
|
||||
switch (disputeAgentType) {
|
||||
case "arbitrator":
|
||||
throw new IllegalArgumentException("arbitrators must be registered in a Bisq UI");
|
||||
case "mediator":
|
||||
ecKey = mediatorManager.getRegistrationKey(registrationKey);
|
||||
signature = mediatorManager.signStorageSignaturePubKey(Objects.requireNonNull(ecKey));
|
||||
registerMediator(nodeAddress, languageCodes, ecKey, signature);
|
||||
return;
|
||||
case "refundagent":
|
||||
ecKey = refundAgentManager.getRegistrationKey(registrationKey);
|
||||
signature = refundAgentManager.signStorageSignaturePubKey(Objects.requireNonNull(ecKey));
|
||||
registerRefundAgent(nodeAddress, languageCodes, ecKey, signature);
|
||||
return;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown dispute agent type " + disputeAgentType);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerMediator(NodeAddress nodeAddress,
|
||||
List<String> languageCodes,
|
||||
ECKey ecKey,
|
||||
String signature) {
|
||||
Mediator mediator = new Mediator(nodeAddress,
|
||||
keyRing.getPubKeyRing(),
|
||||
languageCodes,
|
||||
new Date().getTime(),
|
||||
ecKey.getPubKey(),
|
||||
signature,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
mediatorManager.addDisputeAgent(mediator, () -> {
|
||||
}, errorMessage -> {
|
||||
});
|
||||
mediatorManager.getDisputeAgentByNodeAddress(nodeAddress).orElseThrow(() ->
|
||||
new IllegalStateException("could not register mediator"));
|
||||
}
|
||||
|
||||
private void registerRefundAgent(NodeAddress nodeAddress,
|
||||
List<String> languageCodes,
|
||||
ECKey ecKey,
|
||||
String signature) {
|
||||
RefundAgent refundAgent = new RefundAgent(nodeAddress,
|
||||
keyRing.getPubKeyRing(),
|
||||
languageCodes,
|
||||
new Date().getTime(),
|
||||
ecKey.getPubKey(),
|
||||
signature,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
refundAgentManager.addDisputeAgent(refundAgent, () -> {
|
||||
}, errorMessage -> {
|
||||
});
|
||||
refundAgentManager.getDisputeAgentByNodeAddress(nodeAddress).orElseThrow(() ->
|
||||
new IllegalStateException("could not register refund agent"));
|
||||
}
|
||||
}
|
@ -186,10 +186,6 @@ public final class AddressEntry implements PersistablePayload {
|
||||
return context == Context.MULTI_SIG || context == Context.TRADE_PAYOUT;
|
||||
}
|
||||
|
||||
public boolean isTradable() {
|
||||
return isOpenOffer() || isTrade();
|
||||
}
|
||||
|
||||
public Coin getCoinLockedInMultiSig() {
|
||||
return Coin.valueOf(coinLockedInMultiSig);
|
||||
}
|
||||
@ -197,9 +193,10 @@ public final class AddressEntry implements PersistablePayload {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AddressEntry{" +
|
||||
"offerId='" + getOfferId() + '\'' +
|
||||
"address=" + address +
|
||||
", context=" + context +
|
||||
", address=" + getAddressString() +
|
||||
'}';
|
||||
", offerId='" + offerId + '\'' +
|
||||
", coinLockedInMultiSig=" + coinLockedInMultiSig +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
|
@ -17,38 +17,41 @@
|
||||
|
||||
package bisq.core.btc.model;
|
||||
|
||||
import bisq.common.config.Config;
|
||||
import bisq.common.proto.persistable.PersistedDataHost;
|
||||
import bisq.common.proto.persistable.UserThreadMappedPersistableEnvelope;
|
||||
import bisq.common.storage.Storage;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.crypto.DeterministicKey;
|
||||
import org.bitcoinj.wallet.Wallet;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* The List supporting our persistence solution.
|
||||
* The AddressEntries was previously stored as list, now as hashSet. We still keep the old name to reflect the
|
||||
* associated protobuf message.
|
||||
*/
|
||||
@ToString
|
||||
@Slf4j
|
||||
public final class AddressEntryList implements UserThreadMappedPersistableEnvelope, PersistedDataHost {
|
||||
transient private Storage<AddressEntryList> storage;
|
||||
transient private Wallet wallet;
|
||||
@Getter
|
||||
private List<AddressEntry> list;
|
||||
private final Set<AddressEntry> entrySet = new CopyOnWriteArraySet<>();
|
||||
|
||||
@Inject
|
||||
public AddressEntryList(Storage<AddressEntryList> storage) {
|
||||
@ -58,8 +61,10 @@ public final class AddressEntryList implements UserThreadMappedPersistableEnvelo
|
||||
@Override
|
||||
public void readPersisted() {
|
||||
AddressEntryList persisted = storage.initAndGetPersisted(this, 50);
|
||||
if (persisted != null)
|
||||
list = new ArrayList<>(persisted.getList());
|
||||
if (persisted != null) {
|
||||
entrySet.clear();
|
||||
entrySet.addAll(persisted.entrySet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -67,22 +72,22 @@ public final class AddressEntryList implements UserThreadMappedPersistableEnvelo
|
||||
// PROTO BUFFER
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private AddressEntryList(List<AddressEntry> list) {
|
||||
this.list = list;
|
||||
private AddressEntryList(Set<AddressEntry> entrySet) {
|
||||
this.entrySet.addAll(entrySet);
|
||||
}
|
||||
|
||||
public static AddressEntryList fromProto(protobuf.AddressEntryList proto) {
|
||||
return new AddressEntryList(new ArrayList<>(proto.getAddressEntryList().stream().map(AddressEntry::fromProto).collect(Collectors.toList())));
|
||||
Set<AddressEntry> entrySet = proto.getAddressEntryList().stream()
|
||||
.map(AddressEntry::fromProto)
|
||||
.collect(Collectors.toSet());
|
||||
return new AddressEntryList(entrySet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message toProtoMessage() {
|
||||
// We clone list as we got ConcurrentModificationExceptions
|
||||
List<AddressEntry> clone = new ArrayList<>(list);
|
||||
List<protobuf.AddressEntry> addressEntries = clone.stream()
|
||||
Set<protobuf.AddressEntry> addressEntries = entrySet.stream()
|
||||
.map(AddressEntry::toProtoMessage)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
.collect(Collectors.toSet());
|
||||
return protobuf.PersistableEnvelope.newBuilder()
|
||||
.setAddressEntryList(protobuf.AddressEntryList.newBuilder()
|
||||
.addAllAddressEntry(addressEntries))
|
||||
@ -97,29 +102,50 @@ public final class AddressEntryList implements UserThreadMappedPersistableEnvelo
|
||||
public void onWalletReady(Wallet wallet) {
|
||||
this.wallet = wallet;
|
||||
|
||||
if (list != null) {
|
||||
list.forEach(addressEntry -> {
|
||||
if (!entrySet.isEmpty()) {
|
||||
Set<AddressEntry> toBeRemoved = new HashSet<>();
|
||||
entrySet.forEach(addressEntry -> {
|
||||
DeterministicKey keyFromPubHash = (DeterministicKey) wallet.findKeyFromPubHash(addressEntry.getPubKeyHash());
|
||||
if (keyFromPubHash != null) {
|
||||
Address addressFromKey = keyFromPubHash.toAddress(Config.baseCurrencyNetworkParameters());
|
||||
// We want to ensure key and address matches in case we have address in entry available already
|
||||
if (addressEntry.getAddress() == null || addressFromKey.equals(addressEntry.getAddress())) {
|
||||
addressEntry.setDeterministicKey(keyFromPubHash);
|
||||
} else {
|
||||
log.error("Key from addressEntry not found in that wallet " + addressEntry.toString());
|
||||
log.error("We found an address entry without key but cannot apply the key as the address " +
|
||||
"is not matching. " +
|
||||
"We remove that entry as it seems it is not compatible with our wallet. " +
|
||||
"addressFromKey={}, addressEntry.getAddress()={}",
|
||||
addressFromKey, addressEntry.getAddress());
|
||||
toBeRemoved.add(addressEntry);
|
||||
}
|
||||
} else {
|
||||
log.error("Key from addressEntry {} not found in that wallet. We remove that entry. " +
|
||||
"This is expected at restore from seeds.", addressEntry.toString());
|
||||
toBeRemoved.add(addressEntry);
|
||||
}
|
||||
});
|
||||
|
||||
toBeRemoved.forEach(entrySet::remove);
|
||||
} else {
|
||||
list = new ArrayList<>();
|
||||
add(new AddressEntry(wallet.freshReceiveKey(), AddressEntry.Context.ARBITRATOR));
|
||||
// As long the old arbitration domain is not removed from the code base we still support it here.
|
||||
entrySet.add(new AddressEntry(wallet.freshReceiveKey(), AddressEntry.Context.ARBITRATOR));
|
||||
}
|
||||
|
||||
// In case we restore from seed words and have balance we need to add the relevant addresses to our list.
|
||||
// IssuedReceiveAddresses does not contain all addresses where we expect balance so we need to listen to
|
||||
// incoming txs at blockchain sync to add the rest.
|
||||
if (wallet.getBalance().isPositive()) {
|
||||
wallet.getIssuedReceiveAddresses().forEach(address -> {
|
||||
wallet.getIssuedReceiveAddresses().stream()
|
||||
.filter(this::isAddressNotInEntries)
|
||||
.forEach(address -> {
|
||||
log.info("Create AddressEntry for IssuedReceiveAddress. address={}", address.toString());
|
||||
add(new AddressEntry((DeterministicKey) wallet.findKeyFromPubHash(address.getHash160()), AddressEntry.Context.AVAILABLE));
|
||||
});
|
||||
DeterministicKey key = (DeterministicKey) wallet.findKeyFromPubHash(address.getHash160());
|
||||
if (key != null) {
|
||||
// Address will be derived from key in getAddress method
|
||||
entrySet.add(new AddressEntry(key, AddressEntry.Context.AVAILABLE));
|
||||
}
|
||||
persist();
|
||||
});
|
||||
}
|
||||
|
||||
// We add those listeners to get notified about potential new transactions and
|
||||
@ -127,62 +153,41 @@ public final class AddressEntryList implements UserThreadMappedPersistableEnvelo
|
||||
// but can help as well in case the addressEntry list would miss an address where the wallet was received
|
||||
// funds (e.g. if the user sends funds to an address which has not been provided in the main UI - like from the
|
||||
// wallet details window).
|
||||
wallet.addCoinsReceivedEventListener((w, tx, prevBalance, newBalance) -> {
|
||||
updateList(tx);
|
||||
wallet.addCoinsReceivedEventListener((wallet1, tx, prevBalance, newBalance) -> {
|
||||
maybeAddNewAddressEntry(tx);
|
||||
});
|
||||
wallet.addCoinsSentEventListener((w, tx, prevBalance, newBalance) -> {
|
||||
updateList(tx);
|
||||
wallet.addCoinsSentEventListener((wallet1, tx, prevBalance, newBalance) -> {
|
||||
maybeAddNewAddressEntry(tx);
|
||||
});
|
||||
}
|
||||
|
||||
private void updateList(Transaction tx) {
|
||||
tx.getOutputs().stream()
|
||||
.filter(output -> output.isMine(wallet))
|
||||
.map(output -> output.getAddressFromP2PKHScript(wallet.getNetworkParameters()))
|
||||
.filter(Objects::nonNull)
|
||||
.filter(address -> !listContainsEntryWithAddress(address.toBase58()))
|
||||
.map(address -> (DeterministicKey) wallet.findKeyFromPubHash(address.getHash160()))
|
||||
.filter(Objects::nonNull)
|
||||
.map(deterministicKey -> new AddressEntry(deterministicKey, AddressEntry.Context.AVAILABLE))
|
||||
.forEach(addressEntry -> list.add(addressEntry));
|
||||
}
|
||||
|
||||
private boolean listContainsEntryWithAddress(String addressString) {
|
||||
return list.stream().anyMatch(addressEntry -> Objects.equals(addressEntry.getAddressString(), addressString));
|
||||
}
|
||||
|
||||
private boolean add(AddressEntry addressEntry) {
|
||||
return list.add(addressEntry);
|
||||
}
|
||||
|
||||
private boolean remove(AddressEntry addressEntry) {
|
||||
return list.remove(addressEntry);
|
||||
}
|
||||
|
||||
public AddressEntry addAddressEntry(AddressEntry addressEntry) {
|
||||
boolean changed = add(addressEntry);
|
||||
if (changed)
|
||||
persist();
|
||||
return addressEntry;
|
||||
}
|
||||
|
||||
public void swapTradeToSavings(String offerId) {
|
||||
list.stream().filter(addressEntry -> offerId.equals(addressEntry.getOfferId()))
|
||||
.findAny().ifPresent(this::swapToAvailable);
|
||||
public ImmutableList<AddressEntry> getAddressEntriesAsListImmutable() {
|
||||
return ImmutableList.copyOf(entrySet);
|
||||
}
|
||||
|
||||
public void addAddressEntry(AddressEntry addressEntry) {
|
||||
boolean setChangedByAdd = entrySet.add(addressEntry);
|
||||
if (setChangedByAdd)
|
||||
persist();
|
||||
}
|
||||
|
||||
public void swapToAvailable(AddressEntry addressEntry) {
|
||||
boolean changed1 = remove(addressEntry);
|
||||
boolean changed2 = add(new AddressEntry(addressEntry.getKeyPair(), AddressEntry.Context.AVAILABLE));
|
||||
if (changed1 || changed2)
|
||||
boolean setChangedByRemove = entrySet.remove(addressEntry);
|
||||
boolean setChangedByAdd = entrySet.add(new AddressEntry(addressEntry.getKeyPair(), AddressEntry.Context.AVAILABLE));
|
||||
if (setChangedByRemove || setChangedByAdd) {
|
||||
persist();
|
||||
}
|
||||
}
|
||||
|
||||
public AddressEntry swapAvailableToAddressEntryWithOfferId(AddressEntry addressEntry, AddressEntry.Context context, String offerId) {
|
||||
boolean changed1 = remove(addressEntry);
|
||||
public AddressEntry swapAvailableToAddressEntryWithOfferId(AddressEntry addressEntry,
|
||||
AddressEntry.Context context,
|
||||
String offerId) {
|
||||
boolean setChangedByRemove = entrySet.remove(addressEntry);
|
||||
final AddressEntry newAddressEntry = new AddressEntry(addressEntry.getKeyPair(), context, offerId);
|
||||
boolean changed2 = add(newAddressEntry);
|
||||
if (changed1 || changed2)
|
||||
boolean setChangedByAdd = entrySet.add(newAddressEntry);
|
||||
if (setChangedByRemove || setChangedByAdd)
|
||||
persist();
|
||||
|
||||
return newAddressEntry;
|
||||
@ -192,7 +197,24 @@ public final class AddressEntryList implements UserThreadMappedPersistableEnvelo
|
||||
storage.queueUpForSave(50);
|
||||
}
|
||||
|
||||
public Stream<AddressEntry> stream() {
|
||||
return list.stream();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void maybeAddNewAddressEntry(Transaction tx) {
|
||||
tx.getOutputs().stream()
|
||||
.filter(output -> output.isMine(wallet))
|
||||
.map(output -> output.getAddressFromP2PKHScript(wallet.getNetworkParameters()))
|
||||
.filter(Objects::nonNull)
|
||||
.filter(this::isAddressNotInEntries)
|
||||
.map(address -> (DeterministicKey) wallet.findKeyFromPubHash(address.getHash160()))
|
||||
.filter(Objects::nonNull)
|
||||
.map(deterministicKey -> new AddressEntry(deterministicKey, AddressEntry.Context.AVAILABLE))
|
||||
.forEach(this::addAddressEntry);
|
||||
}
|
||||
|
||||
private boolean isAddressNotInEntries(Address address) {
|
||||
return entrySet.stream().noneMatch(e -> address.equals(e.getAddress()));
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,6 @@ import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.util.concurrent.Service;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -498,7 +497,7 @@ public class WalletsSetup {
|
||||
}
|
||||
|
||||
public Set<Address> getAddressesByContext(@SuppressWarnings("SameParameterValue") AddressEntry.Context context) {
|
||||
return ImmutableList.copyOf(addressEntryList.getList()).stream()
|
||||
return addressEntryList.getAddressEntriesAsListImmutable().stream()
|
||||
.filter(addressEntry -> addressEntry.getContext() == context)
|
||||
.map(AddressEntry::getAddress)
|
||||
.collect(Collectors.toSet());
|
||||
|
@ -47,7 +47,6 @@ import org.bitcoinj.wallet.Wallet;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
|
||||
@ -108,8 +107,8 @@ public class BtcWalletService extends WalletService {
|
||||
void decryptWallet(@NotNull KeyParameter key) {
|
||||
super.decryptWallet(key);
|
||||
|
||||
addressEntryList.stream().forEach(e -> {
|
||||
final DeterministicKey keyPair = e.getKeyPair();
|
||||
addressEntryList.getAddressEntriesAsListImmutable().stream().forEach(e -> {
|
||||
DeterministicKey keyPair = e.getKeyPair();
|
||||
if (keyPair.isEncrypted())
|
||||
e.setDeterministicKey(keyPair.decrypt(key));
|
||||
});
|
||||
@ -119,8 +118,8 @@ public class BtcWalletService extends WalletService {
|
||||
@Override
|
||||
void encryptWallet(KeyCrypterScrypt keyCrypterScrypt, KeyParameter key) {
|
||||
super.encryptWallet(keyCrypterScrypt, key);
|
||||
addressEntryList.stream().forEach(e -> {
|
||||
final DeterministicKey keyPair = e.getKeyPair();
|
||||
addressEntryList.getAddressEntriesAsListImmutable().stream().forEach(e -> {
|
||||
DeterministicKey keyPair = e.getKeyPair();
|
||||
if (keyPair.isEncrypted())
|
||||
e.setDeterministicKey(keyPair.encrypt(keyCrypterScrypt, key));
|
||||
});
|
||||
@ -157,12 +156,18 @@ public class BtcWalletService extends WalletService {
|
||||
// Proposal txs
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public Transaction completePreparedReimbursementRequestTx(Coin issuanceAmount, Address issuanceAddress, Transaction feeTx, byte[] opReturnData)
|
||||
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)
|
||||
public Transaction completePreparedCompensationRequestTx(Coin issuanceAmount,
|
||||
Address issuanceAddress,
|
||||
Transaction feeTx,
|
||||
byte[] opReturnData)
|
||||
throws TransactionVerificationException, WalletException, InsufficientMoneyException {
|
||||
return completePreparedProposalTx(feeTx, opReturnData, issuanceAmount, issuanceAddress);
|
||||
}
|
||||
@ -292,7 +297,8 @@ public class BtcWalletService extends WalletService {
|
||||
return completePreparedBsqTxWithBtcFee(preparedTx, opReturnData);
|
||||
}
|
||||
|
||||
private Transaction completePreparedBsqTxWithBtcFee(Transaction preparedTx, byte[] opReturnData) throws InsufficientMoneyException, TransactionVerificationException, WalletException {
|
||||
private Transaction completePreparedBsqTxWithBtcFee(Transaction preparedTx,
|
||||
byte[] opReturnData) throws InsufficientMoneyException, TransactionVerificationException, WalletException {
|
||||
// Remember index for first BTC input
|
||||
int indexOfBtcFirstInput = preparedTx.getInputs().size();
|
||||
|
||||
@ -306,7 +312,8 @@ public class BtcWalletService extends WalletService {
|
||||
return tx;
|
||||
}
|
||||
|
||||
private Transaction addInputsForMinerFee(Transaction preparedTx, byte[] opReturnData) throws InsufficientMoneyException {
|
||||
private Transaction addInputsForMinerFee(Transaction preparedTx,
|
||||
byte[] opReturnData) throws InsufficientMoneyException {
|
||||
// safety check counter to avoid endless loops
|
||||
int counter = 0;
|
||||
// estimated size of input sig
|
||||
@ -421,7 +428,9 @@ public class BtcWalletService extends WalletService {
|
||||
return completePreparedBsqTx(preparedBsqTx, isSendTx, null);
|
||||
}
|
||||
|
||||
public Transaction completePreparedBsqTx(Transaction preparedBsqTx, boolean useCustomTxFee, @Nullable byte[] opReturnData) throws
|
||||
public Transaction completePreparedBsqTx(Transaction preparedBsqTx,
|
||||
boolean useCustomTxFee,
|
||||
@Nullable byte[] opReturnData) throws
|
||||
TransactionVerificationException, WalletException, InsufficientMoneyException {
|
||||
|
||||
// preparedBsqTx has following structure:
|
||||
@ -545,7 +554,8 @@ public class BtcWalletService extends WalletService {
|
||||
// AddressEntry
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public Optional<AddressEntry> getAddressEntry(String offerId, @SuppressWarnings("SameParameterValue") AddressEntry.Context context) {
|
||||
public Optional<AddressEntry> getAddressEntry(String offerId,
|
||||
@SuppressWarnings("SameParameterValue") AddressEntry.Context context) {
|
||||
return getAddressEntryListAsImmutableList().stream()
|
||||
.filter(e -> offerId.equals(e.getOfferId()))
|
||||
.filter(e -> context == e.getContext())
|
||||
@ -592,17 +602,14 @@ public class BtcWalletService extends WalletService {
|
||||
return getOrCreateAddressEntry(context, addressEntry);
|
||||
}
|
||||
|
||||
public AddressEntry getNewAddressEntry(String offerId, AddressEntry.Context context) {
|
||||
public void getNewAddressEntry(String offerId, AddressEntry.Context context) {
|
||||
AddressEntry entry = new AddressEntry(wallet.freshReceiveKey(), context, offerId);
|
||||
addressEntryList.addAddressEntry(entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public AddressEntry recoverAddressEntry(String offerId, String address, AddressEntry.Context context) {
|
||||
var available = findAddressEntry(address, AddressEntry.Context.AVAILABLE);
|
||||
if (!available.isPresent())
|
||||
return null;
|
||||
return addressEntryList.swapAvailableToAddressEntryWithOfferId(available.get(), context, offerId);
|
||||
public void recoverAddressEntry(String offerId, String address, AddressEntry.Context context) {
|
||||
findAddressEntry(address, AddressEntry.Context.AVAILABLE).ifPresent(addressEntry ->
|
||||
addressEntryList.swapAvailableToAddressEntryWithOfferId(addressEntry, context, offerId));
|
||||
}
|
||||
|
||||
private AddressEntry getOrCreateAddressEntry(AddressEntry.Context context, Optional<AddressEntry> addressEntry) {
|
||||
@ -655,19 +662,17 @@ public class BtcWalletService extends WalletService {
|
||||
}
|
||||
|
||||
public List<AddressEntry> getAddressEntryListAsImmutableList() {
|
||||
return ImmutableList.copyOf(addressEntryList.getList());
|
||||
return addressEntryList.getAddressEntriesAsListImmutable();
|
||||
}
|
||||
|
||||
public void swapTradeEntryToAvailableEntry(String offerId, AddressEntry.Context context) {
|
||||
Optional<AddressEntry> addressEntryOptional = getAddressEntryListAsImmutableList().stream()
|
||||
getAddressEntryListAsImmutableList().stream()
|
||||
.filter(e -> offerId.equals(e.getOfferId()))
|
||||
.filter(e -> context == e.getContext())
|
||||
.findAny();
|
||||
addressEntryOptional.ifPresent(e -> {
|
||||
.forEach(e -> {
|
||||
log.info("swap addressEntry with address {} and offerId {} from context {} to available",
|
||||
e.getAddressString(), e.getOfferId(), context);
|
||||
addressEntryList.swapToAvailable(e);
|
||||
saveAddressEntryList();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -371,26 +371,21 @@ public class FilterManager {
|
||||
return requireUpdateToNewVersion;
|
||||
}
|
||||
|
||||
public boolean arePeersPaymentAccountDataBanned(PaymentAccountPayload paymentAccountPayload,
|
||||
PaymentAccountFilter[] appliedPaymentAccountFilter) {
|
||||
public boolean arePeersPaymentAccountDataBanned(PaymentAccountPayload paymentAccountPayload) {
|
||||
return getFilter() != null &&
|
||||
getFilter().getBannedPaymentAccounts().stream()
|
||||
.filter(paymentAccountFilter -> paymentAccountFilter.getPaymentMethodId().equals(
|
||||
paymentAccountPayload.getPaymentMethodId()))
|
||||
.anyMatch(paymentAccountFilter -> {
|
||||
final boolean samePaymentMethodId = paymentAccountFilter.getPaymentMethodId().equals(
|
||||
paymentAccountPayload.getPaymentMethodId());
|
||||
if (samePaymentMethodId) {
|
||||
try {
|
||||
Method method = paymentAccountPayload.getClass().getMethod(paymentAccountFilter.getGetMethodName());
|
||||
String result = (String) method.invoke(paymentAccountPayload);
|
||||
appliedPaymentAccountFilter[0] = paymentAccountFilter;
|
||||
return result.toLowerCase().equals(paymentAccountFilter.getValue().toLowerCase());
|
||||
// We invoke getter methods (no args), e.g. getHolderName
|
||||
String valueFromInvoke = (String) method.invoke(paymentAccountPayload);
|
||||
return valueFromInvoke.equalsIgnoreCase(paymentAccountFilter.getValue());
|
||||
} catch (Throwable e) {
|
||||
log.error(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -200,18 +200,19 @@ public class MultipleHolderNameDetection {
|
||||
|
||||
private Map<String, List<Dispute>> getAllDisputesByTraderMap() {
|
||||
Map<String, List<Dispute>> allDisputesByTraderMap = new HashMap<>();
|
||||
disputeManager.getDisputesAsObservableList()
|
||||
.forEach(dispute -> {
|
||||
disputeManager.getDisputesAsObservableList().stream()
|
||||
.filter(dispute -> {
|
||||
Contract contract = dispute.getContract();
|
||||
PaymentAccountPayload paymentAccountPayload = isBuyer(dispute) ?
|
||||
contract.getBuyerPaymentAccountPayload() :
|
||||
contract.getSellerPaymentAccountPayload();
|
||||
if (paymentAccountPayload instanceof PayloadWithHolderName) {
|
||||
return paymentAccountPayload instanceof PayloadWithHolderName;
|
||||
})
|
||||
.forEach(dispute -> {
|
||||
String traderPubKeyHash = getSigPubKeyHashAsHex(dispute);
|
||||
allDisputesByTraderMap.putIfAbsent(traderPubKeyHash, new ArrayList<>());
|
||||
List<Dispute> disputes = allDisputesByTraderMap.get(traderPubKeyHash);
|
||||
disputes.add(dispute);
|
||||
}
|
||||
});
|
||||
return allDisputesByTraderMap;
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
package bisq.core.trade.protocol.tasks;
|
||||
|
||||
import bisq.core.filter.FilterManager;
|
||||
import bisq.core.filter.PaymentAccountFilter;
|
||||
import bisq.core.payment.payload.PaymentAccountPayload;
|
||||
import bisq.core.trade.Trade;
|
||||
|
||||
@ -42,9 +41,8 @@ public class ApplyFilter extends TradeTask {
|
||||
try {
|
||||
runInterceptHook();
|
||||
|
||||
final NodeAddress nodeAddress = processModel.getTempTradingPeerNodeAddress();
|
||||
NodeAddress nodeAddress = processModel.getTempTradingPeerNodeAddress();
|
||||
PaymentAccountPayload paymentAccountPayload = checkNotNull(processModel.getTradingPeer().getPaymentAccountPayload());
|
||||
final PaymentAccountFilter[] appliedPaymentAccountFilter = new PaymentAccountFilter[1];
|
||||
|
||||
FilterManager filterManager = processModel.getFilterManager();
|
||||
if (nodeAddress != null && filterManager.isNodeAddressBanned(nodeAddress)) {
|
||||
@ -56,13 +54,12 @@ public class ApplyFilter extends TradeTask {
|
||||
} else if (trade.getOffer() != null && filterManager.isCurrencyBanned(trade.getOffer().getCurrencyCode())) {
|
||||
failed("Currency is banned.\n" +
|
||||
"Currency code=" + trade.getOffer().getCurrencyCode());
|
||||
} else if (filterManager.isPaymentMethodBanned(trade.getOffer().getPaymentMethod())) {
|
||||
} else if (filterManager.isPaymentMethodBanned(checkNotNull(trade.getOffer()).getPaymentMethod())) {
|
||||
failed("Payment method is banned.\n" +
|
||||
"Payment method=" + trade.getOffer().getPaymentMethod().getId());
|
||||
} else if (filterManager.arePeersPaymentAccountDataBanned(paymentAccountPayload, appliedPaymentAccountFilter)) {
|
||||
} else if (filterManager.arePeersPaymentAccountDataBanned(paymentAccountPayload)) {
|
||||
failed("Other trader is banned by their trading account data.\n" +
|
||||
"paymentAccountPayload=" + paymentAccountPayload.getPaymentDetails() + "\n" +
|
||||
"banFilter=" + appliedPaymentAccountFilter[0].toString());
|
||||
"paymentAccountPayload=" + paymentAccountPayload.getPaymentDetails());
|
||||
} else if (filterManager.requireUpdateToNewVersionForTrading()) {
|
||||
failed("Your version of Bisq is not compatible for trading anymore. " +
|
||||
"Please update to the latest Bisq version at https://bisq.network/downloads.");
|
||||
|
@ -166,6 +166,7 @@ public class XmrTxProofService implements AssetTxProofService {
|
||||
|
||||
if (!preferences.findAutoConfirmSettings("XMR").isPresent()) {
|
||||
log.error("AutoConfirmSettings is not present");
|
||||
return;
|
||||
}
|
||||
autoConfirmSettings = preferences.findAutoConfirmSettings("XMR").get();
|
||||
|
||||
|
@ -35,7 +35,7 @@ shared.no=No
|
||||
shared.iUnderstand=I understand
|
||||
shared.na=N/A
|
||||
shared.shutDown=Shut down
|
||||
shared.reportBug=Report bug at GitHub issues
|
||||
shared.reportBug=Report bug on GitHub
|
||||
shared.buyBitcoin=Buy bitcoin
|
||||
shared.sellBitcoin=Sell bitcoin
|
||||
shared.buyCurrency=Buy {0}
|
||||
@ -94,21 +94,21 @@ shared.BTCMinMax=BTC (min - max)
|
||||
shared.removeOffer=Remove offer
|
||||
shared.dontRemoveOffer=Don't remove offer
|
||||
shared.editOffer=Edit offer
|
||||
shared.openLargeQRWindow=Open large QR-Code window
|
||||
shared.openLargeQRWindow=Open large QR code window
|
||||
shared.tradingAccount=Trading account
|
||||
shared.faq=Visit FAQ web page
|
||||
shared.faq=Visit FAQ page
|
||||
shared.yesCancel=Yes, cancel
|
||||
shared.nextStep=Next step
|
||||
shared.selectTradingAccount=Select trading account
|
||||
shared.fundFromSavingsWalletButton=Transfer funds from Bisq wallet
|
||||
shared.fundFromExternalWalletButton=Open your external wallet for funding
|
||||
shared.openDefaultWalletFailed=Opening a default Bitcoin wallet application has failed. Perhaps you don't have one installed?
|
||||
shared.openDefaultWalletFailed=Failed to open a Bitcoin wallet application. Are you sure you have one installed?
|
||||
shared.distanceInPercent=Distance in % from market price
|
||||
shared.belowInPercent=Below % from market price
|
||||
shared.aboveInPercent=Above % from market price
|
||||
shared.enterPercentageValue=Enter % value
|
||||
shared.OR=OR
|
||||
shared.notEnoughFunds=You don''t have enough funds in your Bisq wallet.\nYou need {0} but you have only {1} in your Bisq wallet.\n\nPlease fund the trade from an external Bitcoin wallet or fund your Bisq wallet at \"Funds/Receive funds\".
|
||||
shared.notEnoughFunds=You don''t have enough funds in your Bisq wallet for this transaction—{0} is needed but only {1} is available.\n\nPlease add funds from an external wallet, or fund your Bisq wallet at Funds > Receive Funds.
|
||||
shared.waitingForFunds=Waiting for funds...
|
||||
shared.depositTransactionId=Deposit transaction ID
|
||||
shared.TheBTCBuyer=The BTC buyer
|
||||
@ -116,22 +116,22 @@ shared.You=You
|
||||
shared.reasonForPayment=Reason for payment
|
||||
shared.sendingConfirmation=Sending confirmation...
|
||||
shared.sendingConfirmationAgain=Please send confirmation again
|
||||
shared.exportCSV=Export to csv
|
||||
shared.exportCSV=Export to CSV
|
||||
shared.exportJSON=Export to JSON
|
||||
shared.noDateAvailable=No date available
|
||||
shared.noDetailsAvailable=No details available
|
||||
shared.notUsedYet=Not used yet
|
||||
shared.date=Date
|
||||
shared.sendFundsDetailsWithFee=Sending: {0}\nFrom address: {1}\nTo receiving address: {2}.\nRequired mining fee is: {3} ({4} satoshis/byte)\nTransaction size: {5} Kb\n\nThe recipient will receive: {6}\n\nAre you sure you want to withdraw this amount?
|
||||
shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n
|
||||
shared.sendFundsDetailsDust=Bisq detected that this transaction would create a change output which is below the minimum dust threshold (and therefore not allowed by Bitcoin consensus rules). Instead, this dust ({0} satoshi{1}) will be added to the mining fee.\n\n\n
|
||||
shared.copyToClipboard=Copy to clipboard
|
||||
shared.language=Language
|
||||
shared.country=Country
|
||||
shared.applyAndShutDown=Apply and shut down
|
||||
shared.selectPaymentMethod=Select payment method
|
||||
shared.accountNameAlreadyUsed=That account name is already used in a saved account.\nPlease use another name.
|
||||
shared.accountNameAlreadyUsed=That account name is already used for another saved account.\nPlease choose another name.
|
||||
shared.askConfirmDeleteAccount=Do you really want to delete the selected account?
|
||||
shared.cannotDeleteAccount=You cannot delete that account because it is used in an open offer or in a trade.
|
||||
shared.cannotDeleteAccount=You cannot delete that account because it is being used in an open offer (or in an open trade).
|
||||
shared.noAccountsSetupYet=There are no accounts set up yet
|
||||
shared.manageAccounts=Manage accounts
|
||||
shared.addNewAccount=Add new account
|
||||
@ -375,10 +375,10 @@ offerbook.deactivateOffer.failed=Deactivating of offer failed:\n{0}
|
||||
offerbook.activateOffer.failed=Publishing of offer failed:\n{0}
|
||||
offerbook.withdrawFundsHint=You can withdraw the funds you paid in from the {0} screen.
|
||||
|
||||
offerbook.warning.noTradingAccountForCurrency.headline=No trading account for selected currency
|
||||
offerbook.warning.noTradingAccountForCurrency.msg=You don't have a trading account for the selected currency.\nDo you want to create an offer with one of your existing trading accounts?
|
||||
offerbook.warning.noMatchingAccount.headline=No matching trading account.
|
||||
offerbook.warning.noMatchingAccount.msg=To take this offer, you will need to set up a payment account using this payment method.\n\nWould you like to do this now?
|
||||
offerbook.warning.noTradingAccountForCurrency.headline=No payment account for selected currency
|
||||
offerbook.warning.noTradingAccountForCurrency.msg=You don't have a payment account set up for the selected currency.\n\nWould you like to create an offer for another currency instead?
|
||||
offerbook.warning.noMatchingAccount.headline=No matching payment account.
|
||||
offerbook.warning.noMatchingAccount.msg=This offer uses a payment method you haven't set up yet. \n\nWould you like to set up a new payment account now?
|
||||
|
||||
offerbook.warning.counterpartyTradeRestrictions=This offer cannot be taken due to counterparty trade restrictions
|
||||
|
||||
@ -1931,9 +1931,9 @@ 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=Markets API operator
|
||||
dao.bond.bondedRoleType.MARKETS_OPERATOR=Markets operator
|
||||
# suppress inspection "UnusedProperty"
|
||||
dao.bond.bondedRoleType.BSQ_EXPLORER_OPERATOR=BSQ explorer operator
|
||||
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"
|
||||
@ -2512,8 +2512,8 @@ offerDetailsWindow.confirm.taker=Confirm: Take offer to {0} bitcoin
|
||||
offerDetailsWindow.creationDate=Creation date
|
||||
offerDetailsWindow.makersOnion=Maker's onion address
|
||||
|
||||
qRCodeWindow.headline=QR-Code
|
||||
qRCodeWindow.msg=Please use that QR-Code for funding your Bisq wallet from your external wallet.
|
||||
qRCodeWindow.headline=QR Code
|
||||
qRCodeWindow.msg=Please use this QR code for funding your Bisq wallet from your external wallet.
|
||||
qRCodeWindow.request=Payment request:\n{0}
|
||||
|
||||
selectDepositTxWindow.headline=Select deposit transaction for dispute
|
||||
@ -2859,8 +2859,8 @@ systemTray.tooltip=Bisq: A decentralized bitcoin exchange network
|
||||
# GUI Util
|
||||
####################################################################
|
||||
|
||||
guiUtil.miningFeeInfo=Please be sure that the mining fee used at your external wallet is \
|
||||
at least {0} satoshis/byte. Otherwise the trade transactions cannot be confirmed and a trade would end up in a dispute.
|
||||
guiUtil.miningFeeInfo=Please be sure that the mining fee used by your external wallet is \
|
||||
at least {0} satoshis/byte. Otherwise the trade transactions may not be confirmed in time and the trade will end up in a dispute.
|
||||
|
||||
guiUtil.accountExport.savedToPath=Trading accounts saved to path:\n{0}
|
||||
guiUtil.accountExport.noAccountSetup=You don't have trading accounts set up for exporting.
|
||||
@ -3036,6 +3036,8 @@ Ideally you should specify the date your wallet seed was created.\n\n\n\
|
||||
Are you sure you want to go ahead without specifying a wallet date?
|
||||
seed.restore.success=Wallets restored successfully with the new seed words.\n\nYou need to shut down and restart the application.
|
||||
seed.restore.error=An error occurred when restoring the wallets with seed words.{0}
|
||||
seed.restore.openOffers.warn=You have open offers which will be removed if you restore from seed words.\n\
|
||||
Are you sure that you want to continue?
|
||||
|
||||
|
||||
####################################################################
|
||||
|
@ -218,7 +218,7 @@ public class AccountAgeWitnessServiceTest {
|
||||
when(filterManager.isNodeAddressBanned(any())).thenReturn(false);
|
||||
when(filterManager.isCurrencyBanned(any())).thenReturn(false);
|
||||
when(filterManager.isPaymentMethodBanned(any())).thenReturn(false);
|
||||
when(filterManager.arePeersPaymentAccountDataBanned(any(), any())).thenReturn(false);
|
||||
when(filterManager.arePeersPaymentAccountDataBanned(any())).thenReturn(false);
|
||||
when(filterManager.isWitnessSignerPubKeyBanned(any())).thenReturn(false);
|
||||
|
||||
when(chargeBackRisk.hasChargebackRisk(any(), any())).thenReturn(true);
|
||||
|
@ -0,0 +1,45 @@
|
||||
package bisq.daemon.grpc;
|
||||
|
||||
import bisq.core.api.CoreApi;
|
||||
|
||||
import bisq.proto.grpc.DisputeAgentsGrpc;
|
||||
import bisq.proto.grpc.RegisterDisputeAgentReply;
|
||||
import bisq.proto.grpc.RegisterDisputeAgentRequest;
|
||||
|
||||
import io.grpc.Status;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
class GrpcDisputeAgentsService extends DisputeAgentsGrpc.DisputeAgentsImplBase {
|
||||
|
||||
private final CoreApi coreApi;
|
||||
|
||||
@Inject
|
||||
public GrpcDisputeAgentsService(CoreApi coreApi) {
|
||||
this.coreApi = coreApi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDisputeAgent(RegisterDisputeAgentRequest req,
|
||||
StreamObserver<RegisterDisputeAgentReply> responseObserver) {
|
||||
try {
|
||||
coreApi.registerDisputeAgent(req.getDisputeAgentType(), req.getRegistrationKey());
|
||||
var reply = RegisterDisputeAgentReply.newBuilder().build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
} catch (IllegalArgumentException cause) {
|
||||
var ex = new StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription(cause.getMessage()));
|
||||
responseObserver.onError(ex);
|
||||
throw ex;
|
||||
} catch (IllegalStateException cause) {
|
||||
var ex = new StatusRuntimeException(Status.UNKNOWN.withDescription(cause.getMessage()));
|
||||
responseObserver.onError(ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
@ -51,11 +51,13 @@ public class GrpcServer {
|
||||
@Inject
|
||||
public GrpcServer(Config config,
|
||||
CoreApi coreApi,
|
||||
GrpcDisputeAgentsService disputeAgentsService,
|
||||
GrpcOffersService offersService,
|
||||
GrpcPaymentAccountsService paymentAccountsService,
|
||||
GrpcWalletsService walletsService) {
|
||||
this.coreApi = coreApi;
|
||||
this.server = ServerBuilder.forPort(config.apiPort)
|
||||
.addService(disputeAgentsService)
|
||||
.addService(new GetVersionService())
|
||||
.addService(new GetTradeStatisticsService())
|
||||
.addService(offersService)
|
||||
|
@ -54,6 +54,8 @@ import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import javafx.application.Application;
|
||||
|
||||
import javafx.stage.Modality;
|
||||
@ -69,6 +71,8 @@ import javafx.scene.layout.StackPane;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Rectangle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
@ -241,8 +245,19 @@ public class BisqApp extends Application implements UncaughtExceptionHandler {
|
||||
|
||||
// configure the primary stage
|
||||
String appName = injector.getInstance(Key.get(String.class, Names.named(Config.APP_NAME)));
|
||||
if (!Config.baseCurrencyNetwork().isMainnet())
|
||||
appName += " [" + Res.get(Config.baseCurrencyNetwork().name()) + "]";
|
||||
List<String> postFixes = new ArrayList<>();
|
||||
if (!Config.baseCurrencyNetwork().isMainnet()) {
|
||||
postFixes.add(Config.baseCurrencyNetwork().name());
|
||||
}
|
||||
if (injector.getInstance(Config.class).useLocalhostForP2P) {
|
||||
postFixes.add("LOCALHOST");
|
||||
}
|
||||
if (injector.getInstance(Config.class).useDevMode) {
|
||||
postFixes.add("DEV MODE");
|
||||
}
|
||||
if (!postFixes.isEmpty()) {
|
||||
appName += " [" + Joiner.on(", ").join(postFixes) + " ]";
|
||||
}
|
||||
|
||||
stage.setTitle(appName);
|
||||
stage.setScene(scene);
|
||||
|
@ -38,6 +38,7 @@ import bisq.core.payment.validation.AltCoinAddressValidator;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
import bisq.core.util.validation.InputValidator;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.util.Tuple3;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -123,6 +124,13 @@ public class AssetsForm extends PaymentMethodForm {
|
||||
addressInputTextField.setValidator(altCoinAddressValidator);
|
||||
|
||||
addressInputTextField.textProperty().addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue.startsWith("monero:")) {
|
||||
UserThread.execute(() -> {
|
||||
String addressWithoutPrefix = newValue.replace("monero:", "");
|
||||
addressInputTextField.setText(addressWithoutPrefix);
|
||||
});
|
||||
return;
|
||||
}
|
||||
assetAccount.setAddress(newValue);
|
||||
updateFromInputs();
|
||||
});
|
||||
|
@ -685,10 +685,20 @@ 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
|
||||
// add a badge again it will be needed.
|
||||
@SuppressWarnings("unused")
|
||||
public BooleanProperty getShowAccountUpdatesNotification() {
|
||||
return accountPresentation.getShowAccountUpdatesNotification();
|
||||
}
|
||||
|
||||
public BooleanProperty getShowSettingsUpdatesNotification() {
|
||||
return settingsPresentation.getShowSettingsUpdatesNotification();
|
||||
}
|
||||
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.desktop.main;
|
||||
|
||||
import bisq.desktop.app.BisqApp;
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
|
||||
import bisq.core.btc.wallet.WalletsManager;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.offer.OpenOfferManager;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.storage.FileUtil;
|
||||
|
||||
import org.bitcoinj.wallet.DeterministicSeed;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* This serves as shared space for static methods used from different views where no common parent view would fit as
|
||||
* owner of that code. We keep it strictly static. It should replace GUIUtil for those methods which are not utility
|
||||
* methods.
|
||||
*/
|
||||
@Slf4j
|
||||
public class SharedPresentation {
|
||||
public static void restoreSeedWords(WalletsManager walletsManager,
|
||||
OpenOfferManager openOfferManager,
|
||||
DeterministicSeed seed,
|
||||
File storageDir) {
|
||||
if (!openOfferManager.getObservableList().isEmpty()) {
|
||||
UserThread.runAfter(() ->
|
||||
new Popup().warning(Res.get("seed.restore.openOffers.warn"))
|
||||
.actionButtonText(Res.get("shared.yes"))
|
||||
.onAction(() -> {
|
||||
openOfferManager.removeAllOpenOffers(() -> {
|
||||
doRestoreSeedWords(walletsManager, seed, storageDir);
|
||||
});
|
||||
})
|
||||
.show(), 100, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
doRestoreSeedWords(walletsManager, seed, storageDir);
|
||||
}
|
||||
}
|
||||
|
||||
private static void doRestoreSeedWords(WalletsManager walletsManager,
|
||||
DeterministicSeed seed,
|
||||
File storageDir) {
|
||||
try {
|
||||
File backup = new File(storageDir, "AddressEntryList_backup_pre_wallet_restore_" + System.currentTimeMillis());
|
||||
FileUtil.copyFile(new File(storageDir, "AddressEntryList"), backup);
|
||||
} catch (Throwable t) {
|
||||
new Popup().error(Res.get("error.deleteAddressEntryListFailed", t)).show();
|
||||
}
|
||||
|
||||
walletsManager.restoreSeedWords(
|
||||
seed,
|
||||
() -> UserThread.execute(() -> {
|
||||
log.info("Wallets restored with seed words");
|
||||
new Popup().feedback(Res.get("seed.restore.success")).hideCloseButton().show();
|
||||
BisqApp.getShutDownHandler().run();
|
||||
}),
|
||||
throwable -> UserThread.execute(() -> {
|
||||
log.error(throwable.toString());
|
||||
new Popup().error(Res.get("seed.restore.error", Res.get("shared.errorMessageInline", throwable)))
|
||||
.show();
|
||||
}));
|
||||
}
|
||||
}
|
@ -19,14 +19,15 @@ package bisq.desktop.main.account.content.seedwords;
|
||||
|
||||
import bisq.desktop.common.view.ActivatableView;
|
||||
import bisq.desktop.common.view.FxmlView;
|
||||
import bisq.desktop.main.SharedPresentation;
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.main.overlays.windows.WalletPasswordWindow;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
import bisq.desktop.util.Layout;
|
||||
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.WalletsManager;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.offer.OpenOfferManager;
|
||||
import bisq.core.user.DontShowAgainLookup;
|
||||
|
||||
import bisq.common.config.Config;
|
||||
@ -68,6 +69,7 @@ import static javafx.beans.binding.Bindings.createBooleanBinding;
|
||||
@FxmlView
|
||||
public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
||||
private final WalletsManager walletsManager;
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final WalletPasswordWindow walletPasswordWindow;
|
||||
private final File storageDir;
|
||||
@ -91,10 +93,12 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
||||
|
||||
@Inject
|
||||
private SeedWordsView(WalletsManager walletsManager,
|
||||
OpenOfferManager openOfferManager,
|
||||
BtcWalletService btcWalletService,
|
||||
WalletPasswordWindow walletPasswordWindow,
|
||||
@Named(Config.STORAGE_DIR) File storageDir) {
|
||||
this.walletsManager = walletsManager;
|
||||
this.openOfferManager = openOfferManager;
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.walletPasswordWindow = walletPasswordWindow;
|
||||
this.storageDir = storageDir;
|
||||
@ -166,9 +170,7 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
||||
String key = "showBackupWarningAtSeedPhrase";
|
||||
if (DontShowAgainLookup.showAgain(key)) {
|
||||
new Popup().warning(Res.get("account.seed.backup.warning"))
|
||||
.onAction(() -> {
|
||||
showSeedPhrase();
|
||||
})
|
||||
.onAction(this::showSeedPhrase)
|
||||
.actionButtonText(Res.get("shared.iUnderstand"))
|
||||
.useIUnderstandButton()
|
||||
.dontShowAgainId(key)
|
||||
@ -179,7 +181,7 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
||||
}
|
||||
}
|
||||
|
||||
public void showSeedPhrase() {
|
||||
private void showSeedPhrase() {
|
||||
DeterministicSeed keyChainSeed = btcWalletService.getKeyChainSeed();
|
||||
// wallet creation date is not encrypted
|
||||
walletCreationDate = Instant.ofEpochSecond(walletsManager.getChainSeedCreationTimeSeconds()).atZone(ZoneId.systemDefault()).toLocalDate();
|
||||
@ -305,6 +307,6 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
||||
long date = localDateTime.toEpochSecond(ZoneOffset.UTC);
|
||||
|
||||
DeterministicSeed seed = new DeterministicSeed(Splitter.on(" ").splitToList(seedWordsTextArea.getText()), null, "", date);
|
||||
GUIUtil.restoreSeedWords(seed, walletsManager, storageDir);
|
||||
SharedPresentation.restoreSeedWords(walletsManager, openOfferManager, seed, storageDir);
|
||||
}
|
||||
}
|
||||
|
@ -21,15 +21,16 @@ import bisq.desktop.components.AutoTooltipButton;
|
||||
import bisq.desktop.components.AutoTooltipLabel;
|
||||
import bisq.desktop.components.BusyAnimation;
|
||||
import bisq.desktop.components.PasswordTextField;
|
||||
import bisq.desktop.main.SharedPresentation;
|
||||
import bisq.desktop.main.overlays.Overlay;
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
import bisq.desktop.util.Layout;
|
||||
import bisq.desktop.util.Transitions;
|
||||
|
||||
import bisq.core.btc.wallet.WalletsManager;
|
||||
import bisq.core.crypto.ScryptUtil;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.offer.OpenOfferManager;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.config.Config;
|
||||
@ -88,6 +89,7 @@ import static javafx.beans.binding.Bindings.createBooleanBinding;
|
||||
@Slf4j
|
||||
public class WalletPasswordWindow extends Overlay<WalletPasswordWindow> {
|
||||
private final WalletsManager walletsManager;
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private File storageDir;
|
||||
|
||||
private Button unlockButton;
|
||||
@ -115,8 +117,10 @@ public class WalletPasswordWindow extends Overlay<WalletPasswordWindow> {
|
||||
|
||||
@Inject
|
||||
private WalletPasswordWindow(WalletsManager walletsManager,
|
||||
OpenOfferManager openOfferManager,
|
||||
@Named(Config.STORAGE_DIR) File storageDir) {
|
||||
this.walletsManager = walletsManager;
|
||||
this.openOfferManager = openOfferManager;
|
||||
this.storageDir = storageDir;
|
||||
type = Type.Attention;
|
||||
width = 900;
|
||||
@ -277,7 +281,6 @@ public class WalletPasswordWindow extends Overlay<WalletPasswordWindow> {
|
||||
gridPane.getChildren().add(headLine2Label);
|
||||
|
||||
seedWordsTextArea = addTextArea(gridPane, ++rowIndex, Res.get("seed.enterSeedWords"), 5);
|
||||
;
|
||||
seedWordsTextArea.setPrefHeight(60);
|
||||
|
||||
Tuple2<Label, DatePicker> labelDatePickerTuple2 = addTopLabelDatePicker(gridPane, ++rowIndex,
|
||||
@ -356,6 +359,6 @@ public class WalletPasswordWindow extends Overlay<WalletPasswordWindow> {
|
||||
//TODO Is ZoneOffset correct?
|
||||
long date = value != null ? value.atStartOfDay().toEpochSecond(ZoneOffset.UTC) : 0;
|
||||
DeterministicSeed seed = new DeterministicSeed(Splitter.on(" ").splitToList(seedWordsTextArea.getText()), null, "", date);
|
||||
GUIUtil.restoreSeedWords(seed, walletsManager, storageDir);
|
||||
SharedPresentation.restoreSeedWords(walletsManager, openOfferManager, seed, storageDir);
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ import bisq.desktop.util.validation.RegexValidator;
|
||||
import bisq.core.account.witness.AccountAgeWitness;
|
||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
import bisq.core.btc.setup.WalletsSetup;
|
||||
import bisq.core.btc.wallet.WalletsManager;
|
||||
import bisq.core.locale.Country;
|
||||
import bisq.core.locale.CountryUtil;
|
||||
import bisq.core.locale.CurrencyUtil;
|
||||
@ -63,7 +62,6 @@ import bisq.common.config.Config;
|
||||
import bisq.common.proto.persistable.PersistableList;
|
||||
import bisq.common.proto.persistable.PersistenceProtoResolver;
|
||||
import bisq.common.storage.CorruptedDatabaseFilesHandler;
|
||||
import bisq.common.storage.FileUtil;
|
||||
import bisq.common.storage.Storage;
|
||||
import bisq.common.util.MathUtils;
|
||||
import bisq.common.util.Tuple2;
|
||||
@ -75,7 +73,6 @@ import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
import org.bitcoinj.uri.BitcoinURI;
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
import org.bitcoinj.wallet.DeterministicSeed;
|
||||
|
||||
import com.googlecode.jcsv.CSVStrategy;
|
||||
import com.googlecode.jcsv.writer.CSVEntryConverter;
|
||||
@ -803,26 +800,6 @@ public class GUIUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static void restoreSeedWords(DeterministicSeed seed, WalletsManager walletsManager, File storageDir) {
|
||||
try {
|
||||
FileUtil.renameFile(new File(storageDir, "AddressEntryList"), new File(storageDir, "AddressEntryList_wallet_restore_" + System.currentTimeMillis()));
|
||||
} catch (Throwable t) {
|
||||
new Popup().error(Res.get("error.deleteAddressEntryListFailed", t)).show();
|
||||
}
|
||||
walletsManager.restoreSeedWords(
|
||||
seed,
|
||||
() -> UserThread.execute(() -> {
|
||||
log.info("Wallets restored with seed words");
|
||||
new Popup().feedback(Res.get("seed.restore.success")).hideCloseButton().show();
|
||||
BisqApp.getShutDownHandler().run();
|
||||
}),
|
||||
throwable -> UserThread.execute(() -> {
|
||||
log.error(throwable.toString());
|
||||
new Popup().error(Res.get("seed.restore.error", Res.get("shared.errorMessageInline", throwable)))
|
||||
.show();
|
||||
}));
|
||||
}
|
||||
|
||||
public static void showSelectableTextModal(String title, String text) {
|
||||
TextArea textArea = new BisqTextArea();
|
||||
textArea.setText(text);
|
||||
|
@ -17,8 +17,9 @@ Bisq uses Git LFS to track certain large binary files. Follow the instructions a
|
||||
|
||||
## Build
|
||||
|
||||
You do _not_ need to install Gradle to complete the following command. The `gradlew` shell script will install it for you if necessary.
|
||||
You do _not_ need to install Gradle to complete the following command. The `gradlew` shell script will install it for you if necessary. Pull the lfs data first.
|
||||
|
||||
git lfs pull
|
||||
./gradlew build
|
||||
|
||||
If on Windows run `gradlew.bat build` instead.
|
||||
|
@ -19,14 +19,14 @@ dependencyVerification {
|
||||
'com.fasterxml.jackson.core:jackson-annotations:2566b3a6662afa3c6af4f5b25006cb46be2efc68f1b5116291d6998a8cdf7ed3',
|
||||
'com.fasterxml.jackson.core:jackson-core:39a74610521d7fb9eb3f437bb8739bbf47f6435be12d17bf954c731a0c6352bb',
|
||||
'com.fasterxml.jackson.core:jackson-databind:fcf3c2b0c332f5f54604f7e27fa7ee502378a2cc5df6a944bbfae391872c32ff',
|
||||
'com.github.JesusMcCloud.netlayer:tor.external:fb080d812aa88f5fb1ec74273ae24ae13980b7902c75e0ffc3ac462b4cf6b242',
|
||||
'com.github.JesusMcCloud.netlayer:tor.native:1227275377ac73d9d25dec1d05bc2f7a2ae3b1f1afde77f39049637dcb8fc407',
|
||||
'com.github.JesusMcCloud.netlayer:tor:6ff47a97dc0dc97079d83c1f2c66ebcc956f25dd3bacfa6e24206cb2c742f52b',
|
||||
'com.github.JesusMcCloud.tor-binary:tor-binary-geoip:13a3c6e02f37f9def172ce1d231ea1a45aa1ddbc36b1d9e81b7a91632bc474ed',
|
||||
'com.github.JesusMcCloud.tor-binary:tor-binary-linux32:2022da3a398527d366bd954bea92069cf209218c649aeb3342f85f7434aa0786',
|
||||
'com.github.JesusMcCloud.tor-binary:tor-binary-linux64:972d5a946964176fe849eb872baa64a591d9e6fe0467c62ae71986b946101836',
|
||||
'com.github.JesusMcCloud.tor-binary:tor-binary-macos:95a4d093e2d48099623015e70add1bd7dcc3e392f9e87ed4d691286c868516c9',
|
||||
'com.github.JesusMcCloud.tor-binary:tor-binary-windows:43a07290443a1c55211eaa6001681839f9cb7453574089a939d0da3dd20e2cf2',
|
||||
'com.github.JesusMcCloud.netlayer:tor.external:1facb63d5f4107a1d4a176a03c149b60eed19a76371b58d1b6aa62ad5cacde3f',
|
||||
'com.github.JesusMcCloud.netlayer:tor.native:88267f1a3b2d1433a77a2fd9dc3a8f1d5cd28fbbefa2e078d83b176cbe91d477',
|
||||
'com.github.JesusMcCloud.netlayer:tor:0eaae7bcea11ef25e4f041e77c237ebc96f120462e64a7818073a88dadf9b805',
|
||||
'com.github.JesusMcCloud.tor-binary:tor-binary-geoip:08a461608990aed2c272121dc835a38687017d3bb172bc181ea0793df7ebb5aa',
|
||||
'com.github.JesusMcCloud.tor-binary:tor-binary-linux32:81fb1e80d191ed9d0a509e514311d21d59c87c6edee6a490975061a18390b10c',
|
||||
'com.github.JesusMcCloud.tor-binary:tor-binary-linux64:7b90b5e8f1cc32d86ed8e2e5b1f57d774bc27bebc679ececdbf1ff0699bd1f4c',
|
||||
'com.github.JesusMcCloud.tor-binary:tor-binary-macos:db5aef1082fdd8afb4f7f21cf0956f46a76e93aad75bed6ed149dd3e3767c0a7',
|
||||
'com.github.JesusMcCloud.tor-binary:tor-binary-windows:1bb83786a71449d8edb152e40ce5efc129eaca6a0900f4912d7555b6df2f52a2',
|
||||
'com.github.JesusMcCloud:jtorctl:389d61b1b5a85eb2f23c582c3913ede49f80c9f2b553e4762382c836270e57e5',
|
||||
'com.github.bisq-network.bitcoinj:bitcoinj-core:f979c2187e61ee3b08dd4cbfc49a149734cff64c045d29ed112f2e12f34068a3',
|
||||
'com.github.ravn:jsocks:3c71600af027b2b6d4244e4ad14d98ff2352a379410daebefff5d8cd48d742a4',
|
||||
|
@ -48,6 +48,8 @@ import bisq.common.proto.network.NetworkEnvelope;
|
||||
import bisq.common.proto.network.NetworkProtoResolver;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
@ -283,12 +285,20 @@ public class Connection implements HasCapabilities, Runnable, MessageListener {
|
||||
bundleSender.schedule(() -> {
|
||||
if (!stopped) {
|
||||
synchronized (lock) {
|
||||
BundleOfEnvelopes current = queueOfBundles.poll();
|
||||
if (current != null && !stopped) {
|
||||
if (current.getEnvelopes().size() == 1) {
|
||||
protoOutputStream.writeEnvelope(current.getEnvelopes().get(0));
|
||||
} else {
|
||||
protoOutputStream.writeEnvelope(current);
|
||||
BundleOfEnvelopes bundle = queueOfBundles.poll();
|
||||
if (bundle != null && !stopped) {
|
||||
NetworkEnvelope envelope = bundle.getEnvelopes().size() == 1 ?
|
||||
bundle.getEnvelopes().get(0) :
|
||||
bundle;
|
||||
try {
|
||||
protoOutputStream.writeEnvelope(envelope);
|
||||
} catch (Throwable t) {
|
||||
log.error("Sending envelope of class {} to address {} " +
|
||||
"failed due {}",
|
||||
envelope.getClass().getSimpleName(),
|
||||
this.getPeersNodeAddressOptional(),
|
||||
t.toString());
|
||||
log.error("envelope: {}", envelope);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -876,7 +886,7 @@ public class Connection implements HasCapabilities, Runnable, MessageListener {
|
||||
log.error(e.getMessage());
|
||||
e.printStackTrace();
|
||||
reportInvalidRequest(RuleViolation.INVALID_CLASS);
|
||||
} catch (ProtobufferException | NoClassDefFoundError e) {
|
||||
} catch (ProtobufferException | NoClassDefFoundError | InvalidProtocolBufferException e) {
|
||||
log.error(e.getMessage());
|
||||
e.printStackTrace();
|
||||
reportInvalidRequest(RuleViolation.INVALID_DATA_TYPE);
|
||||
|
@ -24,19 +24,20 @@ option java_package = "bisq.proto.grpc";
|
||||
option java_multiple_files = true;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Version
|
||||
// DisputeAgents
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
service GetVersion {
|
||||
rpc GetVersion (GetVersionRequest) returns (GetVersionReply) {
|
||||
service DisputeAgents {
|
||||
rpc RegisterDisputeAgent (RegisterDisputeAgentRequest) returns (RegisterDisputeAgentReply) {
|
||||
}
|
||||
}
|
||||
|
||||
message GetVersionRequest {
|
||||
message RegisterDisputeAgentRequest {
|
||||
string disputeAgentType = 1;
|
||||
string registrationKey = 2;
|
||||
}
|
||||
|
||||
message GetVersionReply {
|
||||
string version = 1;
|
||||
message RegisterDisputeAgentReply {
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -214,3 +215,20 @@ message AddressBalanceInfo {
|
||||
int64 balance = 2;
|
||||
int64 numConfirmations = 3;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Version
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
service GetVersion {
|
||||
rpc GetVersion (GetVersionRequest) returns (GetVersionReply) {
|
||||
}
|
||||
}
|
||||
|
||||
message GetVersionRequest {
|
||||
}
|
||||
|
||||
message GetVersionReply {
|
||||
string version = 1;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user