mirror of
https://github.com/bisq-network/bisq.git
synced 2025-03-03 10:46:54 +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
|
||||
[ "$status" -eq 0 ]
|
||||
echo "actual output: $output" >&2
|
||||
[ "$output" = "1.3.8" ]
|
||||
[ "$output" = "1.3.9" ]
|
||||
}
|
||||
|
||||
@test "test getversion" {
|
||||
run ./bisq-cli --password=xyz getversion
|
||||
[ "$status" -eq 0 ]
|
||||
echo "actual output: $output" >&2
|
||||
[ "$output" = "1.3.8" ]
|
||||
[ "$output" = "1.3.9" ]
|
||||
}
|
||||
|
||||
@test "test setwalletpassword \"a b c\"" {
|
||||
|
@ -166,15 +166,15 @@
|
|||
[ "$output" = "Error: address bogus not found in wallet" ]
|
||||
}
|
||||
|
||||
@test "test createpaymentacct PerfectMoneyDummy (missing nbr, ccy params)" {
|
||||
run ./bisq-cli --password=xyz createpaymentacct PerfectMoneyDummy
|
||||
@test "test createpaymentacct PerfectMoneyDummy (missing name, nbr, ccy params)" {
|
||||
run ./bisq-cli --password=xyz createpaymentacct PERFECT_MONEY
|
||||
[ "$status" -eq 1 ]
|
||||
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" {
|
||||
run ./bisq-cli --password=xyz createpaymentacct PerfectMoneyDummy 0123456789 USD
|
||||
@test "test createpaymentacct PERFECT_MONEY PerfectMoneyDummy 0123456789 USD" {
|
||||
run ./bisq-cli --password=xyz createpaymentacct PERFECT_MONEY PerfectMoneyDummy 0123456789 USD
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind;
|
||||
import static bisq.apitest.config.BisqAppConfig.*;
|
||||
import static java.lang.String.format;
|
||||
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_FAILURE = 1;
|
||||
|
||||
public enum BitcoinCoreApp {
|
||||
bitcoind
|
||||
}
|
||||
|
||||
public final ApiTestConfig config;
|
||||
|
||||
@Nullable
|
||||
|
@ -295,7 +300,7 @@ public class Scaffold {
|
|||
|
||||
log.info("Starting supporting apps {}", config.supportingApps.toString());
|
||||
|
||||
if (config.hasSupportingApp("bitcoind")) {
|
||||
if (config.hasSupportingApp(bitcoind.name())) {
|
||||
BitcoinDaemon bitcoinDaemon = new BitcoinDaemon(config);
|
||||
bitcoinDaemon.verifyBitcoinPathsExist(true);
|
||||
bitcoindTask = new SetupTask(bitcoinDaemon, countdownLatch);
|
||||
|
|
|
@ -24,7 +24,9 @@ import java.io.IOException;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
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.
|
||||
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 {
|
||||
scaffold = new Scaffold(supportingApps).setUp();
|
||||
scaffold = new Scaffold(stream(supportingApps).map(Enum::name)
|
||||
.collect(Collectors.joining(",")))
|
||||
.setUp();
|
||||
config = scaffold.config;
|
||||
bitcoinCli = new BitcoinCliHelper((config));
|
||||
}
|
||||
|
||||
public static void setUpScaffold(String[] params)
|
||||
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();
|
||||
config = scaffold.config;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,9 @@ import org.junit.jupiter.api.Order;
|
|||
import org.junit.jupiter.api.Test;
|
||||
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.seednode;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
@ -41,7 +43,7 @@ public class GetBalanceTest extends MethodTest {
|
|||
@BeforeAll
|
||||
public static void setUp() {
|
||||
try {
|
||||
setUpScaffold("bitcoind,seednode,alicedaemon");
|
||||
setUpScaffold(bitcoind, seednode, alicedaemon);
|
||||
|
||||
// Have to generate 1 regtest block for alice's wallet to show 10 BTC balance.
|
||||
bitcoinCli.generateBlocks(1);
|
||||
|
|
|
@ -41,7 +41,7 @@ public class GetVersionTest extends MethodTest {
|
|||
@BeforeAll
|
||||
public static void setUp() {
|
||||
try {
|
||||
setUpScaffold(alicedaemon.name());
|
||||
setUpScaffold(alicedaemon);
|
||||
} catch (Exception ex) {
|
||||
fail(ex);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,9 @@ import org.junit.jupiter.api.Order;
|
|||
import org.junit.jupiter.api.Test;
|
||||
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.seednode;
|
||||
import static bisq.common.app.DevEnv.DEV_PRIVILEGE_PRIV_KEY;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
@ -42,10 +44,14 @@ import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
|||
@TestMethodOrder(OrderAnnotation.class)
|
||||
public class RegisterDisputeAgentsTest extends MethodTest {
|
||||
|
||||
private static final String ARBITRATOR = "arbitrator";
|
||||
private static final String MEDIATOR = "mediator";
|
||||
private static final String REFUNDAGENT = "refundagent";
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
try {
|
||||
setUpScaffold("bitcoind,seednode,arbdaemon");
|
||||
setUpScaffold(bitcoind, seednode, arbdaemon);
|
||||
} catch (Exception ex) {
|
||||
fail(ex);
|
||||
}
|
||||
|
@ -55,7 +61,7 @@ public class RegisterDisputeAgentsTest extends MethodTest {
|
|||
@Order(1)
|
||||
public void testRegisterArbitratorShouldThrowException() {
|
||||
var req =
|
||||
createRegisterDisputeAgentRequest("arbitrator");
|
||||
createRegisterDisputeAgentRequest(ARBITRATOR);
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req));
|
||||
assertEquals("INVALID_ARGUMENT: arbitrators must be registered in a Bisq UI",
|
||||
|
@ -77,7 +83,7 @@ public class RegisterDisputeAgentsTest extends MethodTest {
|
|||
@Order(3)
|
||||
public void testInvalidRegistrationKeyArgShouldThrowException() {
|
||||
var req = RegisterDisputeAgentRequest.newBuilder()
|
||||
.setDisputeAgentType("refundagent")
|
||||
.setDisputeAgentType(REFUNDAGENT)
|
||||
.setRegistrationKey("invalid" + DEV_PRIVILEGE_PRIV_KEY).build();
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req));
|
||||
|
@ -89,7 +95,7 @@ public class RegisterDisputeAgentsTest extends MethodTest {
|
|||
@Order(4)
|
||||
public void testRegisterMediator() {
|
||||
var req =
|
||||
createRegisterDisputeAgentRequest("mediator");
|
||||
createRegisterDisputeAgentRequest(MEDIATOR);
|
||||
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req);
|
||||
}
|
||||
|
||||
|
@ -97,7 +103,7 @@ public class RegisterDisputeAgentsTest extends MethodTest {
|
|||
@Order(5)
|
||||
public void testRegisterRefundAgent() {
|
||||
var req =
|
||||
createRegisterDisputeAgentRequest("refundagent");
|
||||
createRegisterDisputeAgentRequest(REFUNDAGENT);
|
||||
grpcStubs(arbdaemon).disputeAgentsService.registerDisputeAgent(req);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ public class WalletProtectionTest extends MethodTest {
|
|||
@BeforeAll
|
||||
public static void setUp() {
|
||||
try {
|
||||
setUpScaffold(alicedaemon.name());
|
||||
setUpScaffold(alicedaemon);
|
||||
MILLISECONDS.sleep(2000);
|
||||
} catch (Exception ex) {
|
||||
fail(ex);
|
||||
|
|
|
@ -26,7 +26,9 @@ import org.junit.jupiter.api.Order;
|
|||
import org.junit.jupiter.api.Test;
|
||||
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.seednode;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
@ -38,7 +40,7 @@ public class FundWalletScenarioTest extends ScenarioTest {
|
|||
@BeforeAll
|
||||
public static void setUp() {
|
||||
try {
|
||||
setUpScaffold("bitcoind,seednode,alicedaemon");
|
||||
setUpScaffold(bitcoind, seednode, alicedaemon);
|
||||
bitcoinCli.generateBlocks(1);
|
||||
MILLISECONDS.sleep(1500);
|
||||
} catch (Exception ex) {
|
||||
|
|
|
@ -25,6 +25,7 @@ import bisq.proto.grpc.GetOffersRequest;
|
|||
import bisq.proto.grpc.GetPaymentAccountsRequest;
|
||||
import bisq.proto.grpc.GetVersionRequest;
|
||||
import bisq.proto.grpc.LockWalletRequest;
|
||||
import bisq.proto.grpc.RegisterDisputeAgentRequest;
|
||||
import bisq.proto.grpc.RemoveWalletPasswordRequest;
|
||||
import bisq.proto.grpc.SetWalletPasswordRequest;
|
||||
import bisq.proto.grpc.UnlockWalletRequest;
|
||||
|
@ -69,7 +70,8 @@ public class CliMain {
|
|||
lockwallet,
|
||||
unlockwallet,
|
||||
removewalletpassword,
|
||||
setwalletpassword
|
||||
setwalletpassword,
|
||||
registerdisputeagent
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
@ -114,9 +116,9 @@ public class CliMain {
|
|||
}
|
||||
|
||||
var methodName = nonOptionArgs.get(0);
|
||||
final Method method;
|
||||
Method method;
|
||||
try {
|
||||
method = Method.valueOf(methodName);
|
||||
method = getMethodFromCmd(methodName);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
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");
|
||||
|
||||
GrpcStubs grpcStubs = new GrpcStubs(host, port, password);
|
||||
var disputeAgentsService = grpcStubs.disputeAgentsService;
|
||||
var versionService = grpcStubs.versionService;
|
||||
var offersService = grpcStubs.offersService;
|
||||
var paymentAccountsService = grpcStubs.paymentAccountsService;
|
||||
|
@ -166,34 +169,38 @@ public class CliMain {
|
|||
}
|
||||
case getoffers: {
|
||||
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 fiatCurrency = nonOptionArgs.get(2);
|
||||
|
||||
var request = GetOffersRequest.newBuilder()
|
||||
.setDirection(direction)
|
||||
.setFiatCurrencyCode(fiatCurrency)
|
||||
.setCurrencyCode(fiatCurrency)
|
||||
.build();
|
||||
var reply = offersService.getOffers(request);
|
||||
out.println(formatOfferTable(reply.getOffersList(), fiatCurrency));
|
||||
return;
|
||||
}
|
||||
case createpaymentacct: {
|
||||
if (nonOptionArgs.size() < 4)
|
||||
if (nonOptionArgs.size() < 5)
|
||||
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 accountNumber = nonOptionArgs.get(2);
|
||||
var fiatCurrencyCode = nonOptionArgs.get(3);
|
||||
var paymentMethodId = nonOptionArgs.get(1);
|
||||
var accountName = nonOptionArgs.get(2);
|
||||
var accountNumber = nonOptionArgs.get(3);
|
||||
var currencyCode = nonOptionArgs.get(4);
|
||||
|
||||
var request = CreatePaymentAccountRequest.newBuilder()
|
||||
.setPaymentMethodId(paymentMethodId)
|
||||
.setAccountName(accountName)
|
||||
.setAccountNumber(accountNumber)
|
||||
.setFiatCurrencyCode(fiatCurrencyCode).build();
|
||||
.setCurrencyCode(currencyCode).build();
|
||||
paymentAccountsService.createPaymentAccount(request);
|
||||
out.println(format("payment account %s saved", accountName));
|
||||
out.printf("payment account %s saved", accountName);
|
||||
return;
|
||||
}
|
||||
case getpaymentaccts: {
|
||||
|
@ -232,7 +239,8 @@ public class CliMain {
|
|||
if (nonOptionArgs.size() < 2)
|
||||
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);
|
||||
out.println("wallet decrypted");
|
||||
return;
|
||||
|
@ -241,7 +249,8 @@ public class CliMain {
|
|||
if (nonOptionArgs.size() < 2)
|
||||
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;
|
||||
if (hasNewPassword)
|
||||
requestBuilder.setNewPassword(nonOptionArgs.get(2));
|
||||
|
@ -249,6 +258,19 @@ public class CliMain {
|
|||
out.println("wallet encrypted" + (hasNewPassword ? " with new password" : ""));
|
||||
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: {
|
||||
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) {
|
||||
try {
|
||||
stream.println("Bisq RPC Client");
|
||||
|
|
|
@ -161,6 +161,19 @@ public class Utilities {
|
|||
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() {
|
||||
return getOSName().contains("mac") || getOSName().contains("darwin");
|
||||
}
|
||||
|
|
|
@ -139,8 +139,14 @@ public class CoreApi {
|
|||
// PaymentAccounts
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void createPaymentAccount(String accountName, String accountNumber, String fiatCurrencyCode) {
|
||||
paymentAccountsService.createPaymentAccount(accountName, accountNumber, fiatCurrencyCode);
|
||||
public void createPaymentAccount(String paymentMethodId,
|
||||
String accountName,
|
||||
String accountNumber,
|
||||
String currencyCode) {
|
||||
paymentAccountsService.createPaymentAccount(paymentMethodId,
|
||||
accountName,
|
||||
accountNumber,
|
||||
currencyCode);
|
||||
}
|
||||
|
||||
public Set<PaymentAccount> getPaymentAccounts() {
|
||||
|
|
|
@ -68,7 +68,7 @@ class CoreDisputeAgentsService {
|
|||
this.languageCodes = Arrays.asList("de", "en", "es", "fr");
|
||||
}
|
||||
|
||||
public void registerDisputeAgent(String disputeAgentType, String registrationKey) {
|
||||
void registerDisputeAgent(String disputeAgentType, String registrationKey) {
|
||||
if (!p2PService.isBootstrapped())
|
||||
throw new IllegalStateException("p2p service is not bootstrapped yet");
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ class CoreOffersService {
|
|||
this.user = user;
|
||||
}
|
||||
|
||||
public List<Offer> getOffers(String direction, String fiatCurrencyCode) {
|
||||
List<Offer> getOffers(String direction, String fiatCurrencyCode) {
|
||||
List<Offer> offers = offerBookService.getOffers().stream()
|
||||
.filter(o -> {
|
||||
var offerOfWantedDirection = o.getDirection().name().equalsIgnoreCase(direction);
|
||||
|
@ -77,7 +77,7 @@ class CoreOffersService {
|
|||
return offers;
|
||||
}
|
||||
|
||||
public void createOffer(String currencyCode,
|
||||
void createOffer(String currencyCode,
|
||||
String directionAsString,
|
||||
long priceAsLong,
|
||||
boolean useMarketBasedPrice,
|
||||
|
@ -111,7 +111,7 @@ class CoreOffersService {
|
|||
resultHandler);
|
||||
}
|
||||
|
||||
public void createOffer(String offerId,
|
||||
void createOffer(String offerId,
|
||||
String currencyCode,
|
||||
OfferPayload.Direction direction,
|
||||
Price price,
|
||||
|
|
|
@ -33,6 +33,9 @@ import java.util.Set;
|
|||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.core.payment.payload.PaymentMethod.*;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@Slf4j
|
||||
class CorePaymentAccountsService {
|
||||
|
||||
|
@ -49,17 +52,19 @@ class CorePaymentAccountsService {
|
|||
this.user = user;
|
||||
}
|
||||
|
||||
public void createPaymentAccount(String accountName, String accountNumber, String fiatCurrencyCode) {
|
||||
// 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.
|
||||
PaymentMethod dummyPaymentMethod = PaymentMethod.getDummyPaymentMethod(PaymentMethod.PERFECT_MONEY_ID);
|
||||
PaymentAccount paymentAccount = PaymentAccountFactory.getPaymentAccount(dummyPaymentMethod);
|
||||
paymentAccount.init();
|
||||
paymentAccount.setAccountName(accountName);
|
||||
((PerfectMoneyAccount) paymentAccount).setAccountNr(accountNumber);
|
||||
paymentAccount.setSingleTradeCurrency(new FiatCurrency(fiatCurrencyCode.toUpperCase()));
|
||||
user.addPaymentAccount(paymentAccount);
|
||||
void createPaymentAccount(String paymentMethodId,
|
||||
String accountName,
|
||||
String accountNumber,
|
||||
String currencyCode) {
|
||||
|
||||
PaymentAccount paymentAccount = getNewPaymentAccount(paymentMethodId,
|
||||
accountName,
|
||||
accountNumber,
|
||||
currencyCode);
|
||||
|
||||
// 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.
|
||||
if (config.baseCurrencyNetwork.isRegtest())
|
||||
|
@ -68,7 +73,64 @@ class CorePaymentAccountsService {
|
|||
log.info("Payment account {} saved", paymentAccount.getId());
|
||||
}
|
||||
|
||||
public Set<PaymentAccount> getPaymentAccounts() {
|
||||
Set<PaymentAccount> 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;
|
||||
}
|
||||
|
||||
public long getAvailableBalance() {
|
||||
long getAvailableBalance() {
|
||||
verifyWalletsAreAvailable();
|
||||
verifyEncryptedWalletIsUnlocked();
|
||||
|
||||
|
@ -82,27 +82,26 @@ class CoreWalletsService {
|
|||
return balance.getValue();
|
||||
}
|
||||
|
||||
public long getAddressBalance(String addressString) {
|
||||
long getAddressBalance(String addressString) {
|
||||
Address address = getAddressEntry(addressString).getAddress();
|
||||
return btcWalletService.getBalanceForAddress(address).value;
|
||||
}
|
||||
|
||||
public AddressBalanceInfo getAddressBalanceInfo(String addressString) {
|
||||
AddressBalanceInfo getAddressBalanceInfo(String addressString) {
|
||||
var satoshiBalance = getAddressBalance(addressString);
|
||||
var numConfirmations = getNumConfirmationsForMostRecentTransaction(addressString);
|
||||
return new AddressBalanceInfo(addressString, satoshiBalance, numConfirmations);
|
||||
}
|
||||
|
||||
public List<AddressBalanceInfo> getFundingAddresses() {
|
||||
List<AddressBalanceInfo> getFundingAddresses() {
|
||||
verifyWalletsAreAvailable();
|
||||
verifyEncryptedWalletIsUnlocked();
|
||||
|
||||
// Create a new funding address if none exists.
|
||||
if (btcWalletService.getAvailableAddressEntries().size() == 0)
|
||||
if (btcWalletService.getAvailableAddressEntries().isEmpty())
|
||||
btcWalletService.getFreshAddressEntry();
|
||||
|
||||
List<String> addressStrings =
|
||||
btcWalletService
|
||||
List<String> addressStrings = btcWalletService
|
||||
.getAvailableAddressEntries()
|
||||
.stream()
|
||||
.map(AddressEntry::getAddressString)
|
||||
|
@ -113,8 +112,7 @@ class CoreWalletsService {
|
|||
// this::getAddressBalance cannot return null.
|
||||
var balances = memoize(this::getAddressBalance);
|
||||
|
||||
boolean noAddressHasZeroBalance =
|
||||
addressStrings.stream()
|
||||
boolean noAddressHasZeroBalance = addressStrings.stream()
|
||||
.allMatch(addressString -> balances.getUnchecked(addressString) != 0);
|
||||
|
||||
if (noAddressHasZeroBalance) {
|
||||
|
@ -129,13 +127,13 @@ class CoreWalletsService {
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public int getNumConfirmationsForMostRecentTransaction(String addressString) {
|
||||
int getNumConfirmationsForMostRecentTransaction(String addressString) {
|
||||
Address address = getAddressEntry(addressString).getAddress();
|
||||
TransactionConfidence confidence = btcWalletService.getConfidenceForAddress(address);
|
||||
return confidence == null ? 0 : confidence.getDepthInBlocks();
|
||||
}
|
||||
|
||||
public void setWalletPassword(String password, String newPassword) {
|
||||
void setWalletPassword(String password, String newPassword) {
|
||||
verifyWalletsAreAvailable();
|
||||
|
||||
KeyCrypterScrypt keyCrypterScrypt = getKeyCrypterScrypt();
|
||||
|
@ -165,7 +163,7 @@ class CoreWalletsService {
|
|||
walletsManager.backupWallets();
|
||||
}
|
||||
|
||||
public void lockWallet() {
|
||||
void lockWallet() {
|
||||
if (!walletsManager.areWalletsEncrypted())
|
||||
throw new IllegalStateException("wallet is not encrypted with a password");
|
||||
|
||||
|
@ -175,7 +173,7 @@ class CoreWalletsService {
|
|||
tempAesKey = null;
|
||||
}
|
||||
|
||||
public void unlockWallet(String password, long timeout) {
|
||||
void unlockWallet(String password, long timeout) {
|
||||
verifyWalletIsAvailableAndEncrypted();
|
||||
|
||||
KeyCrypterScrypt keyCrypterScrypt = getKeyCrypterScrypt();
|
||||
|
@ -213,7 +211,7 @@ class CoreWalletsService {
|
|||
|
||||
// Provided for automated wallet protection method testing, despite the
|
||||
// security risks exposed by providing users the ability to decrypt their wallets.
|
||||
public void removeWalletPassword(String password) {
|
||||
void removeWalletPassword(String password) {
|
||||
verifyWalletIsAvailableAndEncrypted();
|
||||
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.setRevolutAccountsUpdateHandler(revolutAccountList -> log.info("setRevolutAccountsUpdateHandler: revolutAccountList={}", revolutAccountList));
|
||||
bisqSetup.setOsxKeyLoggerWarningHandler(() -> log.info("setOsxKeyLoggerWarningHandler"));
|
||||
bisqSetup.setQubesOSInfoHandler(() -> log.info("setQubesOSInfoHandler"));
|
||||
|
||||
//TODO move to bisqSetup
|
||||
corruptedDatabaseFilesHandler.getCorruptedDatabaseFiles().ifPresent(files -> log.warn("getCorruptedDatabaseFiles. files={}", files));
|
||||
|
|
|
@ -232,6 +232,9 @@ public class BisqSetup {
|
|||
@Setter
|
||||
@Nullable
|
||||
private Runnable osxKeyLoggerWarningHandler;
|
||||
@Setter
|
||||
@Nullable
|
||||
private Runnable qubesOSInfoHandler;
|
||||
|
||||
@Getter
|
||||
final BooleanProperty newVersionAvailableProperty = new SimpleBooleanProperty(false);
|
||||
|
@ -357,6 +360,7 @@ public class BisqSetup {
|
|||
checkCryptoSetup();
|
||||
checkForCorrectOSArchitecture();
|
||||
checkOSXVersion();
|
||||
checkIfRunningOnQubesOS();
|
||||
}
|
||||
|
||||
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() {
|
||||
log.info("initDomainServices");
|
||||
|
||||
|
|
|
@ -211,7 +211,7 @@ public final class AddressEntryList implements UserThreadMappedPersistableEnvelo
|
|||
private void maybeAddNewAddressEntry(Transaction tx) {
|
||||
tx.getOutputs().stream()
|
||||
.filter(output -> output.isMine(wallet))
|
||||
.map(output -> output.getAddressFromP2PKHScript(wallet.getNetworkParameters()))
|
||||
.map(output -> output.getScriptPubKey().getToAddress(wallet.getNetworkParameters()))
|
||||
.filter(Objects::nonNull)
|
||||
.filter(this::isAddressNotInEntries)
|
||||
.map(address -> (DeterministicKey) wallet.findKeyFromPubKeyHash(address.getHash(),
|
||||
|
|
|
@ -55,8 +55,7 @@ class BtcNodeConverter {
|
|||
PeerAddress convertOnionHost(BtcNode node) {
|
||||
// no DNS lookup for onion addresses
|
||||
String onionAddress = Objects.requireNonNull(node.getOnionAddress());
|
||||
PeerAddress result = new PeerAddress(onionAddress, node.getPort());
|
||||
return result;
|
||||
return new PeerAddress(onionAddress, node.getPort());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -69,7 +68,7 @@ class BtcNodeConverter {
|
|||
if (address != null) {
|
||||
result = create(address, port);
|
||||
} else {
|
||||
log.warn("Lookup failed, no address for node", node);
|
||||
log.warn("Lookup failed, no address for node {}", node);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -85,7 +84,7 @@ class BtcNodeConverter {
|
|||
if (address != null) {
|
||||
result = create(proxy, address, port);
|
||||
} else {
|
||||
log.warn("Lookup failed, no address for node", node);
|
||||
log.warn("Lookup failed, no address for node {}", node);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -26,7 +26,6 @@ import org.bitcoinj.wallet.DeterministicKeyChain;
|
|||
import org.bitcoinj.wallet.DeterministicSeed;
|
||||
import org.bitcoinj.wallet.KeyChainGroupStructure;
|
||||
import org.bitcoinj.wallet.Protos;
|
||||
import org.bitcoinj.wallet.UnreadableWalletException;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
|
@ -63,12 +62,12 @@ public class BisqKeyChainFactory extends DefaultKeyChainFactory {
|
|||
}
|
||||
|
||||
@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");
|
||||
}
|
||||
|
||||
@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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package bisq.core.btc.setup;
|
|||
import bisq.core.btc.nodes.LocalBitcoinNode;
|
||||
import bisq.core.btc.nodes.ProxySocketFactory;
|
||||
import bisq.core.btc.wallet.BisqRiskAnalysis;
|
||||
|
||||
import bisq.common.config.Config;
|
||||
|
||||
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
|
||||
* 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
|
||||
* 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>
|
||||
* construct and configure a {@link BlockChain}, {@link SPVBlockStore}, {@link Wallet} and {@link PeerGroup}.</p>
|
||||
*
|
||||
* <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,
|
||||
|
@ -92,16 +91,12 @@ public class WalletConfig extends AbstractIdleService {
|
|||
protected volatile File vBtcWalletFile;
|
||||
protected volatile File vBsqWalletFile;
|
||||
|
||||
protected boolean useAutoSave = true;
|
||||
protected PeerAddress[] peerAddresses;
|
||||
protected DownloadProgressTracker downloadListener;
|
||||
protected boolean autoStop = true;
|
||||
protected InputStream checkpoints;
|
||||
protected boolean blockingStartup = true;
|
||||
protected String userAgent, version;
|
||||
protected WalletProtobufSerializer.WalletFactory walletFactory;
|
||||
@Nullable protected DeterministicSeed restoreFromSeed;
|
||||
@Nullable protected DeterministicKey restoreFromKey;
|
||||
@Nullable protected PeerDiscovery discovery;
|
||||
|
||||
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.
|
||||
*/
|
||||
public WalletConfig(Context context,
|
||||
File directory, String filePrefix) {
|
||||
private WalletConfig(Context context, File directory, String filePrefix) {
|
||||
this.context = context;
|
||||
this.params = checkNotNull(context.getParams());
|
||||
this.directory = checkDir(directory);
|
||||
|
@ -170,29 +164,15 @@ public class WalletConfig extends AbstractIdleService {
|
|||
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
|
||||
* {@link DownloadProgressTracker} is a good choice. This has no effect unless setBlockingStartup(false) has been called
|
||||
* too, due to some missing implementation code.
|
||||
* {@link DownloadProgressTracker} is a good choice.
|
||||
*/
|
||||
public WalletConfig setDownloadListener(DownloadProgressTracker listener) {
|
||||
this.downloadListener = listener;
|
||||
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
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@ -269,16 +217,6 @@ public class WalletConfig extends AbstractIdleService {
|
|||
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
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
protected void startUp() throws Exception {
|
||||
// Runs in a separate thread.
|
||||
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);
|
||||
try {
|
||||
File chainFile = new File(directory, filePrefix + ".spvchain");
|
||||
boolean chainFileExists = chainFile.exists();
|
||||
String btcPrefix = "_BTC";
|
||||
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.allowSpendingUnconfirmedTransactions();
|
||||
vBtcWallet.setRiskAnalyzer(new BisqRiskAnalysis.Analyzer());
|
||||
|
@ -341,8 +247,8 @@ public class WalletConfig extends AbstractIdleService {
|
|||
|
||||
// Initiate Bitcoin network objects (block store, blockchain and peer group)
|
||||
vStore = new SPVBlockStore(params, chainFile);
|
||||
if (!chainFileExists || restoreFromSeed != null || restoreFromKey != null) {
|
||||
if (checkpoints == null && !Utils.isAndroidRuntime()) {
|
||||
if (!chainFileExists || restoreFromSeed != null) {
|
||||
if (checkpoints == null) {
|
||||
checkpoints = CheckpointManager.openStream(params);
|
||||
}
|
||||
|
||||
|
@ -355,12 +261,6 @@ public class WalletConfig extends AbstractIdleService {
|
|||
log.info("Clearing the chain file in preparation for restore.");
|
||||
vStore.clear();
|
||||
}
|
||||
} else if (restoreFromKey != null) {
|
||||
time = restoreFromKey.getCreationTimeSeconds();
|
||||
if (chainFileExists) {
|
||||
log.info("Clearing the chain file in preparation for restore.");
|
||||
vStore.clear();
|
||||
}
|
||||
} else {
|
||||
time = vBtcWallet.getEarliestKeyCreationTime();
|
||||
}
|
||||
|
@ -398,23 +298,12 @@ public class WalletConfig extends AbstractIdleService {
|
|||
vPeerGroup.addWallet(vBsqWallet);
|
||||
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>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Object result) {
|
||||
//completeExtensionInitiations(vPeerGroup);
|
||||
final DownloadProgressTracker l = downloadListener == null ? new DownloadProgressTracker() : downloadListener;
|
||||
vPeerGroup.startBlockChainDownload(l);
|
||||
DownloadProgressTracker tracker = downloadListener == null ? new DownloadProgressTracker() : downloadListener;
|
||||
vPeerGroup.startBlockChainDownload(tracker);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -423,7 +312,6 @@ public class WalletConfig extends AbstractIdleService {
|
|||
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
}
|
||||
} catch (BlockStoreException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
@ -439,9 +327,6 @@ public class WalletConfig extends AbstractIdleService {
|
|||
} else {
|
||||
wallet = createWallet(isBsqWallet);
|
||||
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
|
||||
// deserializing the extension (see WalletExtension#deserializeWalletExtension(Wallet, byte[]))
|
||||
|
@ -450,9 +335,7 @@ public class WalletConfig extends AbstractIdleService {
|
|||
wallet = loadWallet(false, walletFile, isBsqWallet);
|
||||
}
|
||||
|
||||
if (useAutoSave) {
|
||||
this.setupAutoSave(wallet, walletFile);
|
||||
}
|
||||
|
||||
return wallet;
|
||||
}
|
||||
|
@ -465,8 +348,7 @@ public class WalletConfig extends AbstractIdleService {
|
|||
Wallet wallet;
|
||||
FileInputStream walletStream = new FileInputStream(walletFile);
|
||||
try {
|
||||
List<WalletExtension> extensions = provideWalletExtensions();
|
||||
WalletExtension[] extArray = extensions.toArray(new WalletExtension[extensions.size()]);
|
||||
WalletExtension[] extArray = new WalletExtension[]{};
|
||||
Protos.Wallet proto = WalletProtobufSerializer.parseToProto(walletStream);
|
||||
final WalletProtobufSerializer serializer;
|
||||
if (walletFactory != null)
|
||||
|
@ -492,8 +374,6 @@ public class WalletConfig extends AbstractIdleService {
|
|||
KeyChainGroup.Builder kcg = KeyChainGroup.builder(params, structure);
|
||||
if (restoreFromSeed != null) {
|
||||
kcg.fromSeed(restoreFromSeed, preferredOutputScriptType).build();
|
||||
} else if (restoreFromKey != null) {
|
||||
kcg.addChain(DeterministicKeyChain.builder().spend(restoreFromKey).outputScriptType(preferredOutputScriptType).build());
|
||||
} else {
|
||||
// new wallet
|
||||
if (!isBsqWallet) {
|
||||
|
@ -512,7 +392,7 @@ public class WalletConfig extends AbstractIdleService {
|
|||
}
|
||||
|
||||
private void maybeMoveOldWalletOutOfTheWay(File walletFile) {
|
||||
if (restoreFromSeed == null && restoreFromKey == null) return;
|
||||
if (restoreFromSeed == null) return;
|
||||
if (!walletFile.exists()) return;
|
||||
int counter = 1;
|
||||
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() {
|
||||
PeerGroup peerGroup;
|
||||
// no proxy case.
|
||||
|
@ -573,19 +436,6 @@ public class WalletConfig extends AbstractIdleService {
|
|||
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
|
||||
protected void shutDown() throws Exception {
|
||||
// Runs in a separate thread.
|
||||
|
|
|
@ -249,7 +249,7 @@ public class WalletsSetup {
|
|||
UserThread.execute(() -> {
|
||||
addressEntryList.onWalletReady(walletConfig.btcWallet());
|
||||
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
|
||||
|
@ -307,11 +307,12 @@ public class WalletsSetup {
|
|||
}
|
||||
}
|
||||
|
||||
walletConfig.setDownloadListener(downloadListener)
|
||||
.setBlockingStartup(false);
|
||||
walletConfig.setDownloadListener(downloadListener);
|
||||
|
||||
// If seed is non-null it means we are restoring from backup.
|
||||
if (seed != null) {
|
||||
walletConfig.restoreWalletFromSeed(seed);
|
||||
}
|
||||
|
||||
walletConfig.addListener(new Service.Listener() {
|
||||
@Override
|
||||
|
|
|
@ -123,7 +123,7 @@ public class BtcWalletService extends WalletService {
|
|||
@Override
|
||||
void encryptWallet(KeyCrypterScrypt keyCrypterScrypt, KeyParameter key) {
|
||||
super.encryptWallet(keyCrypterScrypt, key);
|
||||
addressEntryList.getAddressEntriesAsListImmutable().stream().forEach(e -> {
|
||||
addressEntryList.getAddressEntriesAsListImmutable().forEach(e -> {
|
||||
DeterministicKey keyPair = e.getKeyPair();
|
||||
if (keyPair.isEncrypted())
|
||||
e.setDeterministicKey(keyPair.encrypt(keyCrypterScrypt, key));
|
||||
|
@ -134,7 +134,7 @@ public class BtcWalletService extends WalletService {
|
|||
@Override
|
||||
String getWalletAsString(boolean includePrivKeys) {
|
||||
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" +
|
||||
sb.toString() +
|
||||
"\n\n" +
|
||||
|
|
|
@ -38,8 +38,6 @@ import org.bitcoinj.core.Context;
|
|||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.InsufficientMoneyException;
|
||||
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.Transaction;
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
|
@ -47,12 +45,14 @@ import org.bitcoinj.core.TransactionInput;
|
|||
import org.bitcoinj.core.TransactionOutput;
|
||||
import org.bitcoinj.core.VerificationException;
|
||||
import org.bitcoinj.core.listeners.NewBestBlockListener;
|
||||
import org.bitcoinj.core.listeners.TransactionConfidenceEventListener;
|
||||
import org.bitcoinj.crypto.DeterministicKey;
|
||||
import org.bitcoinj.crypto.KeyCrypter;
|
||||
import org.bitcoinj.crypto.KeyCrypterScrypt;
|
||||
import org.bitcoinj.crypto.TransactionSignature;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.script.ScriptChunk;
|
||||
import org.bitcoinj.script.ScriptException;
|
||||
import org.bitcoinj.script.ScriptPattern;
|
||||
import org.bitcoinj.signers.TransactionSigner;
|
||||
import org.bitcoinj.utils.Threading;
|
||||
|
@ -106,7 +106,6 @@ public abstract class WalletService {
|
|||
protected final Preferences preferences;
|
||||
protected final FeeService feeService;
|
||||
protected final NetworkParameters params;
|
||||
@SuppressWarnings("deprecation")
|
||||
protected final BisqWalletListener walletEventListener = new BisqWalletListener();
|
||||
protected final CopyOnWriteArraySet<AddressConfidenceListener> addressConfidenceListeners = new CopyOnWriteArraySet<>();
|
||||
protected final CopyOnWriteArraySet<TxConfidenceListener> txConfidenceListeners = new CopyOnWriteArraySet<>();
|
||||
|
@ -508,17 +507,17 @@ public abstract class WalletService {
|
|||
sendRequest.feePerKb = getTxFeeForWithdrawalPerByte().multiply(1000);
|
||||
sendRequest.aesKey = aesKey;
|
||||
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
||||
printTx("empty wallet", sendResult.tx);
|
||||
printTx("empty btc wallet", sendResult.tx);
|
||||
Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<Transaction>() {
|
||||
@Override
|
||||
public void onSuccess(Transaction result) {
|
||||
log.info("emptyWallet onSuccess Transaction=" + result);
|
||||
log.info("emptyBtcWallet onSuccess Transaction=" + result);
|
||||
resultHandler.handleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable t) {
|
||||
log.error("emptyWallet onFailure " + t.toString());
|
||||
log.error("emptyBtcWallet onFailure " + t.toString());
|
||||
errorMessageHandler.handleErrorMessage(t.getMessage());
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
|
@ -551,58 +550,10 @@ public abstract class WalletService {
|
|||
// 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) {
|
||||
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) {
|
||||
wallet.removeChangeEventListener(listener);
|
||||
}
|
||||
|
@ -793,7 +744,6 @@ public abstract class WalletService {
|
|||
return maybeAddTxToWallet(transaction.bitcoinSerialize(), wallet, source);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// bisqWalletEventListener
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -74,7 +74,7 @@ public abstract class BaseProposalFactory<R extends Proposal> {
|
|||
R proposal = createProposalWithoutTxId();
|
||||
proposalValidator.validateDataFields(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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
// mediation/arbitration and log only the issue. The dispute agent will run validation as well and will get
|
||||
// a popup displayed to react.
|
||||
log.error("Donation address invalid. {}", e.toString());
|
||||
log.warn("Donation address is invalid. {}", e.toString());
|
||||
}
|
||||
|
||||
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: {}",
|
||||
new Date(startTime), new Date(tradeTime), new Date(blockTime));
|
||||
} 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;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -291,17 +291,13 @@ public class TradeDataValidation {
|
|||
}
|
||||
|
||||
NetworkParameters params = btcWalletService.getParams();
|
||||
Address address = output.getAddressFromP2PKHScript(params);
|
||||
Address address = output.getScriptPubKey().getToAddress(params);
|
||||
if (address == null) {
|
||||
// The donation address can be a multisig address as well.
|
||||
address = output.getAddressFromP2SH(params);
|
||||
if (address == null) {
|
||||
errorMsg = "Donation address cannot be resolved (not of type P2PKHScript or P2SH). Output: " + output;
|
||||
errorMsg = "Donation address cannot be resolved (not of type P2PK nor P2SH nor P2WH). Output: " + output;
|
||||
log.error(errorMsg);
|
||||
log.error(delayedPayoutTx.toString());
|
||||
throw new AddressException(dispute, errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
String addressAsString = address.toString();
|
||||
if (addressConsumer != null) {
|
||||
|
|
|
@ -58,10 +58,7 @@ public class BuyerSetupDepositTxListener extends TradeTask {
|
|||
NetworkParameters params = walletService.getParams();
|
||||
Transaction preparedDepositTx = new Transaction(params, processModel.getPreparedDepositTx());
|
||||
checkArgument(!preparedDepositTx.getOutputs().isEmpty(), "preparedDepositTx.getOutputs() must not be empty");
|
||||
|
||||
//TODO update to new bitcoinj API
|
||||
Address depositTxAddress = preparedDepositTx.getOutput(0).getAddressFromP2SH(params);
|
||||
|
||||
Address depositTxAddress = preparedDepositTx.getOutput(0).getScriptPubKey().getToAddress(params);
|
||||
TransactionConfidence confidence = walletService.getConfidenceForAddress(depositTxAddress);
|
||||
if (isVisibleInNetwork(confidence)) {
|
||||
applyConfidence(confidence);
|
||||
|
|
|
@ -189,6 +189,12 @@ public class User implements PersistedDataHost {
|
|||
// Collection operations
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void addPaymentAccountIfNotExists(PaymentAccount paymentAccount) {
|
||||
if (!paymentAccountExists(paymentAccount)) {
|
||||
addPaymentAccount(paymentAccount);
|
||||
}
|
||||
}
|
||||
|
||||
public void addPaymentAccount(PaymentAccount paymentAccount) {
|
||||
paymentAccount.onAddToUser();
|
||||
|
||||
|
@ -493,4 +499,8 @@ public class User implements PersistedDataHost {
|
|||
public boolean 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\
|
||||
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).
|
||||
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!
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import bisq.core.app.CoreModule;
|
|||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.app.AppModule;
|
||||
import bisq.common.handlers.ResultHandler;
|
||||
import bisq.common.setup.CommonSetup;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
@ -39,13 +40,15 @@ import bisq.daemon.grpc.GrpcServer;
|
|||
@Slf4j
|
||||
public class BisqDaemonMain extends BisqHeadlessAppMain implements BisqSetup.BisqSetupListener {
|
||||
|
||||
private GrpcServer grpcServer;
|
||||
|
||||
public static void main(String[] args) {
|
||||
new BisqDaemonMain().execute(args);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// First synchronous execution tasks
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void configUserThread() {
|
||||
|
@ -70,9 +73,9 @@ public class BisqDaemonMain extends BisqHeadlessAppMain implements BisqSetup.Bis
|
|||
headlessApp.setGracefulShutDownHandler(this);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// We continue with a series of synchronous execution tasks
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
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...
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -99,7 +103,14 @@ public class BisqDaemonMain extends BisqHeadlessAppMain implements BisqSetup.Bis
|
|||
protected void onApplicationStarted() {
|
||||
super.onApplicationStarted();
|
||||
|
||||
GrpcServer grpcServer = injector.getInstance(GrpcServer.class);
|
||||
grpcServer = injector.getInstance(GrpcServer.class);
|
||||
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.
|
||||
// We use the lighter weight OfferInfo proto wrapper instead, containing just
|
||||
// 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()
|
||||
.withId(offer.getId())
|
||||
.withDirection(offer.getDirection().name())
|
||||
|
|
|
@ -45,7 +45,10 @@ class GrpcPaymentAccountsService extends PaymentAccountsGrpc.PaymentAccountsImpl
|
|||
@Override
|
||||
public void createPaymentAccount(CreatePaymentAccountRequest req,
|
||||
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();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
|
@ -54,10 +57,11 @@ class GrpcPaymentAccountsService extends PaymentAccountsGrpc.PaymentAccountsImpl
|
|||
@Override
|
||||
public void getPaymentAccounts(GetPaymentAccountsRequest req,
|
||||
StreamObserver<GetPaymentAccountsReply> responseObserver) {
|
||||
var tradeStatistics = coreApi.getPaymentAccounts().stream()
|
||||
var paymentAccounts = coreApi.getPaymentAccounts().stream()
|
||||
.map(PaymentAccount::toProtoMessage)
|
||||
.collect(Collectors.toList());
|
||||
var reply = GetPaymentAccountsReply.newBuilder().addAllPaymentAccounts(tradeStatistics).build();
|
||||
var reply = GetPaymentAccountsReply.newBuilder()
|
||||
.addAllPaymentAccounts(paymentAccounts).build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
|
|
|
@ -18,30 +18,21 @@
|
|||
package bisq.daemon.grpc;
|
||||
|
||||
import bisq.core.api.CoreApi;
|
||||
import bisq.core.trade.statistics.TradeStatistics2;
|
||||
|
||||
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.ServerBuilder;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Singleton
|
||||
@Slf4j
|
||||
public class GrpcServer {
|
||||
|
||||
|
@ -51,19 +42,22 @@ public class GrpcServer {
|
|||
@Inject
|
||||
public GrpcServer(Config config,
|
||||
CoreApi coreApi,
|
||||
PasswordAuthInterceptor passwordAuthInterceptor,
|
||||
GrpcDisputeAgentsService disputeAgentsService,
|
||||
GrpcOffersService offersService,
|
||||
GrpcPaymentAccountsService paymentAccountsService,
|
||||
GrpcVersionService versionService,
|
||||
GrpcGetTradeStatisticsService tradeStatisticsService,
|
||||
GrpcWalletsService walletsService) {
|
||||
this.coreApi = coreApi;
|
||||
this.server = ServerBuilder.forPort(config.apiPort)
|
||||
.addService(disputeAgentsService)
|
||||
.addService(new GetVersionService())
|
||||
.addService(new GetTradeStatisticsService())
|
||||
.addService(offersService)
|
||||
.addService(paymentAccountsService)
|
||||
.addService(tradeStatisticsService)
|
||||
.addService(versionService)
|
||||
.addService(walletsService)
|
||||
.intercept(new PasswordAuthInterceptor(config.apiPassword))
|
||||
.intercept(passwordAuthInterceptor)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
@ -71,36 +65,14 @@ public class GrpcServer {
|
|||
try {
|
||||
server.start();
|
||||
log.info("listening on port {}", server.getPort());
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
server.shutdown();
|
||||
log.info("shutdown complete");
|
||||
}));
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
class GetVersionService extends GetVersionGrpc.GetVersionImplBase {
|
||||
@Override
|
||||
public void getVersion(GetVersionRequest req, StreamObserver<GetVersionReply> responseObserver) {
|
||||
var reply = GetVersionReply.newBuilder().setVersion(coreApi.getVersion()).build();
|
||||
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();
|
||||
}
|
||||
public void shutdown() {
|
||||
log.info("Server shutdown started");
|
||||
server.shutdown();
|
||||
log.info("Server shutdown complete");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
// 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
|
||||
public void getBalance(GetBalanceRequest req, StreamObserver<GetBalanceReply> responseObserver) {
|
||||
try {
|
||||
long result = coreApi.getAvailableBalance();
|
||||
var reply = GetBalanceReply.newBuilder().setBalance(result).build();
|
||||
long availableBalance = coreApi.getAvailableBalance();
|
||||
var reply = GetBalanceReply.newBuilder().setBalance(availableBalance).build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
} catch (IllegalStateException cause) {
|
||||
|
@ -72,8 +78,9 @@ class GrpcWalletsService extends WalletsGrpc.WalletsImplBase {
|
|||
public void getAddressBalance(GetAddressBalanceRequest req,
|
||||
StreamObserver<GetAddressBalanceReply> responseObserver) {
|
||||
try {
|
||||
AddressBalanceInfo result = coreApi.getAddressBalanceInfo(req.getAddress());
|
||||
var reply = GetAddressBalanceReply.newBuilder().setAddressBalanceInfo(result.toProtoMessage()).build();
|
||||
AddressBalanceInfo balanceInfo = coreApi.getAddressBalanceInfo(req.getAddress());
|
||||
var reply = GetAddressBalanceReply.newBuilder()
|
||||
.setAddressBalanceInfo(balanceInfo.toProtoMessage()).build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
} catch (IllegalStateException cause) {
|
||||
|
@ -87,10 +94,10 @@ class GrpcWalletsService extends WalletsGrpc.WalletsImplBase {
|
|||
public void getFundingAddresses(GetFundingAddressesRequest req,
|
||||
StreamObserver<GetFundingAddressesReply> responseObserver) {
|
||||
try {
|
||||
List<AddressBalanceInfo> result = coreApi.getFundingAddresses();
|
||||
List<AddressBalanceInfo> balanceInfo = coreApi.getFundingAddresses();
|
||||
var reply = GetFundingAddressesReply.newBuilder()
|
||||
.addAllAddressBalanceInfo(
|
||||
result.stream()
|
||||
balanceInfo.stream()
|
||||
.map(AddressBalanceInfo::toProtoMessage)
|
||||
.collect(Collectors.toList()))
|
||||
.build();
|
||||
|
|
|
@ -17,12 +17,16 @@
|
|||
|
||||
package bisq.daemon.grpc;
|
||||
|
||||
import bisq.common.config.Config;
|
||||
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.ServerCall;
|
||||
import io.grpc.ServerCallHandler;
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;
|
||||
import static io.grpc.Metadata.Key;
|
||||
import static io.grpc.Status.UNAUTHENTICATED;
|
||||
|
@ -36,12 +40,13 @@ import static java.lang.String.format;
|
|||
*/
|
||||
class PasswordAuthInterceptor implements ServerInterceptor {
|
||||
|
||||
public static final String PASSWORD_KEY = "password";
|
||||
private static final String PASSWORD_KEY = "password";
|
||||
|
||||
private final String expectedPasswordValue;
|
||||
|
||||
public PasswordAuthInterceptor(String expectedPasswordValue) {
|
||||
this.expectedPasswordValue = expectedPasswordValue;
|
||||
@Inject
|
||||
public PasswordAuthInterceptor(Config config) {
|
||||
this.expectedPasswordValue = config.apiPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -395,6 +395,15 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener {
|
|||
.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()
|
||||
.warning(Res.get("popup.warning.incompatibleDB", files.toString(), config.appDataDir))
|
||||
|
|
|
@ -157,7 +157,7 @@ class TransactionAwareTrade implements TransactionAwareTradable {
|
|||
tx.getOutputs().forEach(txo -> {
|
||||
if (btcWalletService.isTransactionOutputMine(txo)) {
|
||||
try {
|
||||
Address receiverAddress = txo.getAddressFromP2PKHScript(btcWalletService.getParams());
|
||||
Address receiverAddress = txo.getScriptPubKey().getToAddress(btcWalletService.getParams());
|
||||
Contract contract = checkNotNull(trade.getContract());
|
||||
String myPayoutAddressString = contract.isMyRoleBuyer(pubKeyRing) ?
|
||||
contract.getBuyerPayoutAddressString() :
|
||||
|
|
|
@ -103,12 +103,6 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
private final TradeDetailsWindow tradeDetailsWindow;
|
||||
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 EventHandler<KeyEvent> keyEventEventHandler;
|
||||
|
@ -173,23 +167,6 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||
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 -> {
|
||||
displayedTransactions.update();
|
||||
};
|
||||
|
@ -215,12 +192,6 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
tableView.setItems(sortedDisplayedTransactions);
|
||||
displayedTransactions.update();
|
||||
|
||||
btcWalletService.addCoinsReceivedEventListener(walletCoinsReceivedEventListener);
|
||||
btcWalletService.addCoinsSentEventListener(walletCoinsSentEventListener);
|
||||
btcWalletService.addReorganizeEventListener(walletReorganizeEventListener);
|
||||
btcWalletService.addTransactionConfidenceEventListener(transactionConfidenceEventListener);
|
||||
btcWalletService.addKeyChainEventListener(keyChainEventListener);
|
||||
btcWalletService.addScriptChangeEventListener(scriptsChangeEventListener);
|
||||
btcWalletService.addChangeEventListener(walletChangeEventListener);
|
||||
|
||||
scene = root.getScene();
|
||||
|
@ -257,12 +228,6 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
protected void deactivate() {
|
||||
sortedDisplayedTransactions.comparatorProperty().unbind();
|
||||
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);
|
||||
|
||||
if (scene != null)
|
||||
|
|
|
@ -104,7 +104,9 @@ public class SelectDepositTxWindow extends Overlay<SelectDepositTxWindow> {
|
|||
});
|
||||
transactionsComboBox.setItems(FXCollections.observableArrayList(transactions));
|
||||
transactionsComboBox.setOnAction(event -> {
|
||||
if (selectHandlerOptional.isPresent()) {
|
||||
selectHandlerOptional.get().accept(transactionsComboBox.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
hide();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -108,6 +108,8 @@ Full 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`
|
||||
|
||||
_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 {
|
||||
string direction = 1;
|
||||
string fiatCurrencyCode = 2;
|
||||
string currencyCode = 2;
|
||||
}
|
||||
|
||||
message GetOffersReply {
|
||||
|
@ -61,7 +61,7 @@ message GetOffersReply {
|
|||
}
|
||||
|
||||
message CreateOfferRequest {
|
||||
string currencyCode = 1; // TODO switch order w/ direction field in next PR
|
||||
string currencyCode = 1;
|
||||
string direction = 2;
|
||||
uint64 price = 3;
|
||||
bool useMarketBasedPrice = 4;
|
||||
|
@ -107,9 +107,11 @@ service PaymentAccounts {
|
|||
}
|
||||
|
||||
message CreatePaymentAccountRequest {
|
||||
string accountName = 1;
|
||||
string accountNumber = 2;
|
||||
string fiatCurrencyCode = 3;
|
||||
string paymentMethodId = 1;
|
||||
string accountName = 2;
|
||||
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 {
|
||||
|
@ -123,7 +125,7 @@ message GetPaymentAccountsReply {
|
|||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// TradeStatistics
|
||||
// GetTradeStatistics
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
service GetTradeStatistics {
|
||||
|
|
|
@ -14,5 +14,6 @@
|
|||
<logger name="bisq.common.storage.FileManager" level="WARN"/>
|
||||
|
||||
<logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="INFO"/>
|
||||
<logger name="com.neemre.btcdcli4j" level="WARN"/>
|
||||
|
||||
</configuration>
|
||||
|
|
Loading…
Add table
Reference in a new issue