Merge branch 'master' into misc-dao-improvements

This commit is contained in:
Manfred Karrer 2019-03-18 14:09:20 -05:00
commit 0198bf4c23
No known key found for this signature in database
GPG Key ID: 401250966A6B2C46
13 changed files with 104 additions and 69 deletions

View File

@ -53,29 +53,38 @@ public class Capabilities {
this.capabilities.addAll(capabilities);
}
public void resetCapabilities(Capability... capabilities) {
resetCapabilities(Arrays.asList(capabilities));
public void set(Capability... capabilities) {
set(Arrays.asList(capabilities));
}
public void resetCapabilities(Capabilities capabilities) {
resetCapabilities(capabilities.capabilities);
public void set(Capabilities capabilities) {
set(capabilities.capabilities);
}
public void resetCapabilities(Collection<Capability> capabilities) {
public void set(Collection<Capability> capabilities) {
this.capabilities.clear();
this.capabilities.addAll(capabilities);
}
public boolean isCapabilitySupported(final Set<Capability> requiredItems) {
public void addAll(Capability... capabilities) {
this.capabilities.addAll(Arrays.asList(capabilities));
}
public void addAll(Capabilities capabilities) {
if(capabilities != null)
this.capabilities.addAll(capabilities.capabilities);
}
public boolean containsAll(final Set<Capability> requiredItems) {
return capabilities.containsAll(requiredItems);
}
public boolean isCapabilitySupported(final Capabilities capabilities) {
return isCapabilitySupported(capabilities.capabilities);
public boolean containsAll(final Capabilities capabilities) {
return containsAll(capabilities.capabilities);
}
public boolean hasCapabilities() {
return !capabilities.isEmpty();
public boolean isEmpty() {
return capabilities.isEmpty();
}

View File

@ -0,0 +1,28 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.common.app;
/**
* Holds a set of {@link Capabilities}.
*
* @author Florian Reimair
*/
public interface HasCapabilities {
Capabilities getCapabilities();
}

View File

@ -31,15 +31,15 @@ public class CapabilitiesTest {
public void testNoCapabilitiesAvailable() {
Capabilities DUT = new Capabilities();
assertTrue(DUT.isCapabilitySupported(new HashSet<>()));
assertFalse(DUT.isCapabilitySupported(new Capabilities(SEED_NODE)));
assertTrue(DUT.containsAll(new HashSet<>()));
assertFalse(DUT.containsAll(new Capabilities(SEED_NODE)));
}
@Test
public void testO() {
Capabilities DUT = new Capabilities(TRADE_STATISTICS);
assertTrue(DUT.isCapabilitySupported(new HashSet<>()));
assertTrue(DUT.containsAll(new HashSet<>()));
}
@Test
@ -47,17 +47,17 @@ public class CapabilitiesTest {
Capabilities DUT = new Capabilities(TRADE_STATISTICS);
// single match
assertTrue(DUT.isCapabilitySupported(new Capabilities(TRADE_STATISTICS)));
assertFalse(DUT.isCapabilitySupported(new Capabilities(SEED_NODE)));
assertTrue(DUT.containsAll(new Capabilities(TRADE_STATISTICS)));
assertFalse(DUT.containsAll(new Capabilities(SEED_NODE)));
}
@Test
public void testMultiMatch() {
Capabilities DUT = new Capabilities(TRADE_STATISTICS, TRADE_STATISTICS_2);
assertTrue(DUT.isCapabilitySupported(new Capabilities(TRADE_STATISTICS)));
assertFalse(DUT.isCapabilitySupported(new Capabilities(SEED_NODE)));
assertTrue(DUT.isCapabilitySupported(new Capabilities(TRADE_STATISTICS, TRADE_STATISTICS_2)));
assertFalse(DUT.isCapabilitySupported(new Capabilities(SEED_NODE, TRADE_STATISTICS_2)));
assertTrue(DUT.containsAll(new Capabilities(TRADE_STATISTICS)));
assertFalse(DUT.containsAll(new Capabilities(SEED_NODE)));
assertTrue(DUT.containsAll(new Capabilities(TRADE_STATISTICS, TRADE_STATISTICS_2)));
assertFalse(DUT.containsAll(new Capabilities(SEED_NODE, TRADE_STATISTICS_2)));
}
}

View File

@ -168,17 +168,17 @@ public final class RepublishGovernanceDataHandler {
Capabilities required = new Capabilities(Capability.DAO_FULL_NODE);
List<Peer> list = peerManager.getLivePeers(null).stream()
.filter(peer -> peer.isCapabilitySupported(required))
.filter(peer -> peer.getCapabilities().containsAll(required))
.collect(Collectors.toList());
if (list.isEmpty())
list = peerManager.getReportedPeers().stream()
.filter(peer -> peer.isCapabilitySupported(required))
.filter(peer -> peer.getCapabilities().containsAll(required))
.collect(Collectors.toList());
if (list.isEmpty())
list = peerManager.getPersistedPeers().stream()
.filter(peer -> peer.isCapabilitySupported(required))
.filter(peer -> peer.getCapabilities().containsAll(required))
.collect(Collectors.toList());
if (!list.isEmpty()) {

View File

@ -28,24 +28,14 @@ import java.util.Arrays;
public class CoreNetworkCapabilities {
public static void setSupportedCapabilities(BisqEnvironment bisqEnvironment) {
final ArrayList<Capability> supportedCapabilities = new ArrayList<>(Arrays.asList(
Capability.TRADE_STATISTICS,
Capability.TRADE_STATISTICS_2,
Capability.ACCOUNT_AGE_WITNESS,
Capability.ACK_MSG
));
Capabilities.app.addAll(Capability.TRADE_STATISTICS, Capability.TRADE_STATISTICS_2, Capability.ACCOUNT_AGE_WITNESS, Capability.ACK_MSG);
if (BisqEnvironment.isDaoActivated(bisqEnvironment)) {
supportedCapabilities.add(Capability.PROPOSAL);
supportedCapabilities.add(Capability.BLIND_VOTE);
supportedCapabilities.add(Capability.BSQ_BLOCK);
supportedCapabilities.add(Capability.DAO_STATE);
Capabilities.app.addAll(Capability.PROPOSAL, Capability.BLIND_VOTE, Capability.BSQ_BLOCK, Capability.DAO_STATE);
String isFullDaoNode = bisqEnvironment.getProperty(DaoOptionKeys.FULL_DAO_NODE, String.class, "");
if (isFullDaoNode != null && !isFullDaoNode.isEmpty())
supportedCapabilities.add(Capability.DAO_FULL_NODE);
Capabilities.app.addAll(Capability.DAO_FULL_NODE);
}
Capabilities.app.resetCapabilities(supportedCapabilities);
}
}

View File

@ -138,12 +138,7 @@ public class P2PNetworkLoad extends Metric implements MessageListener, SetupList
history = Collections.synchronizedMap(new FixedSizeHistoryTracker(Integer.parseInt(configuration.getProperty(HISTORY_SIZE, "200"))));
// add all capabilities
List<Integer> capabilityOrdinals = Capabilities.toIntList(Capabilities.app);
if(!capabilityOrdinals.contains(Capability.DAO_FULL_NODE.ordinal())) {
capabilityOrdinals.add(Capability.DAO_FULL_NODE.ordinal());
Capabilities.app.resetCapabilities(Capabilities.fromIntList(capabilityOrdinals));
}
Capabilities.app.addAll(Capability.DAO_FULL_NODE);
}
@Override

View File

@ -619,10 +619,10 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
// it from old versions, so we filter those.
Optional<Peer> optionalPeer = allPeers.stream()
.filter(peer -> peer.getNodeAddress().equals(peersNodeAddress))
.filter(peer -> peer.hasCapabilities())
.filter(peer -> !peer.getCapabilities().isEmpty())
.findAny();
if (optionalPeer.isPresent()) {
boolean result = optionalPeer.get().isCapabilitySupported(((CapabilityRequiringPayload) message).getRequiredCapabilities());
boolean result = optionalPeer.get().getCapabilities().containsAll(((CapabilityRequiringPayload) message).getRequiredCapabilities());
if (!result)
log.warn("We don't send the message because the peer does not support the required capability. " +

View File

@ -38,6 +38,7 @@ import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
import bisq.common.Proto;
import bisq.common.UserThread;
import bisq.common.app.Capabilities;
import bisq.common.app.HasCapabilities;
import bisq.common.app.Version;
import bisq.common.proto.ProtobufferException;
import bisq.common.proto.network.NetworkEnvelope;
@ -93,7 +94,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
* All handlers are called on User thread.
*/
@Slf4j
public class Connection extends Capabilities implements Runnable, MessageListener {
public class Connection implements HasCapabilities, Runnable, MessageListener {
///////////////////////////////////////////////////////////////////////////////////////////
// Enums
@ -166,6 +167,8 @@ public class Connection extends Capabilities implements Runnable, MessageListene
private RuleViolation ruleViolation;
private final ConcurrentHashMap<RuleViolation, Integer> ruleViolations = new ConcurrentHashMap<>();
private Capabilities capabilities = new Capabilities();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -218,6 +221,11 @@ public class Connection extends Capabilities implements Runnable, MessageListene
// API
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public Capabilities getCapabilities() {
return capabilities;
}
// Called from various threads
public void sendMessage(NetworkEnvelope networkEnvelope) {
log.debug(">> Send networkEnvelope of type: " + networkEnvelope.getClass().getSimpleName());
@ -284,14 +292,14 @@ public class Connection extends Capabilities implements Runnable, MessageListene
final ProtectedStoragePayload protectedStoragePayload = (((AddDataMessage) msg).getProtectedStorageEntry()).getProtectedStoragePayload();
result = !(protectedStoragePayload instanceof CapabilityRequiringPayload);
if(!result)
result = isCapabilitySupported(((CapabilityRequiringPayload) protectedStoragePayload).getRequiredCapabilities());
result = capabilities.containsAll(((CapabilityRequiringPayload) protectedStoragePayload).getRequiredCapabilities());
} else if (msg instanceof AddPersistableNetworkPayloadMessage) {
final PersistableNetworkPayload persistableNetworkPayload = ((AddPersistableNetworkPayloadMessage) msg).getPersistableNetworkPayload();
result = !(persistableNetworkPayload instanceof CapabilityRequiringPayload);
if(!result)
result = isCapabilitySupported(((CapabilityRequiringPayload) persistableNetworkPayload).getRequiredCapabilities());
result = capabilities.containsAll(((CapabilityRequiringPayload) persistableNetworkPayload).getRequiredCapabilities());
} else if(msg instanceof CapabilityRequiringPayload) {
result = isCapabilitySupported(((CapabilityRequiringPayload) msg).getRequiredCapabilities());
result = capabilities.containsAll(((CapabilityRequiringPayload) msg).getRequiredCapabilities());
} else {
result = true;
}
@ -739,7 +747,7 @@ public class Connection extends Capabilities implements Runnable, MessageListene
}
if (networkEnvelope instanceof SupportedCapabilitiesMessage)
resetCapabilities(((SupportedCapabilitiesMessage) networkEnvelope).getSupportedCapabilities());
capabilities.set(((SupportedCapabilitiesMessage) networkEnvelope).getSupportedCapabilities());
if (networkEnvelope instanceof CloseConnectionMessage) {
// If we get a CloseConnectionMessage we shut down

View File

@ -282,10 +282,18 @@ public class TorNetworkNode extends NetworkNode {
});
log.info("It will take some time for the HS to be reachable (~40 seconds). You will be notified about this");
} catch (TorCtlException e) {
log.error("Tor node creation failed: " + (e.getCause() != null ? e.getCause().toString() : e.toString()));
restartTor(e.getMessage());
String msg = e.getCause() != null ? e.getCause().toString() : e.toString();
log.error("Tor node creation failed: {}", msg);
if (e.getCause() instanceof IOException) {
// Since we cannot connect to Tor, we cannot do nothing.
// Furthermore, we have no hidden services started yet, so there is no graceful
// shutdown needed either
UserThread.execute(() -> setupListeners.forEach(s -> s.onSetupFailed(new RuntimeException(msg))));
} else {
restartTor(e.getMessage());
}
} catch (IOException e) {
log.error("Could not connect to running Tor: " + e.getMessage());
log.error("Could not connect to running Tor: {}", e.getMessage());
// Since we cannot connect to Tor, we cannot do nothing.
// Furthermore, we have no hidden services started yet, so there is no graceful
// shutdown needed either

View File

@ -176,7 +176,7 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
PeerList persistedPeerList = storage.initAndGetPersistedWithFileName("PeerList", 1000);
if (persistedPeerList != null) {
long peersWithNoCapabilitiesSet = persistedPeerList.getList().stream()
.filter(e -> e.hasCapabilities())
.filter(e -> e.getCapabilities().isEmpty())
.mapToInt(e -> 1)
.count();
if (peersWithNoCapabilitiesSet > 100) {
@ -660,18 +660,18 @@ public class PeerManager implements ConnectionListener, PersistedDataHost {
// filter(connection -> connection.getPeersNodeAddressOptional().isPresent())
return networkNode.getConfirmedConnections().stream()
.map((Connection connection) -> {
Capabilities supportedCapabilities = new Capabilities(connection);
Capabilities supportedCapabilities = new Capabilities(connection.getCapabilities());
// If we have a new connection the supportedCapabilities is empty.
// We lookup if we have already stored the supportedCapabilities at the persisted or reported peers
// and if so we use that.
if (!supportedCapabilities.hasCapabilities()) {
if (supportedCapabilities.isEmpty()) {
Set<Peer> allPeers = new HashSet<>(getPersistedPeers());
allPeers.addAll(getReportedPeers());
Optional<Peer> ourPeer = allPeers.stream().filter(peer -> peer.getNodeAddress().equals(connection.getPeersNodeAddressOptional().get()))
.filter(peer -> !peer.hasCapabilities())
.filter(peer -> !peer.getCapabilities().isEmpty())
.findAny();
if(ourPeer.isPresent())
supportedCapabilities = new Capabilities(ourPeer.get());
supportedCapabilities = new Capabilities(ourPeer.get().getCapabilities());
}
Peer peer = new Peer(connection.getPeersNodeAddressOptional().get(), supportedCapabilities);
connection.addWeakCapabilitiesListener(peer);

View File

@ -158,7 +158,7 @@ public class GetDataRequestHandler {
final ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload();
boolean doAdd = false;
if (protectedStoragePayload instanceof CapabilityRequiringPayload) {
if (connection.isCapabilitySupported(((CapabilityRequiringPayload) protectedStoragePayload).getRequiredCapabilities()))
if (connection.getCapabilities().containsAll(((CapabilityRequiringPayload) protectedStoragePayload).getRequiredCapabilities()))
doAdd = true;
else
log.debug("We do not send the message to the peer because he does not support the required capability for that message type.\n" +

View File

@ -21,6 +21,7 @@ import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.network.SupportedCapabilitiesListener;
import bisq.common.app.Capabilities;
import bisq.common.app.HasCapabilities;
import bisq.common.proto.network.NetworkPayload;
import bisq.common.proto.persistable.PersistablePayload;
@ -28,7 +29,6 @@ import io.bisq.generated.protobuffer.PB;
import java.util.Date;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@ -36,10 +36,8 @@ import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
@Getter
@EqualsAndHashCode(exclude = {"date"}, callSuper = true)
// failedConnectionAttempts is transient and therefore excluded anyway
@Slf4j
public final class Peer extends Capabilities implements NetworkPayload, PersistablePayload, SupportedCapabilitiesListener {
public final class Peer implements HasCapabilities, NetworkPayload, PersistablePayload, SupportedCapabilitiesListener {
private static final int MAX_FAILED_CONNECTION_ATTEMPTS = 5;
private final NodeAddress nodeAddress;
@ -48,6 +46,7 @@ public final class Peer extends Capabilities implements NetworkPayload, Persista
@Setter
transient private int failedConnectionAttempts = 0;
private Capabilities capabilities = new Capabilities();
public Peer(NodeAddress nodeAddress, @Nullable Capabilities supportedCapabilities) {
this(nodeAddress, new Date().getTime(), supportedCapabilities);
@ -58,9 +57,10 @@ public final class Peer extends Capabilities implements NetworkPayload, Persista
///////////////////////////////////////////////////////////////////////////////////////////
private Peer(NodeAddress nodeAddress, long date, Capabilities supportedCapabilities) {
super(supportedCapabilities);
super();
this.nodeAddress = nodeAddress;
this.date = date;
this.capabilities.addAll(supportedCapabilities);
}
@Override
@ -68,7 +68,7 @@ public final class Peer extends Capabilities implements NetworkPayload, Persista
return PB.Peer.newBuilder()
.setNodeAddress(nodeAddress.toProtoMessage())
.setDate(date)
.addAllSupportedCapabilities(Capabilities.toIntList(this))
.addAllSupportedCapabilities(Capabilities.toIntList(getCapabilities()))
.build();
}
@ -97,8 +97,8 @@ public final class Peer extends Capabilities implements NetworkPayload, Persista
@Override
public void onChanged(Capabilities supportedCapabilities) {
if (supportedCapabilities.hasCapabilities())
resetCapabilities(supportedCapabilities);
if (!supportedCapabilities.isEmpty())
capabilities.set(supportedCapabilities);
}

View File

@ -63,10 +63,7 @@ public class SeedNodeMain extends ExecutableForAppWithP2p {
@Override
protected void addCapabilities() {
// TODO got even worse after refactoring
List<Integer> current = Capabilities.toIntList(Capabilities.app);
current.add(Capability.SEED_NODE.ordinal());
Capabilities.app.resetCapabilities(Capabilities.fromIntList(current));
Capabilities.app.addAll(Capability.SEED_NODE);
}
@Override