Skip to content

Commit

Permalink
Add local ipv4 and ipv6 multicast peer discovery for DHT.
Browse files Browse the repository at this point in the history
Update tests for same.

closes #15
  • Loading branch information
fireduck64 committed Jan 3, 2020
1 parent dbf4704 commit c2ade76
Show file tree
Hide file tree
Showing 14 changed files with 369 additions and 37 deletions.
3 changes: 2 additions & 1 deletion WORKSPACE
Expand Up @@ -68,7 +68,8 @@ pinned_maven_install()
git_repository(
name = "snowblossom",
remote = "https://github.com/snowblossomcoin/snowblossom",
commit = "b35a663e16735068a60f625a9baaf8d669c4cf30",
commit = "dbefa041f19a2e3aed2108da9131c2e519ec7fa5",
shallow_since = "1578077101 -0800"
)

git_repository(
Expand Down
15 changes: 14 additions & 1 deletion protolib/channels.proto
Expand Up @@ -127,6 +127,7 @@ message SignedMessagePayload {
ChannelBlockHeader channel_block_header = 20;
ContentInfo content_info = 21;
ChannelSettings channel_settings = 22;
LocalPeerDisco local_peer_disco = 23;
}
TxOutPoint fbo_utxo = 50;
}
Expand All @@ -137,9 +138,21 @@ message ChannelPeerInfo {
bytes address_spec_hash = 3; //Also the node ID
}

// This message being signed indicates that the peer is in fact using
// multicast announcement to find local network peers
// and a node should try to connect to this node via whatever address this multicast appears
// to come from.
// Note: this does open up a MITM attack/DOS attack if an attacker for connections from node A to node B
// if the attack is both on local network B (to get this signed LocalPeerDisco ) and network A (to
// multicast that signed message). In this case, the TLS will still protect the connection.
message LocalPeerDisco {
repeated string ip_addresses = 1;
int32 port = 2;
SignedMessage signed_peer_info = 3;
}

// The local information we store about a peer
message LocalPeerInfo {

ChannelPeerInfo info = 1;
SignedMessage signed_peer_info = 2;
int64 last_connected = 3;
Expand Down
1 change: 1 addition & 0 deletions src/ChannelGateway.java
Expand Up @@ -29,3 +29,4 @@ public ChannelInterface getChannelInterface(ChannelID cid)


}

13 changes: 12 additions & 1 deletion src/ChannelGlobals.java
@@ -1,8 +1,9 @@
package snowblossom.channels;


public class ChannelGlobals
{
public static final String VERSION = "dev.2019.12.20.0";
public static final String VERSION = "dev.2020.01.03.00";

public static final String NODE_ADDRESS_STRING="node";
public static final String CHANNEL_ADDRESS_STRING="chan";
Expand All @@ -11,6 +12,11 @@ public class ChannelGlobals

public static final int NETWORK_PORT=4862;

public static final String MULTICAST_IPV4_ADDRESS="224.48.62.1";

// Subject to change after I understand WTF https://tools.ietf.org/html/rfc7371 is on about
public static final String MULTICAST_IPV6_ADDRESS="FFFF::e030:3e01";

public static final long ALLOWED_CLOCK_SKEW=45000; //ms

public static final String NODE_TAG = "node";
Expand All @@ -35,6 +41,10 @@ public class ChannelGlobals
public static final long PEER_LINK_TIMEOUT=60000L;
public static final long CHANNEL_LINK_TIMEOUT=60000L;

public static final long MULTICAST_BROADCAST_PERIOD=60000L;
public static final long MULTICAST_CACHE_EXPIRE=MULTICAST_BROADCAST_PERIOD*2L;


// 2500 channels, 100 elements each
public static final int DHT_CACHE_ELEMENTS = 2500 * 100;
public static final long DHT_CACHE_EXPIRE = 120000L;
Expand Down Expand Up @@ -68,3 +78,4 @@ public class ChannelGlobals




7 changes: 5 additions & 2 deletions src/ChannelNode.java
Expand Up @@ -47,6 +47,7 @@ public class ChannelNode
private ChannelTipSender channel_tip_sender;
private ChannelChunkGetter channel_chunk_getter;
private ChannelOutsiderSender channel_outsider_sender;
private LocalPeerFinder local_peer_finder;

private HashMap<ChannelID, SingleChannelDB> db_map;
private boolean autojoin = false;
Expand Down Expand Up @@ -133,6 +134,7 @@ else if (db_type.equals("lobstack"))
channel_tip_sender = new ChannelTipSender(this);
channel_chunk_getter = new ChannelChunkGetter(this);
channel_outsider_sender = new ChannelOutsiderSender(this);
local_peer_finder = new LocalPeerFinder(this);

startServer();

Expand All @@ -143,8 +145,8 @@ else if (db_type.equals("lobstack"))
channel_chunk_getter.start();
channel_outsider_sender.start();

channel_subscriber.loadFromDB();

channel_subscriber.loadFromDB();
local_peer_finder.start();

if (config.isSet("web_port"))
{
Expand Down Expand Up @@ -221,6 +223,7 @@ private void startServer()
public Config getConfig(){ return config;}
public WalletDatabase getWalletDB() {return wallet_db; }
public StubHolder getStubHolder() {return stub_holder; }
public LocalPeerFinder getLocalPeerFinder() {return local_peer_finder;}
public boolean getAutoJoin(){ return autojoin;}

public AddressSpecHash getNodeID()
Expand Down
20 changes: 20 additions & 0 deletions src/ChannelSigUtil.java
Expand Up @@ -5,6 +5,8 @@
import snowblossom.channels.proto.*;
import snowblossom.lib.ChainHash;
import snowblossom.lib.DigestUtil;
import snowblossom.lib.AddressSpecHash;
import snowblossom.lib.AddressUtil;
import snowblossom.lib.SignatureUtil;
import snowblossom.lib.ValidationException;
import snowblossom.proto.AddressSpec;
Expand Down Expand Up @@ -131,5 +133,23 @@ public static ChannelID getChannelId(ChainHash message_id)
return new ChannelID( md.digest(message_id.toByteArray() ));
}

public static ChannelPeerInfo validatePeerInfo(SignedMessage peer_signed_message)
throws ValidationException
{
SignedMessagePayload payload = validateSignedMessage(peer_signed_message);

if (!payload.hasPeerInfo()) throw new ValidationException("Signed peer info has no peer info");

ChannelPeerInfo peer_info = payload.getPeerInfo();

AddressSpecHash signed_address = AddressUtil.getHashForSpec(payload.getClaim());
AddressSpecHash node_id = new AddressSpecHash(peer_info.getAddressSpecHash());
if (!signed_address.equals(node_id))
{
throw new ValidationException("Signed address does not match node id");
}

return peer_info;

}
}
9 changes: 4 additions & 5 deletions src/ChannelSubscriber.java
@@ -1,20 +1,19 @@
package snowblossom.channels;


import com.google.common.collect.ImmutableList;
import com.google.protobuf.ByteString;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.LinkedList;
import snowblossom.channels.proto.*;

import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import snowblossom.channels.proto.*;
import snowblossom.lib.DaemonThreadFactory;
import com.google.common.collect.ImmutableList;
import java.util.List;

/**
* Manage channels we are tracking
Expand Down
1 change: 0 additions & 1 deletion src/ChannelWatcherInterface.java
Expand Up @@ -2,7 +2,6 @@

import snowblossom.channels.proto.*;


public interface ChannelWatcherInterface
{

Expand Down
21 changes: 16 additions & 5 deletions src/DHTMaintainer.java
Expand Up @@ -33,6 +33,11 @@ public class DHTMaintainer extends PeriodicThread
private HashMap<AddressSpecHash, Long> connection_attempt_times = new HashMap<>(16,0.5f);
private ImmutableSet<AddressSpecHash> current_links;

// TODO - select older nodes if we get don't have any links
// Something like max_age = 1h.
// If not enough links, max_age = Math.max(max_age * 2, 1 year)
// else max_age = 1h (to reset to one hour once we have some reasonable number)
// https://github.com/snowblossomcoin/channels/issues/21
public DHTMaintainer(ChannelNode node)
{
super(5000L);
Expand Down Expand Up @@ -88,12 +93,19 @@ public void runPass() throws Exception
if (connected_peers.size() + connect_map.size() < ChannelGlobals.NEAR_POINTS)
{
// Add Seeds
for(ChannelPeerInfo info : getSeeds())
if (node.getConfig().getBoolean("testing_skip_seeds"))
{
AddressSpecHash id = new AddressSpecHash(info.getAddressSpecHash());
if (!connected_peers.contains(id))
logger.warning("Not using seeds - config option 'testing_skip_seeds'");
}
else
{
for(ChannelPeerInfo info : getSeeds())
{
connect_map.put(id, info);
AddressSpecHash id = new AddressSpecHash(info.getAddressSpecHash());
if (!connected_peers.contains(id))
{
connect_map.put(id, info);
}
}
}
}
Expand Down Expand Up @@ -297,7 +309,6 @@ public static List<ChannelPeerInfo> getSeeds()
.build())
.build());


return seed_list;

}
Expand Down
16 changes: 4 additions & 12 deletions src/DHTServer.java
Expand Up @@ -63,18 +63,10 @@ public void importPeer(SignedMessage peer_signed_message)
{
try
{
SignedMessagePayload payload = ChannelSigUtil.validateSignedMessage(peer_signed_message);
ChannelPeerInfo peer_info = ChannelSigUtil.validatePeerInfo(peer_signed_message);

if (payload.getPeerInfo() == null) throw new ValidationException("Signed peer info has no peer info");

ChannelPeerInfo peer_info = payload.getPeerInfo();

AddressSpecHash signed_address = AddressUtil.getHashForSpec(payload.getClaim());
AddressSpecHash node_id = new AddressSpecHash(peer_info.getAddressSpecHash());
if (!signed_address.equals(node_id))
{
throw new ValidationException("Signed address does not match node id");
}
SignedMessagePayload payload = ChannelSigUtil.quickPayload(peer_signed_message);

LocalPeerInfo.Builder new_local_info = LocalPeerInfo.newBuilder();

Expand Down Expand Up @@ -105,8 +97,8 @@ public SignedMessage getSignedPeerInfoSelf()
{
SignedMessagePayload.Builder p = SignedMessagePayload.newBuilder();
p.setPeerInfo( node.getNetworkExaminer().createPeerInfo() );

return node.signMessage(p.build());
SignedMessage spi = node.signMessage(p.build());
return spi;
}

@Override
Expand Down

0 comments on commit c2ade76

Please sign in to comment.