mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-22 22:45:21 +01:00
Add support of seed and price rleay node filters. Use guice in P2P network domain classes.
This commit is contained in:
parent
90cfc84d35
commit
afaabf9e36
37 changed files with 1055 additions and 1042 deletions
|
@ -1257,8 +1257,6 @@ message PreferencesPayload {
|
|||
bool use_animations = 31;
|
||||
PaymentAccount selectedPayment_account_for_createOffer = 32;
|
||||
bool pay_fee_in_Btc = 33;
|
||||
repeated string banned_seed_nodes = 34;
|
||||
repeated string banned_price_relay_nodes = 35;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -28,11 +28,13 @@ import io.bisq.core.btc.BtcOptionKeys;
|
|||
import io.bisq.core.btc.UserAgent;
|
||||
import io.bisq.core.dao.DaoOptionKeys;
|
||||
import io.bisq.core.exceptions.BisqException;
|
||||
import io.bisq.core.filter.FilterManager;
|
||||
import io.bisq.network.NetworkOptionKeys;
|
||||
import joptsimple.OptionSet;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.springframework.core.env.*;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
|
@ -46,6 +48,8 @@ import java.io.IOException;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
@ -54,7 +58,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
public class BisqEnvironment extends StandardEnvironment {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Static
|
||||
// Static
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void setDefaultAppName(String defaultAppName) {
|
||||
|
@ -117,9 +121,9 @@ public class BisqEnvironment extends StandardEnvironment {
|
|||
final String newAppName = "Bisq";
|
||||
if (appName.equals(newAppName)) {
|
||||
final String oldAppName = "bisq";
|
||||
Path oldPath = Paths.get(Paths.get(userDataDir, oldAppName).toString());// bisq
|
||||
Path oldPath = Paths.get(Paths.get(userDataDir, oldAppName).toString());// bisq
|
||||
Path newPath = Paths.get(Paths.get(userDataDir, appName).toString());//Bisq
|
||||
File oldDir = new File(oldPath.toString()); // bisq
|
||||
File oldDir = new File(oldPath.toString()); // bisq
|
||||
File newDir = new File(newPath.toString()); //Bisq
|
||||
try {
|
||||
if (Files.exists(oldPath) && oldDir.getCanonicalPath().endsWith(oldAppName)) {
|
||||
|
@ -163,102 +167,114 @@ public class BisqEnvironment extends StandardEnvironment {
|
|||
@Getter
|
||||
@Setter
|
||||
private boolean isBitcoinLocalhostNodeRunning;
|
||||
@Getter
|
||||
private List<String> bannedPriceRelayNodes;
|
||||
@Getter
|
||||
private List<String> bannedSeedNodes;
|
||||
|
||||
private final String btcNodes, seedNodes, ignoreDevMsg, useTorForBtc, rpcUser, rpcPassword,
|
||||
rpcPort, rpcBlockNotificationPort, dumpBlockchainData, fullDaoNode,
|
||||
myAddress, banList, dumpStatistics, maxMemory, socks5ProxyBtcAddress,
|
||||
socks5ProxyHttpAddress;
|
||||
rpcPort, rpcBlockNotificationPort, dumpBlockchainData, fullDaoNode,
|
||||
myAddress, banList, dumpStatistics, maxMemory, socks5ProxyBtcAddress,
|
||||
socks5ProxyHttpAddress;
|
||||
|
||||
|
||||
public BisqEnvironment(OptionSet options) {
|
||||
this(new JOptCommandLinePropertySource(BISQ_COMMANDLINE_PROPERTY_SOURCE_NAME, checkNotNull(
|
||||
options)));
|
||||
options)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public BisqEnvironment(PropertySource commandLineProperties) {
|
||||
//CommonOptionKeys
|
||||
logLevel = commandLineProperties.containsProperty(CommonOptionKeys.LOG_LEVEL_KEY) ?
|
||||
(String) commandLineProperties.getProperty(CommonOptionKeys.LOG_LEVEL_KEY) :
|
||||
LOG_LEVEL_DEFAULT;
|
||||
(String) commandLineProperties.getProperty(CommonOptionKeys.LOG_LEVEL_KEY) :
|
||||
LOG_LEVEL_DEFAULT;
|
||||
|
||||
//AppOptionKeys
|
||||
userDataDir = commandLineProperties.containsProperty(AppOptionKeys.USER_DATA_DIR_KEY) ?
|
||||
(String) commandLineProperties.getProperty(AppOptionKeys.USER_DATA_DIR_KEY) :
|
||||
DEFAULT_USER_DATA_DIR;
|
||||
(String) commandLineProperties.getProperty(AppOptionKeys.USER_DATA_DIR_KEY) :
|
||||
DEFAULT_USER_DATA_DIR;
|
||||
|
||||
appName = commandLineProperties.containsProperty(AppOptionKeys.APP_NAME_KEY) ?
|
||||
(String) commandLineProperties.getProperty(AppOptionKeys.APP_NAME_KEY) :
|
||||
DEFAULT_APP_NAME;
|
||||
(String) commandLineProperties.getProperty(AppOptionKeys.APP_NAME_KEY) :
|
||||
DEFAULT_APP_NAME;
|
||||
|
||||
appDataDir = commandLineProperties.containsProperty(AppOptionKeys.APP_DATA_DIR_KEY) ?
|
||||
(String) commandLineProperties.getProperty(AppOptionKeys.APP_DATA_DIR_KEY) :
|
||||
appDataDir(userDataDir, appName);
|
||||
(String) commandLineProperties.getProperty(AppOptionKeys.APP_DATA_DIR_KEY) :
|
||||
appDataDir(userDataDir, appName);
|
||||
ignoreDevMsg = commandLineProperties.containsProperty(AppOptionKeys.IGNORE_DEV_MSG_KEY) ?
|
||||
(String) commandLineProperties.getProperty(AppOptionKeys.IGNORE_DEV_MSG_KEY) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(AppOptionKeys.IGNORE_DEV_MSG_KEY) :
|
||||
"";
|
||||
dumpStatistics = commandLineProperties.containsProperty(AppOptionKeys.DUMP_STATISTICS) ?
|
||||
(String) commandLineProperties.getProperty(AppOptionKeys.DUMP_STATISTICS) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(AppOptionKeys.DUMP_STATISTICS) :
|
||||
"";
|
||||
maxMemory = commandLineProperties.containsProperty(AppOptionKeys.MAX_MEMORY) ?
|
||||
(String) commandLineProperties.getProperty(AppOptionKeys.MAX_MEMORY) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(AppOptionKeys.MAX_MEMORY) :
|
||||
"";
|
||||
providers = commandLineProperties.containsProperty(AppOptionKeys.PROVIDERS) ?
|
||||
(String) commandLineProperties.getProperty(AppOptionKeys.PROVIDERS) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(AppOptionKeys.PROVIDERS) :
|
||||
"";
|
||||
|
||||
//NetworkOptionKeys
|
||||
seedNodes = commandLineProperties.containsProperty(NetworkOptionKeys.SEED_NODES_KEY) ?
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.SEED_NODES_KEY) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.SEED_NODES_KEY) :
|
||||
"";
|
||||
|
||||
myAddress = commandLineProperties.containsProperty(NetworkOptionKeys.MY_ADDRESS) ?
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.MY_ADDRESS) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.MY_ADDRESS) :
|
||||
"";
|
||||
banList = commandLineProperties.containsProperty(NetworkOptionKeys.BAN_LIST) ?
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.BAN_LIST) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.BAN_LIST) :
|
||||
"";
|
||||
socks5ProxyBtcAddress = commandLineProperties.containsProperty(NetworkOptionKeys.SOCKS_5_PROXY_BTC_ADDRESS) ?
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.SOCKS_5_PROXY_BTC_ADDRESS) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.SOCKS_5_PROXY_BTC_ADDRESS) :
|
||||
"";
|
||||
socks5ProxyHttpAddress = commandLineProperties.containsProperty(NetworkOptionKeys.SOCKS_5_PROXY_HTTP_ADDRESS) ?
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.SOCKS_5_PROXY_HTTP_ADDRESS) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(NetworkOptionKeys.SOCKS_5_PROXY_HTTP_ADDRESS) :
|
||||
"";
|
||||
|
||||
//RpcOptionKeys
|
||||
rpcUser = commandLineProperties.containsProperty(DaoOptionKeys.RPC_USER) ?
|
||||
(String) commandLineProperties.getProperty(DaoOptionKeys.RPC_USER) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(DaoOptionKeys.RPC_USER) :
|
||||
"";
|
||||
rpcPassword = commandLineProperties.containsProperty(DaoOptionKeys.RPC_PASSWORD) ?
|
||||
(String) commandLineProperties.getProperty(DaoOptionKeys.RPC_PASSWORD) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(DaoOptionKeys.RPC_PASSWORD) :
|
||||
"";
|
||||
rpcPort = commandLineProperties.containsProperty(DaoOptionKeys.RPC_PORT) ?
|
||||
(String) commandLineProperties.getProperty(DaoOptionKeys.RPC_PORT) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(DaoOptionKeys.RPC_PORT) :
|
||||
"";
|
||||
rpcBlockNotificationPort = commandLineProperties.containsProperty(DaoOptionKeys.RPC_BLOCK_NOTIFICATION_PORT) ?
|
||||
(String) commandLineProperties.getProperty(DaoOptionKeys.RPC_BLOCK_NOTIFICATION_PORT) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(DaoOptionKeys.RPC_BLOCK_NOTIFICATION_PORT) :
|
||||
"";
|
||||
dumpBlockchainData = commandLineProperties.containsProperty(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) ?
|
||||
(String) commandLineProperties.getProperty(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) :
|
||||
"";
|
||||
fullDaoNode = commandLineProperties.containsProperty(DaoOptionKeys.FULL_DAO_NODE) ?
|
||||
(String) commandLineProperties.getProperty(DaoOptionKeys.FULL_DAO_NODE) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(DaoOptionKeys.FULL_DAO_NODE) :
|
||||
"";
|
||||
|
||||
btcNodes = commandLineProperties.containsProperty(BtcOptionKeys.BTC_NODES) ?
|
||||
(String) commandLineProperties.getProperty(BtcOptionKeys.BTC_NODES) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(BtcOptionKeys.BTC_NODES) :
|
||||
"";
|
||||
|
||||
useTorForBtc = commandLineProperties.containsProperty(BtcOptionKeys.USE_TOR_FOR_BTC) ?
|
||||
(String) commandLineProperties.getProperty(BtcOptionKeys.USE_TOR_FOR_BTC) :
|
||||
"";
|
||||
(String) commandLineProperties.getProperty(BtcOptionKeys.USE_TOR_FOR_BTC) :
|
||||
"";
|
||||
|
||||
MutablePropertySources propertySources = this.getPropertySources();
|
||||
propertySources.addFirst(commandLineProperties);
|
||||
try {
|
||||
propertySources.addLast(getAppDirProperties());
|
||||
|
||||
final String bannedPriceRelayNodesAsString = getProperty(FilterManager.BANNED_PRICE_RELAY_NODES, "");
|
||||
bannedPriceRelayNodes = !bannedPriceRelayNodesAsString.isEmpty() ? Arrays.asList(StringUtils.deleteWhitespace(bannedPriceRelayNodesAsString).split(",")) : null;
|
||||
|
||||
final String bannedSeedNodesAsString = getProperty(FilterManager.BANNED_SEED_NODES, "");
|
||||
bannedSeedNodes = !bannedSeedNodesAsString.isEmpty() ? Arrays.asList(StringUtils.deleteWhitespace(bannedSeedNodesAsString).split(",")) : null;
|
||||
|
||||
baseCurrencyNetwork = BaseCurrencyNetwork.valueOf(getProperty(BtcOptionKeys.BASE_CURRENCY_NETWORK,
|
||||
getDefaultBaseCurrencyNetwork().name()).toUpperCase());
|
||||
getDefaultBaseCurrencyNetwork().name()).toUpperCase());
|
||||
|
||||
btcNetworkDir = Paths.get(appDataDir, baseCurrencyNetwork.name().toLowerCase()).toString();
|
||||
File btcNetworkDirFile = new File(btcNetworkDir);
|
||||
if (!btcNetworkDirFile.exists())
|
||||
|
@ -274,6 +290,18 @@ public class BisqEnvironment extends StandardEnvironment {
|
|||
|
||||
public void saveBaseCryptoNetwork(BaseCurrencyNetwork baseCurrencyNetwork) {
|
||||
BisqEnvironment.baseCurrencyNetwork = baseCurrencyNetwork;
|
||||
setProperty(BtcOptionKeys.BASE_CURRENCY_NETWORK, baseCurrencyNetwork.name());
|
||||
}
|
||||
|
||||
public void saveBannedSeedNodes(List<String> bannedNodes) {
|
||||
setProperty(FilterManager.BANNED_SEED_NODES, String.join(",", bannedNodes));
|
||||
}
|
||||
|
||||
public void saveBannedPriceRelayNodes(List<String> bannedNodes) {
|
||||
setProperty(FilterManager.BANNED_PRICE_RELAY_NODES, String.join(",", bannedNodes));
|
||||
}
|
||||
|
||||
private void setProperty(String key, String value) {
|
||||
try {
|
||||
Resource resource = getAppDirPropertiesResource();
|
||||
File file = resource.getFile();
|
||||
|
@ -286,7 +314,13 @@ public class BisqEnvironment extends StandardEnvironment {
|
|||
log.warn("propertiesObject not instance of Properties");
|
||||
}
|
||||
}
|
||||
properties.setProperty(BtcOptionKeys.BASE_CURRENCY_NETWORK, baseCurrencyNetwork.name());
|
||||
|
||||
if (!value.isEmpty())
|
||||
properties.setProperty(key, value);
|
||||
else
|
||||
properties.remove(key);
|
||||
|
||||
log.info("properties=" + properties);
|
||||
|
||||
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
|
||||
properties.store(fileOutputStream, null);
|
||||
|
|
|
@ -32,6 +32,7 @@ import io.bisq.core.dao.blockchain.vo.BsqBlock;
|
|||
import io.bisq.core.provider.fee.FeeService;
|
||||
import io.bisq.network.p2p.P2PService;
|
||||
import io.bisq.network.p2p.network.Connection;
|
||||
import io.bisq.network.p2p.seed.SeedNodesRepository;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -42,9 +43,9 @@ public class BsqFullNode extends BsqNode {
|
|||
|
||||
private final BsqFullNodeExecutor bsqFullNodeExecutor;
|
||||
private final JsonChainStateExporter jsonChainStateExporter;
|
||||
|
||||
@Getter
|
||||
private boolean parseBlockchainComplete;
|
||||
private RequestManager requestBlocksManager;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -58,11 +59,13 @@ public class BsqFullNode extends BsqNode {
|
|||
BsqFullNodeExecutor bsqFullNodeExecutor,
|
||||
BsqChainState bsqChainState,
|
||||
JsonChainStateExporter jsonChainStateExporter,
|
||||
FeeService feeService) {
|
||||
FeeService feeService,
|
||||
SeedNodesRepository seedNodesRepository) {
|
||||
super(p2PService,
|
||||
bsqParser,
|
||||
bsqChainState,
|
||||
feeService);
|
||||
bsqParser,
|
||||
bsqChainState,
|
||||
feeService,
|
||||
seedNodesRepository);
|
||||
this.bsqFullNodeExecutor = bsqFullNodeExecutor;
|
||||
this.jsonChainStateExporter = jsonChainStateExporter;
|
||||
}
|
||||
|
@ -73,27 +76,27 @@ public class BsqFullNode extends BsqNode {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onAllServicesInitialized(ErrorMessageHandler errorMessageHandler) {
|
||||
// bsqFullNodeExecutor.setup need to return with result handler before
|
||||
// bsqFullNodeExecutor.setup need to return with result handler before
|
||||
// super.onAllServicesInitialized(errorMessageHandler) is called
|
||||
// bsqFullNodeExecutor.setup is and async call.
|
||||
bsqFullNodeExecutor.setup(() -> {
|
||||
super.onAllServicesInitialized(errorMessageHandler);
|
||||
startParseBlocks();
|
||||
},
|
||||
throwable -> {
|
||||
log.error(throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
});
|
||||
super.onAllServicesInitialized(errorMessageHandler);
|
||||
startParseBlocks();
|
||||
},
|
||||
throwable -> {
|
||||
log.error(throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseBlocksWithChainHeadHeight(int startBlockHeight, int genesisBlockHeight, String genesisTxId) {
|
||||
log.info("parseBlocksWithChainHeadHeight startBlockHeight={}", startBlockHeight);
|
||||
bsqFullNodeExecutor.requestChainHeadHeight(chainHeadHeight -> parseBlocks(startBlockHeight, genesisBlockHeight, genesisTxId, chainHeadHeight),
|
||||
throwable -> {
|
||||
log.error(throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
});
|
||||
throwable -> {
|
||||
log.error(throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -102,27 +105,27 @@ public class BsqFullNode extends BsqNode {
|
|||
if (chainHeadHeight != startBlockHeight) {
|
||||
if (startBlockHeight <= chainHeadHeight) {
|
||||
bsqFullNodeExecutor.parseBlocks(startBlockHeight,
|
||||
chainHeadHeight,
|
||||
genesisBlockHeight,
|
||||
genesisTxId,
|
||||
this::onNewBsqBlock,
|
||||
() -> {
|
||||
// we are done but it might be that new blocks have arrived in the meantime,
|
||||
// so we try again with startBlockHeight set to current chainHeadHeight
|
||||
// We also set up the listener in the else main branch where we check
|
||||
// if we at chainTip, so do nto include here another check as it would
|
||||
// not trigger the listener registration.
|
||||
parseBlocksWithChainHeadHeight(chainHeadHeight,
|
||||
genesisBlockHeight,
|
||||
genesisTxId);
|
||||
}, throwable -> {
|
||||
if (throwable instanceof BlockNotConnectingException) {
|
||||
startReOrgFromLastSnapshot();
|
||||
} else {
|
||||
log.error(throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
});
|
||||
chainHeadHeight,
|
||||
genesisBlockHeight,
|
||||
genesisTxId,
|
||||
this::onNewBsqBlock,
|
||||
() -> {
|
||||
// we are done but it might be that new blocks have arrived in the meantime,
|
||||
// so we try again with startBlockHeight set to current chainHeadHeight
|
||||
// We also set up the listener in the else main branch where we check
|
||||
// if we at chainTip, so do nto include here another check as it would
|
||||
// not trigger the listener registration.
|
||||
parseBlocksWithChainHeadHeight(chainHeadHeight,
|
||||
genesisBlockHeight,
|
||||
genesisTxId);
|
||||
}, throwable -> {
|
||||
if (throwable instanceof BlockNotConnectingException) {
|
||||
startReOrgFromLastSnapshot();
|
||||
} else {
|
||||
log.error(throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log.warn("We are trying to start with a block which is above the chain height of bitcoin core. We need probably wait longer until bitcoin core has fully synced. We try again after a delay of 1 min.");
|
||||
UserThread.runAfter(() -> parseBlocksWithChainHeadHeight(startBlockHeight, genesisBlockHeight, genesisTxId), 60);
|
||||
|
@ -137,7 +140,7 @@ public class BsqFullNode extends BsqNode {
|
|||
protected void onP2PNetworkReady() {
|
||||
super.onP2PNetworkReady();
|
||||
|
||||
if (requestBlocksManager == null && p2pNetworkReady) {
|
||||
if (requestManager == null && p2pNetworkReady) {
|
||||
createRequestBlocksManager();
|
||||
addBlockHandler();
|
||||
}
|
||||
|
@ -148,62 +151,62 @@ public class BsqFullNode extends BsqNode {
|
|||
log.info("onParseBlockchainComplete");
|
||||
parseBlockchainComplete = true;
|
||||
|
||||
if (requestBlocksManager == null && p2pNetworkReady) {
|
||||
if (requestManager == null && p2pNetworkReady) {
|
||||
createRequestBlocksManager();
|
||||
addBlockHandler();
|
||||
}
|
||||
}
|
||||
|
||||
private void createRequestBlocksManager() {
|
||||
requestBlocksManager = new RequestManager(p2PService.getNetworkNode(),
|
||||
p2PService.getPeerManager(),
|
||||
p2PService.getBroadcaster(),
|
||||
p2PService.getSeedNodeAddresses(),
|
||||
bsqChainState,
|
||||
new RequestManager.Listener() {
|
||||
@Override
|
||||
public void onBlockReceived(GetBsqBlocksResponse getBsqBlocksResponse) {
|
||||
requestManager = new RequestManager(p2PService.getNetworkNode(),
|
||||
p2PService.getPeerManager(),
|
||||
p2PService.getBroadcaster(),
|
||||
seedNodesRepository.getSeedNodeAddresses(),
|
||||
bsqChainState,
|
||||
new RequestManager.Listener() {
|
||||
@Override
|
||||
public void onBlockReceived(GetBsqBlocksResponse getBsqBlocksResponse) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewBsqBlockBroadcastMessage(NewBsqBlockBroadcastMessage newBsqBlockBroadcastMessage) {
|
||||
@Override
|
||||
public void onNewBsqBlockBroadcastMessage(NewBsqBlockBroadcastMessage newBsqBlockBroadcastMessage) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNoSeedNodeAvailable() {
|
||||
@Override
|
||||
public void onNoSeedNodeAvailable() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFault(String errorMessage, @Nullable Connection connection) {
|
||||
@Override
|
||||
public void onFault(String errorMessage, @Nullable Connection connection) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addBlockHandler() {
|
||||
// We register our handler for new blocks
|
||||
bsqFullNodeExecutor.addBlockHandler(btcdBlock -> bsqFullNodeExecutor.parseBtcdBlock(btcdBlock,
|
||||
genesisBlockHeight,
|
||||
genesisTxId,
|
||||
this::onNewBsqBlock,
|
||||
throwable -> {
|
||||
if (throwable instanceof BlockNotConnectingException) {
|
||||
startReOrgFromLastSnapshot();
|
||||
} else {
|
||||
log.error(throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}));
|
||||
genesisBlockHeight,
|
||||
genesisTxId,
|
||||
this::onNewBsqBlock,
|
||||
throwable -> {
|
||||
if (throwable instanceof BlockNotConnectingException) {
|
||||
startReOrgFromLastSnapshot();
|
||||
} else {
|
||||
log.error(throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewBsqBlock(BsqBlock bsqBlock) {
|
||||
super.onNewBsqBlock(bsqBlock);
|
||||
jsonChainStateExporter.maybeExport();
|
||||
if (parseBlockchainComplete && p2pNetworkReady && requestBlocksManager != null)
|
||||
requestBlocksManager.publishNewBlock(bsqBlock);
|
||||
if (parseBlockchainComplete && p2pNetworkReady && requestManager != null)
|
||||
requestManager.publishNewBlock(bsqBlock);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import io.bisq.core.dao.blockchain.vo.BsqBlock;
|
|||
import io.bisq.core.provider.fee.FeeService;
|
||||
import io.bisq.network.p2p.P2PService;
|
||||
import io.bisq.network.p2p.network.Connection;
|
||||
import io.bisq.network.p2p.seed.SeedNodesRepository;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -41,7 +42,6 @@ import java.util.List;
|
|||
@Slf4j
|
||||
public class BsqLiteNode extends BsqNode {
|
||||
private final BsqLiteNodeExecutor bsqLiteNodeExecutor;
|
||||
private RequestManager requestBlocksManager;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -54,11 +54,13 @@ public class BsqLiteNode extends BsqNode {
|
|||
BsqParser bsqParser,
|
||||
BsqLiteNodeExecutor bsqLiteNodeExecutor,
|
||||
BsqChainState bsqChainState,
|
||||
FeeService feeService) {
|
||||
FeeService feeService,
|
||||
SeedNodesRepository seedNodesRepository) {
|
||||
super(p2PService,
|
||||
bsqParser,
|
||||
bsqChainState,
|
||||
feeService);
|
||||
bsqParser,
|
||||
bsqChainState,
|
||||
feeService,
|
||||
seedNodesRepository);
|
||||
this.bsqLiteNodeExecutor = bsqLiteNodeExecutor;
|
||||
}
|
||||
|
||||
|
@ -76,65 +78,65 @@ public class BsqLiteNode extends BsqNode {
|
|||
protected void onP2PNetworkReady() {
|
||||
super.onP2PNetworkReady();
|
||||
|
||||
requestBlocksManager = new RequestManager(p2PService.getNetworkNode(),
|
||||
p2PService.getPeerManager(),
|
||||
p2PService.getBroadcaster(),
|
||||
p2PService.getSeedNodeAddresses(),
|
||||
bsqChainState,
|
||||
new RequestManager.Listener() {
|
||||
@Override
|
||||
public void onBlockReceived(GetBsqBlocksResponse getBsqBlocksResponse) {
|
||||
List<BsqBlock> bsqBlockList = new ArrayList<>(getBsqBlocksResponse.getBsqBlocks());
|
||||
log.info("received msg with {} items", bsqBlockList.size());
|
||||
if (bsqBlockList.size() > 0)
|
||||
log.info("block height of last item: {}", bsqBlockList.get(bsqBlockList.size() - 1).getHeight());
|
||||
// Be safe and reset all mutable data in case the provider would not have done it
|
||||
bsqBlockList.stream().forEach(BsqBlock::reset);
|
||||
bsqLiteNodeExecutor.parseBsqBlocksForLiteNode(bsqBlockList,
|
||||
genesisBlockHeight,
|
||||
genesisTxId,
|
||||
BsqLiteNode.this::onNewBsqBlock,
|
||||
() -> onParseBlockchainComplete(genesisBlockHeight, genesisTxId), throwable -> {
|
||||
if (throwable instanceof BlockNotConnectingException) {
|
||||
startReOrgFromLastSnapshot();
|
||||
} else {
|
||||
log.error(throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
});
|
||||
requestManager = new RequestManager(p2PService.getNetworkNode(),
|
||||
p2PService.getPeerManager(),
|
||||
p2PService.getBroadcaster(),
|
||||
seedNodesRepository.getSeedNodeAddresses(),
|
||||
bsqChainState,
|
||||
new RequestManager.Listener() {
|
||||
@Override
|
||||
public void onBlockReceived(GetBsqBlocksResponse getBsqBlocksResponse) {
|
||||
List<BsqBlock> bsqBlockList = new ArrayList<>(getBsqBlocksResponse.getBsqBlocks());
|
||||
log.info("received msg with {} items", bsqBlockList.size());
|
||||
if (bsqBlockList.size() > 0)
|
||||
log.info("block height of last item: {}", bsqBlockList.get(bsqBlockList.size() - 1).getHeight());
|
||||
// Be safe and reset all mutable data in case the provider would not have done it
|
||||
bsqBlockList.stream().forEach(BsqBlock::reset);
|
||||
bsqLiteNodeExecutor.parseBsqBlocksForLiteNode(bsqBlockList,
|
||||
genesisBlockHeight,
|
||||
genesisTxId,
|
||||
BsqLiteNode.this::onNewBsqBlock,
|
||||
() -> onParseBlockchainComplete(genesisBlockHeight, genesisTxId), throwable -> {
|
||||
if (throwable instanceof BlockNotConnectingException) {
|
||||
startReOrgFromLastSnapshot();
|
||||
} else {
|
||||
log.error(throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewBsqBlockBroadcastMessage(NewBsqBlockBroadcastMessage newBsqBlockBroadcastMessage) {
|
||||
BsqBlock bsqBlock = newBsqBlockBroadcastMessage.getBsqBlock();
|
||||
// Be safe and reset all mutable data in case the provider would not have done it
|
||||
bsqBlock.reset();
|
||||
log.info("received broadcastNewBsqBlock bsqBlock {}", bsqBlock.getHeight());
|
||||
if (!bsqChainState.containsBlock(bsqBlock)) {
|
||||
bsqLiteNodeExecutor.parseBsqBlockForLiteNode(bsqBlock,
|
||||
genesisBlockHeight,
|
||||
genesisTxId,
|
||||
() -> onNewBsqBlock(bsqBlock), throwable -> {
|
||||
if (throwable instanceof BlockNotConnectingException) {
|
||||
startReOrgFromLastSnapshot();
|
||||
} else {
|
||||
log.error(throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewBsqBlockBroadcastMessage(NewBsqBlockBroadcastMessage newBsqBlockBroadcastMessage) {
|
||||
BsqBlock bsqBlock = newBsqBlockBroadcastMessage.getBsqBlock();
|
||||
// Be safe and reset all mutable data in case the provider would not have done it
|
||||
bsqBlock.reset();
|
||||
log.info("received broadcastNewBsqBlock bsqBlock {}", bsqBlock.getHeight());
|
||||
if (!bsqChainState.containsBlock(bsqBlock)) {
|
||||
bsqLiteNodeExecutor.parseBsqBlockForLiteNode(bsqBlock,
|
||||
genesisBlockHeight,
|
||||
genesisTxId,
|
||||
() -> onNewBsqBlock(bsqBlock), throwable -> {
|
||||
if (throwable instanceof BlockNotConnectingException) {
|
||||
startReOrgFromLastSnapshot();
|
||||
} else {
|
||||
log.error(throwable.toString());
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onNoSeedNodeAvailable() {
|
||||
|
||||
@Override
|
||||
public void onNoSeedNodeAvailable() {
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public void onFault(String errorMessage, @Nullable Connection connection) {
|
||||
|
||||
@Override
|
||||
public void onFault(String errorMessage, @Nullable Connection connection) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// delay a bit to not stress too much at startup
|
||||
UserThread.runAfter(this::startParseBlocks, 2);
|
||||
|
@ -147,7 +149,7 @@ public class BsqLiteNode extends BsqNode {
|
|||
|
||||
@Override
|
||||
protected void parseBlocks(int startBlockHeight, int genesisBlockHeight, String genesisTxId, Integer chainHeadHeight) {
|
||||
requestBlocksManager.requestBlocks(startBlockHeight);
|
||||
requestManager.requestBlocks(startBlockHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,12 +19,14 @@ package io.bisq.core.dao.blockchain;
|
|||
|
||||
import com.google.inject.Inject;
|
||||
import io.bisq.common.handlers.ErrorMessageHandler;
|
||||
import io.bisq.core.dao.blockchain.p2p.RequestManager;
|
||||
import io.bisq.core.dao.blockchain.parse.BsqChainState;
|
||||
import io.bisq.core.dao.blockchain.parse.BsqParser;
|
||||
import io.bisq.core.dao.blockchain.vo.BsqBlock;
|
||||
import io.bisq.core.provider.fee.FeeService;
|
||||
import io.bisq.network.p2p.BootstrapListener;
|
||||
import io.bisq.network.p2p.P2PService;
|
||||
import io.bisq.network.p2p.seed.SeedNodesRepository;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
@ -45,17 +47,18 @@ public abstract class BsqNode {
|
|||
protected final BsqParser bsqParser;
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected final BsqChainState bsqChainState;
|
||||
protected final SeedNodesRepository seedNodesRepository;
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected final List<BsqChainStateListener> bsqChainStateListeners = new ArrayList<>();
|
||||
protected final String genesisTxId;
|
||||
protected final int genesisBlockHeight;
|
||||
protected RequestManager requestManager;
|
||||
|
||||
@Getter
|
||||
protected boolean parseBlockchainComplete;
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected boolean p2pNetworkReady;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -65,19 +68,21 @@ public abstract class BsqNode {
|
|||
public BsqNode(P2PService p2PService,
|
||||
BsqParser bsqParser,
|
||||
BsqChainState bsqChainState,
|
||||
FeeService feeService) {
|
||||
FeeService feeService,
|
||||
SeedNodesRepository seedNodesRepository) {
|
||||
|
||||
this.p2PService = p2PService;
|
||||
this.bsqParser = bsqParser;
|
||||
this.bsqChainState = bsqChainState;
|
||||
this.seedNodesRepository = seedNodesRepository;
|
||||
|
||||
genesisTxId = bsqChainState.getGenesisTxId();
|
||||
genesisBlockHeight = bsqChainState.getGenesisBlockHeight();
|
||||
|
||||
bsqChainState.setCreateCompensationRequestFee(feeService.getCreateCompensationRequestFee().value,
|
||||
genesisBlockHeight);
|
||||
genesisBlockHeight);
|
||||
bsqChainState.setVotingFee(feeService.getVotingTxFee().value,
|
||||
genesisBlockHeight);
|
||||
genesisBlockHeight);
|
||||
}
|
||||
|
||||
|
||||
|
@ -117,18 +122,18 @@ public abstract class BsqNode {
|
|||
log.info("startParseBlocks");
|
||||
int startBlockHeight = Math.max(genesisBlockHeight, bsqChainState.getChainHeadHeight() + 1);
|
||||
log.info("Parse blocks:\n" +
|
||||
" Start block height={}\n" +
|
||||
" Genesis txId={}\n" +
|
||||
" Genesis block height={}\n" +
|
||||
" BsqChainState block height={}\n",
|
||||
startBlockHeight,
|
||||
genesisTxId,
|
||||
genesisBlockHeight,
|
||||
bsqChainState.getChainHeadHeight());
|
||||
" Start block height={}\n" +
|
||||
" Genesis txId={}\n" +
|
||||
" Genesis block height={}\n" +
|
||||
" BsqChainState block height={}\n",
|
||||
startBlockHeight,
|
||||
genesisTxId,
|
||||
genesisBlockHeight,
|
||||
bsqChainState.getChainHeadHeight());
|
||||
|
||||
parseBlocksWithChainHeadHeight(startBlockHeight,
|
||||
genesisBlockHeight,
|
||||
genesisTxId);
|
||||
genesisBlockHeight,
|
||||
genesisTxId);
|
||||
}
|
||||
|
||||
abstract protected void parseBlocksWithChainHeadHeight(int startBlockHeight, int genesisBlockHeight, String genesisTxId);
|
||||
|
|
|
@ -22,10 +22,10 @@ import com.google.inject.name.Named;
|
|||
import io.bisq.common.app.DevEnv;
|
||||
import io.bisq.common.crypto.KeyRing;
|
||||
import io.bisq.core.app.AppOptionKeys;
|
||||
import io.bisq.core.app.BisqEnvironment;
|
||||
import io.bisq.core.payment.payload.PaymentAccountPayload;
|
||||
import io.bisq.core.payment.payload.PaymentMethod;
|
||||
import io.bisq.core.provider.ProvidersRepository;
|
||||
import io.bisq.core.user.Preferences;
|
||||
import io.bisq.core.user.User;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
import io.bisq.network.p2p.P2PService;
|
||||
|
@ -51,10 +51,13 @@ import static org.bitcoinj.core.Utils.HEX;
|
|||
public class FilterManager {
|
||||
private static final Logger log = LoggerFactory.getLogger(FilterManager.class);
|
||||
|
||||
public static final String BANNED_PRICE_RELAY_NODES = "bannedPriceRelayNodes";
|
||||
public static final String BANNED_SEED_NODES = "bannedSeedNodes";
|
||||
|
||||
private final P2PService p2PService;
|
||||
private final KeyRing keyRing;
|
||||
private final User user;
|
||||
private final Preferences preferences;
|
||||
private final BisqEnvironment bisqEnvironment;
|
||||
private final ProvidersRepository providersRepository;
|
||||
private boolean ignoreDevMsg;
|
||||
private final ObjectProperty<Filter> filterProperty = new SimpleObjectProperty<>();
|
||||
|
@ -74,13 +77,13 @@ public class FilterManager {
|
|||
public FilterManager(P2PService p2PService,
|
||||
KeyRing keyRing,
|
||||
User user,
|
||||
Preferences preferences,
|
||||
BisqEnvironment bisqEnvironment,
|
||||
ProvidersRepository providersRepository,
|
||||
@Named(AppOptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
|
||||
this.p2PService = p2PService;
|
||||
this.keyRing = keyRing;
|
||||
this.user = user;
|
||||
this.preferences = preferences;
|
||||
this.bisqEnvironment = bisqEnvironment;
|
||||
this.providersRepository = providersRepository;
|
||||
this.ignoreDevMsg = ignoreDevMsg;
|
||||
}
|
||||
|
@ -94,13 +97,15 @@ public class FilterManager {
|
|||
Filter filter = (Filter) data.getProtectedStoragePayload();
|
||||
if (verifySignature(filter)) {
|
||||
// Seed nodes are requested at startup before we get the filter so we only apply the banned
|
||||
// nodes at the next startup and don't update the list in the P2P network domain
|
||||
preferences.setBannedSeedNodes(filter.getSeedNodes());
|
||||
// nodes at the next startup and don't update the list in the P2P network domain.
|
||||
// We persist it to the property file which is read before any other initialisation.
|
||||
final List<String> seedNodes = filter.getSeedNodes();
|
||||
bisqEnvironment.saveBannedSeedNodes(seedNodes);
|
||||
|
||||
// Banned price relay nodes we can apply at runtime
|
||||
final List<String> priceRelayNodes = filter.getPriceRelayNodes();
|
||||
preferences.setBannedPriceRelayNodes(priceRelayNodes);
|
||||
providersRepository.setBannedNodes(priceRelayNodes);
|
||||
providersRepository.fillProviderList();
|
||||
bisqEnvironment.saveBannedPriceRelayNodes(priceRelayNodes);
|
||||
providersRepository.init(priceRelayNodes);
|
||||
providersRepository.setNewRandomBaseUrl();
|
||||
|
||||
filterProperty.set(filter);
|
||||
|
@ -113,11 +118,10 @@ public class FilterManager {
|
|||
if (data.getProtectedStoragePayload() instanceof Filter) {
|
||||
Filter filter = (Filter) data.getProtectedStoragePayload();
|
||||
if (verifySignature(filter)) {
|
||||
preferences.setBannedSeedNodes(null);
|
||||
bisqEnvironment.saveBannedSeedNodes(null);
|
||||
|
||||
preferences.setBannedPriceRelayNodes(null);
|
||||
providersRepository.setBannedNodes(null);
|
||||
providersRepository.fillProviderList();
|
||||
bisqEnvironment.saveBannedPriceRelayNodes(null);
|
||||
providersRepository.init(null);
|
||||
providersRepository.setNewRandomBaseUrl();
|
||||
|
||||
filterProperty.set(null);
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
package io.bisq.core.network;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.inject.name.Named;
|
||||
import io.bisq.core.app.BisqEnvironment;
|
||||
import io.bisq.network.NetworkOptionKeys;
|
||||
import io.bisq.network.p2p.NodeAddress;
|
||||
import io.bisq.network.p2p.seed.SeedNodesRepository;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Slf4j
|
||||
public class CoreSeedNodesRepository implements SeedNodesRepository {
|
||||
|
||||
@Getter
|
||||
private final Set<NodeAddress> seedNodeAddresses;
|
||||
|
||||
@Inject
|
||||
public CoreSeedNodesRepository(BisqEnvironment bisqEnvironment,
|
||||
@Named(NetworkOptionKeys.USE_LOCALHOST_FOR_P2P) boolean useLocalhostForP2P,
|
||||
@Named(NetworkOptionKeys.NETWORK_ID) int networkId,
|
||||
@Nullable @Named(NetworkOptionKeys.MY_ADDRESS) String myAddress,
|
||||
@Nullable @Named(NetworkOptionKeys.SEED_NODES_KEY) String seedNodes) {
|
||||
List<String> bannedNodes = bisqEnvironment.getBannedSeedNodes();
|
||||
Set<NodeAddress> nodeAddresses;
|
||||
if (seedNodes != null && !seedNodes.isEmpty()) {
|
||||
nodeAddresses = Arrays.asList(StringUtils.deleteWhitespace(seedNodes).split(","))
|
||||
.stream()
|
||||
.map(NodeAddress::new)
|
||||
.collect(Collectors.toSet());
|
||||
} else {
|
||||
nodeAddresses = useLocalhostForP2P ? localhostSeedNodeAddresses : torSeedNodeAddresses;
|
||||
nodeAddresses = nodeAddresses.stream()
|
||||
.filter(e -> String.valueOf(e.getPort()).endsWith("0" + String.valueOf(networkId)))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
seedNodeAddresses = nodeAddresses.stream()
|
||||
.filter(e -> myAddress == null || myAddress.isEmpty() || !e.getFullAddress().equals(myAddress))
|
||||
.filter(e -> bannedNodes == null || !bannedNodes.contains(e.getFullAddress())) //TODO
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (bannedNodes != null)
|
||||
log.warn("We received banned seed nodes={}, seedNodeAddresses={}", bannedNodes, seedNodeAddresses);
|
||||
}
|
||||
|
||||
// Addresses are used if their port match the network id:
|
||||
// - mainnet uses port 8000
|
||||
// - testnet uses port 8001
|
||||
// - regtest uses port 8002
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private Set<NodeAddress> torSeedNodeAddresses = Sets.newHashSet(
|
||||
// BTC mainnet
|
||||
|
||||
//TODO dev dont use live nodes atm!
|
||||
new NodeAddress("3f3cu2yw7u457ztq.onion:8000"),
|
||||
new NodeAddress("723ljisnynbtdohi.onion:8000"),
|
||||
new NodeAddress("rm7b56wbrcczpjvl.onion:8000"),
|
||||
new NodeAddress("fl3mmribyxgrv63c.onion:8000"),
|
||||
|
||||
//TODO dev
|
||||
// local dev
|
||||
// new NodeAddress("joehwtpe7ijnz4df.onion:8000"),
|
||||
|
||||
// BTC testnet
|
||||
new NodeAddress("nbphlanpgbei4okt.onion:8001"),
|
||||
|
||||
// BTC regtest
|
||||
// For development you need to change that to your local onion addresses
|
||||
// 1. Run a seed node with prog args: --bitcoinNetwork=regtest --nodePort=8002 --myAddress=rxdkppp3vicnbgqt:8002 --appName=bisq_seed_node_rxdkppp3vicnbgqt.onion_8002
|
||||
// 2. Find your local onion address in bisq_seed_node_rxdkppp3vicnbgqt.onion_8002/regtest/tor/hiddenservice/hostname
|
||||
// 3. Shut down the seed node
|
||||
// 4. Rename the directory with your local onion address
|
||||
// 5. Edit here your found onion address (new NodeAddress("YOUR_ONION.onion:8002")
|
||||
new NodeAddress("rxdkppp3vicnbgqt.onion:8002"),
|
||||
|
||||
// LTC mainnet
|
||||
new NodeAddress("acyvotgewx46pebw.onion:8003"),
|
||||
// new NodeAddress("pklgy3vdfn3obkur.onion:8003"), removed in version 0.6
|
||||
|
||||
// keep the below but we don't run them atm
|
||||
/* new NodeAddress("cfciqxcowuhjdnkl.onion:8003"),
|
||||
new NodeAddress("bolqw3hs55uii7ku.onion:8003"),*/
|
||||
|
||||
// DOGE mainnet
|
||||
// new NodeAddress("t6bwuj75mvxswavs.onion:8006"), removed in version 0.6 (DOGE not supported anymore)
|
||||
|
||||
//DASH mainnet
|
||||
new NodeAddress("toeu5ikb27ydscxt.onion:8009")
|
||||
//new NodeAddress("ae4yvaivhnekkhqf.onion:8009") removed in version 0.6
|
||||
);
|
||||
|
||||
// Addresses are used if the last digit of their port match the network id:
|
||||
// - mainnet use port ends in 0
|
||||
// - testnet use port ends in 1
|
||||
// - regtest use port ends in 2
|
||||
private Set<NodeAddress> localhostSeedNodeAddresses = Sets.newHashSet(
|
||||
// BTC
|
||||
// mainnet
|
||||
new NodeAddress("localhost:2000"),
|
||||
new NodeAddress("localhost:3000"),
|
||||
new NodeAddress("localhost:4000"),
|
||||
|
||||
// testnet
|
||||
new NodeAddress("localhost:2001"),
|
||||
new NodeAddress("localhost:3001"),
|
||||
new NodeAddress("localhost:4001"),
|
||||
|
||||
// regtest
|
||||
new NodeAddress("localhost:2002"),
|
||||
new NodeAddress("localhost:3002"),
|
||||
/* new NodeAddress("localhost:4002"),*/
|
||||
|
||||
// LTC
|
||||
// mainnet
|
||||
new NodeAddress("localhost:2003"),
|
||||
|
||||
// regtest
|
||||
new NodeAddress("localhost:2005"),
|
||||
|
||||
// DOGE regtest
|
||||
new NodeAddress("localhost:2008"),
|
||||
|
||||
// DASH regtest
|
||||
new NodeAddress("localhost:2011")
|
||||
);
|
||||
|
||||
|
||||
public void setTorSeedNodeAddresses(Set<NodeAddress> torSeedNodeAddresses) {
|
||||
this.torSeedNodeAddresses = torSeedNodeAddresses;
|
||||
}
|
||||
|
||||
public void setLocalhostSeedNodeAddresses(Set<NodeAddress> localhostSeedNodeAddresses) {
|
||||
this.localhostSeedNodeAddresses = localhostSeedNodeAddresses;
|
||||
}
|
||||
|
||||
public boolean isSeedNode(NodeAddress nodeAddress) {
|
||||
return Stream.concat(localhostSeedNodeAddresses.stream(), torSeedNodeAddresses.stream())
|
||||
.filter(e -> e.equals(nodeAddress)).findAny().isPresent();
|
||||
}
|
||||
}
|
|
@ -34,4 +34,11 @@ public abstract class HttpClientProvider {
|
|||
|
||||
httpClient.setIgnoreSocks5Proxy(ignoreSocks5Proxy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HttpClientProvider{" +
|
||||
"\n httpClient=" + httpClient +
|
||||
"\n}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,9 @@ package io.bisq.core.provider;
|
|||
|
||||
import com.google.inject.Inject;
|
||||
import io.bisq.core.app.AppOptionKeys;
|
||||
import io.bisq.core.app.BisqEnvironment;
|
||||
import io.bisq.network.NetworkOptionKeys;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
|
@ -31,39 +33,31 @@ import java.util.stream.Collectors;
|
|||
|
||||
@Slf4j
|
||||
public class ProvidersRepository {
|
||||
private static final String NODES = "http://44mgyoe2b6oqiytt.onion/, http://5bmpx76qllutpcyp.onion/";
|
||||
private final String providersFromProgramArgs;
|
||||
private final boolean useLocalhostForP2P;
|
||||
|
||||
private List<String> providerList;
|
||||
@Getter
|
||||
private String baseUrl = "";
|
||||
|
||||
// added in v0.6
|
||||
@Nullable
|
||||
private List<String> bannedNodes;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public ProvidersRepository(@Named(AppOptionKeys.PROVIDERS) String providers,
|
||||
public ProvidersRepository(BisqEnvironment bisqEnvironment,
|
||||
@Named(AppOptionKeys.PROVIDERS) String providers,
|
||||
@Named(NetworkOptionKeys.USE_LOCALHOST_FOR_P2P) boolean useLocalhostForP2P) {
|
||||
|
||||
this.providersFromProgramArgs = providers;
|
||||
this.useLocalhostForP2P = useLocalhostForP2P;
|
||||
|
||||
init(bisqEnvironment.getBannedSeedNodes());
|
||||
}
|
||||
|
||||
public void setBannedNodes(@Nullable List<String> bannedNodes) {
|
||||
this.bannedNodes = bannedNodes;
|
||||
}
|
||||
|
||||
public void onAllServicesInitialized() {
|
||||
fillProviderList();
|
||||
setBaseUrl();
|
||||
}
|
||||
|
||||
public void fillProviderList() {
|
||||
public void init(@Nullable List<String> bannedNodes) {
|
||||
String providerAsString;
|
||||
if (providersFromProgramArgs == null || providersFromProgramArgs.isEmpty()) {
|
||||
if (useLocalhostForP2P) {
|
||||
|
@ -73,21 +67,21 @@ public class ProvidersRepository {
|
|||
// providerAsString = "http://localhost:8080/, http://37.139.14.34:8080/";
|
||||
providerAsString = "http://37.139.14.34:8080/";
|
||||
} else {
|
||||
providerAsString = "http://44mgyoe2b6oqiytt.onion/, http://5bmpx76qllutpcyp.onion/";
|
||||
providerAsString = NODES;
|
||||
}
|
||||
} else {
|
||||
providerAsString = providersFromProgramArgs;
|
||||
}
|
||||
|
||||
if (bannedNodes != null)
|
||||
log.info("banned provider nodes: " + bannedNodes);
|
||||
|
||||
providerList = Arrays.asList(StringUtils.deleteWhitespace(providerAsString).split(","))
|
||||
.stream()
|
||||
.filter(e -> bannedNodes == null || !bannedNodes.contains(e.replace("http://", "").replace("/", "").replace(".onion", "")))
|
||||
.collect(Collectors.toList());
|
||||
log.info("providerList={}", providerList);
|
||||
}
|
||||
|
||||
private void setBaseUrl() {
|
||||
if (!providerList.isEmpty())
|
||||
baseUrl = providerList.get(new Random().nextInt(providerList.size()));
|
||||
|
||||
|
@ -107,12 +101,7 @@ public class ProvidersRepository {
|
|||
log.info("Use new provider baseUrl: " + baseUrl);
|
||||
}
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
public boolean hasMoreProviders() {
|
||||
return !providerList.isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -65,9 +65,4 @@ public class FeeProvider extends HttpClientProvider {
|
|||
}
|
||||
return new Tuple2<>(tsMap, map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FeeProvider";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ public class FeeService {
|
|||
|
||||
/* How to calculate:
|
||||
MIN_MAKER_FEE_IN_BASE_CUR = target fiat price * 100000000 / price (in btc: 0.5*100000000/2500)
|
||||
DEFAULT_MAKER_FEE_IN_BASE_CUR = target fiat price * (100000000 / price) / maxTradeAmount
|
||||
DEFAULT_MAKER_FEE_IN_BASE_CUR = target fiat price * (100000000 / price) / maxTradeAmount
|
||||
(in btc: 5*100000000/2500 / 1)
|
||||
(in ltc: 5*100000000/40 / 50)
|
||||
*/
|
||||
|
@ -115,10 +115,10 @@ public class FeeService {
|
|||
txFeePerByte = LTC_DEFAULT_TX_FEE;
|
||||
break;
|
||||
case "DOGE":
|
||||
MIN_MAKER_FEE_IN_BASE_CUR = 20_000_000_000L; // 0.5 USD at DOGE price 0.003 USD
|
||||
MIN_MAKER_FEE_IN_BASE_CUR = 20_000_000_000L; // 0.5 USD at DOGE price 0.003 USD
|
||||
MIN_TAKER_FEE_IN_BASE_CUR = 20_000_000_000L;
|
||||
DEFAULT_MAKER_FEE_IN_BASE_CUR = 200_000; // 5 USD at DOGE price 0.003 USD for 800 000 DOGE (maxTradeAmount)
|
||||
DEFAULT_TAKER_FEE_IN_BASE_CUR = 300_000; // 7.5 USD at DOGE price 0.003 USD
|
||||
DEFAULT_TAKER_FEE_IN_BASE_CUR = 300_000; // 7.5 USD at DOGE price 0.003 USD
|
||||
txFeePerByte = DOGE_DEFAULT_TX_FEE;
|
||||
break;
|
||||
case "DASH":
|
||||
|
@ -161,7 +161,7 @@ public class FeeService {
|
|||
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable throwable) {
|
||||
log.warn("Could not load fees. " + throwable.toString());
|
||||
log.warn("Could not load fees. feeProvider={}, error={}", feeProvider.toString(), throwable.toString());
|
||||
if (faultHandler != null)
|
||||
UserThread.execute(() -> faultHandler.handleFault("Could not load fees", throwable));
|
||||
}
|
||||
|
|
|
@ -480,16 +480,6 @@ public final class Preferences implements PersistedDataHost {
|
|||
persist();
|
||||
}
|
||||
|
||||
public void setBannedSeedNodes(@Nullable List<String> seedNodes) {
|
||||
prefPayload.setBannedSeedNodes(seedNodes);
|
||||
persist();
|
||||
}
|
||||
|
||||
public void setBannedPriceRelayNodes(@Nullable List<String> priceRelayNodes) {
|
||||
prefPayload.setBannedPriceRelayNodes(priceRelayNodes);
|
||||
persist();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getter
|
||||
|
@ -660,9 +650,5 @@ public final class Preferences implements PersistedDataHost {
|
|||
void setDontShowAgainMap(Map<String, Boolean> dontShowAgainMap);
|
||||
|
||||
void setPeerTagMap(Map<String, String> peerTagMap);
|
||||
|
||||
void setBannedSeedNodes(List<String> bannedSeedNodes);
|
||||
|
||||
void setBannedPriceRelayNodes(List<String> bannedPriceRelayNodes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,12 +65,6 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
|||
private PaymentAccount selectedPaymentAccountForCreateOffer;
|
||||
private boolean payFeeInBtc = true;
|
||||
|
||||
// added in v0.6
|
||||
@Nullable
|
||||
private List<String> bannedSeedNodes;
|
||||
@Nullable
|
||||
private List<String> bannedPriceRelayNodes;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
|
@ -116,9 +110,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
|||
long buyerSecurityDepositAsLong,
|
||||
boolean useAnimations,
|
||||
@Nullable PaymentAccount selectedPaymentAccountForCreateOffer,
|
||||
boolean payFeeInBtc,
|
||||
@Nullable List<String> bannedSeedNodes,
|
||||
@Nullable List<String> bannedPriceRelayNodes) {
|
||||
boolean payFeeInBtc) {
|
||||
this.userLanguage = userLanguage;
|
||||
this.userCountry = userCountry;
|
||||
this.fiatCurrencies = fiatCurrencies;
|
||||
|
@ -152,8 +144,6 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
|||
this.useAnimations = useAnimations;
|
||||
this.selectedPaymentAccountForCreateOffer = selectedPaymentAccountForCreateOffer;
|
||||
this.payFeeInBtc = payFeeInBtc;
|
||||
this.bannedSeedNodes = bannedSeedNodes;
|
||||
this.bannedPriceRelayNodes = bannedPriceRelayNodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -198,8 +188,6 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
|||
Optional.ofNullable(sellScreenCurrencyCode).ifPresent(builder::setSellScreenCurrencyCode);
|
||||
Optional.ofNullable(selectedPaymentAccountForCreateOffer).ifPresent(
|
||||
account -> builder.setSelectedPaymentAccountForCreateOffer(selectedPaymentAccountForCreateOffer.toProtoMessage()));
|
||||
Optional.ofNullable(bannedSeedNodes).ifPresent(e -> builder.addAllBannedSeedNodes(e.stream().collect(Collectors.toList())));
|
||||
Optional.ofNullable(bannedPriceRelayNodes).ifPresent(e -> builder.addAllBannedPriceRelayNodes(e.stream().collect(Collectors.toList())));
|
||||
|
||||
return PB.PersistableEnvelope.newBuilder().setPreferencesPayload(builder).build();
|
||||
}
|
||||
|
@ -249,10 +237,6 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
|||
proto.getBuyerSecurityDepositAsLong(),
|
||||
proto.getUseAnimations(),
|
||||
paymentAccount,
|
||||
proto.getPayFeeInBtc(),
|
||||
proto.getBannedSeedNodesList().isEmpty() ? null :
|
||||
new ArrayList<>(proto.getBannedSeedNodesList()),
|
||||
proto.getBannedPriceRelayNodesList().isEmpty() ? null :
|
||||
new ArrayList<>(proto.getBannedPriceRelayNodesList()));
|
||||
proto.getPayFeeInBtc());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,6 @@ import io.bisq.gui.main.overlays.popups.Popup;
|
|||
import io.bisq.gui.main.overlays.windows.*;
|
||||
import io.bisq.gui.util.ImageUtil;
|
||||
import io.bisq.network.p2p.P2PService;
|
||||
import io.bisq.network.p2p.seed.SeedNodesRepository;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Parent;
|
||||
|
@ -136,10 +135,10 @@ public class BisqApp extends Application {
|
|||
Thread.UncaughtExceptionHandler handler = (thread, throwable) -> {
|
||||
// Might come from another thread
|
||||
if (throwable.getCause() != null && throwable.getCause().getCause() != null &&
|
||||
throwable.getCause().getCause() instanceof BlockStoreException) {
|
||||
throwable.getCause().getCause() instanceof BlockStoreException) {
|
||||
log.error(throwable.getMessage());
|
||||
} else if (throwable instanceof ClassCastException &&
|
||||
"sun.awt.image.BufImgSurfaceData cannot be cast to sun.java2d.xr.XRSurfaceData".equals(throwable.getMessage())) {
|
||||
"sun.awt.image.BufImgSurfaceData cannot be cast to sun.java2d.xr.XRSurfaceData".equals(throwable.getMessage())) {
|
||||
log.warn(throwable.getMessage());
|
||||
} else {
|
||||
log.error("Uncaught Exception from thread " + Thread.currentThread().getName());
|
||||
|
@ -173,14 +172,6 @@ public class BisqApp extends Application {
|
|||
bisqAppModule = new BisqAppModule(bisqEnvironment, primaryStage);
|
||||
injector = Guice.createInjector(bisqAppModule);
|
||||
injector.getInstance(InjectorViewFactory.class).setInjector(injector);
|
||||
/*
|
||||
PrintWriter out = new PrintWriter(new File("grapher.dot"), "UTF-8");
|
||||
Injector injector = Guice.createInjector(new GraphvizModule());
|
||||
GraphvizGrapher grapher = injector.getInstance(GraphvizGrapher.class);
|
||||
grapher.setOut(out);
|
||||
grapher.setRankdir("TB");
|
||||
grapher.graph(injector);
|
||||
*/
|
||||
|
||||
// All classes which are persisting objects need to be added here
|
||||
// Maintain order!
|
||||
|
@ -209,8 +200,6 @@ public class BisqApp extends Application {
|
|||
log.error("readPersisted error", e1);
|
||||
}
|
||||
});
|
||||
//TODO
|
||||
SeedNodesRepository.setBannedNodes(preferences.getBannedSeedNodes());
|
||||
|
||||
Version.setBaseCryptoNetworkId(BisqEnvironment.getBaseCurrencyNetwork().ordinal());
|
||||
Version.printVersion();
|
||||
|
@ -242,9 +231,9 @@ public class BisqApp extends Application {
|
|||
Font.loadFont(getClass().getResource("/fonts/VerdanaItalic.ttf").toExternalForm(), 13);
|
||||
Font.loadFont(getClass().getResource("/fonts/VerdanaBoldItalic.ttf").toExternalForm(), 13);
|
||||
scene.getStylesheets().setAll(
|
||||
"/io/bisq/gui/bisq.css",
|
||||
"/io/bisq/gui/images.css",
|
||||
"/io/bisq/gui/CandleStickChart.css");
|
||||
"/io/bisq/gui/bisq.css",
|
||||
"/io/bisq/gui/images.css",
|
||||
"/io/bisq/gui/CandleStickChart.css");
|
||||
|
||||
// configure the system tray
|
||||
SystemTray.create(primaryStage, shutDownHandler);
|
||||
|
@ -256,7 +245,7 @@ public class BisqApp extends Application {
|
|||
scene.addEventHandler(KeyEvent.KEY_RELEASED, keyEvent -> {
|
||||
Utilities.isAltOrCtrlPressed(KeyCode.W, keyEvent);
|
||||
if (Utilities.isCtrlPressed(KeyCode.W, keyEvent) ||
|
||||
Utilities.isCtrlPressed(KeyCode.Q, keyEvent)) {
|
||||
Utilities.isCtrlPressed(KeyCode.Q, keyEvent)) {
|
||||
stop();
|
||||
} else {
|
||||
if (Utilities.isAltOrCtrlPressed(KeyCode.E, keyEvent)) {
|
||||
|
@ -321,10 +310,10 @@ public class BisqApp extends Application {
|
|||
// We don't force a shutdown as the osArchitecture might in strange cases return a wrong value.
|
||||
// Needs at least more testing on different machines...
|
||||
new Popup<>().warning(Res.get("popup.warning.wrongVersion",
|
||||
osArchitecture,
|
||||
Utilities.getJVMArchitecture(),
|
||||
osArchitecture))
|
||||
.show();
|
||||
osArchitecture,
|
||||
Utilities.getJVMArchitecture(),
|
||||
osArchitecture))
|
||||
.show();
|
||||
}
|
||||
UserThread.runPeriodically(() -> Profiler.printSystemLoad(log), LOG_MEMORY_PERIOD_MIN, TimeUnit.MINUTES);
|
||||
} catch (Throwable throwable) {
|
||||
|
@ -336,17 +325,17 @@ public class BisqApp extends Application {
|
|||
private void showSendAlertMessagePopup() {
|
||||
AlertManager alertManager = injector.getInstance(AlertManager.class);
|
||||
new SendAlertMessageWindow()
|
||||
.onAddAlertMessage(alertManager::addAlertMessageIfKeyIsValid)
|
||||
.onRemoveAlertMessage(alertManager::removeAlertMessageIfKeyIsValid)
|
||||
.show();
|
||||
.onAddAlertMessage(alertManager::addAlertMessageIfKeyIsValid)
|
||||
.onRemoveAlertMessage(alertManager::removeAlertMessageIfKeyIsValid)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showFilterPopup() {
|
||||
FilterManager filterManager = injector.getInstance(FilterManager.class);
|
||||
new FilterWindow(filterManager)
|
||||
.onAddFilter(filterManager::addFilterMessageIfKeyIsValid)
|
||||
.onRemoveFilter(filterManager::removeFilterMessageIfKeyIsValid)
|
||||
.show();
|
||||
.onAddFilter(filterManager::addFilterMessageIfKeyIsValid)
|
||||
.onRemoveFilter(filterManager::removeFilterMessageIfKeyIsValid)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showEmptyWalletPopup(WalletService walletService) {
|
||||
|
@ -361,8 +350,8 @@ public class BisqApp extends Application {
|
|||
log.warn("Scene not available yet, we create a new scene. The bug might be caused by an exception in a constructor or by a circular dependency in guice. throwable=" + throwable.toString());
|
||||
scene = new Scene(new StackPane(), 1000, 650);
|
||||
scene.getStylesheets().setAll(
|
||||
"/io/bisq/gui/bisq.css",
|
||||
"/io/bisq/gui/images.css");
|
||||
"/io/bisq/gui/bisq.css",
|
||||
"/io/bisq/gui/images.css");
|
||||
primaryStage.setScene(scene);
|
||||
primaryStage.show();
|
||||
}
|
||||
|
@ -410,13 +399,13 @@ public class BisqApp extends Application {
|
|||
private void showFPSWindow() {
|
||||
Label label = new Label();
|
||||
EventStreams.animationTicks()
|
||||
.latestN(100)
|
||||
.map(ticks -> {
|
||||
int n = ticks.size() - 1;
|
||||
return n * 1_000_000_000.0 / (ticks.get(n) - ticks.get(0));
|
||||
})
|
||||
.map(d -> String.format("FPS: %.3f", d)) // Don't translate, just for dev
|
||||
.feedTo(label.textProperty());
|
||||
.latestN(100)
|
||||
.map(ticks -> {
|
||||
int n = ticks.size() - 1;
|
||||
return n * 1_000_000_000.0 / (ticks.get(n) - ticks.get(0));
|
||||
})
|
||||
.map(d -> String.format("FPS: %.3f", d)) // Don't translate, just for dev
|
||||
.feedTo(label.textProperty());
|
||||
|
||||
Pane root = new StackPane();
|
||||
root.getChildren().add(label);
|
||||
|
@ -438,10 +427,10 @@ public class BisqApp extends Application {
|
|||
public void stop() {
|
||||
if (!shutDownRequested) {
|
||||
new Popup<>().headLine(Res.get("popup.shutDownInProgress.headline"))
|
||||
.backgroundInfo(Res.get("popup.shutDownInProgress.msg"))
|
||||
.hideCloseButton()
|
||||
.useAnimation(false)
|
||||
.show();
|
||||
.backgroundInfo(Res.get("popup.shutDownInProgress.msg"))
|
||||
.hideCloseButton()
|
||||
.useAnimation(false)
|
||||
.show();
|
||||
//noinspection CodeBlock2Expr
|
||||
UserThread.runAfter(() -> {
|
||||
gracefulShutDown(() -> {
|
||||
|
|
|
@ -31,6 +31,7 @@ import io.bisq.core.arbitration.ArbitratorModule;
|
|||
import io.bisq.core.btc.BitcoinModule;
|
||||
import io.bisq.core.dao.DaoModule;
|
||||
import io.bisq.core.filter.FilterModule;
|
||||
import io.bisq.core.network.CoreSeedNodesRepository;
|
||||
import io.bisq.core.offer.OfferModule;
|
||||
import io.bisq.core.proto.network.CoreNetworkProtoResolver;
|
||||
import io.bisq.core.proto.persistable.CorePersistenceProtoResolver;
|
||||
|
@ -42,6 +43,7 @@ import io.bisq.gui.common.view.CachingViewLoader;
|
|||
import io.bisq.gui.main.overlays.notifications.NotificationCenter;
|
||||
import io.bisq.network.crypto.EncryptionServiceModule;
|
||||
import io.bisq.network.p2p.P2PModule;
|
||||
import io.bisq.network.p2p.seed.SeedNodesRepository;
|
||||
import javafx.stage.Stage;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
|
@ -70,6 +72,8 @@ class BisqAppModule extends AppModule {
|
|||
bind(Clock.class).in(Singleton.class);
|
||||
bind(Preferences.class).in(Singleton.class);
|
||||
|
||||
bind(SeedNodesRepository.class).to(CoreSeedNodesRepository.class).in(Singleton.class);
|
||||
|
||||
File storageDir = new File(environment.getRequiredProperty(Storage.STORAGE_DIR));
|
||||
bind(File.class).annotatedWith(named(Storage.STORAGE_DIR)).toInstance(storageDir);
|
||||
|
||||
|
@ -78,7 +82,6 @@ class BisqAppModule extends AppModule {
|
|||
|
||||
bind(NetworkProtoResolver.class).to(CoreNetworkProtoResolver.class).in(Singleton.class);
|
||||
bind(PersistenceProtoResolver.class).to(CorePersistenceProtoResolver.class).in(Singleton.class);
|
||||
bind(Preferences.class).in(Singleton.class);
|
||||
|
||||
// ordering is used for shut down sequence
|
||||
install(tradeModule());
|
||||
|
|
|
@ -653,9 +653,6 @@ public class MainViewModel implements ViewModel {
|
|||
|
||||
filterManager.onAllServicesInitialized();
|
||||
|
||||
providersRepository.setBannedNodes(preferences.getBannedPriceRelayNodes());
|
||||
providersRepository.onAllServicesInitialized();
|
||||
|
||||
setupBtcNumPeersWatcher();
|
||||
setupP2PNumPeersWatcher();
|
||||
updateBalance();
|
||||
|
|
|
@ -15,10 +15,9 @@ import io.bisq.gui.main.disputes.trader.TraderDisputeView;
|
|||
import io.bisq.gui.main.portfolio.PortfolioView;
|
||||
import io.bisq.gui.main.portfolio.pendingtrades.PendingTradesView;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.Subscription;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
|
@ -27,8 +26,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Slf4j
|
||||
public class NotificationCenter {
|
||||
private static final Logger log = LoggerFactory.getLogger(NotificationCenter.class);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -97,7 +96,7 @@ public class NotificationCenter {
|
|||
log.debug("We have already an entry in disputeStateSubscriptionsMap.");
|
||||
} else {
|
||||
Subscription disputeStateSubscription = EasyBind.subscribe(trade.disputeStateProperty(),
|
||||
disputeState -> onDisputeStateChanged(trade, disputeState));
|
||||
disputeState -> onDisputeStateChanged(trade, disputeState));
|
||||
disputeStateSubscriptionsMap.put(tradeId, disputeStateSubscription);
|
||||
}
|
||||
|
||||
|
@ -105,7 +104,7 @@ public class NotificationCenter {
|
|||
log.debug("We have already an entry in tradePhaseSubscriptionsMap.");
|
||||
} else {
|
||||
Subscription tradePhaseSubscription = EasyBind.subscribe(trade.statePhaseProperty(),
|
||||
phase -> onTradePhaseChanged(trade, phase));
|
||||
phase -> onTradePhaseChanged(trade, phase));
|
||||
tradePhaseSubscriptionsMap.put(tradeId, tradePhaseSubscription);
|
||||
}
|
||||
});
|
||||
|
@ -113,17 +112,17 @@ public class NotificationCenter {
|
|||
});
|
||||
|
||||
tradeManager.getTradableList().stream()
|
||||
.forEach(trade -> {
|
||||
String tradeId = trade.getId();
|
||||
Subscription disputeStateSubscription = EasyBind.subscribe(trade.disputeStateProperty(),
|
||||
disputeState -> onDisputeStateChanged(trade, disputeState));
|
||||
disputeStateSubscriptionsMap.put(tradeId, disputeStateSubscription);
|
||||
.forEach(trade -> {
|
||||
String tradeId = trade.getId();
|
||||
Subscription disputeStateSubscription = EasyBind.subscribe(trade.disputeStateProperty(),
|
||||
disputeState -> onDisputeStateChanged(trade, disputeState));
|
||||
disputeStateSubscriptionsMap.put(tradeId, disputeStateSubscription);
|
||||
|
||||
Subscription tradePhaseSubscription = EasyBind.subscribe(trade.statePhaseProperty(),
|
||||
phase -> onTradePhaseChanged(trade, phase));
|
||||
tradePhaseSubscriptionsMap.put(tradeId, tradePhaseSubscription);
|
||||
}
|
||||
);
|
||||
Subscription tradePhaseSubscription = EasyBind.subscribe(trade.statePhaseProperty(),
|
||||
phase -> onTradePhaseChanged(trade, phase));
|
||||
tradePhaseSubscriptionsMap.put(tradeId, tradePhaseSubscription);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
@ -156,7 +155,7 @@ public class NotificationCenter {
|
|||
message = Res.get("notification.trade.completed");
|
||||
} else {
|
||||
if (trade instanceof MakerTrade &&
|
||||
phase.ordinal() == Trade.Phase.DEPOSIT_PUBLISHED.ordinal()) {
|
||||
phase.ordinal() == Trade.Phase.DEPOSIT_PUBLISHED.ordinal()) {
|
||||
final String role = trade instanceof BuyerTrade ? Res.get("shared.seller") : Res.get("shared.buyer");
|
||||
message = Res.get("notification.trade.accepted", role);
|
||||
}
|
||||
|
@ -173,24 +172,24 @@ public class NotificationCenter {
|
|||
Notification notification = new Notification().tradeHeadLine(trade.getShortId()).message(message);
|
||||
if (navigation.getCurrentPath() != null && !navigation.getCurrentPath().contains(PendingTradesView.class)) {
|
||||
notification.actionButtonTextWithGoTo("navigation.portfolio.pending")
|
||||
.onAction(() -> {
|
||||
DontShowAgainLookup.dontShowAgain(key, true);
|
||||
//noinspection unchecked
|
||||
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
|
||||
if (selectItemByTradeIdConsumer != null)
|
||||
UserThread.runAfter(() -> selectItemByTradeIdConsumer.accept(trade.getId()), 1);
|
||||
})
|
||||
.onClose(() -> DontShowAgainLookup.dontShowAgain(key, true))
|
||||
.show();
|
||||
.onAction(() -> {
|
||||
DontShowAgainLookup.dontShowAgain(key, true);
|
||||
//noinspection unchecked
|
||||
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
|
||||
if (selectItemByTradeIdConsumer != null)
|
||||
UserThread.runAfter(() -> selectItemByTradeIdConsumer.accept(trade.getId()), 1);
|
||||
})
|
||||
.onClose(() -> DontShowAgainLookup.dontShowAgain(key, true))
|
||||
.show();
|
||||
} else if (selectedTradeId != null && !trade.getId().equals(selectedTradeId)) {
|
||||
notification.actionButtonText(Res.get("notification.trade.selectTrade"))
|
||||
.onAction(() -> {
|
||||
DontShowAgainLookup.dontShowAgain(key, true);
|
||||
if (selectItemByTradeIdConsumer != null)
|
||||
selectItemByTradeIdConsumer.accept(trade.getId());
|
||||
})
|
||||
.onClose(() -> DontShowAgainLookup.dontShowAgain(key, true))
|
||||
.show();
|
||||
.onAction(() -> {
|
||||
DontShowAgainLookup.dontShowAgain(key, true);
|
||||
if (selectItemByTradeIdConsumer != null)
|
||||
selectItemByTradeIdConsumer.accept(trade.getId());
|
||||
})
|
||||
.onClose(() -> DontShowAgainLookup.dontShowAgain(key, true))
|
||||
.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -201,8 +200,8 @@ public class NotificationCenter {
|
|||
String message = null;
|
||||
if (disputeManager.findOwnDispute(trade.getId()).isPresent()) {
|
||||
String disputeOrTicket = disputeManager.findOwnDispute(trade.getId()).get().isSupportTicket() ?
|
||||
Res.get("shared.supportTicket") :
|
||||
Res.get("shared.dispute");
|
||||
Res.get("shared.supportTicket") :
|
||||
Res.get("shared.dispute");
|
||||
switch (disputeState) {
|
||||
case NO_DISPUTE:
|
||||
break;
|
||||
|
@ -220,8 +219,8 @@ public class NotificationCenter {
|
|||
if (navigation.getCurrentPath() != null && !navigation.getCurrentPath().contains(TraderDisputeView.class)) {
|
||||
//noinspection unchecked
|
||||
notification.actionButtonTextWithGoTo("navigation.support")
|
||||
.onAction(() -> navigation.navigateTo(MainView.class, DisputesView.class, TraderDisputeView.class))
|
||||
.show();
|
||||
.onAction(() -> navigation.navigateTo(MainView.class, DisputesView.class, TraderDisputeView.class))
|
||||
.show();
|
||||
} else {
|
||||
notification.show();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* This file is part of bisq.
|
||||
*
|
||||
* bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.network.p2p;
|
||||
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.name.Named;
|
||||
import io.bisq.common.proto.network.NetworkProtoResolver;
|
||||
import io.bisq.network.NetworkOptionKeys;
|
||||
import io.bisq.network.p2p.network.LocalhostNetworkNode;
|
||||
import io.bisq.network.p2p.network.NetworkNode;
|
||||
import io.bisq.network.p2p.network.TorNetworkNode;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
|
||||
public class NetworkNodeProvider implements Provider<NetworkNode> {
|
||||
|
||||
private final NetworkNode networkNode;
|
||||
|
||||
@Inject
|
||||
public NetworkNodeProvider( NetworkProtoResolver networkProtoResolver,
|
||||
@Named(NetworkOptionKeys.USE_LOCALHOST_FOR_P2P) boolean useLocalhostForP2P,
|
||||
@Named(NetworkOptionKeys.PORT_KEY) int port,
|
||||
@Named(NetworkOptionKeys.TOR_DIR) File torDir) {
|
||||
networkNode = useLocalhostForP2P ?
|
||||
new LocalhostNetworkNode(port, networkProtoResolver) :
|
||||
new TorNetworkNode(port, torDir, networkProtoResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkNode get() {
|
||||
return networkNode;
|
||||
}
|
||||
}
|
|
@ -22,7 +22,14 @@ import com.google.inject.name.Names;
|
|||
import io.bisq.common.app.AppModule;
|
||||
import io.bisq.network.NetworkOptionKeys;
|
||||
import io.bisq.network.Socks5ProxyProvider;
|
||||
import io.bisq.network.p2p.seed.SeedNodesRepository;
|
||||
import io.bisq.network.p2p.network.NetworkNode;
|
||||
import io.bisq.network.p2p.peers.BanList;
|
||||
import io.bisq.network.p2p.peers.Broadcaster;
|
||||
import io.bisq.network.p2p.peers.PeerManager;
|
||||
import io.bisq.network.p2p.peers.getdata.RequestDataManager;
|
||||
import io.bisq.network.p2p.peers.keepalive.KeepAliveManager;
|
||||
import io.bisq.network.p2p.peers.peerexchange.PeerExchangeManager;
|
||||
import io.bisq.network.p2p.storage.P2PDataStorage;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -38,8 +45,16 @@ public class P2PModule extends AppModule {
|
|||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(SeedNodesRepository.class).in(Singleton.class);
|
||||
bind(P2PService.class).in(Singleton.class);
|
||||
bind(PeerManager.class).in(Singleton.class);
|
||||
bind(P2PDataStorage.class).in(Singleton.class);
|
||||
bind(RequestDataManager.class).in(Singleton.class);
|
||||
bind(PeerExchangeManager.class).in(Singleton.class);
|
||||
bind(KeepAliveManager.class).in(Singleton.class);
|
||||
bind(Broadcaster.class).in(Singleton.class);
|
||||
bind(BanList.class).in(Singleton.class);
|
||||
bind(NetworkNode.class).toProvider(NetworkNodeProvider.class).in(Singleton.class);
|
||||
|
||||
bind(Socks5ProxyProvider.class).in(Singleton.class);
|
||||
|
||||
Boolean useLocalhostForP2P = environment.getProperty(NetworkOptionKeys.USE_LOCALHOST_FOR_P2P, boolean.class, false);
|
||||
|
@ -63,4 +78,4 @@ public class P2PModule extends AppModule {
|
|||
bindConstant().annotatedWith(named(NetworkOptionKeys.SOCKS_5_PROXY_BTC_ADDRESS)).to(environment.getRequiredProperty(NetworkOptionKeys.SOCKS_5_PROXY_BTC_ADDRESS));
|
||||
bindConstant().annotatedWith(named(NetworkOptionKeys.SOCKS_5_PROXY_HTTP_ADDRESS)).to(environment.getRequiredProperty(NetworkOptionKeys.SOCKS_5_PROXY_HTTP_ADDRESS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,26 +5,18 @@ import com.google.common.util.concurrent.FutureCallback;
|
|||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import io.bisq.common.Clock;
|
||||
import io.bisq.common.UserThread;
|
||||
import io.bisq.common.app.Log;
|
||||
import io.bisq.common.crypto.CryptoException;
|
||||
import io.bisq.common.crypto.KeyRing;
|
||||
import io.bisq.common.crypto.PubKeyRing;
|
||||
import io.bisq.common.proto.network.NetworkEnvelope;
|
||||
import io.bisq.common.proto.network.NetworkProtoResolver;
|
||||
import io.bisq.common.proto.persistable.PersistedDataHost;
|
||||
import io.bisq.common.proto.persistable.PersistenceProtoResolver;
|
||||
import io.bisq.common.storage.FileUtil;
|
||||
import io.bisq.common.storage.Storage;
|
||||
import io.bisq.common.util.Utilities;
|
||||
import io.bisq.network.NetworkOptionKeys;
|
||||
import io.bisq.network.Socks5ProxyProvider;
|
||||
import io.bisq.network.crypto.EncryptionService;
|
||||
import io.bisq.network.p2p.messaging.DecryptedMailboxListener;
|
||||
import io.bisq.network.p2p.network.*;
|
||||
import io.bisq.network.p2p.peers.BanList;
|
||||
import io.bisq.network.p2p.peers.BroadcastHandler;
|
||||
import io.bisq.network.p2p.peers.Broadcaster;
|
||||
import io.bisq.network.p2p.peers.PeerManager;
|
||||
|
@ -40,7 +32,6 @@ import io.bisq.network.p2p.storage.messages.RefreshOfferMessage;
|
|||
import io.bisq.network.p2p.storage.payload.*;
|
||||
import javafx.beans.property.*;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.Subscription;
|
||||
import org.fxmisc.easybind.monadic.MonadicBinding;
|
||||
|
@ -49,12 +40,9 @@ import org.jetbrains.annotations.Nullable;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.PublicKey;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
@ -65,25 +53,19 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
public static final int MAX_CONNECTIONS_DEFAULT = 12;
|
||||
|
||||
private final SeedNodesRepository seedNodesRepository;
|
||||
private final int port;
|
||||
private final int maxConnections;
|
||||
private final File torDir;
|
||||
private final Clock clock;
|
||||
//TODO optional can be removed as seednode are created with those objects now
|
||||
private final Optional<EncryptionService> optionalEncryptionService;
|
||||
private final Optional<KeyRing> optionalKeyRing;
|
||||
private final EncryptionService encryptionService;
|
||||
private final KeyRing keyRing;
|
||||
|
||||
// set in init
|
||||
private NetworkNode networkNode;
|
||||
private final NetworkNode networkNode;
|
||||
private final PeerManager peerManager;
|
||||
@Getter
|
||||
private Broadcaster broadcaster;
|
||||
private P2PDataStorage p2PDataStorage;
|
||||
private PeerManager peerManager;
|
||||
private RequestDataManager requestDataManager;
|
||||
private PeerExchangeManager peerExchangeManager;
|
||||
private final Broadcaster broadcaster;
|
||||
private final P2PDataStorage p2PDataStorage;
|
||||
private final RequestDataManager requestDataManager;
|
||||
private final PeerExchangeManager peerExchangeManager;
|
||||
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private MonadicBinding<Boolean> networkReadyBinding;
|
||||
private final MonadicBinding<Boolean> networkReadyBinding;
|
||||
private final Set<DecryptedDirectMessageListener> decryptedDirectMessageListeners = new CopyOnWriteArraySet<>();
|
||||
private final Set<DecryptedMailboxListener> decryptedMailboxListeners = new CopyOnWriteArraySet<>();
|
||||
private final Set<P2PServiceListener> p2pServiceListeners = new CopyOnWriteArraySet<>();
|
||||
|
@ -95,12 +77,10 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
|
||||
private volatile boolean shutDownInProgress;
|
||||
private boolean shutDownComplete;
|
||||
private Subscription networkReadySubscription;
|
||||
private final Subscription networkReadySubscription;
|
||||
private boolean isBootstrapped;
|
||||
private KeepAliveManager keepAliveManager;
|
||||
private final KeepAliveManager keepAliveManager;
|
||||
private final Socks5ProxyProvider socks5ProxyProvider;
|
||||
@Getter
|
||||
private Set<NodeAddress> seedNodeAddresses;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -108,114 +88,34 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Called also from SeedNodeP2PService
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
@Inject
|
||||
public P2PService(SeedNodesRepository seedNodesRepository,
|
||||
@Named(NetworkOptionKeys.PORT_KEY) int port,
|
||||
@Named(NetworkOptionKeys.TOR_DIR) File torDir,
|
||||
@Named(NetworkOptionKeys.USE_LOCALHOST_FOR_P2P) boolean useLocalhostForP2P,
|
||||
@Named(NetworkOptionKeys.NETWORK_ID) int networkId,
|
||||
@Named(NetworkOptionKeys.MAX_CONNECTIONS) int maxConnections,
|
||||
@Named(Storage.STORAGE_DIR) File storageDir,
|
||||
@Named(NetworkOptionKeys.SEED_NODES_KEY) String seedNodes,
|
||||
@Named(NetworkOptionKeys.MY_ADDRESS) String myAddress,
|
||||
@Named(NetworkOptionKeys.BAN_LIST) String banList,
|
||||
Clock clock,
|
||||
public P2PService(NetworkNode networkNode,
|
||||
PeerManager peerManager,
|
||||
P2PDataStorage p2PDataStorage,
|
||||
RequestDataManager requestDataManager,
|
||||
PeerExchangeManager peerExchangeManager,
|
||||
KeepAliveManager keepAliveManager,
|
||||
Broadcaster broadcaster,
|
||||
SeedNodesRepository seedNodesRepository,
|
||||
Socks5ProxyProvider socks5ProxyProvider,
|
||||
@Nullable EncryptionService encryptionService,
|
||||
@Nullable KeyRing keyRing,
|
||||
NetworkProtoResolver networkProtoResolver,
|
||||
PersistenceProtoResolver persistenceProtoResolver) {
|
||||
this(
|
||||
seedNodesRepository,
|
||||
port,
|
||||
maxConnections,
|
||||
torDir,
|
||||
useLocalhostForP2P,
|
||||
networkId,
|
||||
storageDir,
|
||||
seedNodes,
|
||||
myAddress,
|
||||
banList,
|
||||
clock,
|
||||
socks5ProxyProvider,
|
||||
encryptionService,
|
||||
keyRing,
|
||||
networkProtoResolver,
|
||||
persistenceProtoResolver
|
||||
);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public P2PService(SeedNodesRepository seedNodesRepository,
|
||||
int port, int maxConnections,
|
||||
File torDir,
|
||||
boolean useLocalhostForP2P,
|
||||
int networkId,
|
||||
File storageDir,
|
||||
String seedNodes,
|
||||
String myAddress,
|
||||
String banList,
|
||||
Clock clock,
|
||||
Socks5ProxyProvider socks5ProxyProvider,
|
||||
@Nullable EncryptionService encryptionService,
|
||||
@Nullable KeyRing keyRing,
|
||||
NetworkProtoResolver networkProtoResolver,
|
||||
PersistenceProtoResolver persistenceProtoResolver) {
|
||||
EncryptionService encryptionService,
|
||||
KeyRing keyRing ) {
|
||||
this.networkNode = networkNode;
|
||||
this.peerManager = peerManager;
|
||||
this.p2PDataStorage = p2PDataStorage;
|
||||
this.requestDataManager = requestDataManager;
|
||||
this.peerExchangeManager = peerExchangeManager;
|
||||
this.keepAliveManager = keepAliveManager;
|
||||
this.broadcaster = broadcaster;
|
||||
this.seedNodesRepository = seedNodesRepository;
|
||||
this.port = port;
|
||||
this.maxConnections = maxConnections;
|
||||
this.torDir = torDir;
|
||||
this.clock = clock;
|
||||
this.socks5ProxyProvider = socks5ProxyProvider;
|
||||
this.encryptionService = encryptionService;
|
||||
this.keyRing = keyRing;
|
||||
|
||||
optionalEncryptionService = Optional.ofNullable(encryptionService);
|
||||
optionalKeyRing = Optional.ofNullable(keyRing);
|
||||
|
||||
init(useLocalhostForP2P,
|
||||
networkId,
|
||||
storageDir,
|
||||
seedNodes,
|
||||
myAddress,
|
||||
banList,
|
||||
networkProtoResolver,
|
||||
persistenceProtoResolver);
|
||||
}
|
||||
|
||||
private void init(boolean useLocalhostForP2P,
|
||||
int networkId,
|
||||
File storageDir,
|
||||
String seedNodes,
|
||||
String myAddress,
|
||||
String banList,
|
||||
NetworkProtoResolver networkProtoResolver,
|
||||
PersistenceProtoResolver persistenceProtoResolver) {
|
||||
if (!useLocalhostForP2P)
|
||||
FileUtil.rollingBackup(new File(Paths.get(torDir.getAbsolutePath(), "hiddenservice").toString()), "private_key", 20);
|
||||
|
||||
if (banList != null && !banList.isEmpty())
|
||||
BanList.setList(Arrays.asList(StringUtils.deleteWhitespace(banList).split(",")).stream().map(NodeAddress::new).collect(Collectors.toList()));
|
||||
if (myAddress != null && !myAddress.isEmpty())
|
||||
seedNodesRepository.setNodeAddressToExclude(new NodeAddress(myAddress));
|
||||
|
||||
networkNode = useLocalhostForP2P ?
|
||||
new LocalhostNetworkNode(port, networkProtoResolver) :
|
||||
new TorNetworkNode(port, torDir, networkProtoResolver);
|
||||
networkNode.addConnectionListener(this);
|
||||
networkNode.addMessageListener(this);
|
||||
|
||||
if (seedNodes != null && !seedNodes.isEmpty())
|
||||
seedNodeAddresses = Arrays.asList(StringUtils.deleteWhitespace(seedNodes).split(",")).stream().map(NodeAddress::new).collect(Collectors.toSet());
|
||||
else
|
||||
seedNodeAddresses = seedNodesRepository.getSeedNodeAddresses(useLocalhostForP2P, networkId);
|
||||
|
||||
peerManager = new PeerManager(networkNode, maxConnections, seedNodeAddresses, storageDir, clock, persistenceProtoResolver);
|
||||
broadcaster = new Broadcaster(networkNode, peerManager);
|
||||
p2PDataStorage = new P2PDataStorage(broadcaster, networkNode, storageDir, persistenceProtoResolver);
|
||||
p2PDataStorage.addHashMapChangedListener(this);
|
||||
requestDataManager = new RequestDataManager(networkNode, p2PDataStorage, peerManager, seedNodeAddresses, this);
|
||||
peerExchangeManager = new PeerExchangeManager(networkNode, peerManager, seedNodeAddresses);
|
||||
keepAliveManager = new KeepAliveManager(networkNode, peerManager);
|
||||
this.networkNode.addConnectionListener(this);
|
||||
this.networkNode.addMessageListener(this);
|
||||
this.p2PDataStorage.addHashMapChangedListener(this);
|
||||
this.requestDataManager.addListener(this);
|
||||
|
||||
// We need to have both the initial data delivered and the hidden service published
|
||||
networkReadyBinding = EasyBind.combine(hiddenServicePublished, preliminaryDataReceived,
|
||||
|
@ -324,11 +224,11 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
|
||||
/**
|
||||
* Startup sequence:
|
||||
* <p>
|
||||
* <p/>
|
||||
* Variant 1 (normal expected mode):
|
||||
* onTorNodeReady -> requestDataManager.firstDataRequestFromAnySeedNode()
|
||||
* RequestDataManager.Listener.onDataReceived && onHiddenServicePublished -> onNetworkReady()
|
||||
* <p>
|
||||
* <p/>
|
||||
* Variant 2 (no seed node available):
|
||||
* onTorNodeReady -> requestDataManager.firstDataRequestFromAnySeedNode
|
||||
* retry after 20-30 sec until we get at least one seed node connected
|
||||
|
@ -458,35 +358,33 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
if (networkEnvelop instanceof PrefixedSealedAndSignedMessage) {
|
||||
Log.traceCall("\n\t" + networkEnvelop.toString() + "\n\tconnection=" + connection);
|
||||
// Seed nodes don't have set the encryptionService
|
||||
if (optionalEncryptionService.isPresent()) {
|
||||
try {
|
||||
PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage = (PrefixedSealedAndSignedMessage) networkEnvelop;
|
||||
if (verifyAddressPrefixHash(prefixedSealedAndSignedMessage)) {
|
||||
// We set connectionType to that connection to avoid that is get closed when
|
||||
// we get too many connection attempts.
|
||||
connection.setPeerType(Connection.PeerType.DIRECT_MSG_PEER);
|
||||
try {
|
||||
PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage = (PrefixedSealedAndSignedMessage) networkEnvelop;
|
||||
if (verifyAddressPrefixHash(prefixedSealedAndSignedMessage)) {
|
||||
// We set connectionType to that connection to avoid that is get closed when
|
||||
// we get too many connection attempts.
|
||||
connection.setPeerType(Connection.PeerType.DIRECT_MSG_PEER);
|
||||
|
||||
log.debug("Try to decrypt...");
|
||||
DecryptedMessageWithPubKey decryptedMessageWithPubKey = optionalEncryptionService.get().decryptAndVerify(
|
||||
prefixedSealedAndSignedMessage.getSealedAndSigned());
|
||||
log.debug("Try to decrypt...");
|
||||
DecryptedMessageWithPubKey decryptedMessageWithPubKey = encryptionService.decryptAndVerify(
|
||||
prefixedSealedAndSignedMessage.getSealedAndSigned());
|
||||
|
||||
log.debug("\n\nDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\n" +
|
||||
"Decrypted SealedAndSignedMessage:\ndecryptedMsgWithPubKey={}"
|
||||
+ "\nDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\n", decryptedMessageWithPubKey);
|
||||
if (connection.getPeersNodeAddressOptional().isPresent())
|
||||
decryptedDirectMessageListeners.stream().forEach(
|
||||
e -> e.onDirectMessage(decryptedMessageWithPubKey, connection.getPeersNodeAddressOptional().get()));
|
||||
else
|
||||
log.error("peersNodeAddress is not available at onMessage.");
|
||||
} else {
|
||||
log.debug("Wrong receiverAddressMaskHash. The message is not intended for us.");
|
||||
}
|
||||
} catch (CryptoException e) {
|
||||
log.debug(networkEnvelop.toString());
|
||||
log.debug(e.toString());
|
||||
log.debug("Decryption of prefixedSealedAndSignedMessage.sealedAndSigned failed. " +
|
||||
"That is expected if the message is not intended for us.");
|
||||
log.debug("\n\nDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\n" +
|
||||
"Decrypted SealedAndSignedMessage:\ndecryptedMsgWithPubKey={}"
|
||||
+ "\nDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\n", decryptedMessageWithPubKey);
|
||||
if (connection.getPeersNodeAddressOptional().isPresent())
|
||||
decryptedDirectMessageListeners.stream().forEach(
|
||||
e -> e.onDirectMessage(decryptedMessageWithPubKey, connection.getPeersNodeAddressOptional().get()));
|
||||
else
|
||||
log.error("peersNodeAddress is not available at onMessage.");
|
||||
} else {
|
||||
log.debug("Wrong receiverAddressMaskHash. The message is not intended for us.");
|
||||
}
|
||||
} catch (CryptoException e) {
|
||||
log.debug(networkEnvelop.toString());
|
||||
log.debug(e.toString());
|
||||
log.debug("Decryption of prefixedSealedAndSignedMessage.sealedAndSigned failed. " +
|
||||
"That is expected if the message is not intended for us.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -526,7 +424,6 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
SendDirectMessageListener sendDirectMessageListener) {
|
||||
Log.traceCall();
|
||||
checkNotNull(peersNodeAddress, "Peer node address must not be null at doSendEncryptedDirectMessage");
|
||||
checkArgument(optionalEncryptionService.isPresent(), "EncryptionService not set. Seems that is called on a seed node which must not happen.");
|
||||
checkNotNull(networkNode.getNodeAddress(), "My node address must not be null at doSendEncryptedDirectMessage");
|
||||
try {
|
||||
log.debug("\n\nEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n" +
|
||||
|
@ -534,7 +431,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
+ "\nEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n", message);
|
||||
PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage = new PrefixedSealedAndSignedMessage(
|
||||
networkNode.getNodeAddress(),
|
||||
optionalEncryptionService.get().encryptAndSign(pubKeyRing, message),
|
||||
encryptionService.encryptAndSign(pubKeyRing, message),
|
||||
peersNodeAddress.getAddressPrefixHash(),
|
||||
UUID.randomUUID().toString());
|
||||
SettableFuture<Connection> future = networkNode.sendMessage(peersNodeAddress, prefixedSealedAndSignedMessage);
|
||||
|
@ -567,13 +464,13 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
Log.traceCall();
|
||||
final NodeAddress nodeAddress = networkNode.getNodeAddress();
|
||||
// Seed nodes don't receive mailbox network_messages
|
||||
if (optionalEncryptionService.isPresent() && nodeAddress != null && !seedNodesRepository.isSeedNode(nodeAddress)) {
|
||||
if (nodeAddress != null && !seedNodesRepository.isSeedNode(nodeAddress)) {
|
||||
Log.traceCall();
|
||||
MailboxStoragePayload mailboxStoragePayload = protectedMailboxStorageEntry.getMailboxStoragePayload();
|
||||
PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage = mailboxStoragePayload.getPrefixedSealedAndSignedMessage();
|
||||
if (verifyAddressPrefixHash(prefixedSealedAndSignedMessage)) {
|
||||
try {
|
||||
DecryptedMessageWithPubKey decryptedMessageWithPubKey = optionalEncryptionService.get().decryptAndVerify(
|
||||
DecryptedMessageWithPubKey decryptedMessageWithPubKey = encryptionService.decryptAndVerify(
|
||||
prefixedSealedAndSignedMessage.getSealedAndSigned());
|
||||
if (decryptedMessageWithPubKey.getNetworkEnvelope() instanceof MailboxMessage) {
|
||||
MailboxMessage mailboxMessage = (MailboxMessage) decryptedMessageWithPubKey.getNetworkEnvelope();
|
||||
|
@ -608,12 +505,8 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
"PeerAddress must not be null (sendEncryptedMailboxMessage)");
|
||||
checkNotNull(networkNode.getNodeAddress(),
|
||||
"My node address must not be null at sendEncryptedMailboxMessage");
|
||||
checkArgument(optionalKeyRing.isPresent(),
|
||||
"keyRing not set. Seems that is called on a seed node which must not happen.");
|
||||
checkArgument(!optionalKeyRing.get().getPubKeyRing().equals(peersPubKeyRing),
|
||||
checkArgument(!keyRing.getPubKeyRing().equals(peersPubKeyRing),
|
||||
"We got own keyring instead of that from peer");
|
||||
checkArgument(optionalEncryptionService.isPresent(),
|
||||
"EncryptionService not set. Seems that is called on a seed node which must not happen.");
|
||||
|
||||
if (isBootstrapped()) {
|
||||
if (!networkNode.getAllConnections().isEmpty()) {
|
||||
|
@ -623,7 +516,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
+ "\nEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n", message);
|
||||
PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage = new PrefixedSealedAndSignedMessage(
|
||||
networkNode.getNodeAddress(),
|
||||
optionalEncryptionService.get().encryptAndSign(peersPubKeyRing, message),
|
||||
encryptionService.encryptAndSign(peersPubKeyRing, message),
|
||||
peersNodeAddress.getAddressPrefixHash(),
|
||||
UUID.randomUUID().toString());
|
||||
SettableFuture<Connection> future = networkNode.sendMessage(peersNodeAddress, prefixedSealedAndSignedMessage);
|
||||
|
@ -642,7 +535,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
log.trace("create MailboxEntry with peerAddress " + peersNodeAddress);
|
||||
PublicKey receiverStoragePublicKey = peersPubKeyRing.getSignaturePubKey();
|
||||
addMailboxData(new MailboxStoragePayload(prefixedSealedAndSignedMessage,
|
||||
optionalKeyRing.get().getSignatureKeyPair().getPublic(),
|
||||
keyRing.getSignatureKeyPair().getPublic(),
|
||||
receiverStoragePublicKey),
|
||||
receiverStoragePublicKey,
|
||||
sendMailboxMessageListener);
|
||||
|
@ -667,15 +560,13 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
PublicKey receiversPublicKey,
|
||||
SendMailboxMessageListener sendMailboxMessageListener) {
|
||||
Log.traceCall();
|
||||
checkArgument(optionalKeyRing.isPresent(),
|
||||
"keyRing not set. Seems that is called on a seed node which must not happen.");
|
||||
|
||||
if (isBootstrapped()) {
|
||||
if (!networkNode.getAllConnections().isEmpty()) {
|
||||
try {
|
||||
ProtectedMailboxStorageEntry protectedMailboxStorageEntry = p2PDataStorage.getMailboxDataWithSignedSeqNr(
|
||||
expirableMailboxStoragePayload,
|
||||
optionalKeyRing.get().getSignatureKeyPair(),
|
||||
keyRing.getSignatureKeyPair(),
|
||||
receiversPublicKey);
|
||||
|
||||
BroadcastHandler.Listener listener = new BroadcastHandler.Listener() {
|
||||
|
@ -749,7 +640,6 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
|
||||
private void delayedRemoveEntryFromMailbox(DecryptedMessageWithPubKey decryptedMessageWithPubKey) {
|
||||
Log.traceCall();
|
||||
checkArgument(optionalKeyRing.isPresent(), "keyRing not set. Seems that is called on a seed node which must not happen.");
|
||||
if (isBootstrapped()) {
|
||||
MailboxMessage mailboxMessage = (MailboxMessage) decryptedMessageWithPubKey.getNetworkEnvelope();
|
||||
String uid = mailboxMessage.getUid();
|
||||
|
@ -758,12 +648,12 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
if (mailboxData != null && mailboxData.getProtectedStoragePayload() instanceof MailboxStoragePayload) {
|
||||
MailboxStoragePayload expirableMailboxStoragePayload = (MailboxStoragePayload) mailboxData.getProtectedStoragePayload();
|
||||
PublicKey receiversPubKey = mailboxData.getReceiversPubKey();
|
||||
checkArgument(receiversPubKey.equals(optionalKeyRing.get().getSignatureKeyPair().getPublic()),
|
||||
checkArgument(receiversPubKey.equals(keyRing.getSignatureKeyPair().getPublic()),
|
||||
"receiversPubKey is not matching with our key. That must not happen.");
|
||||
try {
|
||||
ProtectedMailboxStorageEntry protectedMailboxStorageEntry = p2PDataStorage.getMailboxDataWithSignedSeqNr(
|
||||
expirableMailboxStoragePayload,
|
||||
optionalKeyRing.get().getSignatureKeyPair(),
|
||||
keyRing.getSignatureKeyPair(),
|
||||
receiversPubKey);
|
||||
p2PDataStorage.removeMailboxData(protectedMailboxStorageEntry, networkNode.getNodeAddress(), true);
|
||||
} catch (CryptoException e) {
|
||||
|
@ -794,10 +684,9 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
|
||||
public boolean addData(ProtectedStoragePayload protectedStoragePayload, boolean isDataOwner) {
|
||||
Log.traceCall();
|
||||
checkArgument(optionalKeyRing.isPresent(), "keyRing not set. Seems that is called on a seed node which must not happen.");
|
||||
if (isBootstrapped()) {
|
||||
try {
|
||||
ProtectedStorageEntry protectedStorageEntry = p2PDataStorage.getProtectedStorageEntry(protectedStoragePayload, optionalKeyRing.get().getSignatureKeyPair());
|
||||
ProtectedStorageEntry protectedStorageEntry = p2PDataStorage.getProtectedStorageEntry(protectedStoragePayload, keyRing.getSignatureKeyPair());
|
||||
return p2PDataStorage.add(protectedStorageEntry, networkNode.getNodeAddress(), null, isDataOwner);
|
||||
} catch (CryptoException e) {
|
||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||
|
@ -810,10 +699,9 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
|
||||
public boolean refreshTTL(ProtectedStoragePayload protectedStoragePayload, boolean isDataOwner) {
|
||||
Log.traceCall();
|
||||
checkArgument(optionalKeyRing.isPresent(), "keyRing not set. Seems that is called on a seed node which must not happen.");
|
||||
if (isBootstrapped()) {
|
||||
try {
|
||||
RefreshOfferMessage refreshTTLMessage = p2PDataStorage.getRefreshTTLMessage(protectedStoragePayload, optionalKeyRing.get().getSignatureKeyPair());
|
||||
RefreshOfferMessage refreshTTLMessage = p2PDataStorage.getRefreshTTLMessage(protectedStoragePayload, keyRing.getSignatureKeyPair());
|
||||
return p2PDataStorage.refreshTTL(refreshTTLMessage, networkNode.getNodeAddress(), isDataOwner);
|
||||
} catch (CryptoException e) {
|
||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||
|
@ -826,10 +714,9 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
|
||||
public boolean removeData(ProtectedStoragePayload protectedStoragePayload, boolean isDataOwner) {
|
||||
Log.traceCall();
|
||||
checkArgument(optionalKeyRing.isPresent(), "keyRing not set. Seems that is called on a seed node which must not happen.");
|
||||
if (isBootstrapped()) {
|
||||
try {
|
||||
ProtectedStorageEntry protectedStorageEntry = p2PDataStorage.getProtectedStorageEntry(protectedStoragePayload, optionalKeyRing.get().getSignatureKeyPair());
|
||||
ProtectedStorageEntry protectedStorageEntry = p2PDataStorage.getProtectedStorageEntry(protectedStoragePayload, keyRing.getSignatureKeyPair());
|
||||
return p2PDataStorage.remove(protectedStorageEntry, networkNode.getNodeAddress(), isDataOwner);
|
||||
} catch (CryptoException e) {
|
||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||
|
@ -910,9 +797,8 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
|||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
public KeyRing getKeyRing() {
|
||||
return optionalKeyRing.isPresent() ? optionalKeyRing.get() : null;
|
||||
return keyRing;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -387,7 +387,7 @@ public class Connection implements MessageListener {
|
|||
|
||||
peersNodeAddressProperty.set(peerNodeAddress);
|
||||
|
||||
if (BanList.contains(peerNodeAddress)) {
|
||||
if (BanList.isBanned(peerNodeAddress)) {
|
||||
log.warn("We detected a connection to a banned peer. We will close that connection. (setPeersNodeAddress)");
|
||||
sharedModel.reportInvalidRequest(RuleViolation.PEER_BANNED);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import io.bisq.common.Timer;
|
|||
import io.bisq.common.UserThread;
|
||||
import io.bisq.common.app.Log;
|
||||
import io.bisq.common.proto.network.NetworkProtoResolver;
|
||||
import io.bisq.common.storage.FileUtil;
|
||||
import io.bisq.common.util.Utilities;
|
||||
import io.bisq.network.p2p.NodeAddress;
|
||||
import io.bisq.network.p2p.Utils;
|
||||
|
@ -29,6 +30,7 @@ import org.slf4j.LoggerFactory;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
@ -66,6 +68,8 @@ public class TorNetworkNode extends NetworkNode {
|
|||
|
||||
@Override
|
||||
public void start(@Nullable SetupListener setupListener) {
|
||||
FileUtil.rollingBackup(new File(Paths.get(torDir.getAbsolutePath(), "hiddenservice").toString()), "private_key", 20);
|
||||
|
||||
if (setupListener != null)
|
||||
addSetupListener(setupListener);
|
||||
|
||||
|
@ -73,24 +77,24 @@ public class TorNetworkNode extends NetworkNode {
|
|||
|
||||
// Create the tor node (takes about 6 sec.)
|
||||
createTorNode(torDir,
|
||||
torNode -> {
|
||||
Log.traceCall("torNode created");
|
||||
TorNetworkNode.this.torNetworkNode = torNode;
|
||||
torNode -> {
|
||||
Log.traceCall("torNode created");
|
||||
TorNetworkNode.this.torNetworkNode = torNode;
|
||||
|
||||
setupListeners.stream().forEach(SetupListener::onTorNodeReady);
|
||||
setupListeners.stream().forEach(SetupListener::onTorNodeReady);
|
||||
|
||||
// Create Hidden Service (takes about 40 sec.)
|
||||
createHiddenService(torNode,
|
||||
Utils.findFreeSystemPort(),
|
||||
servicePort,
|
||||
hiddenServiceDescriptor -> {
|
||||
Log.traceCall("hiddenService created");
|
||||
TorNetworkNode.this.hiddenServiceDescriptor = hiddenServiceDescriptor;
|
||||
nodeAddressProperty.set(new NodeAddress(hiddenServiceDescriptor.getFullAddress()));
|
||||
startServer(hiddenServiceDescriptor.getServerSocket());
|
||||
setupListeners.stream().forEach(SetupListener::onHiddenServicePublished);
|
||||
});
|
||||
});
|
||||
// Create Hidden Service (takes about 40 sec.)
|
||||
createHiddenService(torNode,
|
||||
Utils.findFreeSystemPort(),
|
||||
servicePort,
|
||||
hiddenServiceDescriptor -> {
|
||||
Log.traceCall("hiddenService created");
|
||||
TorNetworkNode.this.hiddenServiceDescriptor = hiddenServiceDescriptor;
|
||||
nodeAddressProperty.set(new NodeAddress(hiddenServiceDescriptor.getFullAddress()));
|
||||
startServer(hiddenServiceDescriptor.getServerSocket());
|
||||
setupListeners.stream().forEach(SetupListener::onHiddenServicePublished);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -181,9 +185,9 @@ public class TorNetworkNode extends NetworkNode {
|
|||
restartCounter++;
|
||||
if (restartCounter > MAX_RESTART_ATTEMPTS) {
|
||||
String msg = "We tried to restart Tor " + restartCounter +
|
||||
" times, but it continued to fail with error message:\n" +
|
||||
errorMessage + "\n\n" +
|
||||
"Please check your internet connection and firewall and try to start again.";
|
||||
" times, but it continued to fail with error message:\n" +
|
||||
errorMessage + "\n\n" +
|
||||
"Please check your internet connection and firewall and try to start again.";
|
||||
log.error(msg);
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
|
@ -202,9 +206,9 @@ public class TorNetworkNode extends NetworkNode {
|
|||
log.trace("Created directory for tor at {}", torDir.getAbsolutePath());
|
||||
TorNode<JavaOnionProxyManager, JavaOnionProxyContext> torNode = new JavaTorNode(torDir);
|
||||
log.debug("\n\n############################################################\n" +
|
||||
"TorNode created:" +
|
||||
"\nTook " + (System.currentTimeMillis() - ts) + " ms"
|
||||
+ "\n############################################################\n");
|
||||
"TorNode created:" +
|
||||
"\nTook " + (System.currentTimeMillis() - ts) + " ms"
|
||||
+ "\n############################################################\n");
|
||||
return torNode;
|
||||
});
|
||||
Futures.addCallback(future, new FutureCallback<TorNode<JavaOnionProxyManager, JavaOnionProxyContext>>() {
|
||||
|
@ -231,10 +235,10 @@ public class TorNetworkNode extends NetworkNode {
|
|||
HiddenServiceDescriptor hiddenServiceDescriptor = torNode.createHiddenService(localPort, servicePort);
|
||||
torNode.addHiddenServiceReadyListener(hiddenServiceDescriptor, descriptor -> {
|
||||
log.debug("\n\n############################################################\n" +
|
||||
"Hidden service published:" +
|
||||
"\nAddress=" + descriptor.getFullAddress() +
|
||||
"\nTook " + (System.currentTimeMillis() - ts) + " ms"
|
||||
+ "\n############################################################\n");
|
||||
"Hidden service published:" +
|
||||
"\nAddress=" + descriptor.getFullAddress() +
|
||||
"\nTook " + (System.currentTimeMillis() - ts) + " ms"
|
||||
+ "\n############################################################\n");
|
||||
|
||||
UserThread.execute(() -> resultHandler.accept(hiddenServiceDescriptor));
|
||||
});
|
||||
|
|
|
@ -1,33 +1,30 @@
|
|||
package io.bisq.network.p2p.peers;
|
||||
|
||||
import com.google.inject.name.Named;
|
||||
import io.bisq.network.NetworkOptionKeys;
|
||||
import io.bisq.network.p2p.NodeAddress;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BanList {
|
||||
private static final Logger log = LoggerFactory.getLogger(BanList.class);
|
||||
private static List<NodeAddress> list = new ArrayList<>();
|
||||
|
||||
public static List<NodeAddress> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void setList(List<NodeAddress> list) {
|
||||
BanList.list = list;
|
||||
}
|
||||
|
||||
public static void add(NodeAddress onionAddress) {
|
||||
list.add(onionAddress);
|
||||
}
|
||||
|
||||
public static void remove(NodeAddress onionAddress) {
|
||||
list.add(onionAddress);
|
||||
}
|
||||
|
||||
public static boolean contains(NodeAddress nodeAddress) {
|
||||
public static boolean isBanned(NodeAddress nodeAddress) {
|
||||
return list.contains(nodeAddress);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public BanList(@Named(NetworkOptionKeys.BAN_LIST) String banList) {
|
||||
if (banList != null && !banList.isEmpty())
|
||||
BanList.list =Arrays.asList(StringUtils.deleteWhitespace(banList).split(",")).stream().map(NodeAddress::new).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,17 +6,15 @@ import io.bisq.network.p2p.NodeAddress;
|
|||
import io.bisq.network.p2p.network.NetworkNode;
|
||||
import io.bisq.network.p2p.storage.messages.BroadcastMessage;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
public class Broadcaster implements BroadcastHandler.ResultHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(Broadcaster.class);
|
||||
|
||||
private final NetworkNode networkNode;
|
||||
private final PeerManager peerManager;
|
||||
|
||||
private final Set<BroadcastHandler> broadcastHandlers = new CopyOnWriteArraySet<>();
|
||||
|
||||
|
||||
|
@ -24,6 +22,7 @@ public class Broadcaster implements BroadcastHandler.ResultHandler {
|
|||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public Broadcaster(NetworkNode networkNode, PeerManager peerManager) {
|
||||
this.networkNode = networkNode;
|
||||
this.peerManager = peerManager;
|
||||
|
@ -42,7 +41,7 @@ public class Broadcaster implements BroadcastHandler.ResultHandler {
|
|||
public void broadcast(BroadcastMessage message, @Nullable NodeAddress sender,
|
||||
@Nullable BroadcastHandler.Listener listener, boolean isDataOwner) {
|
||||
Log.traceCall("Sender=" + sender + "\n\t" +
|
||||
"Message=" + Utilities.toTruncatedString(message));
|
||||
"Message=" + Utilities.toTruncatedString(message));
|
||||
|
||||
BroadcastHandler broadcastHandler = new BroadcastHandler(networkNode, peerManager);
|
||||
broadcastHandler.broadcast(message, sender, this, listener, isDataOwner);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.bisq.network.p2p.peers;
|
||||
|
||||
import com.google.inject.name.Named;
|
||||
import io.bisq.common.Clock;
|
||||
import io.bisq.common.Timer;
|
||||
import io.bisq.common.UserThread;
|
||||
|
@ -7,13 +8,16 @@ import io.bisq.common.app.Log;
|
|||
import io.bisq.common.proto.persistable.PersistedDataHost;
|
||||
import io.bisq.common.proto.persistable.PersistenceProtoResolver;
|
||||
import io.bisq.common.storage.Storage;
|
||||
import io.bisq.network.NetworkOptionKeys;
|
||||
import io.bisq.network.p2p.NodeAddress;
|
||||
import io.bisq.network.p2p.network.*;
|
||||
import io.bisq.network.p2p.peers.peerexchange.Peer;
|
||||
import io.bisq.network.p2p.peers.peerexchange.PeerList;
|
||||
import io.bisq.network.p2p.seed.SeedNodesRepository;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
@ -54,10 +58,12 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
// Instance fields
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
private final NetworkNode networkNode;
|
||||
private final Clock clock;
|
||||
|
||||
private int maxConnections;
|
||||
private final Set<NodeAddress> seedNodeAddresses;
|
||||
|
||||
private final Storage<PeerList> storage;
|
||||
private final HashSet<Peer> persistedPeers = new HashSet<>();
|
||||
private final Set<Peer> reportedPeers = new HashSet<>();
|
||||
|
@ -66,7 +72,7 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
private Timer checkMaxConnectionsTimer;
|
||||
private boolean stopped;
|
||||
private boolean lostAllConnections;
|
||||
private int maxConnections;
|
||||
|
||||
private int minConnections;
|
||||
private int maxConnectionsPeer;
|
||||
private int maxConnectionsNonDirect;
|
||||
|
@ -77,16 +83,21 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public PeerManager(NetworkNode networkNode, int maxConnections, Set<NodeAddress> seedNodeAddresses,
|
||||
File storageDir, Clock clock, PersistenceProtoResolver persistenceProtoResolver) {
|
||||
setConnectionLimits(maxConnections);
|
||||
@Inject
|
||||
public PeerManager(NetworkNode networkNode,
|
||||
SeedNodesRepository seedNodesRepository,
|
||||
Clock clock,
|
||||
PersistenceProtoResolver persistenceProtoResolver,
|
||||
@Named(NetworkOptionKeys.MAX_CONNECTIONS) int maxConnections,
|
||||
@Named(Storage.STORAGE_DIR) File storageDir) {
|
||||
this.networkNode = networkNode;
|
||||
this.seedNodeAddresses = new HashSet<>(seedNodesRepository.getSeedNodeAddresses());
|
||||
this.clock = clock;
|
||||
// seedNodeAddresses can be empty (in case there is only 1 seed node, the seed node starting up has no other seed nodes)
|
||||
this.seedNodeAddresses = new HashSet<>(seedNodeAddresses);
|
||||
networkNode.addConnectionListener(this);
|
||||
storage = new Storage<>(storageDir, persistenceProtoResolver);
|
||||
|
||||
this.networkNode.addConnectionListener(this);
|
||||
|
||||
setConnectionLimits(maxConnections);
|
||||
|
||||
// we check if app was idle for more then 5 sec.
|
||||
listener = new Clock.Listener() {
|
||||
|
@ -158,15 +169,15 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
@Override
|
||||
public void onConnection(Connection connection) {
|
||||
Log.logIfStressTests("onConnection to peer " +
|
||||
(connection.getPeersNodeAddressOptional().isPresent() ? connection.getPeersNodeAddressOptional().get() : "PeersNode unknown") +
|
||||
" / No. of connections: " + networkNode.getAllConnections().size());
|
||||
(connection.getPeersNodeAddressOptional().isPresent() ? connection.getPeersNodeAddressOptional().get() : "PeersNode unknown") +
|
||||
" / No. of connections: " + networkNode.getAllConnections().size());
|
||||
|
||||
final boolean seedNode = isSeedNode(connection);
|
||||
|
||||
final Optional<NodeAddress> addressOptional = connection.getPeersNodeAddressOptional();
|
||||
log.debug("onConnection: peer = {}{}",
|
||||
(addressOptional.isPresent() ? addressOptional.get().getFullAddress() : "not known yet (connection id=" + connection.getUid() + ")"),
|
||||
seedNode ? " (SeedNode)" : "");
|
||||
(addressOptional.isPresent() ? addressOptional.get().getFullAddress() : "not known yet (connection id=" + connection.getUid() + ")"),
|
||||
seedNode ? " (SeedNode)" : "");
|
||||
|
||||
if (seedNode)
|
||||
connection.setPeerType(Connection.PeerType.SEED_NODE);
|
||||
|
@ -183,15 +194,15 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
@Override
|
||||
public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) {
|
||||
Log.logIfStressTests("onDisconnect of peer " +
|
||||
(connection.getPeersNodeAddressOptional().isPresent() ? connection.getPeersNodeAddressOptional().get() : "PeersNode unknown") +
|
||||
" / No. of connections: " + networkNode.getAllConnections().size() +
|
||||
" / closeConnectionReason: " + closeConnectionReason);
|
||||
(connection.getPeersNodeAddressOptional().isPresent() ? connection.getPeersNodeAddressOptional().get() : "PeersNode unknown") +
|
||||
" / No. of connections: " + networkNode.getAllConnections().size() +
|
||||
" / closeConnectionReason: " + closeConnectionReason);
|
||||
|
||||
final Optional<NodeAddress> addressOptional = connection.getPeersNodeAddressOptional();
|
||||
log.debug("onDisconnect: peer = {}{} / closeConnectionReason: {}",
|
||||
(addressOptional.isPresent() ? addressOptional.get().getFullAddress() : "not known yet (connection id=" + connection.getUid() + ")"),
|
||||
isSeedNode(connection) ? " (SeedNode)" : "",
|
||||
closeConnectionReason);
|
||||
(addressOptional.isPresent() ? addressOptional.get().getFullAddress() : "not known yet (connection id=" + connection.getUid() + ")"),
|
||||
isSeedNode(connection) ? " (SeedNode)" : "",
|
||||
closeConnectionReason);
|
||||
|
||||
handleConnectionFault(connection);
|
||||
|
||||
|
@ -211,7 +222,7 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
|
||||
public boolean isNodeBanned(CloseConnectionReason closeConnectionReason, Connection connection) {
|
||||
return closeConnectionReason == CloseConnectionReason.PEER_BANNED &&
|
||||
connection.getPeersNodeAddressOptional().isPresent();
|
||||
connection.getPeersNodeAddressOptional().isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -249,33 +260,33 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
|
||||
if (size > limit) {
|
||||
log.debug("We have too many connections open.\n\t" +
|
||||
"Lets try first to remove the inbound connections of type PEER.");
|
||||
"Lets try first to remove the inbound connections of type PEER.");
|
||||
List<Connection> candidates = allConnections.stream()
|
||||
.filter(e -> e instanceof InboundConnection)
|
||||
.filter(e -> e.getPeerType() == Connection.PeerType.PEER)
|
||||
.collect(Collectors.toList());
|
||||
.filter(e -> e instanceof InboundConnection)
|
||||
.filter(e -> e.getPeerType() == Connection.PeerType.PEER)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (candidates.size() == 0) {
|
||||
log.debug("No candidates found. We check if we exceed our " +
|
||||
"maxConnectionsPeer limit of {}", maxConnectionsPeer);
|
||||
"maxConnectionsPeer limit of {}", maxConnectionsPeer);
|
||||
if (size > maxConnectionsPeer) {
|
||||
log.debug("Lets try to remove ANY connection of type PEER.");
|
||||
candidates = allConnections.stream()
|
||||
.filter(e -> e.getPeerType() == Connection.PeerType.PEER)
|
||||
.collect(Collectors.toList());
|
||||
.filter(e -> e.getPeerType() == Connection.PeerType.PEER)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (candidates.size() == 0) {
|
||||
log.debug("No candidates found. We check if we exceed our " +
|
||||
"maxConnectionsNonDirect limit of {}", maxConnectionsNonDirect);
|
||||
"maxConnectionsNonDirect limit of {}", maxConnectionsNonDirect);
|
||||
if (size > maxConnectionsNonDirect) {
|
||||
log.debug("Lets try to remove any connection which is not of type DIRECT_MSG_PEER or INITIAL_DATA_REQUEST.");
|
||||
candidates = allConnections.stream()
|
||||
.filter(e -> e.getPeerType() != Connection.PeerType.DIRECT_MSG_PEER && e.getPeerType() != Connection.PeerType.INITIAL_DATA_REQUEST)
|
||||
.collect(Collectors.toList());
|
||||
.filter(e -> e.getPeerType() != Connection.PeerType.DIRECT_MSG_PEER && e.getPeerType() != Connection.PeerType.INITIAL_DATA_REQUEST)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (candidates.size() == 0) {
|
||||
log.debug("No candidates found. We check if we exceed our " +
|
||||
"maxConnectionsAbsolute limit of {}", maxConnectionsAbsolute);
|
||||
"maxConnectionsAbsolute limit of {}", maxConnectionsAbsolute);
|
||||
if (size > maxConnectionsAbsolute) {
|
||||
log.debug("Lets try to remove any connection.");
|
||||
candidates = allConnections.stream().collect(Collectors.toList());
|
||||
|
@ -296,8 +307,8 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
return true;
|
||||
} else {
|
||||
log.warn("No candidates found to remove (That case should not be possible as we use in the " +
|
||||
"last case all connections).\n\t" +
|
||||
"allConnections=", allConnections);
|
||||
"last case all connections).\n\t" +
|
||||
"allConnections=", allConnections);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
@ -309,15 +320,15 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
private void removeAnonymousPeers() {
|
||||
Log.traceCall();
|
||||
networkNode.getAllConnections().stream()
|
||||
.filter(connection -> !connection.hasPeersNodeAddress())
|
||||
.forEach(connection -> UserThread.runAfter(() -> {
|
||||
// We give 30 seconds delay and check again if still no address is set
|
||||
if (!connection.hasPeersNodeAddress() && !connection.isStopped()) {
|
||||
log.debug("We close the connection as the peer address is still unknown.\n\t" +
|
||||
"connection=" + connection);
|
||||
connection.shutDown(CloseConnectionReason.UNKNOWN_PEER_ADDRESS);
|
||||
}
|
||||
}, REMOVE_ANONYMOUS_PEER_SEC));
|
||||
.filter(connection -> !connection.hasPeersNodeAddress())
|
||||
.forEach(connection -> UserThread.runAfter(() -> {
|
||||
// We give 30 seconds delay and check again if still no address is set
|
||||
if (!connection.hasPeersNodeAddress() && !connection.isStopped()) {
|
||||
log.debug("We close the connection as the peer address is still unknown.\n\t" +
|
||||
"connection=" + connection);
|
||||
connection.shutDown(CloseConnectionReason.UNKNOWN_PEER_ADDRESS);
|
||||
}
|
||||
}, REMOVE_ANONYMOUS_PEER_SEC));
|
||||
}
|
||||
|
||||
private void removeSuperfluousSeedNodes() {
|
||||
|
@ -326,8 +337,8 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
Set<Connection> connections = networkNode.getConfirmedConnections();
|
||||
if (hasSufficientConnections()) {
|
||||
List<Connection> candidates = connections.stream()
|
||||
.filter(this::isSeedNode)
|
||||
.collect(Collectors.toList());
|
||||
.filter(this::isSeedNode)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (candidates.size() > 1) {
|
||||
candidates.sort((o1, o2) -> ((Long) o1.getStatistic().getLastActivityTimestamp()).compareTo(((Long) o2.getStatistic().getLastActivityTimestamp())));
|
||||
|
@ -356,7 +367,7 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
private Peer removeReportedPeer(NodeAddress nodeAddress) {
|
||||
List<Peer> reportedPeersClone = new ArrayList<>(reportedPeers);
|
||||
Optional<Peer> reportedPeerOptional = reportedPeersClone.stream()
|
||||
.filter(e -> e.getNodeAddress().equals(nodeAddress)).findAny();
|
||||
.filter(e -> e.getNodeAddress().equals(nodeAddress)).findAny();
|
||||
if (reportedPeerOptional.isPresent()) {
|
||||
Peer reportedPeer = reportedPeerOptional.get();
|
||||
removeReportedPeer(reportedPeer);
|
||||
|
@ -370,8 +381,8 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
Log.traceCall();
|
||||
List<Peer> reportedPeersClone = new ArrayList<>(reportedPeers);
|
||||
Set<Peer> reportedPeersToRemove = reportedPeersClone.stream()
|
||||
.filter(reportedPeer -> new Date().getTime() - reportedPeer.getDate().getTime() > MAX_AGE)
|
||||
.collect(Collectors.toSet());
|
||||
.filter(reportedPeer -> new Date().getTime() - reportedPeer.getDate().getTime() > MAX_AGE)
|
||||
.collect(Collectors.toSet());
|
||||
reportedPeersToRemove.forEach(this::removeReportedPeer);
|
||||
}
|
||||
|
||||
|
@ -389,8 +400,7 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
|
||||
persistedPeers.addAll(reportedPeersToAdd);
|
||||
purgePersistedPeersIfExceeds();
|
||||
if (storage != null)
|
||||
storage.queueUpForSave(new PeerList(new ArrayList<>(persistedPeers)), 2000);
|
||||
storage.queueUpForSave(new PeerList(new ArrayList<>(persistedPeers)), 2000);
|
||||
|
||||
printReportedPeers();
|
||||
} else {
|
||||
|
@ -407,7 +417,7 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
int limit = MAX_REPORTED_PEERS - maxConnectionsAbsolute;
|
||||
if (size > limit) {
|
||||
log.trace("We have already {} reported peers which exceeds our limit of {}." +
|
||||
"We remove random peers from the reported peers list.", size, limit);
|
||||
"We remove random peers from the reported peers list.", size, limit);
|
||||
int diff = size - limit;
|
||||
List<Peer> list = new ArrayList<>(reportedPeers);
|
||||
// we dont use sorting by lastActivityDate to keep it more random
|
||||
|
@ -425,7 +435,7 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
//noinspection ConstantConditions
|
||||
if (PRINT_REPORTED_PEERS_DETAILS) {
|
||||
StringBuilder result = new StringBuilder("\n\n------------------------------------------------------------\n" +
|
||||
"Collected reported peers:");
|
||||
"Collected reported peers:");
|
||||
List<Peer> reportedPeersClone = new ArrayList<>(reportedPeers);
|
||||
reportedPeersClone.stream().forEach(e -> result.append("\n").append(e));
|
||||
result.append("\n------------------------------------------------------------\n");
|
||||
|
@ -454,10 +464,7 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
private boolean removePersistedPeer(Peer persistedPeer) {
|
||||
if (persistedPeers.contains(persistedPeer)) {
|
||||
persistedPeers.remove(persistedPeer);
|
||||
|
||||
if (storage != null)
|
||||
storage.queueUpForSave(new PeerList(new ArrayList<>(persistedPeers)), 2000);
|
||||
|
||||
storage.queueUpForSave(new PeerList(new ArrayList<>(persistedPeers)), 2000);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -472,14 +479,14 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
|
||||
private Optional<Peer> getPersistedPeerOptional(NodeAddress nodeAddress) {
|
||||
return persistedPeers.stream()
|
||||
.filter(e -> e.getNodeAddress().equals(nodeAddress)).findAny();
|
||||
.filter(e -> e.getNodeAddress().equals(nodeAddress)).findAny();
|
||||
}
|
||||
|
||||
private void removeTooOldPersistedPeers() {
|
||||
Log.traceCall();
|
||||
Set<Peer> persistedPeersToRemove = persistedPeers.stream()
|
||||
.filter(reportedPeer -> new Date().getTime() - reportedPeer.getDate().getTime() > MAX_AGE)
|
||||
.collect(Collectors.toSet());
|
||||
.filter(reportedPeer -> new Date().getTime() - reportedPeer.getDate().getTime() > MAX_AGE)
|
||||
.collect(Collectors.toSet());
|
||||
persistedPeersToRemove.forEach(this::removePersistedPeer);
|
||||
}
|
||||
|
||||
|
@ -489,7 +496,7 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
int limit = MAX_PERSISTED_PEERS;
|
||||
if (size > limit) {
|
||||
log.trace("We have already {} persisted peers which exceeds our limit of {}." +
|
||||
"We remove random peers from the persisted peers list.", size, limit);
|
||||
"We remove random peers from the persisted peers list.", size, limit);
|
||||
int diff = size - limit;
|
||||
List<Peer> list = new ArrayList<>(persistedPeers);
|
||||
// we dont use sorting by lastActivityDate to avoid attack vectors and keep it more random
|
||||
|
@ -515,7 +522,7 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
return networkNode.getNodeAddressesOfConfirmedConnections().size() >= minConnections;
|
||||
}
|
||||
|
||||
public boolean isSeedNode(Peer reportedPeer) {
|
||||
private boolean isSeedNode(Peer reportedPeer) {
|
||||
return seedNodeAddresses.contains(reportedPeer.getNodeAddress());
|
||||
}
|
||||
|
||||
|
@ -577,17 +584,17 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
|
||||
public void shutDownConnection(NodeAddress peersNodeAddress, CloseConnectionReason closeConnectionReason) {
|
||||
networkNode.getAllConnections().stream()
|
||||
.filter(connection -> connection.getPeersNodeAddressOptional().isPresent() &&
|
||||
connection.getPeersNodeAddressOptional().get().equals(peersNodeAddress) &&
|
||||
connection.getPeerType() != Connection.PeerType.DIRECT_MSG_PEER)
|
||||
.findAny()
|
||||
.ifPresent(connection -> connection.shutDown(closeConnectionReason));
|
||||
.filter(connection -> connection.getPeersNodeAddressOptional().isPresent() &&
|
||||
connection.getPeersNodeAddressOptional().get().equals(peersNodeAddress) &&
|
||||
connection.getPeerType() != Connection.PeerType.DIRECT_MSG_PEER)
|
||||
.findAny()
|
||||
.ifPresent(connection -> connection.shutDown(closeConnectionReason));
|
||||
}
|
||||
|
||||
public HashSet<Peer> getConnectedNonSeedNodeReportedPeers(NodeAddress excludedNodeAddress) {
|
||||
return new HashSet<>(getConnectedNonSeedNodeReportedPeers().stream()
|
||||
.filter(e -> !e.getNodeAddress().equals(excludedNodeAddress))
|
||||
.collect(Collectors.toSet()));
|
||||
.filter(e -> !e.getNodeAddress().equals(excludedNodeAddress))
|
||||
.collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -598,14 +605,14 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
// networkNode.getConfirmedConnections includes:
|
||||
// filter(connection -> connection.getPeersNodeAddressOptional().isPresent())
|
||||
return networkNode.getConfirmedConnections().stream()
|
||||
.map(c -> new Peer(c.getPeersNodeAddressOptional().get()))
|
||||
.collect(Collectors.toSet());
|
||||
.map(c -> new Peer(c.getPeersNodeAddressOptional().get()))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private HashSet<Peer> getConnectedNonSeedNodeReportedPeers() {
|
||||
return new HashSet<>(getConnectedReportedPeers().stream()
|
||||
.filter(e -> !isSeedNode(e))
|
||||
.collect(Collectors.toSet()));
|
||||
.filter(e -> !isSeedNode(e))
|
||||
.collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
private void stopCheckMaxConnectionsTimer() {
|
||||
|
@ -618,9 +625,9 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
|
|||
private void printConnectedPeers() {
|
||||
if (!networkNode.getConfirmedConnections().isEmpty()) {
|
||||
StringBuilder result = new StringBuilder("\n\n------------------------------------------------------------\n" +
|
||||
"Connected peers for node " + networkNode.getNodeAddress() + ":");
|
||||
"Connected peers for node " + networkNode.getNodeAddress() + ":");
|
||||
networkNode.getConfirmedConnections().stream().forEach(e -> result.append("\n")
|
||||
.append(e.getPeersNodeAddressOptional().get()).append(" ").append(e.getPeerType()));
|
||||
.append(e.getPeersNodeAddressOptional().get()).append(" ").append(e.getPeerType()));
|
||||
result.append("\n------------------------------------------------------------\n");
|
||||
log.debug(result.toString());
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ import java.util.stream.Collectors;
|
|||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
public class RequestDataHandler implements MessageListener {
|
||||
class RequestDataHandler implements MessageListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(RequestDataHandler.class);
|
||||
|
||||
private static final long TIME_OUT_SEC = 60;
|
||||
|
@ -65,14 +65,15 @@ public class RequestDataHandler implements MessageListener {
|
|||
private Timer timeoutTimer;
|
||||
private final int nonce = new Random().nextInt();
|
||||
private boolean stopped;
|
||||
private Connection connection;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public RequestDataHandler(NetworkNode networkNode, P2PDataStorage dataStorage, PeerManager peerManager,
|
||||
public RequestDataHandler(NetworkNode networkNode,
|
||||
P2PDataStorage dataStorage,
|
||||
PeerManager peerManager,
|
||||
Listener listener) {
|
||||
this.networkNode = networkNode;
|
||||
this.dataStorage = dataStorage;
|
||||
|
@ -135,7 +136,6 @@ public class RequestDataHandler implements MessageListener {
|
|||
@Override
|
||||
public void onSuccess(Connection connection) {
|
||||
if (!stopped) {
|
||||
RequestDataHandler.this.connection = connection;
|
||||
log.trace("Send " + getDataRequest + " to " + nodeAddress + " succeeded.");
|
||||
} else {
|
||||
log.trace("We have stopped already. We ignore that networkNode.sendMessage.onSuccess call." +
|
||||
|
|
|
@ -9,11 +9,13 @@ import io.bisq.network.p2p.network.*;
|
|||
import io.bisq.network.p2p.peers.PeerManager;
|
||||
import io.bisq.network.p2p.peers.getdata.messages.GetDataRequest;
|
||||
import io.bisq.network.p2p.peers.peerexchange.Peer;
|
||||
import io.bisq.network.p2p.seed.SeedNodesRepository;
|
||||
import io.bisq.network.p2p.storage.P2PDataStorage;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -26,7 +28,6 @@ public class RequestDataManager implements MessageListener, ConnectionListener,
|
|||
private static final long CLEANUP_TIMER = 120;
|
||||
private boolean isPreliminaryDataRequest = true;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Listener
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -51,8 +52,8 @@ public class RequestDataManager implements MessageListener, ConnectionListener,
|
|||
private final NetworkNode networkNode;
|
||||
private final P2PDataStorage dataStorage;
|
||||
private final PeerManager peerManager;
|
||||
private final Collection<NodeAddress> seedNodeAddresses;
|
||||
private final Listener listener;
|
||||
private final Set<NodeAddress> seedNodeAddresses;
|
||||
private Listener listener;
|
||||
|
||||
private final Map<NodeAddress, RequestDataHandler> handlerMap = new HashMap<>();
|
||||
private final Map<String, GetDataRequestHandler> getDataRequestHandlers = new HashMap<>();
|
||||
|
@ -66,18 +67,20 @@ public class RequestDataManager implements MessageListener, ConnectionListener,
|
|||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public RequestDataManager(NetworkNode networkNode, P2PDataStorage dataStorage, PeerManager peerManager,
|
||||
Set<NodeAddress> seedNodeAddresses, Listener listener) {
|
||||
@Inject
|
||||
public RequestDataManager(NetworkNode networkNode,
|
||||
SeedNodesRepository seedNodesRepository,
|
||||
P2PDataStorage dataStorage,
|
||||
PeerManager peerManager) {
|
||||
this.networkNode = networkNode;
|
||||
this.dataStorage = dataStorage;
|
||||
this.peerManager = peerManager;
|
||||
// seedNodeAddresses can be empty (in case there is only 1 seed node, the seed node starting up has no other seed nodes)
|
||||
this.seedNodeAddresses = new HashSet<>(seedNodeAddresses);
|
||||
this.listener = listener;
|
||||
|
||||
networkNode.addMessageListener(this);
|
||||
networkNode.addConnectionListener(this);
|
||||
peerManager.addListener(this);
|
||||
this.networkNode.addMessageListener(this);
|
||||
this.networkNode.addConnectionListener(this);
|
||||
this.peerManager.addListener(this);
|
||||
|
||||
this.seedNodeAddresses = new HashSet<>(seedNodesRepository.getSeedNodeAddresses());
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
|
@ -95,6 +98,10 @@ public class RequestDataManager implements MessageListener, ConnectionListener,
|
|||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void addListener(Listener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public boolean requestPreliminaryData() {
|
||||
Log.traceCall();
|
||||
ArrayList<NodeAddress> nodeAddresses = new ArrayList<>(seedNodeAddresses);
|
||||
|
@ -201,30 +208,30 @@ public class RequestDataManager implements MessageListener, ConnectionListener,
|
|||
final String uid = connection.getUid();
|
||||
if (!getDataRequestHandlers.containsKey(uid)) {
|
||||
GetDataRequestHandler getDataRequestHandler = new GetDataRequestHandler(networkNode, dataStorage,
|
||||
new GetDataRequestHandler.Listener() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
getDataRequestHandlers.remove(uid);
|
||||
log.trace("requestDataHandshake completed.\n\tConnection={}", connection);
|
||||
}
|
||||
new GetDataRequestHandler.Listener() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
getDataRequestHandlers.remove(uid);
|
||||
log.trace("requestDataHandshake completed.\n\tConnection={}", connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFault(String errorMessage, @Nullable Connection connection) {
|
||||
getDataRequestHandlers.remove(uid);
|
||||
if (!stopped) {
|
||||
log.trace("GetDataRequestHandler failed.\n\tConnection={}\n\t" +
|
||||
"ErrorMessage={}", connection, errorMessage);
|
||||
peerManager.handleConnectionFault(connection);
|
||||
} else {
|
||||
log.warn("We have stopped already. We ignore that getDataRequestHandler.handle.onFault call.");
|
||||
}
|
||||
@Override
|
||||
public void onFault(String errorMessage, @Nullable Connection connection) {
|
||||
getDataRequestHandlers.remove(uid);
|
||||
if (!stopped) {
|
||||
log.trace("GetDataRequestHandler failed.\n\tConnection={}\n\t" +
|
||||
"ErrorMessage={}", connection, errorMessage);
|
||||
peerManager.handleConnectionFault(connection);
|
||||
} else {
|
||||
log.warn("We have stopped already. We ignore that getDataRequestHandler.handle.onFault call.");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
getDataRequestHandlers.put(uid, getDataRequestHandler);
|
||||
getDataRequestHandler.handle((GetDataRequest) networkEnvelop, connection);
|
||||
} else {
|
||||
log.warn("We have already a GetDataRequestHandler for that connection started. " +
|
||||
"We start a cleanup timer if the handler has not closed by itself in between 2 minutes.");
|
||||
"We start a cleanup timer if the handler has not closed by itself in between 2 minutes.");
|
||||
|
||||
UserThread.runAfter(() -> {
|
||||
if (getDataRequestHandlers.containsKey(uid)) {
|
||||
|
@ -249,68 +256,68 @@ public class RequestDataManager implements MessageListener, ConnectionListener,
|
|||
if (!stopped) {
|
||||
if (!handlerMap.containsKey(nodeAddress)) {
|
||||
RequestDataHandler requestDataHandler = new RequestDataHandler(networkNode, dataStorage, peerManager,
|
||||
new RequestDataHandler.Listener() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
log.trace("RequestDataHandshake of outbound connection complete. nodeAddress={}",
|
||||
nodeAddress);
|
||||
stopRetryTimer();
|
||||
new RequestDataHandler.Listener() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
log.trace("RequestDataHandshake of outbound connection complete. nodeAddress={}",
|
||||
nodeAddress);
|
||||
stopRetryTimer();
|
||||
|
||||
// need to remove before listeners are notified as they cause the update call
|
||||
handlerMap.remove(nodeAddress);
|
||||
// need to remove before listeners are notified as they cause the update call
|
||||
handlerMap.remove(nodeAddress);
|
||||
|
||||
// 1. We get a response from requestPreliminaryData
|
||||
// 1. We get a response from requestPreliminaryData
|
||||
if (!nodeAddressOfPreliminaryDataRequest.isPresent()) {
|
||||
nodeAddressOfPreliminaryDataRequest = Optional.of(nodeAddress);
|
||||
listener.onPreliminaryDataReceived();
|
||||
}
|
||||
|
||||
// 2. Later we get a response from requestUpdatesData
|
||||
if (dataUpdateRequested) {
|
||||
dataUpdateRequested = false;
|
||||
listener.onUpdatedDataReceived();
|
||||
}
|
||||
|
||||
listener.onDataReceived();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFault(String errorMessage, @Nullable Connection connection) {
|
||||
log.trace("requestDataHandshake with outbound connection failed.\n\tnodeAddress={}\n\t" +
|
||||
"ErrorMessage={}", nodeAddress, errorMessage);
|
||||
|
||||
peerManager.handleConnectionFault(nodeAddress);
|
||||
handlerMap.remove(nodeAddress);
|
||||
|
||||
if (!remainingNodeAddresses.isEmpty()) {
|
||||
log.debug("There are remaining nodes available for requesting data. " +
|
||||
"We will try requestDataFromPeers again.");
|
||||
NodeAddress nextCandidate = remainingNodeAddresses.get(0);
|
||||
remainingNodeAddresses.remove(nextCandidate);
|
||||
requestData(nextCandidate, remainingNodeAddresses);
|
||||
} else {
|
||||
log.debug("There is no remaining node available for requesting data. " +
|
||||
"That is expected if no other node is online.\n\t" +
|
||||
"We will try to use reported peers (if no available we use persisted peers) " +
|
||||
"and try again to request data from our seed nodes after a random pause.");
|
||||
|
||||
// Notify listeners
|
||||
if (!nodeAddressOfPreliminaryDataRequest.isPresent()) {
|
||||
nodeAddressOfPreliminaryDataRequest = Optional.of(nodeAddress);
|
||||
listener.onPreliminaryDataReceived();
|
||||
if (peerManager.isSeedNode(nodeAddress))
|
||||
listener.onNoSeedNodeAvailable();
|
||||
else
|
||||
listener.onNoPeersAvailable();
|
||||
}
|
||||
|
||||
// 2. Later we get a response from requestUpdatesData
|
||||
if (dataUpdateRequested) {
|
||||
dataUpdateRequested = false;
|
||||
listener.onUpdatedDataReceived();
|
||||
}
|
||||
|
||||
listener.onDataReceived();
|
||||
restart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFault(String errorMessage, @Nullable Connection connection) {
|
||||
log.trace("requestDataHandshake with outbound connection failed.\n\tnodeAddress={}\n\t" +
|
||||
"ErrorMessage={}", nodeAddress, errorMessage);
|
||||
|
||||
peerManager.handleConnectionFault(nodeAddress);
|
||||
handlerMap.remove(nodeAddress);
|
||||
|
||||
if (!remainingNodeAddresses.isEmpty()) {
|
||||
log.debug("There are remaining nodes available for requesting data. " +
|
||||
"We will try requestDataFromPeers again.");
|
||||
NodeAddress nextCandidate = remainingNodeAddresses.get(0);
|
||||
remainingNodeAddresses.remove(nextCandidate);
|
||||
requestData(nextCandidate, remainingNodeAddresses);
|
||||
} else {
|
||||
log.debug("There is no remaining node available for requesting data. " +
|
||||
"That is expected if no other node is online.\n\t" +
|
||||
"We will try to use reported peers (if no available we use persisted peers) " +
|
||||
"and try again to request data from our seed nodes after a random pause.");
|
||||
|
||||
// Notify listeners
|
||||
if (!nodeAddressOfPreliminaryDataRequest.isPresent()) {
|
||||
if (peerManager.isSeedNode(nodeAddress))
|
||||
listener.onNoSeedNodeAvailable();
|
||||
else
|
||||
listener.onNoPeersAvailable();
|
||||
}
|
||||
|
||||
restart();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
handlerMap.put(nodeAddress, requestDataHandler);
|
||||
requestDataHandler.requestData(nodeAddress, isPreliminaryDataRequest);
|
||||
} else {
|
||||
log.warn("We have started already a requestDataHandshake to peer. nodeAddress=" + nodeAddress + "\n" +
|
||||
"We start a cleanup timer if the handler has not closed by itself in between 2 minutes.");
|
||||
"We start a cleanup timer if the handler has not closed by itself in between 2 minutes.");
|
||||
|
||||
UserThread.runAfter(() -> {
|
||||
if (handlerMap.containsKey(nodeAddress)) {
|
||||
|
@ -334,54 +341,54 @@ public class RequestDataManager implements MessageListener, ConnectionListener,
|
|||
Log.traceCall();
|
||||
if (retryTimer == null) {
|
||||
retryTimer = UserThread.runAfter(() -> {
|
||||
log.trace("retryTimer called");
|
||||
stopped = false;
|
||||
log.trace("retryTimer called");
|
||||
stopped = false;
|
||||
|
||||
stopRetryTimer();
|
||||
stopRetryTimer();
|
||||
|
||||
// We create a new list of candidates
|
||||
// 1. shuffled seedNodes
|
||||
// 2. reported peers sorted by last activity date
|
||||
// 3. Add as last persisted peers sorted by last activity date
|
||||
List<NodeAddress> list = getFilteredList(new ArrayList<>(seedNodeAddresses), new ArrayList<>());
|
||||
Collections.shuffle(list);
|
||||
// We create a new list of candidates
|
||||
// 1. shuffled seedNodes
|
||||
// 2. reported peers sorted by last activity date
|
||||
// 3. Add as last persisted peers sorted by last activity date
|
||||
List<NodeAddress> list = getFilteredList(new ArrayList<>(seedNodeAddresses), new ArrayList<>());
|
||||
Collections.shuffle(list);
|
||||
|
||||
List<NodeAddress> filteredReportedPeers = getFilteredNonSeedNodeList(getSortedNodeAddresses(peerManager.getReportedPeers()), list);
|
||||
list.addAll(filteredReportedPeers);
|
||||
List<NodeAddress> filteredReportedPeers = getFilteredNonSeedNodeList(getSortedNodeAddresses(peerManager.getReportedPeers()), list);
|
||||
list.addAll(filteredReportedPeers);
|
||||
|
||||
List<NodeAddress> filteredPersistedPeers = getFilteredNonSeedNodeList(getSortedNodeAddresses(peerManager.getPersistedPeers()), list);
|
||||
list.addAll(filteredPersistedPeers);
|
||||
List<NodeAddress> filteredPersistedPeers = getFilteredNonSeedNodeList(getSortedNodeAddresses(peerManager.getPersistedPeers()), list);
|
||||
list.addAll(filteredPersistedPeers);
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
NodeAddress nextCandidate = list.get(0);
|
||||
list.remove(nextCandidate);
|
||||
requestData(nextCandidate, list);
|
||||
}
|
||||
},
|
||||
RETRY_DELAY_SEC);
|
||||
if (!list.isEmpty()) {
|
||||
NodeAddress nextCandidate = list.get(0);
|
||||
list.remove(nextCandidate);
|
||||
requestData(nextCandidate, list);
|
||||
}
|
||||
},
|
||||
RETRY_DELAY_SEC);
|
||||
}
|
||||
}
|
||||
|
||||
private List<NodeAddress> getSortedNodeAddresses(Collection<Peer> collection) {
|
||||
return collection.stream()
|
||||
.collect(Collectors.toList())
|
||||
.stream()
|
||||
.sorted((o1, o2) -> o2.getDate().compareTo(o1.getDate()))
|
||||
.map(Peer::getNodeAddress)
|
||||
.collect(Collectors.toList());
|
||||
.collect(Collectors.toList())
|
||||
.stream()
|
||||
.sorted((o1, o2) -> o2.getDate().compareTo(o1.getDate()))
|
||||
.map(Peer::getNodeAddress)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<NodeAddress> getFilteredList(Collection<NodeAddress> collection, List<NodeAddress> list) {
|
||||
return collection.stream()
|
||||
.filter(e -> !list.contains(e) &&
|
||||
!peerManager.isSelf(e))
|
||||
.collect(Collectors.toList());
|
||||
.filter(e -> !list.contains(e) &&
|
||||
!peerManager.isSelf(e))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<NodeAddress> getFilteredNonSeedNodeList(Collection<NodeAddress> collection, List<NodeAddress> list) {
|
||||
return getFilteredList(collection, list).stream()
|
||||
.filter(e -> !peerManager.isSeedNode(e))
|
||||
.collect(Collectors.toList());
|
||||
.filter(e -> !peerManager.isSeedNode(e))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void stopRetryTimer() {
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.jetbrains.annotations.NotNull;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
@ -28,6 +29,7 @@ public class KeepAliveManager implements MessageListener, ConnectionListener, Pe
|
|||
private final NetworkNode networkNode;
|
||||
private final PeerManager peerManager;
|
||||
private final Map<String, KeepAliveHandler> handlerMap = new HashMap<>();
|
||||
|
||||
private boolean stopped;
|
||||
private Timer keepAliveTimer;
|
||||
|
||||
|
@ -36,13 +38,15 @@ public class KeepAliveManager implements MessageListener, ConnectionListener, Pe
|
|||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public KeepAliveManager(NetworkNode networkNode, PeerManager peerManager) {
|
||||
@Inject
|
||||
public KeepAliveManager(NetworkNode networkNode,
|
||||
PeerManager peerManager) {
|
||||
this.networkNode = networkNode;
|
||||
this.peerManager = peerManager;
|
||||
|
||||
networkNode.addMessageListener(this);
|
||||
networkNode.addConnectionListener(this);
|
||||
peerManager.addListener(this);
|
||||
this.networkNode.addMessageListener(this);
|
||||
this.networkNode.addConnectionListener(this);
|
||||
this.peerManager.addListener(this);
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
|
@ -91,8 +95,8 @@ public class KeepAliveManager implements MessageListener, ConnectionListener, Pe
|
|||
public void onFailure(@NotNull Throwable throwable) {
|
||||
if (!stopped) {
|
||||
String errorMessage = "Sending pong to " + connection +
|
||||
" failed. That is expected if the peer is offline. pong=" + pong + "." +
|
||||
"Exception: " + throwable.getMessage();
|
||||
" failed. That is expected if the peer is offline. pong=" + pong + "." +
|
||||
"Exception: " + throwable.getMessage();
|
||||
log.debug(errorMessage);
|
||||
peerManager.handleConnectionFault(connection);
|
||||
} else {
|
||||
|
@ -174,36 +178,36 @@ public class KeepAliveManager implements MessageListener, ConnectionListener, Pe
|
|||
if (!stopped) {
|
||||
Log.traceCall();
|
||||
networkNode.getConfirmedConnections().stream()
|
||||
.filter(connection -> connection instanceof OutboundConnection &&
|
||||
connection.getStatistic().getLastActivityAge() > LAST_ACTIVITY_AGE_MS)
|
||||
.forEach(connection -> {
|
||||
final String uid = connection.getUid();
|
||||
if (!handlerMap.containsKey(uid)) {
|
||||
KeepAliveHandler keepAliveHandler = new KeepAliveHandler(networkNode, peerManager, new KeepAliveHandler.Listener() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
handlerMap.remove(uid);
|
||||
}
|
||||
.filter(connection -> connection instanceof OutboundConnection &&
|
||||
connection.getStatistic().getLastActivityAge() > LAST_ACTIVITY_AGE_MS)
|
||||
.forEach(connection -> {
|
||||
final String uid = connection.getUid();
|
||||
if (!handlerMap.containsKey(uid)) {
|
||||
KeepAliveHandler keepAliveHandler = new KeepAliveHandler(networkNode, peerManager, new KeepAliveHandler.Listener() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
handlerMap.remove(uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFault(String errorMessage) {
|
||||
handlerMap.remove(uid);
|
||||
}
|
||||
});
|
||||
handlerMap.put(uid, keepAliveHandler);
|
||||
keepAliveHandler.sendPingAfterRandomDelay(connection);
|
||||
} else {
|
||||
// TODO check if this situation causes any issues
|
||||
log.debug("Connection with id {} has not completed and is still in our map. " +
|
||||
"We will try to ping that peer at the next schedule.", uid);
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onFault(String errorMessage) {
|
||||
handlerMap.remove(uid);
|
||||
}
|
||||
});
|
||||
handlerMap.put(uid, keepAliveHandler);
|
||||
keepAliveHandler.sendPingAfterRandomDelay(connection);
|
||||
} else {
|
||||
// TODO check if this situation causes any issues
|
||||
log.debug("Connection with id {} has not completed and is still in our map. " +
|
||||
"We will try to ping that peer at the next schedule.", uid);
|
||||
}
|
||||
});
|
||||
|
||||
int size = handlerMap.size();
|
||||
log.debug("handlerMap size=" + size);
|
||||
if (size > peerManager.getMaxConnections())
|
||||
log.warn("Seems we didn't clean up out map correctly.\n" +
|
||||
"handlerMap size={}, peerManager.getMaxConnections()={}", size, peerManager.getMaxConnections());
|
||||
"handlerMap size={}, peerManager.getMaxConnections()={}", size, peerManager.getMaxConnections());
|
||||
} else {
|
||||
log.warn("We have stopped already. We ignore that keepAlive call.");
|
||||
}
|
||||
|
|
|
@ -9,10 +9,12 @@ import io.bisq.network.p2p.NodeAddress;
|
|||
import io.bisq.network.p2p.network.*;
|
||||
import io.bisq.network.p2p.peers.PeerManager;
|
||||
import io.bisq.network.p2p.peers.peerexchange.messages.GetPeersRequest;
|
||||
import io.bisq.network.p2p.seed.SeedNodesRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -26,8 +28,10 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener,
|
|||
|
||||
private final NetworkNode networkNode;
|
||||
private final PeerManager peerManager;
|
||||
|
||||
private final Set<NodeAddress> seedNodeAddresses;
|
||||
private final Map<NodeAddress, PeerExchangeHandler> handlerMap = new HashMap<>();
|
||||
|
||||
private Timer retryTimer, periodicTimer;
|
||||
private boolean stopped;
|
||||
|
||||
|
@ -36,18 +40,20 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener,
|
|||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public PeerExchangeManager(NetworkNode networkNode, PeerManager peerManager, Set<NodeAddress> seedNodeAddresses) {
|
||||
@Inject
|
||||
public PeerExchangeManager(NetworkNode networkNode,
|
||||
SeedNodesRepository seedNodesRepository,
|
||||
PeerManager peerManager) {
|
||||
this.networkNode = networkNode;
|
||||
this.peerManager = peerManager;
|
||||
// seedNodeAddresses can be empty (in case there is only 1 seed node, the seed node starting up has no other seed nodes)
|
||||
this.seedNodeAddresses = new HashSet<>(seedNodeAddresses);
|
||||
|
||||
networkNode.addMessageListener(this);
|
||||
networkNode.addConnectionListener(this);
|
||||
peerManager.addListener(this);
|
||||
this.networkNode.addMessageListener(this);
|
||||
this.networkNode.addConnectionListener(this);
|
||||
this.peerManager.addListener(this);
|
||||
|
||||
this.seedNodeAddresses = new HashSet<>(seedNodesRepository.getSeedNodeAddresses());
|
||||
}
|
||||
|
||||
|
||||
public void shutDown() {
|
||||
Log.traceCall();
|
||||
stopped = true;
|
||||
|
@ -152,20 +158,20 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener,
|
|||
connection.setPeerType(Connection.PeerType.SEED_NODE);
|
||||
|
||||
GetPeersRequestHandler getPeersRequestHandler = new GetPeersRequestHandler(networkNode,
|
||||
peerManager,
|
||||
new GetPeersRequestHandler.Listener() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
log.trace("PeerExchangeHandshake completed.\n\tConnection={}", connection);
|
||||
}
|
||||
peerManager,
|
||||
new GetPeersRequestHandler.Listener() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
log.trace("PeerExchangeHandshake completed.\n\tConnection={}", connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFault(String errorMessage, Connection connection) {
|
||||
log.trace("PeerExchangeHandshake failed.\n\terrorMessage={}\n\t" +
|
||||
"connection={}", errorMessage, connection);
|
||||
peerManager.handleConnectionFault(connection);
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onFault(String errorMessage, Connection connection) {
|
||||
log.trace("PeerExchangeHandshake failed.\n\terrorMessage={}\n\t" +
|
||||
"connection={}", errorMessage, connection);
|
||||
peerManager.handleConnectionFault(connection);
|
||||
}
|
||||
});
|
||||
getPeersRequestHandler.handle((GetPeersRequest) networkEnvelop, connection);
|
||||
} else {
|
||||
log.warn("We have stopped already. We ignore that onMessage call.");
|
||||
|
@ -183,56 +189,56 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener,
|
|||
if (!stopped) {
|
||||
if (!handlerMap.containsKey(nodeAddress)) {
|
||||
PeerExchangeHandler peerExchangeHandler = new PeerExchangeHandler(networkNode,
|
||||
peerManager,
|
||||
new PeerExchangeHandler.Listener() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
log.trace("PeerExchangeHandshake of outbound connection complete. nodeAddress={}", nodeAddress);
|
||||
handlerMap.remove(nodeAddress);
|
||||
requestWithAvailablePeers();
|
||||
}
|
||||
peerManager,
|
||||
new PeerExchangeHandler.Listener() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
log.trace("PeerExchangeHandshake of outbound connection complete. nodeAddress={}", nodeAddress);
|
||||
handlerMap.remove(nodeAddress);
|
||||
requestWithAvailablePeers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFault(String errorMessage, @Nullable Connection connection) {
|
||||
log.trace("PeerExchangeHandshake of outbound connection failed.\n\terrorMessage={}\n\t" +
|
||||
"nodeAddress={}", errorMessage, nodeAddress);
|
||||
@Override
|
||||
public void onFault(String errorMessage, @Nullable Connection connection) {
|
||||
log.trace("PeerExchangeHandshake of outbound connection failed.\n\terrorMessage={}\n\t" +
|
||||
"nodeAddress={}", errorMessage, nodeAddress);
|
||||
|
||||
peerManager.handleConnectionFault(nodeAddress);
|
||||
handlerMap.remove(nodeAddress);
|
||||
if (!remainingNodeAddresses.isEmpty()) {
|
||||
if (!peerManager.hasSufficientConnections()) {
|
||||
log.debug("There are remaining nodes available for requesting peers. " +
|
||||
"We will try getReportedPeers again.");
|
||||
NodeAddress nextCandidate = remainingNodeAddresses.get(new Random().nextInt(remainingNodeAddresses.size()));
|
||||
remainingNodeAddresses.remove(nextCandidate);
|
||||
requestReportedPeers(nextCandidate, remainingNodeAddresses);
|
||||
} else {
|
||||
// That path will rarely be reached
|
||||
log.debug("We have already sufficient connections.");
|
||||
}
|
||||
peerManager.handleConnectionFault(nodeAddress);
|
||||
handlerMap.remove(nodeAddress);
|
||||
if (!remainingNodeAddresses.isEmpty()) {
|
||||
if (!peerManager.hasSufficientConnections()) {
|
||||
log.debug("There are remaining nodes available for requesting peers. " +
|
||||
"We will try getReportedPeers again.");
|
||||
NodeAddress nextCandidate = remainingNodeAddresses.get(new Random().nextInt(remainingNodeAddresses.size()));
|
||||
remainingNodeAddresses.remove(nextCandidate);
|
||||
requestReportedPeers(nextCandidate, remainingNodeAddresses);
|
||||
} else {
|
||||
log.debug("There is no remaining node available for requesting peers. " +
|
||||
"That is expected if no other node is online.\n\t" +
|
||||
"We will try again after a pause.");
|
||||
if (retryTimer == null)
|
||||
retryTimer = UserThread.runAfter(() -> {
|
||||
if (!stopped) {
|
||||
log.trace("retryTimer called from requestReportedPeers code path");
|
||||
stopRetryTimer();
|
||||
requestWithAvailablePeers();
|
||||
} else {
|
||||
stopRetryTimer();
|
||||
log.warn("We have stopped already. We ignore that retryTimer.run call.");
|
||||
}
|
||||
}, RETRY_DELAY_SEC);
|
||||
// That path will rarely be reached
|
||||
log.debug("We have already sufficient connections.");
|
||||
}
|
||||
} else {
|
||||
log.debug("There is no remaining node available for requesting peers. " +
|
||||
"That is expected if no other node is online.\n\t" +
|
||||
"We will try again after a pause.");
|
||||
if (retryTimer == null)
|
||||
retryTimer = UserThread.runAfter(() -> {
|
||||
if (!stopped) {
|
||||
log.trace("retryTimer called from requestReportedPeers code path");
|
||||
stopRetryTimer();
|
||||
requestWithAvailablePeers();
|
||||
} else {
|
||||
stopRetryTimer();
|
||||
log.warn("We have stopped already. We ignore that retryTimer.run call.");
|
||||
}
|
||||
}, RETRY_DELAY_SEC);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
handlerMap.put(nodeAddress, peerExchangeHandler);
|
||||
peerExchangeHandler.sendGetPeersRequestAfterRandomDelay(nodeAddress);
|
||||
} else {
|
||||
log.trace("We have started already a peerExchangeHandler. " +
|
||||
"We ignore that call. nodeAddress=" + nodeAddress);
|
||||
"We ignore that call. nodeAddress=" + nodeAddress);
|
||||
}
|
||||
} else {
|
||||
log.trace("We have stopped already. We ignore that requestReportedPeers call.");
|
||||
|
@ -244,8 +250,8 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener,
|
|||
if (!stopped) {
|
||||
if (!peerManager.hasSufficientConnections()) {
|
||||
// We create a new list of not connected candidates
|
||||
// 1. shuffled reported peers
|
||||
// 2. shuffled persisted peers
|
||||
// 1. shuffled reported peers
|
||||
// 2. shuffled persisted peers
|
||||
// 3. Add as last shuffled seedNodes (least priority)
|
||||
List<NodeAddress> list = getFilteredNonSeedNodeList(getNodeAddresses(peerManager.getReportedPeers()), new ArrayList<>());
|
||||
Collections.shuffle(list);
|
||||
|
@ -296,7 +302,7 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener,
|
|||
stopped = false;
|
||||
if (periodicTimer == null)
|
||||
periodicTimer = UserThread.runPeriodically(this::requestWithAvailablePeers,
|
||||
REQUEST_PERIODICALLY_INTERVAL_SEC, TimeUnit.SECONDS);
|
||||
REQUEST_PERIODICALLY_INTERVAL_SEC, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void restart() {
|
||||
|
@ -316,22 +322,22 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener,
|
|||
|
||||
private List<NodeAddress> getNodeAddresses(Collection<Peer> collection) {
|
||||
return collection.stream()
|
||||
.map(Peer::getNodeAddress)
|
||||
.collect(Collectors.toList());
|
||||
.map(Peer::getNodeAddress)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<NodeAddress> getFilteredList(Collection<NodeAddress> collection, List<NodeAddress> list) {
|
||||
return collection.stream()
|
||||
.filter(e -> !list.contains(e) &&
|
||||
!peerManager.isSelf(e) &&
|
||||
!peerManager.isConfirmed(e))
|
||||
.collect(Collectors.toList());
|
||||
.filter(e -> !list.contains(e) &&
|
||||
!peerManager.isSelf(e) &&
|
||||
!peerManager.isConfirmed(e))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<NodeAddress> getFilteredNonSeedNodeList(Collection<NodeAddress> collection, List<NodeAddress> list) {
|
||||
return getFilteredList(collection, list).stream()
|
||||
.filter(e -> !peerManager.isSeedNode(e))
|
||||
.collect(Collectors.toList());
|
||||
.filter(e -> !peerManager.isSeedNode(e))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void stopPeriodicTimer() {
|
||||
|
|
|
@ -1,139 +1,32 @@
|
|||
/*
|
||||
* This file is part of bisq.
|
||||
*
|
||||
* bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.network.p2p.seed;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import io.bisq.network.p2p.NodeAddress;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class SeedNodesRepository {
|
||||
private static final Logger log = LoggerFactory.getLogger(SeedNodesRepository.class);
|
||||
public interface SeedNodesRepository {
|
||||
void setTorSeedNodeAddresses(Set<NodeAddress> torSeedNodeAddresses);
|
||||
|
||||
@Nullable
|
||||
private static List<String> bannedNodes;
|
||||
void setLocalhostSeedNodeAddresses(Set<NodeAddress> localhostSeedNodeAddresses);
|
||||
|
||||
public static void setBannedNodes(@Nullable List<String> bannedNodes) {
|
||||
SeedNodesRepository.bannedNodes = bannedNodes;
|
||||
}
|
||||
boolean isSeedNode(NodeAddress nodeAddress);
|
||||
|
||||
// Addresses are used if their port match the network id:
|
||||
// - mainnet uses port 8000
|
||||
// - testnet uses port 8001
|
||||
// - regtest uses port 8002
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private Set<NodeAddress> torSeedNodeAddresses = Sets.newHashSet(
|
||||
// BTC mainnet
|
||||
|
||||
//TODO dev dont use live nodes atm!
|
||||
new NodeAddress("3f3cu2yw7u457ztq.onion:8000"),
|
||||
new NodeAddress("723ljisnynbtdohi.onion:8000"),
|
||||
new NodeAddress("rm7b56wbrcczpjvl.onion:8000"),
|
||||
new NodeAddress("fl3mmribyxgrv63c.onion:8000"),
|
||||
|
||||
//TODO dev
|
||||
// local dev
|
||||
// new NodeAddress("joehwtpe7ijnz4df.onion:8000"),
|
||||
|
||||
// BTC testnet
|
||||
new NodeAddress("nbphlanpgbei4okt.onion:8001"),
|
||||
|
||||
// BTC regtest
|
||||
// For development you need to change that to your local onion addresses
|
||||
// 1. Run a seed node with prog args: --bitcoinNetwork=regtest --nodePort=8002 --myAddress=rxdkppp3vicnbgqt:8002 --appName=bisq_seed_node_rxdkppp3vicnbgqt.onion_8002
|
||||
// 2. Find your local onion address in bisq_seed_node_rxdkppp3vicnbgqt.onion_8002/regtest/tor/hiddenservice/hostname
|
||||
// 3. Shut down the seed node
|
||||
// 4. Rename the directory with your local onion address
|
||||
// 5. Edit here your found onion address (new NodeAddress("YOUR_ONION.onion:8002")
|
||||
new NodeAddress("rxdkppp3vicnbgqt.onion:8002"),
|
||||
|
||||
// LTC mainnet
|
||||
new NodeAddress("acyvotgewx46pebw.onion:8003"),
|
||||
// new NodeAddress("pklgy3vdfn3obkur.onion:8003"), removed in version 0.6
|
||||
|
||||
// keep the below but we don't run them atm
|
||||
/* new NodeAddress("cfciqxcowuhjdnkl.onion:8003"),
|
||||
new NodeAddress("bolqw3hs55uii7ku.onion:8003"),*/
|
||||
|
||||
// DOGE mainnet
|
||||
// new NodeAddress("t6bwuj75mvxswavs.onion:8006"), removed in version 0.6 (DOGE not supported anymore)
|
||||
|
||||
//DASH mainnet
|
||||
new NodeAddress("toeu5ikb27ydscxt.onion:8009")
|
||||
//new NodeAddress("ae4yvaivhnekkhqf.onion:8009") removed in version 0.6
|
||||
);
|
||||
|
||||
// Addresses are used if the last digit of their port match the network id:
|
||||
// - mainnet use port ends in 0
|
||||
// - testnet use port ends in 1
|
||||
// - regtest use port ends in 2
|
||||
private Set<NodeAddress> localhostSeedNodeAddresses = Sets.newHashSet(
|
||||
// BTC
|
||||
// mainnet
|
||||
new NodeAddress("localhost:2000"),
|
||||
new NodeAddress("localhost:3000"),
|
||||
new NodeAddress("localhost:4000"),
|
||||
|
||||
// testnet
|
||||
new NodeAddress("localhost:2001"),
|
||||
new NodeAddress("localhost:3001"),
|
||||
new NodeAddress("localhost:4001"),
|
||||
|
||||
// regtest
|
||||
new NodeAddress("localhost:2002"),
|
||||
new NodeAddress("localhost:3002"),
|
||||
/* new NodeAddress("localhost:4002"),*/
|
||||
|
||||
// LTC
|
||||
// mainnet
|
||||
new NodeAddress("localhost:2003"),
|
||||
|
||||
// regtest
|
||||
new NodeAddress("localhost:2005"),
|
||||
|
||||
// DOGE regtest
|
||||
new NodeAddress("localhost:2008"),
|
||||
|
||||
// DASH regtest
|
||||
new NodeAddress("localhost:2011")
|
||||
);
|
||||
private NodeAddress nodeAddressToExclude;
|
||||
|
||||
|
||||
public Set<NodeAddress> getSeedNodeAddresses(boolean useLocalhostForP2P, int networkId) {
|
||||
String networkIdAsString = String.valueOf(networkId);
|
||||
Set<NodeAddress> nodeAddresses = useLocalhostForP2P ? localhostSeedNodeAddresses : torSeedNodeAddresses;
|
||||
Set<NodeAddress> nodes = nodeAddresses.stream()
|
||||
.filter(e -> String.valueOf(e.getPort()).endsWith("0" + networkIdAsString))
|
||||
.filter(e -> !e.equals(nodeAddressToExclude))
|
||||
.filter(e -> bannedNodes == null || !bannedNodes.contains(e.getFullAddress())) //TODO
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (bannedNodes != null)
|
||||
log.warn("banned seed nodes=" + bannedNodes);
|
||||
log.info("SeedNodeAddresses (useLocalhostForP2P={}) for networkId {}) nodes={}",
|
||||
useLocalhostForP2P, networkId, nodes);
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public void setTorSeedNodeAddresses(Set<NodeAddress> torSeedNodeAddresses) {
|
||||
this.torSeedNodeAddresses = torSeedNodeAddresses;
|
||||
}
|
||||
|
||||
public void setLocalhostSeedNodeAddresses(Set<NodeAddress> localhostSeedNodeAddresses) {
|
||||
this.localhostSeedNodeAddresses = localhostSeedNodeAddresses;
|
||||
}
|
||||
|
||||
public boolean isSeedNode(NodeAddress nodeAddress) {
|
||||
return Stream.concat(localhostSeedNodeAddresses.stream(), torSeedNodeAddresses.stream())
|
||||
.filter(e -> e.equals(nodeAddress)).findAny().isPresent();
|
||||
}
|
||||
|
||||
public void setNodeAddressToExclude(NodeAddress nodeAddress) {
|
||||
this.nodeAddressToExclude = nodeAddress;
|
||||
}
|
||||
Set<NodeAddress> getSeedNodeAddresses();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.bisq.network.p2p.storage;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.inject.name.Named;
|
||||
import com.google.protobuf.ByteString;
|
||||
import io.bisq.common.Timer;
|
||||
import io.bisq.common.UserThread;
|
||||
|
@ -34,6 +35,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.KeyPair;
|
||||
|
@ -51,13 +53,14 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
|||
/**
|
||||
* How many days to keep an entry before it is purged.
|
||||
*/
|
||||
public static final int PURGE_AGE_DAYS = 10;
|
||||
private static final int PURGE_AGE_DAYS = 10;
|
||||
|
||||
@VisibleForTesting
|
||||
public static int CHECK_TTL_INTERVAL_SEC = 60;
|
||||
|
||||
private final Broadcaster broadcaster;
|
||||
private final File storageDir;
|
||||
|
||||
@Getter
|
||||
private final Map<ByteArray, ProtectedStorageEntry> map = new ConcurrentHashMap<>();
|
||||
private final CopyOnWriteArraySet<HashMapChangedListener> hashMapChangedListeners = new CopyOnWriteArraySet<>();
|
||||
|
@ -79,9 +82,10 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
|||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public P2PDataStorage(Broadcaster broadcaster,
|
||||
NetworkNode networkNode,
|
||||
File storageDir,
|
||||
@Inject
|
||||
public P2PDataStorage(NetworkNode networkNode,
|
||||
Broadcaster broadcaster,
|
||||
@Named(Storage.STORAGE_DIR) File storageDir,
|
||||
PersistenceProtoResolver persistenceProtoResolver) {
|
||||
this.broadcaster = broadcaster;
|
||||
this.storageDir = storageDir;
|
||||
|
@ -627,7 +631,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
|||
}
|
||||
}
|
||||
|
||||
boolean checkSignature(PublicKey ownerPubKey, byte[] hashOfDataAndSeqNr, byte[] signature) {
|
||||
private boolean checkSignature(PublicKey ownerPubKey, byte[] hashOfDataAndSeqNr, byte[] signature) {
|
||||
try {
|
||||
boolean result = Sig.verify(ownerPubKey, hashOfDataAndSeqNr, signature);
|
||||
if (!result)
|
||||
|
@ -648,7 +652,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
|||
|
||||
// Check that the pubkey of the storage entry matches the allowed pubkey for the addition or removal operation
|
||||
// in the contained mailbox message, or the pubKey of other kinds of network_messages.
|
||||
boolean checkPublicKeys(ProtectedStorageEntry protectedStorageEntry, boolean isAddOperation) {
|
||||
private boolean checkPublicKeys(ProtectedStorageEntry protectedStorageEntry, boolean isAddOperation) {
|
||||
boolean result;
|
||||
final ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload();
|
||||
if (protectedStoragePayload instanceof MailboxStoragePayload) {
|
||||
|
|
|
@ -2,7 +2,6 @@ package io.bisq.network.p2p;
|
|||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import io.bisq.common.Clock;
|
||||
import io.bisq.common.CommonOptionKeys;
|
||||
import io.bisq.common.UserThread;
|
||||
import io.bisq.common.app.Log;
|
||||
|
@ -10,7 +9,6 @@ import io.bisq.common.app.Version;
|
|||
import io.bisq.common.util.Utilities;
|
||||
import io.bisq.network.NetworkOptionKeys;
|
||||
import io.bisq.network.p2p.peers.BanList;
|
||||
import io.bisq.network.p2p.seed.SeedNodesRepository;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -170,13 +168,13 @@ public class DummySeedNode {
|
|||
Utilities.printSysInfo();
|
||||
Log.setLevel(logLevel);
|
||||
|
||||
SeedNodesRepository seedNodesRepository = new SeedNodesRepository();
|
||||
/* SeedNodesRepository seedNodesRepository = new SeedNodesRepository();
|
||||
if (progArgSeedNodes != null && !progArgSeedNodes.isEmpty()) {
|
||||
if (useLocalhostForP2P)
|
||||
seedNodesRepository.setLocalhostSeedNodeAddresses(progArgSeedNodes);
|
||||
else
|
||||
seedNodesRepository.setTorSeedNodeAddresses(progArgSeedNodes);
|
||||
}
|
||||
}*/
|
||||
|
||||
File storageDir = Paths.get(appPath.toString(), "db").toFile();
|
||||
if (storageDir.mkdirs())
|
||||
|
@ -186,11 +184,11 @@ public class DummySeedNode {
|
|||
if (torDir.mkdirs())
|
||||
log.debug("Created torDir at " + torDir.getAbsolutePath());
|
||||
|
||||
seedNodesRepository.setNodeAddressToExclude(mySeedNodeAddress);
|
||||
seedNodeP2PService = new P2PService(seedNodesRepository, mySeedNodeAddress.getPort(), maxConnections,
|
||||
// seedNodesRepository.setNodeAddressToExclude(mySeedNodeAddress);
|
||||
/* seedNodeP2PService = new P2PService(seedNodesRepository, mySeedNodeAddress.getPort(), maxConnections,
|
||||
torDir, useLocalhostForP2P, networkId, storageDir, null, null, null, new Clock(), null, null,
|
||||
null, TestUtils.getNetworkProtoResolver(), TestUtils.getPersistenceProtoResolver());
|
||||
seedNodeP2PService.start(listener);
|
||||
seedNodeP2PService.start(listener);*/
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
|
|
@ -1,20 +1,15 @@
|
|||
package io.bisq.network.p2p;
|
||||
|
||||
import io.bisq.common.Clock;
|
||||
import io.bisq.common.Payload;
|
||||
import io.bisq.common.crypto.KeyRing;
|
||||
import io.bisq.common.proto.network.NetworkEnvelope;
|
||||
import io.bisq.common.proto.network.NetworkPayload;
|
||||
import io.bisq.common.proto.network.NetworkProtoResolver;
|
||||
import io.bisq.common.proto.persistable.PersistableEnvelope;
|
||||
import io.bisq.common.proto.persistable.PersistenceProtoResolver;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
import io.bisq.network.crypto.EncryptionService;
|
||||
import io.bisq.network.p2p.seed.SeedNodesRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
@ -91,7 +86,7 @@ public class TestUtils {
|
|||
return seedNode;
|
||||
}
|
||||
|
||||
public static P2PService getAndAuthenticateP2PService(int port, EncryptionService encryptionService, KeyRing keyRing,
|
||||
/* public static P2PService getAndAuthenticateP2PService(int port, EncryptionService encryptionService, KeyRing keyRing,
|
||||
boolean useLocalhostForP2P, Set<NodeAddress> seedNodes)
|
||||
throws InterruptedException {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
@ -140,7 +135,7 @@ public class TestUtils {
|
|||
Thread.sleep(2000);
|
||||
return p2PService;
|
||||
}
|
||||
|
||||
*/
|
||||
public static NetworkProtoResolver getNetworkProtoResolver() {
|
||||
return new NetworkProtoResolver() {
|
||||
@Override
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.bisq.network.p2p.network;
|
||||
|
||||
import io.bisq.common.Clock;
|
||||
import io.bisq.common.UserThread;
|
||||
import io.bisq.common.app.Version;
|
||||
import io.bisq.common.crypto.KeyRing;
|
||||
|
@ -146,7 +145,7 @@ public class NetworkStressTest {
|
|||
/**
|
||||
* The repository of seed nodes used in the test.
|
||||
*/
|
||||
private final SeedNodesRepository seedNodesRepository = new SeedNodesRepository();
|
||||
private final SeedNodesRepository seedNodesRepository = null;
|
||||
/**
|
||||
* A list of peer nodes represented as P2P services.
|
||||
*/
|
||||
|
@ -382,10 +381,11 @@ public class NetworkStressTest {
|
|||
final KeyRing peerKeyRing = new KeyRing(peerKeyStorage);
|
||||
final EncryptionService peerEncryptionService = new EncryptionService(peerKeyRing, TestUtils.getNetworkProtoResolver());
|
||||
|
||||
return new P2PService(seedNodesRepository, port, peerTorDir, useLocalhostForP2P,
|
||||
REGTEST_NETWORK_ID, P2PService.MAX_CONNECTIONS_DEFAULT, peerStorageDir, null, null, null,
|
||||
return null;
|
||||
/*new P2PService(seedNodesRepository, port, peerTorDir, useLocalhostForP2P,
|
||||
REGTEST_NETWORK_ID, P2PService.MAX_CONNECTIONS_DEFAULT, peerStorageDir,0,null,false, 0, null, null, null,
|
||||
new Clock(), null, peerEncryptionService, peerKeyRing,
|
||||
TestUtils.getNetworkProtoResolver(), TestUtils.getPersistenceProtoResolver());
|
||||
TestUtils.getNetworkProtoResolver());*/
|
||||
}
|
||||
|
||||
// ## TEST SETUP: P2P service listener classes
|
||||
|
|
|
@ -74,7 +74,7 @@ public class P2PDataStorageTest {
|
|||
keyRing2 = new KeyRing(new KeyStorage(dir2));
|
||||
storageSignatureKeyPair2 = keyRing2.getSignatureKeyPair();
|
||||
encryptionService2 = new EncryptionService(keyRing2, TestUtils.getNetworkProtoResolver());
|
||||
dataStorage1 = new P2PDataStorage(broadcaster, networkNode, dir1, persistenceProtoResolver);
|
||||
//dataStorage1 = new P2PDataStorage(broadcaster, networkNode, dir1, persistenceProtoResolver);
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -119,7 +119,7 @@ public class P2PDataStorageTest {
|
|||
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
|
||||
data.toEnvelopeProto().writeTo(byteOutputStream);
|
||||
|
||||
//TODO Use NetworkProtoResolver, PersistenceProtoResolver or ProtoResolver which are all in io.bisq.common.
|
||||
//TODO Use NetworkProtoResolver, PersistenceProtoResolver or ProtoResolver which are all in io.bisq.common.
|
||||
ProtectedStorageEntry protectedStorageEntry = ProtoBufferUtilities.getProtectedStorageEntry(PB.ProtectedStorageEntry.parseFrom(new ByteArrayInputStream(byteOutputStream.toByteArray())));
|
||||
|
||||
assertTrue(Arrays.equals(Hash.getHash(data.getStoragePayload()), Hash.getHash(protectedStorageEntry.getStoragePayload())));
|
||||
|
@ -127,7 +127,7 @@ public class P2PDataStorageTest {
|
|||
assertTrue(checkSignature(protectedStorageEntry));
|
||||
}*/
|
||||
|
||||
//TODO Use NetworkProtoResolver, PersistenceProtoResolver or ProtoResolver which are all in io.bisq.common.
|
||||
//TODO Use NetworkProtoResolver, PersistenceProtoResolver or ProtoResolver which are all in io.bisq.common.
|
||||
/* @Test
|
||||
public void testOfferRoundtrip() throws InvalidProtocolBufferException {
|
||||
OfferPayload offer = getDummyOffer();
|
||||
|
|
|
@ -57,7 +57,7 @@ public class SeedNodeMain extends BisqExecutable {
|
|||
|
||||
Utilities.removeCryptographyRestrictions();
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setNameFormat("SeedNodeMain")
|
||||
|
|
|
@ -31,6 +31,7 @@ import io.bisq.core.arbitration.ArbitratorModule;
|
|||
import io.bisq.core.btc.BitcoinModule;
|
||||
import io.bisq.core.dao.DaoModule;
|
||||
import io.bisq.core.filter.FilterModule;
|
||||
import io.bisq.core.network.CoreSeedNodesRepository;
|
||||
import io.bisq.core.offer.OfferModule;
|
||||
import io.bisq.core.proto.network.CoreNetworkProtoResolver;
|
||||
import io.bisq.core.proto.persistable.CorePersistenceProtoResolver;
|
||||
|
@ -39,6 +40,7 @@ import io.bisq.core.user.Preferences;
|
|||
import io.bisq.core.user.User;
|
||||
import io.bisq.network.crypto.EncryptionServiceModule;
|
||||
import io.bisq.network.p2p.P2PModule;
|
||||
import io.bisq.network.p2p.seed.SeedNodesRepository;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -55,7 +57,7 @@ class SeedNodeModule extends AppModule {
|
|||
protected void configure() {
|
||||
bind(BisqEnvironment.class).toInstance((BisqEnvironment) environment);
|
||||
|
||||
//bind(CachingViewLoader.class).in(Singleton.class);
|
||||
// bind(CachingViewLoader.class).in(Singleton.class);
|
||||
bind(KeyStorage.class).in(Singleton.class);
|
||||
bind(KeyRing.class).in(Singleton.class);
|
||||
bind(User.class).in(Singleton.class);
|
||||
|
@ -65,6 +67,8 @@ class SeedNodeModule extends AppModule {
|
|||
bind(PersistenceProtoResolver.class).to(CorePersistenceProtoResolver.class).in(Singleton.class);
|
||||
bind(Preferences.class).in(Singleton.class);
|
||||
|
||||
bind(SeedNodesRepository.class).to(CoreSeedNodesRepository.class).in(Singleton.class);
|
||||
|
||||
File storageDir = new File(environment.getRequiredProperty(Storage.STORAGE_DIR));
|
||||
bind(File.class).annotatedWith(named(Storage.STORAGE_DIR)).toInstance(storageDir);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue