mirror of
https://github.com/bisq-network/bisq.git
synced 2025-03-03 18:56:59 +01:00
Merge branch 'dispute-agent-branch' into fix-delayed-payout-tx-issues
# Conflicts: # core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSetupDepositTxListener.java
This commit is contained in:
commit
10bedee803
47 changed files with 507 additions and 449 deletions
|
@ -48,14 +48,14 @@
|
||||||
run ./bisq-cli --password="xyz" getversion
|
run ./bisq-cli --password="xyz" getversion
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
echo "actual output: $output" >&2
|
echo "actual output: $output" >&2
|
||||||
[ "$output" = "1.3.8" ]
|
[ "$output" = "1.3.9" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "test getversion" {
|
@test "test getversion" {
|
||||||
run ./bisq-cli --password=xyz getversion
|
run ./bisq-cli --password=xyz getversion
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
echo "actual output: $output" >&2
|
echo "actual output: $output" >&2
|
||||||
[ "$output" = "1.3.8" ]
|
[ "$output" = "1.3.9" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "test setwalletpassword \"a b c\"" {
|
@test "test setwalletpassword \"a b c\"" {
|
||||||
|
@ -166,15 +166,15 @@
|
||||||
[ "$output" = "Error: address bogus not found in wallet" ]
|
[ "$output" = "Error: address bogus not found in wallet" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "test createpaymentacct PerfectMoneyDummy (missing nbr, ccy params)" {
|
@test "test createpaymentacct PerfectMoneyDummy (missing name, nbr, ccy params)" {
|
||||||
run ./bisq-cli --password=xyz createpaymentacct PerfectMoneyDummy
|
run ./bisq-cli --password=xyz createpaymentacct PERFECT_MONEY
|
||||||
[ "$status" -eq 1 ]
|
[ "$status" -eq 1 ]
|
||||||
echo "actual output: $output" >&2
|
echo "actual output: $output" >&2
|
||||||
[ "$output" = "Error: incorrect parameter count, expecting account name, account number, currency code" ]
|
[ "$output" = "Error: incorrect parameter count, expecting payment method id, account name, account number, currency code" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "test createpaymentacct PerfectMoneyDummy 0123456789 USD" {
|
@test "test createpaymentacct PERFECT_MONEY PerfectMoneyDummy 0123456789 USD" {
|
||||||
run ./bisq-cli --password=xyz createpaymentacct PerfectMoneyDummy 0123456789 USD
|
run ./bisq-cli --password=xyz createpaymentacct PERFECT_MONEY PerfectMoneyDummy 0123456789 USD
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind;
|
||||||
import static bisq.apitest.config.BisqAppConfig.*;
|
import static bisq.apitest.config.BisqAppConfig.*;
|
||||||
import static java.lang.String.format;
|
import static java.lang.String.format;
|
||||||
import static java.lang.System.exit;
|
import static java.lang.System.exit;
|
||||||
|
@ -64,6 +65,10 @@ public class Scaffold {
|
||||||
public static final int EXIT_SUCCESS = 0;
|
public static final int EXIT_SUCCESS = 0;
|
||||||
public static final int EXIT_FAILURE = 1;
|
public static final int EXIT_FAILURE = 1;
|
||||||
|
|
||||||
|
public enum BitcoinCoreApp {
|
||||||
|
bitcoind
|
||||||
|
}
|
||||||
|
|
||||||
public final ApiTestConfig config;
|
public final ApiTestConfig config;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -295,7 +300,7 @@ public class Scaffold {
|
||||||
|
|
||||||
log.info("Starting supporting apps {}", config.supportingApps.toString());
|
log.info("Starting supporting apps {}", config.supportingApps.toString());
|
||||||
|
|
||||||
if (config.hasSupportingApp("bitcoind")) {
|
if (config.hasSupportingApp(bitcoind.name())) {
|
||||||
BitcoinDaemon bitcoinDaemon = new BitcoinDaemon(config);
|
BitcoinDaemon bitcoinDaemon = new BitcoinDaemon(config);
|
||||||
bitcoinDaemon.verifyBitcoinPathsExist(true);
|
bitcoinDaemon.verifyBitcoinPathsExist(true);
|
||||||
bitcoindTask = new SetupTask(bitcoinDaemon, countdownLatch);
|
bitcoindTask = new SetupTask(bitcoinDaemon, countdownLatch);
|
||||||
|
|
|
@ -24,7 +24,9 @@ import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static java.util.Arrays.stream;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,15 +70,21 @@ public class ApiTestCase {
|
||||||
// gRPC service stubs are used by method & scenario tests, but not e2e tests.
|
// gRPC service stubs are used by method & scenario tests, but not e2e tests.
|
||||||
private static final Map<BisqAppConfig, GrpcStubs> grpcStubsCache = new HashMap<>();
|
private static final Map<BisqAppConfig, GrpcStubs> grpcStubsCache = new HashMap<>();
|
||||||
|
|
||||||
public static void setUpScaffold(String supportingApps)
|
public static void setUpScaffold(Enum<?>... supportingApps)
|
||||||
throws InterruptedException, ExecutionException, IOException {
|
throws InterruptedException, ExecutionException, IOException {
|
||||||
scaffold = new Scaffold(supportingApps).setUp();
|
scaffold = new Scaffold(stream(supportingApps).map(Enum::name)
|
||||||
|
.collect(Collectors.joining(",")))
|
||||||
|
.setUp();
|
||||||
config = scaffold.config;
|
config = scaffold.config;
|
||||||
bitcoinCli = new BitcoinCliHelper((config));
|
bitcoinCli = new BitcoinCliHelper((config));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setUpScaffold(String[] params)
|
public static void setUpScaffold(String[] params)
|
||||||
throws InterruptedException, ExecutionException, IOException {
|
throws InterruptedException, ExecutionException, IOException {
|
||||||
|
// Test cases needing to pass more than just an ApiTestConfig
|
||||||
|
// --supportingApps option will use this setup method, but the
|
||||||
|
// --supportingApps option will need to be passed too, with its comma
|
||||||
|
// delimited app list value, e.g., "bitcoind,seednode,arbdaemon".
|
||||||
scaffold = new Scaffold(params).setUp();
|
scaffold = new Scaffold(params).setUp();
|
||||||
config = scaffold.config;
|
config = scaffold.config;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,9 @@ import org.junit.jupiter.api.Order;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestMethodOrder;
|
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.alicedaemon;
|
||||||
|
import static bisq.apitest.config.BisqAppConfig.seednode;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
@ -41,7 +43,7 @@ public class GetBalanceTest extends MethodTest {
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void setUp() {
|
public static void setUp() {
|
||||||
try {
|
try {
|
||||||
setUpScaffold("bitcoind,seednode,alicedaemon");
|
setUpScaffold(bitcoind, seednode, alicedaemon);
|
||||||
|
|
||||||
// Have to generate 1 regtest block for alice's wallet to show 10 BTC balance.
|
// Have to generate 1 regtest block for alice's wallet to show 10 BTC balance.
|
||||||
bitcoinCli.generateBlocks(1);
|
bitcoinCli.generateBlocks(1);
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class GetVersionTest extends MethodTest {
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void setUp() {
|
public static void setUp() {
|
||||||
try {
|
try {
|
||||||
setUpScaffold(alicedaemon.name());
|
setUpScaffold(alicedaemon);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
fail(ex);
|
fail(ex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,9 @@ import org.junit.jupiter.api.Order;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestMethodOrder;
|
import org.junit.jupiter.api.TestMethodOrder;
|
||||||
|
|
||||||
|
import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind;
|
||||||
import static bisq.apitest.config.BisqAppConfig.arbdaemon;
|
import static bisq.apitest.config.BisqAppConfig.arbdaemon;
|
||||||
|
import static bisq.apitest.config.BisqAppConfig.seednode;
|
||||||
import static bisq.common.app.DevEnv.DEV_PRIVILEGE_PRIV_KEY;
|
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.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
@ -42,10 +44,14 @@ import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||||
@TestMethodOrder(OrderAnnotation.class)
|
@TestMethodOrder(OrderAnnotation.class)
|
||||||
public class RegisterDisputeAgentsTest extends MethodTest {
|
public class RegisterDisputeAgentsTest extends MethodTest {
|
||||||
|
|
||||||
|
private static final String ARBITRATOR = "arbitrator";
|
||||||
|
private static final String MEDIATOR = "mediator";
|
||||||
|
private static final String REFUNDAGENT = "refundagent";
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void setUp() {
|
public static void setUp() {
|
||||||
try {
|
try {
|
||||||
setUpScaffold("bitcoind,seednode,arbdaemon");
|
setUpScaffold(bitcoind, seednode, arbdaemon);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
fail(ex);
|
fail(ex);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +61,7 @@ public class RegisterDisputeAgentsTest extends MethodTest {
|
||||||
@Order(1)
|
@Order(1)
|
||||||
public void testRegisterArbitratorShouldThrowException() {
|
public void testRegisterArbitratorShouldThrowException() {
|
||||||
var req =
|
var req =
|
||||||
createRegisterDisputeAgentRequest("arbitrator");
|
createRegisterDisputeAgentRequest(ARBITRATOR);
|
||||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||||
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req));
|
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req));
|
||||||
assertEquals("INVALID_ARGUMENT: arbitrators must be registered in a Bisq UI",
|
assertEquals("INVALID_ARGUMENT: arbitrators must be registered in a Bisq UI",
|
||||||
|
@ -77,7 +83,7 @@ public class RegisterDisputeAgentsTest extends MethodTest {
|
||||||
@Order(3)
|
@Order(3)
|
||||||
public void testInvalidRegistrationKeyArgShouldThrowException() {
|
public void testInvalidRegistrationKeyArgShouldThrowException() {
|
||||||
var req = RegisterDisputeAgentRequest.newBuilder()
|
var req = RegisterDisputeAgentRequest.newBuilder()
|
||||||
.setDisputeAgentType("refundagent")
|
.setDisputeAgentType(REFUNDAGENT)
|
||||||
.setRegistrationKey("invalid" + DEV_PRIVILEGE_PRIV_KEY).build();
|
.setRegistrationKey("invalid" + DEV_PRIVILEGE_PRIV_KEY).build();
|
||||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||||
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req));
|
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req));
|
||||||
|
@ -89,7 +95,7 @@ public class RegisterDisputeAgentsTest extends MethodTest {
|
||||||
@Order(4)
|
@Order(4)
|
||||||
public void testRegisterMediator() {
|
public void testRegisterMediator() {
|
||||||
var req =
|
var req =
|
||||||
createRegisterDisputeAgentRequest("mediator");
|
createRegisterDisputeAgentRequest(MEDIATOR);
|
||||||
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req);
|
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +103,7 @@ public class RegisterDisputeAgentsTest extends MethodTest {
|
||||||
@Order(5)
|
@Order(5)
|
||||||
public void testRegisterRefundAgent() {
|
public void testRegisterRefundAgent() {
|
||||||
var req =
|
var req =
|
||||||
createRegisterDisputeAgentRequest("refundagent");
|
createRegisterDisputeAgentRequest(REFUNDAGENT);
|
||||||
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req);
|
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ public class WalletProtectionTest extends MethodTest {
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void setUp() {
|
public static void setUp() {
|
||||||
try {
|
try {
|
||||||
setUpScaffold(alicedaemon.name());
|
setUpScaffold(alicedaemon);
|
||||||
MILLISECONDS.sleep(2000);
|
MILLISECONDS.sleep(2000);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
fail(ex);
|
fail(ex);
|
||||||
|
|
|
@ -26,7 +26,9 @@ import org.junit.jupiter.api.Order;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestMethodOrder;
|
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.alicedaemon;
|
||||||
|
import static bisq.apitest.config.BisqAppConfig.seednode;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
@ -38,7 +40,7 @@ public class FundWalletScenarioTest extends ScenarioTest {
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void setUp() {
|
public static void setUp() {
|
||||||
try {
|
try {
|
||||||
setUpScaffold("bitcoind,seednode,alicedaemon");
|
setUpScaffold(bitcoind, seednode, alicedaemon);
|
||||||
bitcoinCli.generateBlocks(1);
|
bitcoinCli.generateBlocks(1);
|
||||||
MILLISECONDS.sleep(1500);
|
MILLISECONDS.sleep(1500);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import bisq.proto.grpc.GetOffersRequest;
|
||||||
import bisq.proto.grpc.GetPaymentAccountsRequest;
|
import bisq.proto.grpc.GetPaymentAccountsRequest;
|
||||||
import bisq.proto.grpc.GetVersionRequest;
|
import bisq.proto.grpc.GetVersionRequest;
|
||||||
import bisq.proto.grpc.LockWalletRequest;
|
import bisq.proto.grpc.LockWalletRequest;
|
||||||
|
import bisq.proto.grpc.RegisterDisputeAgentRequest;
|
||||||
import bisq.proto.grpc.RemoveWalletPasswordRequest;
|
import bisq.proto.grpc.RemoveWalletPasswordRequest;
|
||||||
import bisq.proto.grpc.SetWalletPasswordRequest;
|
import bisq.proto.grpc.SetWalletPasswordRequest;
|
||||||
import bisq.proto.grpc.UnlockWalletRequest;
|
import bisq.proto.grpc.UnlockWalletRequest;
|
||||||
|
@ -69,7 +70,8 @@ public class CliMain {
|
||||||
lockwallet,
|
lockwallet,
|
||||||
unlockwallet,
|
unlockwallet,
|
||||||
removewalletpassword,
|
removewalletpassword,
|
||||||
setwalletpassword
|
setwalletpassword,
|
||||||
|
registerdisputeagent
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
@ -114,9 +116,9 @@ public class CliMain {
|
||||||
}
|
}
|
||||||
|
|
||||||
var methodName = nonOptionArgs.get(0);
|
var methodName = nonOptionArgs.get(0);
|
||||||
final Method method;
|
Method method;
|
||||||
try {
|
try {
|
||||||
method = Method.valueOf(methodName);
|
method = getMethodFromCmd(methodName);
|
||||||
} catch (IllegalArgumentException ex) {
|
} catch (IllegalArgumentException ex) {
|
||||||
throw new IllegalArgumentException(format("'%s' is not a supported method", methodName));
|
throw new IllegalArgumentException(format("'%s' is not a supported method", methodName));
|
||||||
}
|
}
|
||||||
|
@ -128,6 +130,7 @@ public class CliMain {
|
||||||
throw new IllegalArgumentException("missing required 'password' option");
|
throw new IllegalArgumentException("missing required 'password' option");
|
||||||
|
|
||||||
GrpcStubs grpcStubs = new GrpcStubs(host, port, password);
|
GrpcStubs grpcStubs = new GrpcStubs(host, port, password);
|
||||||
|
var disputeAgentsService = grpcStubs.disputeAgentsService;
|
||||||
var versionService = grpcStubs.versionService;
|
var versionService = grpcStubs.versionService;
|
||||||
var offersService = grpcStubs.offersService;
|
var offersService = grpcStubs.offersService;
|
||||||
var paymentAccountsService = grpcStubs.paymentAccountsService;
|
var paymentAccountsService = grpcStubs.paymentAccountsService;
|
||||||
|
@ -166,34 +169,38 @@ public class CliMain {
|
||||||
}
|
}
|
||||||
case getoffers: {
|
case getoffers: {
|
||||||
if (nonOptionArgs.size() < 3)
|
if (nonOptionArgs.size() < 3)
|
||||||
throw new IllegalArgumentException("incorrect parameter count, expecting direction (buy|sell), currency code");
|
throw new IllegalArgumentException("incorrect parameter count,"
|
||||||
|
+ " expecting direction (buy|sell), currency code");
|
||||||
|
|
||||||
var direction = nonOptionArgs.get(1);
|
var direction = nonOptionArgs.get(1);
|
||||||
var fiatCurrency = nonOptionArgs.get(2);
|
var fiatCurrency = nonOptionArgs.get(2);
|
||||||
|
|
||||||
var request = GetOffersRequest.newBuilder()
|
var request = GetOffersRequest.newBuilder()
|
||||||
.setDirection(direction)
|
.setDirection(direction)
|
||||||
.setFiatCurrencyCode(fiatCurrency)
|
.setCurrencyCode(fiatCurrency)
|
||||||
.build();
|
.build();
|
||||||
var reply = offersService.getOffers(request);
|
var reply = offersService.getOffers(request);
|
||||||
out.println(formatOfferTable(reply.getOffersList(), fiatCurrency));
|
out.println(formatOfferTable(reply.getOffersList(), fiatCurrency));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case createpaymentacct: {
|
case createpaymentacct: {
|
||||||
if (nonOptionArgs.size() < 4)
|
if (nonOptionArgs.size() < 5)
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"incorrect parameter count, expecting account name, account number, currency code");
|
"incorrect parameter count, expecting payment method id,"
|
||||||
|
+ " account name, account number, currency code");
|
||||||
|
|
||||||
var accountName = nonOptionArgs.get(1);
|
var paymentMethodId = nonOptionArgs.get(1);
|
||||||
var accountNumber = nonOptionArgs.get(2);
|
var accountName = nonOptionArgs.get(2);
|
||||||
var fiatCurrencyCode = nonOptionArgs.get(3);
|
var accountNumber = nonOptionArgs.get(3);
|
||||||
|
var currencyCode = nonOptionArgs.get(4);
|
||||||
|
|
||||||
var request = CreatePaymentAccountRequest.newBuilder()
|
var request = CreatePaymentAccountRequest.newBuilder()
|
||||||
|
.setPaymentMethodId(paymentMethodId)
|
||||||
.setAccountName(accountName)
|
.setAccountName(accountName)
|
||||||
.setAccountNumber(accountNumber)
|
.setAccountNumber(accountNumber)
|
||||||
.setFiatCurrencyCode(fiatCurrencyCode).build();
|
.setCurrencyCode(currencyCode).build();
|
||||||
paymentAccountsService.createPaymentAccount(request);
|
paymentAccountsService.createPaymentAccount(request);
|
||||||
out.println(format("payment account %s saved", accountName));
|
out.printf("payment account %s saved", accountName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case getpaymentaccts: {
|
case getpaymentaccts: {
|
||||||
|
@ -232,7 +239,8 @@ public class CliMain {
|
||||||
if (nonOptionArgs.size() < 2)
|
if (nonOptionArgs.size() < 2)
|
||||||
throw new IllegalArgumentException("no password specified");
|
throw new IllegalArgumentException("no password specified");
|
||||||
|
|
||||||
var request = RemoveWalletPasswordRequest.newBuilder().setPassword(nonOptionArgs.get(1)).build();
|
var request = RemoveWalletPasswordRequest.newBuilder()
|
||||||
|
.setPassword(nonOptionArgs.get(1)).build();
|
||||||
walletsService.removeWalletPassword(request);
|
walletsService.removeWalletPassword(request);
|
||||||
out.println("wallet decrypted");
|
out.println("wallet decrypted");
|
||||||
return;
|
return;
|
||||||
|
@ -241,7 +249,8 @@ public class CliMain {
|
||||||
if (nonOptionArgs.size() < 2)
|
if (nonOptionArgs.size() < 2)
|
||||||
throw new IllegalArgumentException("no password specified");
|
throw new IllegalArgumentException("no password specified");
|
||||||
|
|
||||||
var requestBuilder = SetWalletPasswordRequest.newBuilder().setPassword(nonOptionArgs.get(1));
|
var requestBuilder = SetWalletPasswordRequest.newBuilder()
|
||||||
|
.setPassword(nonOptionArgs.get(1));
|
||||||
var hasNewPassword = nonOptionArgs.size() == 3;
|
var hasNewPassword = nonOptionArgs.size() == 3;
|
||||||
if (hasNewPassword)
|
if (hasNewPassword)
|
||||||
requestBuilder.setNewPassword(nonOptionArgs.get(2));
|
requestBuilder.setNewPassword(nonOptionArgs.get(2));
|
||||||
|
@ -249,6 +258,19 @@ public class CliMain {
|
||||||
out.println("wallet encrypted" + (hasNewPassword ? " with new password" : ""));
|
out.println("wallet encrypted" + (hasNewPassword ? " with new password" : ""));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case registerdisputeagent: {
|
||||||
|
if (nonOptionArgs.size() < 3)
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"incorrect parameter count, expecting dispute agent type, registration key");
|
||||||
|
|
||||||
|
var disputeAgentType = nonOptionArgs.get(1);
|
||||||
|
var registrationKey = nonOptionArgs.get(2);
|
||||||
|
var requestBuilder = RegisterDisputeAgentRequest.newBuilder()
|
||||||
|
.setDisputeAgentType(disputeAgentType).setRegistrationKey(registrationKey);
|
||||||
|
disputeAgentsService.registerDisputeAgent(requestBuilder.build());
|
||||||
|
out.println(disputeAgentType + " registered");
|
||||||
|
return;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
throw new RuntimeException(format("unhandled method '%s'", method));
|
throw new RuntimeException(format("unhandled method '%s'", method));
|
||||||
}
|
}
|
||||||
|
@ -260,6 +282,13 @@ public class CliMain {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Method getMethodFromCmd(String methodName) {
|
||||||
|
// TODO if we use const type for enum we need add some mapping. Even if we don't
|
||||||
|
// change now it is handy to have flexibility in case we change internal code
|
||||||
|
// and don't want to break user commands.
|
||||||
|
return Method.valueOf(methodName.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
private static void printHelp(OptionParser parser, PrintStream stream) {
|
private static void printHelp(OptionParser parser, PrintStream stream) {
|
||||||
try {
|
try {
|
||||||
stream.println("Bisq RPC Client");
|
stream.println("Bisq RPC Client");
|
||||||
|
|
|
@ -161,6 +161,19 @@ public class Utilities {
|
||||||
return getOSName().contains("win");
|
return getOSName().contains("win");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True, if Bisq is running on a virtualized OS within Qubes, false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean isQubesOS() {
|
||||||
|
// For Linux qubes, "os.version" looks like "4.19.132-1.pvops.qubes.x86_64"
|
||||||
|
// The presence of the "qubes" substring indicates this Linux is running as a qube
|
||||||
|
// This is the case for all 3 virtualization modes (PV, PVH, HVM)
|
||||||
|
// In addition, this works for both simple AppVMs, as well as for StandaloneVMs
|
||||||
|
// TODO This might not work for detecting Qubes virtualization for other OSes
|
||||||
|
// like Windows
|
||||||
|
return getOSVersion().contains("qubes");
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isOSX() {
|
public static boolean isOSX() {
|
||||||
return getOSName().contains("mac") || getOSName().contains("darwin");
|
return getOSName().contains("mac") || getOSName().contains("darwin");
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,8 +139,14 @@ public class CoreApi {
|
||||||
// PaymentAccounts
|
// PaymentAccounts
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void createPaymentAccount(String accountName, String accountNumber, String fiatCurrencyCode) {
|
public void createPaymentAccount(String paymentMethodId,
|
||||||
paymentAccountsService.createPaymentAccount(accountName, accountNumber, fiatCurrencyCode);
|
String accountName,
|
||||||
|
String accountNumber,
|
||||||
|
String currencyCode) {
|
||||||
|
paymentAccountsService.createPaymentAccount(paymentMethodId,
|
||||||
|
accountName,
|
||||||
|
accountNumber,
|
||||||
|
currencyCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<PaymentAccount> getPaymentAccounts() {
|
public Set<PaymentAccount> getPaymentAccounts() {
|
||||||
|
|
|
@ -68,7 +68,7 @@ class CoreDisputeAgentsService {
|
||||||
this.languageCodes = Arrays.asList("de", "en", "es", "fr");
|
this.languageCodes = Arrays.asList("de", "en", "es", "fr");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerDisputeAgent(String disputeAgentType, String registrationKey) {
|
void registerDisputeAgent(String disputeAgentType, String registrationKey) {
|
||||||
if (!p2PService.isBootstrapped())
|
if (!p2PService.isBootstrapped())
|
||||||
throw new IllegalStateException("p2p service is not bootstrapped yet");
|
throw new IllegalStateException("p2p service is not bootstrapped yet");
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ class CoreOffersService {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Offer> getOffers(String direction, String fiatCurrencyCode) {
|
List<Offer> getOffers(String direction, String fiatCurrencyCode) {
|
||||||
List<Offer> offers = offerBookService.getOffers().stream()
|
List<Offer> offers = offerBookService.getOffers().stream()
|
||||||
.filter(o -> {
|
.filter(o -> {
|
||||||
var offerOfWantedDirection = o.getDirection().name().equalsIgnoreCase(direction);
|
var offerOfWantedDirection = o.getDirection().name().equalsIgnoreCase(direction);
|
||||||
|
@ -77,7 +77,7 @@ class CoreOffersService {
|
||||||
return offers;
|
return offers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createOffer(String currencyCode,
|
void createOffer(String currencyCode,
|
||||||
String directionAsString,
|
String directionAsString,
|
||||||
long priceAsLong,
|
long priceAsLong,
|
||||||
boolean useMarketBasedPrice,
|
boolean useMarketBasedPrice,
|
||||||
|
@ -111,7 +111,7 @@ class CoreOffersService {
|
||||||
resultHandler);
|
resultHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createOffer(String offerId,
|
void createOffer(String offerId,
|
||||||
String currencyCode,
|
String currencyCode,
|
||||||
OfferPayload.Direction direction,
|
OfferPayload.Direction direction,
|
||||||
Price price,
|
Price price,
|
||||||
|
|
|
@ -33,6 +33,9 @@ import java.util.Set;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import static bisq.core.payment.payload.PaymentMethod.*;
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
class CorePaymentAccountsService {
|
class CorePaymentAccountsService {
|
||||||
|
|
||||||
|
@ -49,17 +52,19 @@ class CorePaymentAccountsService {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createPaymentAccount(String accountName, String accountNumber, String fiatCurrencyCode) {
|
void createPaymentAccount(String paymentMethodId,
|
||||||
// Create and persist a PerfectMoney dummy payment account. There is no guard
|
String accountName,
|
||||||
// against creating accounts with duplicate names & numbers, only the uuid and
|
String accountNumber,
|
||||||
// creation date are unique.
|
String currencyCode) {
|
||||||
PaymentMethod dummyPaymentMethod = PaymentMethod.getDummyPaymentMethod(PaymentMethod.PERFECT_MONEY_ID);
|
|
||||||
PaymentAccount paymentAccount = PaymentAccountFactory.getPaymentAccount(dummyPaymentMethod);
|
PaymentAccount paymentAccount = getNewPaymentAccount(paymentMethodId,
|
||||||
paymentAccount.init();
|
accountName,
|
||||||
paymentAccount.setAccountName(accountName);
|
accountNumber,
|
||||||
((PerfectMoneyAccount) paymentAccount).setAccountNr(accountNumber);
|
currencyCode);
|
||||||
paymentAccount.setSingleTradeCurrency(new FiatCurrency(fiatCurrencyCode.toUpperCase()));
|
|
||||||
user.addPaymentAccount(paymentAccount);
|
// TODO not sure if there is more to do at account creation.
|
||||||
|
// Need to check all the flow when its created from UI.
|
||||||
|
user.addPaymentAccountIfNotExists(paymentAccount);
|
||||||
|
|
||||||
// Don't do this on mainnet until thoroughly tested.
|
// Don't do this on mainnet until thoroughly tested.
|
||||||
if (config.baseCurrencyNetwork.isRegtest())
|
if (config.baseCurrencyNetwork.isRegtest())
|
||||||
|
@ -68,7 +73,64 @@ class CorePaymentAccountsService {
|
||||||
log.info("Payment account {} saved", paymentAccount.getId());
|
log.info("Payment account {} saved", paymentAccount.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<PaymentAccount> getPaymentAccounts() {
|
Set<PaymentAccount> getPaymentAccounts() {
|
||||||
return user.getPaymentAccounts();
|
return user.getPaymentAccounts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PaymentAccount getNewPaymentAccount(String paymentMethodId,
|
||||||
|
String accountName,
|
||||||
|
String accountNumber,
|
||||||
|
String currencyCode) {
|
||||||
|
PaymentAccount paymentAccount = null;
|
||||||
|
PaymentMethod paymentMethod = getPaymentMethodById(paymentMethodId);
|
||||||
|
|
||||||
|
switch (paymentMethod.getId()) {
|
||||||
|
case UPHOLD_ID:
|
||||||
|
case MONEY_BEAM_ID:
|
||||||
|
case POPMONEY_ID:
|
||||||
|
case REVOLUT_ID:
|
||||||
|
//noinspection DuplicateBranchesInSwitch
|
||||||
|
log.error("PaymentMethod {} not supported yet.", paymentMethod);
|
||||||
|
break;
|
||||||
|
case PERFECT_MONEY_ID:
|
||||||
|
// Create and persist a PerfectMoney dummy payment account. There is no
|
||||||
|
// guard against creating accounts with duplicate names & numbers, only
|
||||||
|
// the uuid and creation date are unique.
|
||||||
|
paymentAccount = PaymentAccountFactory.getPaymentAccount(paymentMethod);
|
||||||
|
paymentAccount.init();
|
||||||
|
paymentAccount.setAccountName(accountName);
|
||||||
|
((PerfectMoneyAccount) paymentAccount).setAccountNr(accountNumber);
|
||||||
|
paymentAccount.setSingleTradeCurrency(new FiatCurrency(currencyCode));
|
||||||
|
break;
|
||||||
|
case SEPA_ID:
|
||||||
|
case SEPA_INSTANT_ID:
|
||||||
|
case FASTER_PAYMENTS_ID:
|
||||||
|
case NATIONAL_BANK_ID:
|
||||||
|
case SAME_BANK_ID:
|
||||||
|
case SPECIFIC_BANKS_ID:
|
||||||
|
case JAPAN_BANK_ID:
|
||||||
|
case ALI_PAY_ID:
|
||||||
|
case WECHAT_PAY_ID:
|
||||||
|
case SWISH_ID:
|
||||||
|
case CLEAR_X_CHANGE_ID:
|
||||||
|
case CHASE_QUICK_PAY_ID:
|
||||||
|
case INTERAC_E_TRANSFER_ID:
|
||||||
|
case US_POSTAL_MONEY_ORDER_ID:
|
||||||
|
case MONEY_GRAM_ID:
|
||||||
|
case WESTERN_UNION_ID:
|
||||||
|
case CASH_DEPOSIT_ID:
|
||||||
|
case HAL_CASH_ID:
|
||||||
|
case F2F_ID:
|
||||||
|
case PROMPT_PAY_ID:
|
||||||
|
case ADVANCED_CASH_ID:
|
||||||
|
default:
|
||||||
|
log.error("PaymentMethod {} not supported yet.", paymentMethod);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkNotNull(paymentAccount,
|
||||||
|
"Could not create payment account with paymentMethodId "
|
||||||
|
+ paymentMethodId + ".");
|
||||||
|
return paymentAccount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ class CoreWalletsService {
|
||||||
this.btcWalletService = btcWalletService;
|
this.btcWalletService = btcWalletService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getAvailableBalance() {
|
long getAvailableBalance() {
|
||||||
verifyWalletsAreAvailable();
|
verifyWalletsAreAvailable();
|
||||||
verifyEncryptedWalletIsUnlocked();
|
verifyEncryptedWalletIsUnlocked();
|
||||||
|
|
||||||
|
@ -82,27 +82,26 @@ class CoreWalletsService {
|
||||||
return balance.getValue();
|
return balance.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getAddressBalance(String addressString) {
|
long getAddressBalance(String addressString) {
|
||||||
Address address = getAddressEntry(addressString).getAddress();
|
Address address = getAddressEntry(addressString).getAddress();
|
||||||
return btcWalletService.getBalanceForAddress(address).value;
|
return btcWalletService.getBalanceForAddress(address).value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddressBalanceInfo getAddressBalanceInfo(String addressString) {
|
AddressBalanceInfo getAddressBalanceInfo(String addressString) {
|
||||||
var satoshiBalance = getAddressBalance(addressString);
|
var satoshiBalance = getAddressBalance(addressString);
|
||||||
var numConfirmations = getNumConfirmationsForMostRecentTransaction(addressString);
|
var numConfirmations = getNumConfirmationsForMostRecentTransaction(addressString);
|
||||||
return new AddressBalanceInfo(addressString, satoshiBalance, numConfirmations);
|
return new AddressBalanceInfo(addressString, satoshiBalance, numConfirmations);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<AddressBalanceInfo> getFundingAddresses() {
|
List<AddressBalanceInfo> getFundingAddresses() {
|
||||||
verifyWalletsAreAvailable();
|
verifyWalletsAreAvailable();
|
||||||
verifyEncryptedWalletIsUnlocked();
|
verifyEncryptedWalletIsUnlocked();
|
||||||
|
|
||||||
// Create a new funding address if none exists.
|
// Create a new funding address if none exists.
|
||||||
if (btcWalletService.getAvailableAddressEntries().size() == 0)
|
if (btcWalletService.getAvailableAddressEntries().isEmpty())
|
||||||
btcWalletService.getFreshAddressEntry();
|
btcWalletService.getFreshAddressEntry();
|
||||||
|
|
||||||
List<String> addressStrings =
|
List<String> addressStrings = btcWalletService
|
||||||
btcWalletService
|
|
||||||
.getAvailableAddressEntries()
|
.getAvailableAddressEntries()
|
||||||
.stream()
|
.stream()
|
||||||
.map(AddressEntry::getAddressString)
|
.map(AddressEntry::getAddressString)
|
||||||
|
@ -113,8 +112,7 @@ class CoreWalletsService {
|
||||||
// this::getAddressBalance cannot return null.
|
// this::getAddressBalance cannot return null.
|
||||||
var balances = memoize(this::getAddressBalance);
|
var balances = memoize(this::getAddressBalance);
|
||||||
|
|
||||||
boolean noAddressHasZeroBalance =
|
boolean noAddressHasZeroBalance = addressStrings.stream()
|
||||||
addressStrings.stream()
|
|
||||||
.allMatch(addressString -> balances.getUnchecked(addressString) != 0);
|
.allMatch(addressString -> balances.getUnchecked(addressString) != 0);
|
||||||
|
|
||||||
if (noAddressHasZeroBalance) {
|
if (noAddressHasZeroBalance) {
|
||||||
|
@ -129,13 +127,13 @@ class CoreWalletsService {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNumConfirmationsForMostRecentTransaction(String addressString) {
|
int getNumConfirmationsForMostRecentTransaction(String addressString) {
|
||||||
Address address = getAddressEntry(addressString).getAddress();
|
Address address = getAddressEntry(addressString).getAddress();
|
||||||
TransactionConfidence confidence = btcWalletService.getConfidenceForAddress(address);
|
TransactionConfidence confidence = btcWalletService.getConfidenceForAddress(address);
|
||||||
return confidence == null ? 0 : confidence.getDepthInBlocks();
|
return confidence == null ? 0 : confidence.getDepthInBlocks();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWalletPassword(String password, String newPassword) {
|
void setWalletPassword(String password, String newPassword) {
|
||||||
verifyWalletsAreAvailable();
|
verifyWalletsAreAvailable();
|
||||||
|
|
||||||
KeyCrypterScrypt keyCrypterScrypt = getKeyCrypterScrypt();
|
KeyCrypterScrypt keyCrypterScrypt = getKeyCrypterScrypt();
|
||||||
|
@ -165,7 +163,7 @@ class CoreWalletsService {
|
||||||
walletsManager.backupWallets();
|
walletsManager.backupWallets();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void lockWallet() {
|
void lockWallet() {
|
||||||
if (!walletsManager.areWalletsEncrypted())
|
if (!walletsManager.areWalletsEncrypted())
|
||||||
throw new IllegalStateException("wallet is not encrypted with a password");
|
throw new IllegalStateException("wallet is not encrypted with a password");
|
||||||
|
|
||||||
|
@ -175,7 +173,7 @@ class CoreWalletsService {
|
||||||
tempAesKey = null;
|
tempAesKey = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unlockWallet(String password, long timeout) {
|
void unlockWallet(String password, long timeout) {
|
||||||
verifyWalletIsAvailableAndEncrypted();
|
verifyWalletIsAvailableAndEncrypted();
|
||||||
|
|
||||||
KeyCrypterScrypt keyCrypterScrypt = getKeyCrypterScrypt();
|
KeyCrypterScrypt keyCrypterScrypt = getKeyCrypterScrypt();
|
||||||
|
@ -213,7 +211,7 @@ class CoreWalletsService {
|
||||||
|
|
||||||
// Provided for automated wallet protection method testing, despite the
|
// Provided for automated wallet protection method testing, despite the
|
||||||
// security risks exposed by providing users the ability to decrypt their wallets.
|
// security risks exposed by providing users the ability to decrypt their wallets.
|
||||||
public void removeWalletPassword(String password) {
|
void removeWalletPassword(String password) {
|
||||||
verifyWalletIsAvailableAndEncrypted();
|
verifyWalletIsAvailableAndEncrypted();
|
||||||
KeyCrypterScrypt keyCrypterScrypt = getKeyCrypterScrypt();
|
KeyCrypterScrypt keyCrypterScrypt = getKeyCrypterScrypt();
|
||||||
|
|
||||||
|
|
60
core/src/main/java/bisq/core/api/StatusCheck.java
Normal file
60
core/src/main/java/bisq/core/api/StatusCheck.java
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package bisq.core.api;
|
||||||
|
|
||||||
|
import bisq.core.btc.Balances;
|
||||||
|
import bisq.core.btc.setup.WalletsSetup;
|
||||||
|
import bisq.core.btc.wallet.WalletsManager;
|
||||||
|
import bisq.core.dao.state.DaoStateService;
|
||||||
|
|
||||||
|
import bisq.network.p2p.P2PService;
|
||||||
|
|
||||||
|
import bisq.common.config.Config;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Slf4j
|
||||||
|
class StatusCheck {
|
||||||
|
|
||||||
|
private final Config config;
|
||||||
|
private final P2PService p2PService;
|
||||||
|
private final DaoStateService daoStateService;
|
||||||
|
private final WalletsSetup walletsSetup;
|
||||||
|
private final WalletsManager walletsManager;
|
||||||
|
private final Balances balances;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public StatusCheck(Config config,
|
||||||
|
P2PService p2PService,
|
||||||
|
DaoStateService daoStateService,
|
||||||
|
WalletsSetup walletsSetup,
|
||||||
|
WalletsManager walletsManager,
|
||||||
|
Balances balances) {
|
||||||
|
this.config = config;
|
||||||
|
this.p2PService = p2PService;
|
||||||
|
this.daoStateService = daoStateService;
|
||||||
|
this.walletsSetup = walletsSetup;
|
||||||
|
this.walletsManager = walletsManager;
|
||||||
|
this.balances = balances;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void verifyCanTrade() {
|
||||||
|
if (!p2PService.isBootstrapped())
|
||||||
|
throw new IllegalStateException("p2p service is not yet bootstrapped");
|
||||||
|
|
||||||
|
if (!daoStateService.isParseBlockChainComplete())
|
||||||
|
throw new IllegalStateException("dao block chain sync is not yet complete");
|
||||||
|
|
||||||
|
if (config.baseCurrencyNetwork.isMainnet()
|
||||||
|
&& p2PService.getNumConnectedPeers().get() < walletsSetup.getMinBroadcastConnections())
|
||||||
|
throw new IllegalStateException("not enough connected peers");
|
||||||
|
|
||||||
|
if (!walletsManager.areWalletsAvailable())
|
||||||
|
throw new IllegalStateException("wallet is not yet available");
|
||||||
|
|
||||||
|
if (balances.getAvailableBalance().get() == null)
|
||||||
|
throw new IllegalStateException("balance is not yet available");
|
||||||
|
}
|
||||||
|
}
|
|
@ -98,6 +98,7 @@ public class BisqHeadlessApp implements HeadlessApp {
|
||||||
bisqSetup.setShowPopupIfInvalidBtcConfigHandler(() -> log.error("onShowPopupIfInvalidBtcConfigHandler"));
|
bisqSetup.setShowPopupIfInvalidBtcConfigHandler(() -> log.error("onShowPopupIfInvalidBtcConfigHandler"));
|
||||||
bisqSetup.setRevolutAccountsUpdateHandler(revolutAccountList -> log.info("setRevolutAccountsUpdateHandler: revolutAccountList={}", revolutAccountList));
|
bisqSetup.setRevolutAccountsUpdateHandler(revolutAccountList -> log.info("setRevolutAccountsUpdateHandler: revolutAccountList={}", revolutAccountList));
|
||||||
bisqSetup.setOsxKeyLoggerWarningHandler(() -> log.info("setOsxKeyLoggerWarningHandler"));
|
bisqSetup.setOsxKeyLoggerWarningHandler(() -> log.info("setOsxKeyLoggerWarningHandler"));
|
||||||
|
bisqSetup.setQubesOSInfoHandler(() -> log.info("setQubesOSInfoHandler"));
|
||||||
|
|
||||||
//TODO move to bisqSetup
|
//TODO move to bisqSetup
|
||||||
corruptedDatabaseFilesHandler.getCorruptedDatabaseFiles().ifPresent(files -> log.warn("getCorruptedDatabaseFiles. files={}", files));
|
corruptedDatabaseFilesHandler.getCorruptedDatabaseFiles().ifPresent(files -> log.warn("getCorruptedDatabaseFiles. files={}", files));
|
||||||
|
|
|
@ -232,6 +232,9 @@ public class BisqSetup {
|
||||||
@Setter
|
@Setter
|
||||||
@Nullable
|
@Nullable
|
||||||
private Runnable osxKeyLoggerWarningHandler;
|
private Runnable osxKeyLoggerWarningHandler;
|
||||||
|
@Setter
|
||||||
|
@Nullable
|
||||||
|
private Runnable qubesOSInfoHandler;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
final BooleanProperty newVersionAvailableProperty = new SimpleBooleanProperty(false);
|
final BooleanProperty newVersionAvailableProperty = new SimpleBooleanProperty(false);
|
||||||
|
@ -357,6 +360,7 @@ public class BisqSetup {
|
||||||
checkCryptoSetup();
|
checkCryptoSetup();
|
||||||
checkForCorrectOSArchitecture();
|
checkForCorrectOSArchitecture();
|
||||||
checkOSXVersion();
|
checkOSXVersion();
|
||||||
|
checkIfRunningOnQubesOS();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void step3() {
|
private void step3() {
|
||||||
|
@ -678,6 +682,17 @@ public class BisqSetup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If Bisq is running on an OS that is virtualized under Qubes, show info popup with
|
||||||
|
* link to the Setup Guide. The guide documents what other steps are needed, in
|
||||||
|
* addition to installing the Linux package (qube sizing, etc)
|
||||||
|
*/
|
||||||
|
private void checkIfRunningOnQubesOS() {
|
||||||
|
if (Utilities.isQubesOS() && qubesOSInfoHandler != null) {
|
||||||
|
qubesOSInfoHandler.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void initDomainServices() {
|
private void initDomainServices() {
|
||||||
log.info("initDomainServices");
|
log.info("initDomainServices");
|
||||||
|
|
||||||
|
|
|
@ -211,7 +211,7 @@ public final class AddressEntryList implements UserThreadMappedPersistableEnvelo
|
||||||
private void maybeAddNewAddressEntry(Transaction tx) {
|
private void maybeAddNewAddressEntry(Transaction tx) {
|
||||||
tx.getOutputs().stream()
|
tx.getOutputs().stream()
|
||||||
.filter(output -> output.isMine(wallet))
|
.filter(output -> output.isMine(wallet))
|
||||||
.map(output -> output.getAddressFromP2PKHScript(wallet.getNetworkParameters()))
|
.map(output -> output.getScriptPubKey().getToAddress(wallet.getNetworkParameters()))
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.filter(this::isAddressNotInEntries)
|
.filter(this::isAddressNotInEntries)
|
||||||
.map(address -> (DeterministicKey) wallet.findKeyFromPubKeyHash(address.getHash(),
|
.map(address -> (DeterministicKey) wallet.findKeyFromPubKeyHash(address.getHash(),
|
||||||
|
|
|
@ -55,8 +55,7 @@ class BtcNodeConverter {
|
||||||
PeerAddress convertOnionHost(BtcNode node) {
|
PeerAddress convertOnionHost(BtcNode node) {
|
||||||
// no DNS lookup for onion addresses
|
// no DNS lookup for onion addresses
|
||||||
String onionAddress = Objects.requireNonNull(node.getOnionAddress());
|
String onionAddress = Objects.requireNonNull(node.getOnionAddress());
|
||||||
PeerAddress result = new PeerAddress(onionAddress, node.getPort());
|
return new PeerAddress(onionAddress, node.getPort());
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -69,7 +68,7 @@ class BtcNodeConverter {
|
||||||
if (address != null) {
|
if (address != null) {
|
||||||
result = create(address, port);
|
result = create(address, port);
|
||||||
} else {
|
} else {
|
||||||
log.warn("Lookup failed, no address for node", node);
|
log.warn("Lookup failed, no address for node {}", node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -85,7 +84,7 @@ class BtcNodeConverter {
|
||||||
if (address != null) {
|
if (address != null) {
|
||||||
result = create(proxy, address, port);
|
result = create(proxy, address, port);
|
||||||
} else {
|
} else {
|
||||||
log.warn("Lookup failed, no address for node", node);
|
log.warn("Lookup failed, no address for node {}", node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -26,7 +26,6 @@ import org.bitcoinj.wallet.DeterministicKeyChain;
|
||||||
import org.bitcoinj.wallet.DeterministicSeed;
|
import org.bitcoinj.wallet.DeterministicSeed;
|
||||||
import org.bitcoinj.wallet.KeyChainGroupStructure;
|
import org.bitcoinj.wallet.KeyChainGroupStructure;
|
||||||
import org.bitcoinj.wallet.Protos;
|
import org.bitcoinj.wallet.Protos;
|
||||||
import org.bitcoinj.wallet.UnreadableWalletException;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
@ -63,12 +62,12 @@ public class BisqKeyChainFactory extends DefaultKeyChainFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DeterministicKeyChain makeWatchingKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicKey accountKey, boolean isFollowingKey, boolean isMarried, Script.ScriptType outputScriptType) throws UnreadableWalletException {
|
public DeterministicKeyChain makeWatchingKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicKey accountKey, boolean isFollowingKey, boolean isMarried, Script.ScriptType outputScriptType) {
|
||||||
throw new UnsupportedOperationException("Bisq is not supposed to use this");
|
throw new UnsupportedOperationException("Bisq is not supposed to use this");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DeterministicKeyChain makeSpendingKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicKey accountKey, boolean isMarried, Script.ScriptType outputScriptType) throws UnreadableWalletException {
|
public DeterministicKeyChain makeSpendingKeyChain(Protos.Key key, Protos.Key firstSubKey, DeterministicKey accountKey, boolean isMarried, Script.ScriptType outputScriptType) {
|
||||||
throw new UnsupportedOperationException("Bisq is not supposed to use this");
|
throw new UnsupportedOperationException("Bisq is not supposed to use this");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ package bisq.core.btc.setup;
|
||||||
import bisq.core.btc.nodes.LocalBitcoinNode;
|
import bisq.core.btc.nodes.LocalBitcoinNode;
|
||||||
import bisq.core.btc.nodes.ProxySocketFactory;
|
import bisq.core.btc.nodes.ProxySocketFactory;
|
||||||
import bisq.core.btc.wallet.BisqRiskAnalysis;
|
import bisq.core.btc.wallet.BisqRiskAnalysis;
|
||||||
|
|
||||||
import bisq.common.config.Config;
|
import bisq.common.config.Config;
|
||||||
|
|
||||||
import com.google.common.collect.*;
|
import com.google.common.collect.*;
|
||||||
|
@ -54,9 +55,7 @@ import static com.google.common.base.Preconditions.*;
|
||||||
/**
|
/**
|
||||||
* <p>Utility class that wraps the boilerplate needed to set up a new SPV bitcoinj app. Instantiate it with a directory
|
* <p>Utility class that wraps the boilerplate needed to set up a new SPV bitcoinj app. Instantiate it with a directory
|
||||||
* and file prefix, optionally configure a few things, then use startAsync and optionally awaitRunning. The object will
|
* and file prefix, optionally configure a few things, then use startAsync and optionally awaitRunning. The object will
|
||||||
* construct and configure a {@link BlockChain}, {@link SPVBlockStore}, {@link Wallet} and {@link PeerGroup}. Depending
|
* construct and configure a {@link BlockChain}, {@link SPVBlockStore}, {@link Wallet} and {@link PeerGroup}.</p>
|
||||||
* on the value of the blockingStartup property, startup will be considered complete once the block chain has fully
|
|
||||||
* synchronized, so it can take a while.</p>
|
|
||||||
*
|
*
|
||||||
* <p>To add listeners and modify the objects that are constructed, you can either do that by overriding the
|
* <p>To add listeners and modify the objects that are constructed, you can either do that by overriding the
|
||||||
* {@link #onSetupCompleted()} method (which will run on a background thread) and make your changes there,
|
* {@link #onSetupCompleted()} method (which will run on a background thread) and make your changes there,
|
||||||
|
@ -92,16 +91,12 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
protected volatile File vBtcWalletFile;
|
protected volatile File vBtcWalletFile;
|
||||||
protected volatile File vBsqWalletFile;
|
protected volatile File vBsqWalletFile;
|
||||||
|
|
||||||
protected boolean useAutoSave = true;
|
|
||||||
protected PeerAddress[] peerAddresses;
|
protected PeerAddress[] peerAddresses;
|
||||||
protected DownloadProgressTracker downloadListener;
|
protected DownloadProgressTracker downloadListener;
|
||||||
protected boolean autoStop = true;
|
|
||||||
protected InputStream checkpoints;
|
protected InputStream checkpoints;
|
||||||
protected boolean blockingStartup = true;
|
|
||||||
protected String userAgent, version;
|
protected String userAgent, version;
|
||||||
protected WalletProtobufSerializer.WalletFactory walletFactory;
|
protected WalletProtobufSerializer.WalletFactory walletFactory;
|
||||||
@Nullable protected DeterministicSeed restoreFromSeed;
|
@Nullable protected DeterministicSeed restoreFromSeed;
|
||||||
@Nullable protected DeterministicKey restoreFromKey;
|
|
||||||
@Nullable protected PeerDiscovery discovery;
|
@Nullable protected PeerDiscovery discovery;
|
||||||
|
|
||||||
protected volatile Context context;
|
protected volatile Context context;
|
||||||
|
@ -124,8 +119,7 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
/**
|
/**
|
||||||
* Creates a new WalletConfig, with the given {@link Context}. Files will be stored in the given directory.
|
* Creates a new WalletConfig, with the given {@link Context}. Files will be stored in the given directory.
|
||||||
*/
|
*/
|
||||||
public WalletConfig(Context context,
|
private WalletConfig(Context context, File directory, String filePrefix) {
|
||||||
File directory, String filePrefix) {
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.params = checkNotNull(context.getParams());
|
this.params = checkNotNull(context.getParams());
|
||||||
this.directory = checkDir(directory);
|
this.directory = checkDir(directory);
|
||||||
|
@ -170,29 +164,15 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
return setPeerNodes(new PeerAddress(params, localHost, params.getPort()));
|
return setPeerNodes(new PeerAddress(params, localHost, params.getPort()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** If true, the wallet will save itself to disk automatically whenever it changes. */
|
|
||||||
public WalletConfig setAutoSave(boolean value) {
|
|
||||||
checkState(state() == State.NEW, "Cannot call after startup");
|
|
||||||
useAutoSave = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If you want to learn about the sync process, you can provide a listener here. For instance, a
|
* If you want to learn about the sync process, you can provide a listener here. For instance, a
|
||||||
* {@link DownloadProgressTracker} is a good choice. This has no effect unless setBlockingStartup(false) has been called
|
* {@link DownloadProgressTracker} is a good choice.
|
||||||
* too, due to some missing implementation code.
|
|
||||||
*/
|
*/
|
||||||
public WalletConfig setDownloadListener(DownloadProgressTracker listener) {
|
public WalletConfig setDownloadListener(DownloadProgressTracker listener) {
|
||||||
this.downloadListener = listener;
|
this.downloadListener = listener;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** If true, will register a shutdown hook to stop the library. Defaults to true. */
|
|
||||||
public WalletConfig setAutoStop(boolean autoStop) {
|
|
||||||
this.autoStop = autoStop;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set, the file is expected to contain a checkpoints file calculated with BuildCheckpoints. It makes initial
|
* If set, the file is expected to contain a checkpoints file calculated with BuildCheckpoints. It makes initial
|
||||||
* block sync faster for new users - please refer to the documentation on the bitcoinj website
|
* block sync faster for new users - please refer to the documentation on the bitcoinj website
|
||||||
|
@ -205,17 +185,6 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If true (the default) then the startup of this service won't be considered complete until the network has been
|
|
||||||
* brought up, peer connections established and the block chain synchronised. Therefore {@link #awaitRunning()} can
|
|
||||||
* potentially take a very long time. If false, then startup is considered complete once the network activity
|
|
||||||
* begins and peer connections/block chain sync will continue in the background.
|
|
||||||
*/
|
|
||||||
public WalletConfig setBlockingStartup(boolean blockingStartup) {
|
|
||||||
this.blockingStartup = blockingStartup;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the string that will appear in the subver field of the version message.
|
* Sets the string that will appear in the subver field of the version message.
|
||||||
* @param userAgent A short string that should be the name of your app, e.g. "My Wallet"
|
* @param userAgent A short string that should be the name of your app, e.g. "My Wallet"
|
||||||
|
@ -227,14 +196,6 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a wallet factory which will be used when the kit creates a new wallet.
|
|
||||||
*/
|
|
||||||
public WalletConfig setWalletFactory(WalletProtobufSerializer.WalletFactory walletFactory) {
|
|
||||||
this.walletFactory = walletFactory;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a seed is set here then any existing wallet that matches the file name will be renamed to a backup name,
|
* If a seed is set here then any existing wallet that matches the file name will be renamed to a backup name,
|
||||||
* the chain file will be deleted, and the wallet object will be instantiated with the given seed instead of
|
* the chain file will be deleted, and the wallet object will be instantiated with the given seed instead of
|
||||||
|
@ -248,19 +209,6 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If an account key is set here then any existing wallet that matches the file name will be renamed to a backup name,
|
|
||||||
* the chain file will be deleted, and the wallet object will be instantiated with the given key instead of
|
|
||||||
* a fresh seed being created. This is intended for restoring a wallet from an account key. To implement restore
|
|
||||||
* you would shut down the existing appkit, if any, then recreate it with the key given by the user, then start
|
|
||||||
* up the new kit. The next time your app starts it should work as normal (that is, don't keep calling this each
|
|
||||||
* time).
|
|
||||||
*/
|
|
||||||
public WalletConfig restoreWalletFromKey(DeterministicKey accountKey) {
|
|
||||||
this.restoreFromKey = accountKey;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the peer discovery class to use. If none is provided then DNS is used, which is a reasonable default.
|
* Sets the peer discovery class to use. If none is provided then DNS is used, which is a reasonable default.
|
||||||
*/
|
*/
|
||||||
|
@ -269,16 +217,6 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Override this to return wallet extensions if any are necessary.</p>
|
|
||||||
*
|
|
||||||
* <p>When this is called, chain(), store(), and peerGroup() will return the created objects, however they are not
|
|
||||||
* initialized/started.</p>
|
|
||||||
*/
|
|
||||||
protected List<WalletExtension> provideWalletExtensions() throws Exception {
|
|
||||||
return ImmutableList.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is invoked on a background thread after all objects are initialised, but before the peer group
|
* This method is invoked on a background thread after all objects are initialised, but before the peer group
|
||||||
* or block chain download is started. You can tweak the objects configuration here.
|
* or block chain download is started. You can tweak the objects configuration here.
|
||||||
|
@ -287,49 +225,17 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
// Meant to be overridden by subclasses
|
// Meant to be overridden by subclasses
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests to see if the spvchain file has an operating system file lock on it. Useful for checking if your app
|
|
||||||
* is already running. If another copy of your app is running and you start the appkit anyway, an exception will
|
|
||||||
* be thrown during the startup process. Returns false if the chain file does not exist or is a directory.
|
|
||||||
*/
|
|
||||||
public boolean isChainFileLocked() throws IOException {
|
|
||||||
RandomAccessFile file2 = null;
|
|
||||||
try {
|
|
||||||
File file = new File(directory, filePrefix + ".spvchain");
|
|
||||||
if (!file.exists())
|
|
||||||
return false;
|
|
||||||
if (file.isDirectory())
|
|
||||||
return false;
|
|
||||||
file2 = new RandomAccessFile(file, "rw");
|
|
||||||
FileLock lock = file2.getChannel().tryLock();
|
|
||||||
if (lock == null)
|
|
||||||
return true;
|
|
||||||
lock.release();
|
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
if (file2 != null)
|
|
||||||
file2.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void startUp() throws Exception {
|
protected void startUp() throws Exception {
|
||||||
// Runs in a separate thread.
|
// Runs in a separate thread.
|
||||||
Context.propagate(context);
|
Context.propagate(context);
|
||||||
// bitcoinj's WalletAppKit creates the wallet directory if it was not created already,
|
|
||||||
// but WalletConfig should not do that.
|
|
||||||
// if (!directory.exists()) {
|
|
||||||
// if (!directory.mkdirs()) {
|
|
||||||
// throw new IOException("Could not create directory " + directory.getAbsolutePath());
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
log.info("Starting up with directory = {}", directory);
|
log.info("Starting up with directory = {}", directory);
|
||||||
try {
|
try {
|
||||||
File chainFile = new File(directory, filePrefix + ".spvchain");
|
File chainFile = new File(directory, filePrefix + ".spvchain");
|
||||||
boolean chainFileExists = chainFile.exists();
|
boolean chainFileExists = chainFile.exists();
|
||||||
String btcPrefix = "_BTC";
|
String btcPrefix = "_BTC";
|
||||||
vBtcWalletFile = new File(directory, filePrefix + btcPrefix + ".wallet");
|
vBtcWalletFile = new File(directory, filePrefix + btcPrefix + ".wallet");
|
||||||
boolean shouldReplayWallet = (vBtcWalletFile.exists() && !chainFileExists) || restoreFromSeed != null || restoreFromKey != null;
|
boolean shouldReplayWallet = (vBtcWalletFile.exists() && !chainFileExists) || restoreFromSeed != null;
|
||||||
vBtcWallet = createOrLoadWallet(shouldReplayWallet, vBtcWalletFile, false);
|
vBtcWallet = createOrLoadWallet(shouldReplayWallet, vBtcWalletFile, false);
|
||||||
vBtcWallet.allowSpendingUnconfirmedTransactions();
|
vBtcWallet.allowSpendingUnconfirmedTransactions();
|
||||||
vBtcWallet.setRiskAnalyzer(new BisqRiskAnalysis.Analyzer());
|
vBtcWallet.setRiskAnalyzer(new BisqRiskAnalysis.Analyzer());
|
||||||
|
@ -341,8 +247,8 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
|
|
||||||
// Initiate Bitcoin network objects (block store, blockchain and peer group)
|
// Initiate Bitcoin network objects (block store, blockchain and peer group)
|
||||||
vStore = new SPVBlockStore(params, chainFile);
|
vStore = new SPVBlockStore(params, chainFile);
|
||||||
if (!chainFileExists || restoreFromSeed != null || restoreFromKey != null) {
|
if (!chainFileExists || restoreFromSeed != null) {
|
||||||
if (checkpoints == null && !Utils.isAndroidRuntime()) {
|
if (checkpoints == null) {
|
||||||
checkpoints = CheckpointManager.openStream(params);
|
checkpoints = CheckpointManager.openStream(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,12 +261,6 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
log.info("Clearing the chain file in preparation for restore.");
|
log.info("Clearing the chain file in preparation for restore.");
|
||||||
vStore.clear();
|
vStore.clear();
|
||||||
}
|
}
|
||||||
} else if (restoreFromKey != null) {
|
|
||||||
time = restoreFromKey.getCreationTimeSeconds();
|
|
||||||
if (chainFileExists) {
|
|
||||||
log.info("Clearing the chain file in preparation for restore.");
|
|
||||||
vStore.clear();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
time = vBtcWallet.getEarliestKeyCreationTime();
|
time = vBtcWallet.getEarliestKeyCreationTime();
|
||||||
}
|
}
|
||||||
|
@ -398,23 +298,12 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
vPeerGroup.addWallet(vBsqWallet);
|
vPeerGroup.addWallet(vBsqWallet);
|
||||||
onSetupCompleted();
|
onSetupCompleted();
|
||||||
|
|
||||||
if (blockingStartup) {
|
|
||||||
vPeerGroup.start();
|
|
||||||
// Make sure we shut down cleanly.
|
|
||||||
installShutdownHook();
|
|
||||||
//completeExtensionInitiations(vPeerGroup);
|
|
||||||
|
|
||||||
// TODO: Be able to use the provided download listener when doing a blocking startup.
|
|
||||||
final DownloadProgressTracker listener = new DownloadProgressTracker();
|
|
||||||
vPeerGroup.startBlockChainDownload(listener);
|
|
||||||
listener.await();
|
|
||||||
} else {
|
|
||||||
Futures.addCallback((ListenableFuture<?>) vPeerGroup.startAsync(), new FutureCallback<Object>() {
|
Futures.addCallback((ListenableFuture<?>) vPeerGroup.startAsync(), new FutureCallback<Object>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(@Nullable Object result) {
|
public void onSuccess(@Nullable Object result) {
|
||||||
//completeExtensionInitiations(vPeerGroup);
|
//completeExtensionInitiations(vPeerGroup);
|
||||||
final DownloadProgressTracker l = downloadListener == null ? new DownloadProgressTracker() : downloadListener;
|
DownloadProgressTracker tracker = downloadListener == null ? new DownloadProgressTracker() : downloadListener;
|
||||||
vPeerGroup.startBlockChainDownload(l);
|
vPeerGroup.startBlockChainDownload(tracker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -423,7 +312,6 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
|
|
||||||
}
|
}
|
||||||
}, MoreExecutors.directExecutor());
|
}, MoreExecutors.directExecutor());
|
||||||
}
|
|
||||||
} catch (BlockStoreException e) {
|
} catch (BlockStoreException e) {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
|
@ -439,9 +327,6 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
} else {
|
} else {
|
||||||
wallet = createWallet(isBsqWallet);
|
wallet = createWallet(isBsqWallet);
|
||||||
wallet.freshReceiveKey();
|
wallet.freshReceiveKey();
|
||||||
for (WalletExtension e : provideWalletExtensions()) {
|
|
||||||
wallet.addExtension(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Currently the only way we can be sure that an extension is aware of its containing wallet is by
|
// 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[]))
|
// deserializing the extension (see WalletExtension#deserializeWalletExtension(Wallet, byte[]))
|
||||||
|
@ -450,9 +335,7 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
wallet = loadWallet(false, walletFile, isBsqWallet);
|
wallet = loadWallet(false, walletFile, isBsqWallet);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useAutoSave) {
|
|
||||||
this.setupAutoSave(wallet, walletFile);
|
this.setupAutoSave(wallet, walletFile);
|
||||||
}
|
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
@ -465,8 +348,7 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
Wallet wallet;
|
Wallet wallet;
|
||||||
FileInputStream walletStream = new FileInputStream(walletFile);
|
FileInputStream walletStream = new FileInputStream(walletFile);
|
||||||
try {
|
try {
|
||||||
List<WalletExtension> extensions = provideWalletExtensions();
|
WalletExtension[] extArray = new WalletExtension[]{};
|
||||||
WalletExtension[] extArray = extensions.toArray(new WalletExtension[extensions.size()]);
|
|
||||||
Protos.Wallet proto = WalletProtobufSerializer.parseToProto(walletStream);
|
Protos.Wallet proto = WalletProtobufSerializer.parseToProto(walletStream);
|
||||||
final WalletProtobufSerializer serializer;
|
final WalletProtobufSerializer serializer;
|
||||||
if (walletFactory != null)
|
if (walletFactory != null)
|
||||||
|
@ -492,8 +374,6 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
KeyChainGroup.Builder kcg = KeyChainGroup.builder(params, structure);
|
KeyChainGroup.Builder kcg = KeyChainGroup.builder(params, structure);
|
||||||
if (restoreFromSeed != null) {
|
if (restoreFromSeed != null) {
|
||||||
kcg.fromSeed(restoreFromSeed, preferredOutputScriptType).build();
|
kcg.fromSeed(restoreFromSeed, preferredOutputScriptType).build();
|
||||||
} else if (restoreFromKey != null) {
|
|
||||||
kcg.addChain(DeterministicKeyChain.builder().spend(restoreFromKey).outputScriptType(preferredOutputScriptType).build());
|
|
||||||
} else {
|
} else {
|
||||||
// new wallet
|
// new wallet
|
||||||
if (!isBsqWallet) {
|
if (!isBsqWallet) {
|
||||||
|
@ -512,7 +392,7 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeMoveOldWalletOutOfTheWay(File walletFile) {
|
private void maybeMoveOldWalletOutOfTheWay(File walletFile) {
|
||||||
if (restoreFromSeed == null && restoreFromKey == null) return;
|
if (restoreFromSeed == null) return;
|
||||||
if (!walletFile.exists()) return;
|
if (!walletFile.exists()) return;
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
File newName;
|
File newName;
|
||||||
|
@ -527,23 +407,6 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* As soon as the transaction broadcaster han been created we will pass it to the
|
|
||||||
* payment channel extensions
|
|
||||||
*/
|
|
||||||
// private void completeExtensionInitiations(TransactionBroadcaster transactionBroadcaster) {
|
|
||||||
// StoredPaymentChannelClientStates clientStoredChannels = (StoredPaymentChannelClientStates)
|
|
||||||
// vWallet.getExtensions().get(StoredPaymentChannelClientStates.class.getName());
|
|
||||||
// if(clientStoredChannels != null) {
|
|
||||||
// clientStoredChannels.setTransactionBroadcaster(transactionBroadcaster);
|
|
||||||
// }
|
|
||||||
// StoredPaymentChannelServerStates serverStoredChannels = (StoredPaymentChannelServerStates)
|
|
||||||
// vWallet.getExtensions().get(StoredPaymentChannelServerStates.class.getName());
|
|
||||||
// if(serverStoredChannels != null) {
|
|
||||||
// serverStoredChannels.setTransactionBroadcaster(transactionBroadcaster);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
private PeerGroup createPeerGroup() {
|
private PeerGroup createPeerGroup() {
|
||||||
PeerGroup peerGroup;
|
PeerGroup peerGroup;
|
||||||
// no proxy case.
|
// no proxy case.
|
||||||
|
@ -573,19 +436,6 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
return peerGroup;
|
return peerGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void installShutdownHook() {
|
|
||||||
if (autoStop) Runtime.getRuntime().addShutdownHook(new Thread("WalletConfig ShutdownHook") {
|
|
||||||
@Override public void run() {
|
|
||||||
try {
|
|
||||||
WalletConfig.this.stopAsync();
|
|
||||||
WalletConfig.this.awaitTerminated();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void shutDown() throws Exception {
|
protected void shutDown() throws Exception {
|
||||||
// Runs in a separate thread.
|
// Runs in a separate thread.
|
||||||
|
|
|
@ -249,7 +249,7 @@ public class WalletsSetup {
|
||||||
UserThread.execute(() -> {
|
UserThread.execute(() -> {
|
||||||
addressEntryList.onWalletReady(walletConfig.btcWallet());
|
addressEntryList.onWalletReady(walletConfig.btcWallet());
|
||||||
timeoutTimer.stop();
|
timeoutTimer.stop();
|
||||||
setupCompletedHandlers.stream().forEach(Runnable::run);
|
setupCompletedHandlers.forEach(Runnable::run);
|
||||||
});
|
});
|
||||||
|
|
||||||
// onSetupCompleted in walletAppKit is not the called on the last invocations, so we add a bit of delay
|
// onSetupCompleted in walletAppKit is not the called on the last invocations, so we add a bit of delay
|
||||||
|
@ -307,11 +307,12 @@ public class WalletsSetup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
walletConfig.setDownloadListener(downloadListener)
|
walletConfig.setDownloadListener(downloadListener);
|
||||||
.setBlockingStartup(false);
|
|
||||||
|
|
||||||
// If seed is non-null it means we are restoring from backup.
|
// If seed is non-null it means we are restoring from backup.
|
||||||
|
if (seed != null) {
|
||||||
walletConfig.restoreWalletFromSeed(seed);
|
walletConfig.restoreWalletFromSeed(seed);
|
||||||
|
}
|
||||||
|
|
||||||
walletConfig.addListener(new Service.Listener() {
|
walletConfig.addListener(new Service.Listener() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -123,7 +123,7 @@ public class BtcWalletService extends WalletService {
|
||||||
@Override
|
@Override
|
||||||
void encryptWallet(KeyCrypterScrypt keyCrypterScrypt, KeyParameter key) {
|
void encryptWallet(KeyCrypterScrypt keyCrypterScrypt, KeyParameter key) {
|
||||||
super.encryptWallet(keyCrypterScrypt, key);
|
super.encryptWallet(keyCrypterScrypt, key);
|
||||||
addressEntryList.getAddressEntriesAsListImmutable().stream().forEach(e -> {
|
addressEntryList.getAddressEntriesAsListImmutable().forEach(e -> {
|
||||||
DeterministicKey keyPair = e.getKeyPair();
|
DeterministicKey keyPair = e.getKeyPair();
|
||||||
if (keyPair.isEncrypted())
|
if (keyPair.isEncrypted())
|
||||||
e.setDeterministicKey(keyPair.encrypt(keyCrypterScrypt, key));
|
e.setDeterministicKey(keyPair.encrypt(keyCrypterScrypt, key));
|
||||||
|
@ -134,7 +134,7 @@ public class BtcWalletService extends WalletService {
|
||||||
@Override
|
@Override
|
||||||
String getWalletAsString(boolean includePrivKeys) {
|
String getWalletAsString(boolean includePrivKeys) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
getAddressEntryListAsImmutableList().stream().forEach(e -> sb.append(e.toString()).append("\n"));
|
getAddressEntryListAsImmutableList().forEach(e -> sb.append(e.toString()).append("\n"));
|
||||||
return "Address entry list:\n" +
|
return "Address entry list:\n" +
|
||||||
sb.toString() +
|
sb.toString() +
|
||||||
"\n\n" +
|
"\n\n" +
|
||||||
|
|
|
@ -38,8 +38,6 @@ import org.bitcoinj.core.Context;
|
||||||
import org.bitcoinj.core.ECKey;
|
import org.bitcoinj.core.ECKey;
|
||||||
import org.bitcoinj.core.InsufficientMoneyException;
|
import org.bitcoinj.core.InsufficientMoneyException;
|
||||||
import org.bitcoinj.core.NetworkParameters;
|
import org.bitcoinj.core.NetworkParameters;
|
||||||
import org.bitcoinj.core.listeners.TransactionConfidenceEventListener;
|
|
||||||
import org.bitcoinj.script.ScriptException;
|
|
||||||
import org.bitcoinj.core.Sha256Hash;
|
import org.bitcoinj.core.Sha256Hash;
|
||||||
import org.bitcoinj.core.Transaction;
|
import org.bitcoinj.core.Transaction;
|
||||||
import org.bitcoinj.core.TransactionConfidence;
|
import org.bitcoinj.core.TransactionConfidence;
|
||||||
|
@ -47,12 +45,14 @@ import org.bitcoinj.core.TransactionInput;
|
||||||
import org.bitcoinj.core.TransactionOutput;
|
import org.bitcoinj.core.TransactionOutput;
|
||||||
import org.bitcoinj.core.VerificationException;
|
import org.bitcoinj.core.VerificationException;
|
||||||
import org.bitcoinj.core.listeners.NewBestBlockListener;
|
import org.bitcoinj.core.listeners.NewBestBlockListener;
|
||||||
|
import org.bitcoinj.core.listeners.TransactionConfidenceEventListener;
|
||||||
import org.bitcoinj.crypto.DeterministicKey;
|
import org.bitcoinj.crypto.DeterministicKey;
|
||||||
import org.bitcoinj.crypto.KeyCrypter;
|
import org.bitcoinj.crypto.KeyCrypter;
|
||||||
import org.bitcoinj.crypto.KeyCrypterScrypt;
|
import org.bitcoinj.crypto.KeyCrypterScrypt;
|
||||||
import org.bitcoinj.crypto.TransactionSignature;
|
import org.bitcoinj.crypto.TransactionSignature;
|
||||||
import org.bitcoinj.script.Script;
|
import org.bitcoinj.script.Script;
|
||||||
import org.bitcoinj.script.ScriptChunk;
|
import org.bitcoinj.script.ScriptChunk;
|
||||||
|
import org.bitcoinj.script.ScriptException;
|
||||||
import org.bitcoinj.script.ScriptPattern;
|
import org.bitcoinj.script.ScriptPattern;
|
||||||
import org.bitcoinj.signers.TransactionSigner;
|
import org.bitcoinj.signers.TransactionSigner;
|
||||||
import org.bitcoinj.utils.Threading;
|
import org.bitcoinj.utils.Threading;
|
||||||
|
@ -106,7 +106,6 @@ public abstract class WalletService {
|
||||||
protected final Preferences preferences;
|
protected final Preferences preferences;
|
||||||
protected final FeeService feeService;
|
protected final FeeService feeService;
|
||||||
protected final NetworkParameters params;
|
protected final NetworkParameters params;
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
protected final BisqWalletListener walletEventListener = new BisqWalletListener();
|
protected final BisqWalletListener walletEventListener = new BisqWalletListener();
|
||||||
protected final CopyOnWriteArraySet<AddressConfidenceListener> addressConfidenceListeners = new CopyOnWriteArraySet<>();
|
protected final CopyOnWriteArraySet<AddressConfidenceListener> addressConfidenceListeners = new CopyOnWriteArraySet<>();
|
||||||
protected final CopyOnWriteArraySet<TxConfidenceListener> txConfidenceListeners = new CopyOnWriteArraySet<>();
|
protected final CopyOnWriteArraySet<TxConfidenceListener> txConfidenceListeners = new CopyOnWriteArraySet<>();
|
||||||
|
@ -508,17 +507,17 @@ public abstract class WalletService {
|
||||||
sendRequest.feePerKb = getTxFeeForWithdrawalPerByte().multiply(1000);
|
sendRequest.feePerKb = getTxFeeForWithdrawalPerByte().multiply(1000);
|
||||||
sendRequest.aesKey = aesKey;
|
sendRequest.aesKey = aesKey;
|
||||||
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
||||||
printTx("empty wallet", sendResult.tx);
|
printTx("empty btc wallet", sendResult.tx);
|
||||||
Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<Transaction>() {
|
Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<Transaction>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Transaction result) {
|
public void onSuccess(Transaction result) {
|
||||||
log.info("emptyWallet onSuccess Transaction=" + result);
|
log.info("emptyBtcWallet onSuccess Transaction=" + result);
|
||||||
resultHandler.handleResult();
|
resultHandler.handleResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NotNull Throwable t) {
|
public void onFailure(@NotNull Throwable t) {
|
||||||
log.error("emptyWallet onFailure " + t.toString());
|
log.error("emptyBtcWallet onFailure " + t.toString());
|
||||||
errorMessageHandler.handleErrorMessage(t.getMessage());
|
errorMessageHandler.handleErrorMessage(t.getMessage());
|
||||||
}
|
}
|
||||||
}, MoreExecutors.directExecutor());
|
}, MoreExecutors.directExecutor());
|
||||||
|
@ -551,58 +550,10 @@ public abstract class WalletService {
|
||||||
// Wallet delegates to avoid direct access to wallet outside the service class
|
// Wallet delegates to avoid direct access to wallet outside the service class
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void addCoinsReceivedEventListener(WalletCoinsReceivedEventListener listener) {
|
|
||||||
wallet.addCoinsReceivedEventListener(Threading.USER_THREAD, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addCoinsSentEventListener(WalletCoinsSentEventListener listener) {
|
|
||||||
wallet.addCoinsSentEventListener(Threading.USER_THREAD, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addReorganizeEventListener(WalletReorganizeEventListener listener) {
|
|
||||||
wallet.addReorganizeEventListener(Threading.USER_THREAD, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTransactionConfidenceEventListener(TransactionConfidenceEventListener listener) {
|
|
||||||
wallet.addTransactionConfidenceEventListener(Threading.USER_THREAD, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addKeyChainEventListener(KeyChainEventListener listener) {
|
|
||||||
wallet.addKeyChainEventListener(Threading.USER_THREAD, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addScriptChangeEventListener(ScriptsChangeEventListener listener) {
|
|
||||||
wallet.addScriptChangeEventListener(Threading.USER_THREAD, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addChangeEventListener(WalletChangeEventListener listener) {
|
public void addChangeEventListener(WalletChangeEventListener listener) {
|
||||||
wallet.addChangeEventListener(Threading.USER_THREAD, listener);
|
wallet.addChangeEventListener(Threading.USER_THREAD, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeCoinsReceivedEventListener(WalletCoinsReceivedEventListener listener) {
|
|
||||||
wallet.removeCoinsReceivedEventListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeCoinsSentEventListener(WalletCoinsSentEventListener listener) {
|
|
||||||
wallet.removeCoinsSentEventListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeReorganizeEventListener(WalletReorganizeEventListener listener) {
|
|
||||||
wallet.removeReorganizeEventListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeTransactionConfidenceEventListener(TransactionConfidenceEventListener listener) {
|
|
||||||
wallet.removeTransactionConfidenceEventListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeKeyChainEventListener(KeyChainEventListener listener) {
|
|
||||||
wallet.removeKeyChainEventListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeScriptChangeEventListener(ScriptsChangeEventListener listener) {
|
|
||||||
wallet.removeScriptChangeEventListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeChangeEventListener(WalletChangeEventListener listener) {
|
public void removeChangeEventListener(WalletChangeEventListener listener) {
|
||||||
wallet.removeChangeEventListener(listener);
|
wallet.removeChangeEventListener(listener);
|
||||||
}
|
}
|
||||||
|
@ -793,7 +744,6 @@ public abstract class WalletService {
|
||||||
return maybeAddTxToWallet(transaction.bitcoinSerialize(), wallet, source);
|
return maybeAddTxToWallet(transaction.bitcoinSerialize(), wallet, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// bisqWalletEventListener
|
// bisqWalletEventListener
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -74,7 +74,7 @@ public abstract class BaseProposalFactory<R extends Proposal> {
|
||||||
R proposal = createProposalWithoutTxId();
|
R proposal = createProposalWithoutTxId();
|
||||||
proposalValidator.validateDataFields(proposal);
|
proposalValidator.validateDataFields(proposal);
|
||||||
Transaction transaction = createTransaction(proposal);
|
Transaction transaction = createTransaction(proposal);
|
||||||
final Proposal proposalWithTxId = proposal.cloneProposalAndAddTxId(transaction.getTxId().toString());
|
Proposal proposalWithTxId = proposal.cloneProposalAndAddTxId(transaction.getTxId().toString());
|
||||||
return new ProposalWithTransaction(proposalWithTxId, transaction);
|
return new ProposalWithTransaction(proposalWithTxId, transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -388,7 +388,7 @@ public abstract class DisputeManager<T extends DisputeList<? extends DisputeList
|
||||||
// The peer sent us an invalid donation address. We do not return here as we don't want to break
|
// The peer sent us an invalid donation address. We do not return here as we don't want to break
|
||||||
// mediation/arbitration and log only the issue. The dispute agent will run validation as well and will get
|
// mediation/arbitration and log only the issue. The dispute agent will run validation as well and will get
|
||||||
// a popup displayed to react.
|
// a popup displayed to react.
|
||||||
log.error("Donation address invalid. {}", e.toString());
|
log.warn("Donation address is invalid. {}", e.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isAgent(dispute)) {
|
if (!isAgent(dispute)) {
|
||||||
|
|
|
@ -1005,7 +1005,8 @@ public abstract class Trade implements Tradable, Model {
|
||||||
log.debug("We set the start for the trade period to {}. Trade started at: {}. Block got mined at: {}",
|
log.debug("We set the start for the trade period to {}. Trade started at: {}. Block got mined at: {}",
|
||||||
new Date(startTime), new Date(tradeTime), new Date(blockTime));
|
new Date(startTime), new Date(tradeTime), new Date(blockTime));
|
||||||
} else {
|
} else {
|
||||||
log.debug("depositTx not confirmed yet. We don't start counting remaining trade period yet. txId={}", depositTx.getTxId().toString());
|
log.debug("depositTx not confirmed yet. We don't start counting remaining trade period yet. txId={}",
|
||||||
|
depositTx.getTxId().toString());
|
||||||
startTime = now;
|
startTime = now;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -291,17 +291,13 @@ public class TradeDataValidation {
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkParameters params = btcWalletService.getParams();
|
NetworkParameters params = btcWalletService.getParams();
|
||||||
Address address = output.getAddressFromP2PKHScript(params);
|
Address address = output.getScriptPubKey().getToAddress(params);
|
||||||
if (address == null) {
|
if (address == null) {
|
||||||
// The donation address can be a multisig address as well.
|
errorMsg = "Donation address cannot be resolved (not of type P2PK nor P2SH nor P2WH). Output: " + output;
|
||||||
address = output.getAddressFromP2SH(params);
|
|
||||||
if (address == null) {
|
|
||||||
errorMsg = "Donation address cannot be resolved (not of type P2PKHScript or P2SH). Output: " + output;
|
|
||||||
log.error(errorMsg);
|
log.error(errorMsg);
|
||||||
log.error(delayedPayoutTx.toString());
|
log.error(delayedPayoutTx.toString());
|
||||||
throw new AddressException(dispute, errorMsg);
|
throw new AddressException(dispute, errorMsg);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
String addressAsString = address.toString();
|
String addressAsString = address.toString();
|
||||||
if (addressConsumer != null) {
|
if (addressConsumer != null) {
|
||||||
|
|
|
@ -58,10 +58,7 @@ public class BuyerSetupDepositTxListener extends TradeTask {
|
||||||
NetworkParameters params = walletService.getParams();
|
NetworkParameters params = walletService.getParams();
|
||||||
Transaction preparedDepositTx = new Transaction(params, processModel.getPreparedDepositTx());
|
Transaction preparedDepositTx = new Transaction(params, processModel.getPreparedDepositTx());
|
||||||
checkArgument(!preparedDepositTx.getOutputs().isEmpty(), "preparedDepositTx.getOutputs() must not be empty");
|
checkArgument(!preparedDepositTx.getOutputs().isEmpty(), "preparedDepositTx.getOutputs() must not be empty");
|
||||||
|
Address depositTxAddress = preparedDepositTx.getOutput(0).getScriptPubKey().getToAddress(params);
|
||||||
//TODO update to new bitcoinj API
|
|
||||||
Address depositTxAddress = preparedDepositTx.getOutput(0).getAddressFromP2SH(params);
|
|
||||||
|
|
||||||
TransactionConfidence confidence = walletService.getConfidenceForAddress(depositTxAddress);
|
TransactionConfidence confidence = walletService.getConfidenceForAddress(depositTxAddress);
|
||||||
if (isVisibleInNetwork(confidence)) {
|
if (isVisibleInNetwork(confidence)) {
|
||||||
applyConfidence(confidence);
|
applyConfidence(confidence);
|
||||||
|
|
|
@ -189,6 +189,12 @@ public class User implements PersistedDataHost {
|
||||||
// Collection operations
|
// Collection operations
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public void addPaymentAccountIfNotExists(PaymentAccount paymentAccount) {
|
||||||
|
if (!paymentAccountExists(paymentAccount)) {
|
||||||
|
addPaymentAccount(paymentAccount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void addPaymentAccount(PaymentAccount paymentAccount) {
|
public void addPaymentAccount(PaymentAccount paymentAccount) {
|
||||||
paymentAccount.onAddToUser();
|
paymentAccount.onAddToUser();
|
||||||
|
|
||||||
|
@ -493,4 +499,8 @@ public class User implements PersistedDataHost {
|
||||||
public boolean isPaymentAccountImport() {
|
public boolean isPaymentAccountImport() {
|
||||||
return isPaymentAccountImport;
|
return isPaymentAccountImport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean paymentAccountExists(PaymentAccount paymentAccount) {
|
||||||
|
return getPaymentAccountsAsObservable().stream().anyMatch(e -> e.equals(paymentAccount));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2846,6 +2846,9 @@ popup.info.shutDownWithOpenOffers=Bisq is being shut down, but there are open of
|
||||||
they will be re-published to the P2P network the next time you start Bisq.\n\n\
|
they will be re-published to the P2P network the next time you start Bisq.\n\n\
|
||||||
To keep your offers online, keep Bisq running and make sure this computer remains online too \
|
To keep your offers online, keep Bisq running and make sure this computer remains online too \
|
||||||
(i.e., make sure it doesn't go into standby mode...monitor standby is not a problem).
|
(i.e., make sure it doesn't go into standby mode...monitor standby is not a problem).
|
||||||
|
popup.info.qubesOSSetupInfo=It appears you are running Bisq on Qubes OS. \n\n\
|
||||||
|
Please make sure your Bisq qube is setup according to our Setup Guide at \
|
||||||
|
https://bisq.wiki/Running_Bisq_on_Qubes
|
||||||
|
|
||||||
popup.privateNotification.headline=Important private notification!
|
popup.privateNotification.headline=Important private notification!
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import bisq.core.app.CoreModule;
|
||||||
|
|
||||||
import bisq.common.UserThread;
|
import bisq.common.UserThread;
|
||||||
import bisq.common.app.AppModule;
|
import bisq.common.app.AppModule;
|
||||||
|
import bisq.common.handlers.ResultHandler;
|
||||||
import bisq.common.setup.CommonSetup;
|
import bisq.common.setup.CommonSetup;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
@ -39,13 +40,15 @@ import bisq.daemon.grpc.GrpcServer;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class BisqDaemonMain extends BisqHeadlessAppMain implements BisqSetup.BisqSetupListener {
|
public class BisqDaemonMain extends BisqHeadlessAppMain implements BisqSetup.BisqSetupListener {
|
||||||
|
|
||||||
|
private GrpcServer grpcServer;
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
new BisqDaemonMain().execute(args);
|
new BisqDaemonMain().execute(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
// First synchronous execution tasks
|
// First synchronous execution tasks
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configUserThread() {
|
protected void configUserThread() {
|
||||||
|
@ -70,9 +73,9 @@ public class BisqDaemonMain extends BisqHeadlessAppMain implements BisqSetup.Bis
|
||||||
headlessApp.setGracefulShutDownHandler(this);
|
headlessApp.setGracefulShutDownHandler(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
// We continue with a series of synchronous execution tasks
|
// We continue with a series of synchronous execution tasks
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AppModule getModule() {
|
protected AppModule getModule() {
|
||||||
|
@ -91,7 +94,8 @@ public class BisqDaemonMain extends BisqHeadlessAppMain implements BisqSetup.Bis
|
||||||
// We need to be in user thread! We mapped at launchApplication already...
|
// We need to be in user thread! We mapped at launchApplication already...
|
||||||
headlessApp.startApplication();
|
headlessApp.startApplication();
|
||||||
|
|
||||||
// In headless mode we don't have an async behaviour so we trigger the setup by calling onApplicationStarted
|
// In headless mode we don't have an async behaviour so we trigger the setup by
|
||||||
|
// calling onApplicationStarted.
|
||||||
onApplicationStarted();
|
onApplicationStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +103,14 @@ public class BisqDaemonMain extends BisqHeadlessAppMain implements BisqSetup.Bis
|
||||||
protected void onApplicationStarted() {
|
protected void onApplicationStarted() {
|
||||||
super.onApplicationStarted();
|
super.onApplicationStarted();
|
||||||
|
|
||||||
GrpcServer grpcServer = injector.getInstance(GrpcServer.class);
|
grpcServer = injector.getInstance(GrpcServer.class);
|
||||||
grpcServer.start();
|
grpcServer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void gracefulShutDown(ResultHandler resultHandler) {
|
||||||
|
super.gracefulShutDown(resultHandler);
|
||||||
|
|
||||||
|
grpcServer.shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package bisq.daemon.grpc;
|
||||||
|
|
||||||
|
import bisq.core.api.CoreApi;
|
||||||
|
import bisq.core.trade.statistics.TradeStatistics2;
|
||||||
|
|
||||||
|
import bisq.proto.grpc.GetTradeStatisticsGrpc;
|
||||||
|
import bisq.proto.grpc.GetTradeStatisticsReply;
|
||||||
|
import bisq.proto.grpc.GetTradeStatisticsRequest;
|
||||||
|
|
||||||
|
import io.grpc.stub.StreamObserver;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
class GrpcGetTradeStatisticsService extends GetTradeStatisticsGrpc.GetTradeStatisticsImplBase {
|
||||||
|
|
||||||
|
private final CoreApi coreApi;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public GrpcGetTradeStatisticsService(CoreApi coreApi) {
|
||||||
|
this.coreApi = coreApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getTradeStatistics(GetTradeStatisticsRequest req,
|
||||||
|
StreamObserver<GetTradeStatisticsReply> responseObserver) {
|
||||||
|
|
||||||
|
var tradeStatistics = coreApi.getTradeStatistics().stream()
|
||||||
|
.map(TradeStatistics2::toProtoTradeStatistics2)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
var reply = GetTradeStatisticsReply.newBuilder().addAllTradeStatistics(tradeStatistics).build();
|
||||||
|
responseObserver.onNext(reply);
|
||||||
|
responseObserver.onCompleted();
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,7 +52,7 @@ class GrpcOffersService extends OffersGrpc.OffersImplBase {
|
||||||
// The client cannot see bisq.core.Offer or its fromProto method.
|
// The client cannot see bisq.core.Offer or its fromProto method.
|
||||||
// We use the lighter weight OfferInfo proto wrapper instead, containing just
|
// We use the lighter weight OfferInfo proto wrapper instead, containing just
|
||||||
// enough fields to view and create offers.
|
// enough fields to view and create offers.
|
||||||
List<OfferInfo> result = coreApi.getOffers(req.getDirection(), req.getFiatCurrencyCode())
|
List<OfferInfo> result = coreApi.getOffers(req.getDirection(), req.getCurrencyCode())
|
||||||
.stream().map(offer -> new OfferInfo.OfferInfoBuilder()
|
.stream().map(offer -> new OfferInfo.OfferInfoBuilder()
|
||||||
.withId(offer.getId())
|
.withId(offer.getId())
|
||||||
.withDirection(offer.getDirection().name())
|
.withDirection(offer.getDirection().name())
|
||||||
|
|
|
@ -45,7 +45,10 @@ class GrpcPaymentAccountsService extends PaymentAccountsGrpc.PaymentAccountsImpl
|
||||||
@Override
|
@Override
|
||||||
public void createPaymentAccount(CreatePaymentAccountRequest req,
|
public void createPaymentAccount(CreatePaymentAccountRequest req,
|
||||||
StreamObserver<CreatePaymentAccountReply> responseObserver) {
|
StreamObserver<CreatePaymentAccountReply> responseObserver) {
|
||||||
coreApi.createPaymentAccount(req.getAccountName(), req.getAccountNumber(), req.getFiatCurrencyCode());
|
coreApi.createPaymentAccount(req.getPaymentMethodId(),
|
||||||
|
req.getAccountName(),
|
||||||
|
req.getAccountNumber(),
|
||||||
|
req.getCurrencyCode());
|
||||||
var reply = CreatePaymentAccountReply.newBuilder().build();
|
var reply = CreatePaymentAccountReply.newBuilder().build();
|
||||||
responseObserver.onNext(reply);
|
responseObserver.onNext(reply);
|
||||||
responseObserver.onCompleted();
|
responseObserver.onCompleted();
|
||||||
|
@ -54,10 +57,11 @@ class GrpcPaymentAccountsService extends PaymentAccountsGrpc.PaymentAccountsImpl
|
||||||
@Override
|
@Override
|
||||||
public void getPaymentAccounts(GetPaymentAccountsRequest req,
|
public void getPaymentAccounts(GetPaymentAccountsRequest req,
|
||||||
StreamObserver<GetPaymentAccountsReply> responseObserver) {
|
StreamObserver<GetPaymentAccountsReply> responseObserver) {
|
||||||
var tradeStatistics = coreApi.getPaymentAccounts().stream()
|
var paymentAccounts = coreApi.getPaymentAccounts().stream()
|
||||||
.map(PaymentAccount::toProtoMessage)
|
.map(PaymentAccount::toProtoMessage)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
var reply = GetPaymentAccountsReply.newBuilder().addAllPaymentAccounts(tradeStatistics).build();
|
var reply = GetPaymentAccountsReply.newBuilder()
|
||||||
|
.addAllPaymentAccounts(paymentAccounts).build();
|
||||||
responseObserver.onNext(reply);
|
responseObserver.onNext(reply);
|
||||||
responseObserver.onCompleted();
|
responseObserver.onCompleted();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,30 +18,21 @@
|
||||||
package bisq.daemon.grpc;
|
package bisq.daemon.grpc;
|
||||||
|
|
||||||
import bisq.core.api.CoreApi;
|
import bisq.core.api.CoreApi;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2;
|
|
||||||
|
|
||||||
import bisq.common.config.Config;
|
import bisq.common.config.Config;
|
||||||
|
|
||||||
import bisq.proto.grpc.GetTradeStatisticsGrpc;
|
|
||||||
import bisq.proto.grpc.GetTradeStatisticsReply;
|
|
||||||
import bisq.proto.grpc.GetTradeStatisticsRequest;
|
|
||||||
import bisq.proto.grpc.GetVersionGrpc;
|
|
||||||
import bisq.proto.grpc.GetVersionReply;
|
|
||||||
import bisq.proto.grpc.GetVersionRequest;
|
|
||||||
|
|
||||||
import io.grpc.Server;
|
import io.grpc.Server;
|
||||||
import io.grpc.ServerBuilder;
|
import io.grpc.ServerBuilder;
|
||||||
import io.grpc.stub.StreamObserver;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class GrpcServer {
|
public class GrpcServer {
|
||||||
|
|
||||||
|
@ -51,19 +42,22 @@ public class GrpcServer {
|
||||||
@Inject
|
@Inject
|
||||||
public GrpcServer(Config config,
|
public GrpcServer(Config config,
|
||||||
CoreApi coreApi,
|
CoreApi coreApi,
|
||||||
|
PasswordAuthInterceptor passwordAuthInterceptor,
|
||||||
GrpcDisputeAgentsService disputeAgentsService,
|
GrpcDisputeAgentsService disputeAgentsService,
|
||||||
GrpcOffersService offersService,
|
GrpcOffersService offersService,
|
||||||
GrpcPaymentAccountsService paymentAccountsService,
|
GrpcPaymentAccountsService paymentAccountsService,
|
||||||
|
GrpcVersionService versionService,
|
||||||
|
GrpcGetTradeStatisticsService tradeStatisticsService,
|
||||||
GrpcWalletsService walletsService) {
|
GrpcWalletsService walletsService) {
|
||||||
this.coreApi = coreApi;
|
this.coreApi = coreApi;
|
||||||
this.server = ServerBuilder.forPort(config.apiPort)
|
this.server = ServerBuilder.forPort(config.apiPort)
|
||||||
.addService(disputeAgentsService)
|
.addService(disputeAgentsService)
|
||||||
.addService(new GetVersionService())
|
|
||||||
.addService(new GetTradeStatisticsService())
|
|
||||||
.addService(offersService)
|
.addService(offersService)
|
||||||
.addService(paymentAccountsService)
|
.addService(paymentAccountsService)
|
||||||
|
.addService(tradeStatisticsService)
|
||||||
|
.addService(versionService)
|
||||||
.addService(walletsService)
|
.addService(walletsService)
|
||||||
.intercept(new PasswordAuthInterceptor(config.apiPassword))
|
.intercept(passwordAuthInterceptor)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,36 +65,14 @@ public class GrpcServer {
|
||||||
try {
|
try {
|
||||||
server.start();
|
server.start();
|
||||||
log.info("listening on port {}", server.getPort());
|
log.info("listening on port {}", server.getPort());
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
|
||||||
server.shutdown();
|
|
||||||
log.info("shutdown complete");
|
|
||||||
}));
|
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new UncheckedIOException(ex);
|
throw new UncheckedIOException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GetVersionService extends GetVersionGrpc.GetVersionImplBase {
|
public void shutdown() {
|
||||||
@Override
|
log.info("Server shutdown started");
|
||||||
public void getVersion(GetVersionRequest req, StreamObserver<GetVersionReply> responseObserver) {
|
server.shutdown();
|
||||||
var reply = GetVersionReply.newBuilder().setVersion(coreApi.getVersion()).build();
|
log.info("Server shutdown complete");
|
||||||
responseObserver.onNext(reply);
|
|
||||||
responseObserver.onCompleted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GetTradeStatisticsService extends GetTradeStatisticsGrpc.GetTradeStatisticsImplBase {
|
|
||||||
@Override
|
|
||||||
public void getTradeStatistics(GetTradeStatisticsRequest req,
|
|
||||||
StreamObserver<GetTradeStatisticsReply> responseObserver) {
|
|
||||||
|
|
||||||
var tradeStatistics = coreApi.getTradeStatistics().stream()
|
|
||||||
.map(TradeStatistics2::toProtoTradeStatistics2)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
var reply = GetTradeStatisticsReply.newBuilder().addAllTradeStatistics(tradeStatistics).build();
|
|
||||||
responseObserver.onNext(reply);
|
|
||||||
responseObserver.onCompleted();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package bisq.daemon.grpc;
|
||||||
|
|
||||||
|
import bisq.core.api.CoreApi;
|
||||||
|
|
||||||
|
import bisq.proto.grpc.GetVersionGrpc;
|
||||||
|
import bisq.proto.grpc.GetVersionReply;
|
||||||
|
import bisq.proto.grpc.GetVersionRequest;
|
||||||
|
|
||||||
|
import io.grpc.stub.StreamObserver;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
class GrpcVersionService extends GetVersionGrpc.GetVersionImplBase {
|
||||||
|
|
||||||
|
private final CoreApi coreApi;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public GrpcVersionService(CoreApi coreApi) {
|
||||||
|
this.coreApi = coreApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getVersion(GetVersionRequest req, StreamObserver<GetVersionReply> responseObserver) {
|
||||||
|
var reply = GetVersionReply.newBuilder().setVersion(coreApi.getVersion()).build();
|
||||||
|
responseObserver.onNext(reply);
|
||||||
|
responseObserver.onCompleted();
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,11 +54,17 @@ class GrpcWalletsService extends WalletsGrpc.WalletsImplBase {
|
||||||
this.coreApi = coreApi;
|
this.coreApi = coreApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO we need to support 3 or 4 balance types: available, reserved, lockedInTrade
|
||||||
|
// and maybe total wallet balance (available+reserved). To not duplicate the methods,
|
||||||
|
// we should pass an enum type. Enums in proto are a bit cumbersome as they are
|
||||||
|
// global so you quickly run into namespace conflicts if not always prefixes which
|
||||||
|
// makes it more verbose. In the core code base we move to the strategy to store the
|
||||||
|
// enum name and map it. This gives also more flexibility with updates.
|
||||||
@Override
|
@Override
|
||||||
public void getBalance(GetBalanceRequest req, StreamObserver<GetBalanceReply> responseObserver) {
|
public void getBalance(GetBalanceRequest req, StreamObserver<GetBalanceReply> responseObserver) {
|
||||||
try {
|
try {
|
||||||
long result = coreApi.getAvailableBalance();
|
long availableBalance = coreApi.getAvailableBalance();
|
||||||
var reply = GetBalanceReply.newBuilder().setBalance(result).build();
|
var reply = GetBalanceReply.newBuilder().setBalance(availableBalance).build();
|
||||||
responseObserver.onNext(reply);
|
responseObserver.onNext(reply);
|
||||||
responseObserver.onCompleted();
|
responseObserver.onCompleted();
|
||||||
} catch (IllegalStateException cause) {
|
} catch (IllegalStateException cause) {
|
||||||
|
@ -72,8 +78,9 @@ class GrpcWalletsService extends WalletsGrpc.WalletsImplBase {
|
||||||
public void getAddressBalance(GetAddressBalanceRequest req,
|
public void getAddressBalance(GetAddressBalanceRequest req,
|
||||||
StreamObserver<GetAddressBalanceReply> responseObserver) {
|
StreamObserver<GetAddressBalanceReply> responseObserver) {
|
||||||
try {
|
try {
|
||||||
AddressBalanceInfo result = coreApi.getAddressBalanceInfo(req.getAddress());
|
AddressBalanceInfo balanceInfo = coreApi.getAddressBalanceInfo(req.getAddress());
|
||||||
var reply = GetAddressBalanceReply.newBuilder().setAddressBalanceInfo(result.toProtoMessage()).build();
|
var reply = GetAddressBalanceReply.newBuilder()
|
||||||
|
.setAddressBalanceInfo(balanceInfo.toProtoMessage()).build();
|
||||||
responseObserver.onNext(reply);
|
responseObserver.onNext(reply);
|
||||||
responseObserver.onCompleted();
|
responseObserver.onCompleted();
|
||||||
} catch (IllegalStateException cause) {
|
} catch (IllegalStateException cause) {
|
||||||
|
@ -87,10 +94,10 @@ class GrpcWalletsService extends WalletsGrpc.WalletsImplBase {
|
||||||
public void getFundingAddresses(GetFundingAddressesRequest req,
|
public void getFundingAddresses(GetFundingAddressesRequest req,
|
||||||
StreamObserver<GetFundingAddressesReply> responseObserver) {
|
StreamObserver<GetFundingAddressesReply> responseObserver) {
|
||||||
try {
|
try {
|
||||||
List<AddressBalanceInfo> result = coreApi.getFundingAddresses();
|
List<AddressBalanceInfo> balanceInfo = coreApi.getFundingAddresses();
|
||||||
var reply = GetFundingAddressesReply.newBuilder()
|
var reply = GetFundingAddressesReply.newBuilder()
|
||||||
.addAllAddressBalanceInfo(
|
.addAllAddressBalanceInfo(
|
||||||
result.stream()
|
balanceInfo.stream()
|
||||||
.map(AddressBalanceInfo::toProtoMessage)
|
.map(AddressBalanceInfo::toProtoMessage)
|
||||||
.collect(Collectors.toList()))
|
.collect(Collectors.toList()))
|
||||||
.build();
|
.build();
|
||||||
|
|
|
@ -17,12 +17,16 @@
|
||||||
|
|
||||||
package bisq.daemon.grpc;
|
package bisq.daemon.grpc;
|
||||||
|
|
||||||
|
import bisq.common.config.Config;
|
||||||
|
|
||||||
import io.grpc.Metadata;
|
import io.grpc.Metadata;
|
||||||
import io.grpc.ServerCall;
|
import io.grpc.ServerCall;
|
||||||
import io.grpc.ServerCallHandler;
|
import io.grpc.ServerCallHandler;
|
||||||
import io.grpc.ServerInterceptor;
|
import io.grpc.ServerInterceptor;
|
||||||
import io.grpc.StatusRuntimeException;
|
import io.grpc.StatusRuntimeException;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;
|
import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;
|
||||||
import static io.grpc.Metadata.Key;
|
import static io.grpc.Metadata.Key;
|
||||||
import static io.grpc.Status.UNAUTHENTICATED;
|
import static io.grpc.Status.UNAUTHENTICATED;
|
||||||
|
@ -36,12 +40,13 @@ import static java.lang.String.format;
|
||||||
*/
|
*/
|
||||||
class PasswordAuthInterceptor implements ServerInterceptor {
|
class PasswordAuthInterceptor implements ServerInterceptor {
|
||||||
|
|
||||||
public static final String PASSWORD_KEY = "password";
|
private static final String PASSWORD_KEY = "password";
|
||||||
|
|
||||||
private final String expectedPasswordValue;
|
private final String expectedPasswordValue;
|
||||||
|
|
||||||
public PasswordAuthInterceptor(String expectedPasswordValue) {
|
@Inject
|
||||||
this.expectedPasswordValue = expectedPasswordValue;
|
public PasswordAuthInterceptor(Config config) {
|
||||||
|
this.expectedPasswordValue = config.apiPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -395,6 +395,15 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener {
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
bisqSetup.setQubesOSInfoHandler(() -> {
|
||||||
|
String key = "qubesOSSetupInfo";
|
||||||
|
if (preferences.showAgain(key)) {
|
||||||
|
new Popup().information(Res.get("popup.info.qubesOSSetupInfo"))
|
||||||
|
.closeButtonText(Res.get("shared.iUnderstand"))
|
||||||
|
.dontShowAgainId(key)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
corruptedDatabaseFilesHandler.getCorruptedDatabaseFiles().ifPresent(files -> new Popup()
|
corruptedDatabaseFilesHandler.getCorruptedDatabaseFiles().ifPresent(files -> new Popup()
|
||||||
.warning(Res.get("popup.warning.incompatibleDB", files.toString(), config.appDataDir))
|
.warning(Res.get("popup.warning.incompatibleDB", files.toString(), config.appDataDir))
|
||||||
|
|
|
@ -157,7 +157,7 @@ class TransactionAwareTrade implements TransactionAwareTradable {
|
||||||
tx.getOutputs().forEach(txo -> {
|
tx.getOutputs().forEach(txo -> {
|
||||||
if (btcWalletService.isTransactionOutputMine(txo)) {
|
if (btcWalletService.isTransactionOutputMine(txo)) {
|
||||||
try {
|
try {
|
||||||
Address receiverAddress = txo.getAddressFromP2PKHScript(btcWalletService.getParams());
|
Address receiverAddress = txo.getScriptPubKey().getToAddress(btcWalletService.getParams());
|
||||||
Contract contract = checkNotNull(trade.getContract());
|
Contract contract = checkNotNull(trade.getContract());
|
||||||
String myPayoutAddressString = contract.isMyRoleBuyer(pubKeyRing) ?
|
String myPayoutAddressString = contract.isMyRoleBuyer(pubKeyRing) ?
|
||||||
contract.getBuyerPayoutAddressString() :
|
contract.getBuyerPayoutAddressString() :
|
||||||
|
|
|
@ -103,12 +103,6 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||||
private final TradeDetailsWindow tradeDetailsWindow;
|
private final TradeDetailsWindow tradeDetailsWindow;
|
||||||
private final OfferDetailsWindow offerDetailsWindow;
|
private final OfferDetailsWindow offerDetailsWindow;
|
||||||
|
|
||||||
private WalletCoinsReceivedEventListener walletCoinsReceivedEventListener;
|
|
||||||
private WalletCoinsSentEventListener walletCoinsSentEventListener;
|
|
||||||
private WalletReorganizeEventListener walletReorganizeEventListener;
|
|
||||||
private TransactionConfidenceEventListener transactionConfidenceEventListener;
|
|
||||||
private KeyChainEventListener keyChainEventListener;
|
|
||||||
private ScriptsChangeEventListener scriptsChangeEventListener;
|
|
||||||
private WalletChangeEventListener walletChangeEventListener;
|
private WalletChangeEventListener walletChangeEventListener;
|
||||||
|
|
||||||
private EventHandler<KeyEvent> keyEventEventHandler;
|
private EventHandler<KeyEvent> keyEventEventHandler;
|
||||||
|
@ -173,23 +167,6 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||||
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||||
tableView.getSortOrder().add(dateColumn);
|
tableView.getSortOrder().add(dateColumn);
|
||||||
|
|
||||||
walletCoinsReceivedEventListener = (wallet, tx, prevBalance, newBalance) -> {
|
|
||||||
displayedTransactions.update();
|
|
||||||
};
|
|
||||||
walletCoinsSentEventListener = (wallet, tx, prevBalance, newBalance) -> {
|
|
||||||
displayedTransactions.update();
|
|
||||||
};
|
|
||||||
walletReorganizeEventListener = wallet -> {
|
|
||||||
displayedTransactions.update();
|
|
||||||
};
|
|
||||||
transactionConfidenceEventListener = (wallet, tx) -> {
|
|
||||||
};
|
|
||||||
keyChainEventListener = keys -> {
|
|
||||||
displayedTransactions.update();
|
|
||||||
};
|
|
||||||
scriptsChangeEventListener = (wallet, scripts, isAddingScripts) -> {
|
|
||||||
displayedTransactions.update();
|
|
||||||
};
|
|
||||||
walletChangeEventListener = wallet -> {
|
walletChangeEventListener = wallet -> {
|
||||||
displayedTransactions.update();
|
displayedTransactions.update();
|
||||||
};
|
};
|
||||||
|
@ -215,12 +192,6 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||||
tableView.setItems(sortedDisplayedTransactions);
|
tableView.setItems(sortedDisplayedTransactions);
|
||||||
displayedTransactions.update();
|
displayedTransactions.update();
|
||||||
|
|
||||||
btcWalletService.addCoinsReceivedEventListener(walletCoinsReceivedEventListener);
|
|
||||||
btcWalletService.addCoinsSentEventListener(walletCoinsSentEventListener);
|
|
||||||
btcWalletService.addReorganizeEventListener(walletReorganizeEventListener);
|
|
||||||
btcWalletService.addTransactionConfidenceEventListener(transactionConfidenceEventListener);
|
|
||||||
btcWalletService.addKeyChainEventListener(keyChainEventListener);
|
|
||||||
btcWalletService.addScriptChangeEventListener(scriptsChangeEventListener);
|
|
||||||
btcWalletService.addChangeEventListener(walletChangeEventListener);
|
btcWalletService.addChangeEventListener(walletChangeEventListener);
|
||||||
|
|
||||||
scene = root.getScene();
|
scene = root.getScene();
|
||||||
|
@ -257,12 +228,6 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||||
protected void deactivate() {
|
protected void deactivate() {
|
||||||
sortedDisplayedTransactions.comparatorProperty().unbind();
|
sortedDisplayedTransactions.comparatorProperty().unbind();
|
||||||
displayedTransactions.forEach(TransactionsListItem::cleanup);
|
displayedTransactions.forEach(TransactionsListItem::cleanup);
|
||||||
btcWalletService.removeCoinsReceivedEventListener(walletCoinsReceivedEventListener);
|
|
||||||
btcWalletService.removeCoinsSentEventListener(walletCoinsSentEventListener);
|
|
||||||
btcWalletService.removeReorganizeEventListener(walletReorganizeEventListener);
|
|
||||||
btcWalletService.removeTransactionConfidenceEventListener(transactionConfidenceEventListener);
|
|
||||||
btcWalletService.removeKeyChainEventListener(keyChainEventListener);
|
|
||||||
btcWalletService.removeScriptChangeEventListener(scriptsChangeEventListener);
|
|
||||||
btcWalletService.removeChangeEventListener(walletChangeEventListener);
|
btcWalletService.removeChangeEventListener(walletChangeEventListener);
|
||||||
|
|
||||||
if (scene != null)
|
if (scene != null)
|
||||||
|
|
|
@ -104,7 +104,9 @@ public class SelectDepositTxWindow extends Overlay<SelectDepositTxWindow> {
|
||||||
});
|
});
|
||||||
transactionsComboBox.setItems(FXCollections.observableArrayList(transactions));
|
transactionsComboBox.setItems(FXCollections.observableArrayList(transactions));
|
||||||
transactionsComboBox.setOnAction(event -> {
|
transactionsComboBox.setOnAction(event -> {
|
||||||
|
if (selectHandlerOptional.isPresent()) {
|
||||||
selectHandlerOptional.get().accept(transactionsComboBox.getSelectionModel().getSelectedItem());
|
selectHandlerOptional.get().accept(transactionsComboBox.getSelectionModel().getSelectedItem());
|
||||||
|
}
|
||||||
hide();
|
hide();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,8 @@ Full node mode:
|
||||||
|
|
||||||
Lite node mode:
|
Lite node mode:
|
||||||
|
|
||||||
|
Note: At least one seed node must be running as a full DAO node to support other lite nodes.
|
||||||
|
|
||||||
`--daoActivated=true --genesisBlockHeight=111 --genesisTxId=30af0050040befd8af25068cc697e418e09c2d8ebd8d411d2240591b9ec203cf --baseCurrencyNetwork=BTC_REGTEST --useDevPrivilegeKeys=true --useLocalhostForP2P=true --nodePort=8888 --appName=bisq-BTC_REGTEST_Bob_dao`
|
`--daoActivated=true --genesisBlockHeight=111 --genesisTxId=30af0050040befd8af25068cc697e418e09c2d8ebd8d411d2240591b9ec203cf --baseCurrencyNetwork=BTC_REGTEST --useDevPrivilegeKeys=true --useLocalhostForP2P=true --nodePort=8888 --appName=bisq-BTC_REGTEST_Bob_dao`
|
||||||
|
|
||||||
_Don't forget to use different rpcBlockNotificationPorts for different full node instances, otherwise only one node will receive the new block event forwarded to that port._
|
_Don't forget to use different rpcBlockNotificationPorts for different full node instances, otherwise only one node will receive the new block event forwarded to that port._
|
||||||
|
|
|
@ -53,7 +53,7 @@ service Offers {
|
||||||
|
|
||||||
message GetOffersRequest {
|
message GetOffersRequest {
|
||||||
string direction = 1;
|
string direction = 1;
|
||||||
string fiatCurrencyCode = 2;
|
string currencyCode = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetOffersReply {
|
message GetOffersReply {
|
||||||
|
@ -61,7 +61,7 @@ message GetOffersReply {
|
||||||
}
|
}
|
||||||
|
|
||||||
message CreateOfferRequest {
|
message CreateOfferRequest {
|
||||||
string currencyCode = 1; // TODO switch order w/ direction field in next PR
|
string currencyCode = 1;
|
||||||
string direction = 2;
|
string direction = 2;
|
||||||
uint64 price = 3;
|
uint64 price = 3;
|
||||||
bool useMarketBasedPrice = 4;
|
bool useMarketBasedPrice = 4;
|
||||||
|
@ -107,9 +107,11 @@ service PaymentAccounts {
|
||||||
}
|
}
|
||||||
|
|
||||||
message CreatePaymentAccountRequest {
|
message CreatePaymentAccountRequest {
|
||||||
string accountName = 1;
|
string paymentMethodId = 1;
|
||||||
string accountNumber = 2;
|
string accountName = 2;
|
||||||
string fiatCurrencyCode = 3;
|
string accountNumber = 3;
|
||||||
|
// TODO Support all currencies. Maybe add a repeated and if only one is used its a singletonList.
|
||||||
|
string currencyCode = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CreatePaymentAccountReply {
|
message CreatePaymentAccountReply {
|
||||||
|
@ -123,7 +125,7 @@ message GetPaymentAccountsReply {
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// TradeStatistics
|
// GetTradeStatistics
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
service GetTradeStatistics {
|
service GetTradeStatistics {
|
||||||
|
|
|
@ -14,5 +14,6 @@
|
||||||
<logger name="bisq.common.storage.FileManager" level="WARN"/>
|
<logger name="bisq.common.storage.FileManager" level="WARN"/>
|
||||||
|
|
||||||
<logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="INFO"/>
|
<logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="INFO"/>
|
||||||
|
<logger name="com.neemre.btcdcli4j" level="WARN"/>
|
||||||
|
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
Loading…
Add table
Reference in a new issue