Add support of seed and price rleay node filters. Use guice in P2P network domain classes.

This commit is contained in:
Manfred Karrer 2017-10-26 15:46:48 -05:00
parent 90cfc84d35
commit afaabf9e36
No known key found for this signature in database
GPG key ID: 401250966A6B2C46
37 changed files with 1055 additions and 1042 deletions

View file

@ -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;
}

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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();
}
}

View file

@ -34,4 +34,11 @@ public abstract class HttpClientProvider {
httpClient.setIgnoreSocks5Proxy(ignoreSocks5Proxy);
}
@Override
public String toString() {
return "HttpClientProvider{" +
"\n httpClient=" + httpClient +
"\n}";
}
}

View file

@ -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();
}
}

View file

@ -65,9 +65,4 @@ public class FeeProvider extends HttpClientProvider {
}
return new Tuple2<>(tsMap, map);
}
@Override
public String toString() {
return "FeeProvider";
}
}

View file

@ -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));
}

View file

@ -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);
}
}

View file

@ -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());
}
}

View file

@ -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(() -> {

View file

@ -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());

View file

@ -653,9 +653,6 @@ public class MainViewModel implements ViewModel {
filterManager.onAllServicesInitialized();
providersRepository.setBannedNodes(preferences.getBannedPriceRelayNodes());
providersRepository.onAllServicesInitialized();
setupBtcNumPeersWatcher();
setupP2PNumPeersWatcher();
updateBalance();

View file

@ -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();
}

View file

@ -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;
}
}

View file

@ -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));
}
}
}

View file

@ -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;
}
///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -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);
}

View file

@ -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));
});

View file

@ -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());
}
}

View file

@ -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);

View file

@ -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());
}

View file

@ -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." +

View file

@ -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() {

View file

@ -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.");
}

View file

@ -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() {

View file

@ -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();
}

View file

@ -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) {

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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();

View file

@ -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")

View file

@ -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);