Tunnels: Switch from SHA256 to SipHash for arbitrary deterministic sort

of peers. For efficiency.
ref: http://zzz.i2p/topics/3082
thx: jogger
This commit is contained in:
zzz
2021-06-08 09:44:17 -04:00
parent aee9a3f639
commit c2c922b665
2 changed files with 38 additions and 28 deletions

View File

@@ -17,6 +17,7 @@ import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import net.i2p.crypto.SipHashInline;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.router.RouterAddress;
@@ -1352,6 +1353,12 @@ public class ProfileOrganizer {
private void locked_selectPeers(Map<Hash, PeerProfile> peers, int howMany, Set<Hash> toExclude,
Set<Hash> matches, Hash randomKey, Slice subTierMode) {
List<Hash> all = new ArrayList<Hash>(peers.keySet());
byte[] rk = randomKey.getData();
// we use the first half of the random key here,
// the second half is used in TunnelPeerSelector.
long k0 = DataHelper.fromLong8(rk, 0);
long k1 = DataHelper.fromLong8(rk, 8);
// use RandomIterator to avoid shuffling the whole thing
for (Iterator<Hash> iter = new RandomIterator<Hash>(all); (matches.size() < howMany) && iter.hasNext(); ) {
Hash peer = iter.next();
@@ -1361,7 +1368,7 @@ public class ProfileOrganizer {
continue;
if (_us.equals(peer))
continue;
int subTier = getSubTier(peer, randomKey);
int subTier = getSubTier(peer, k0, k1);
if ((subTier & subTierMode.mask) != subTierMode.val)
continue;
boolean ok = isSelectable(peer);
@@ -1375,15 +1382,13 @@ public class ProfileOrganizer {
/**
* Implement a random, deterministic split into 4 groups that cannot be predicted by
* others.
*
* @param k0 random key part 1
* @param k1 random key part 2
* @return 0-3
*/
private int getSubTier(Hash peer, Hash randomKey) {
// input is first 64 bytes; output is last 32 bytes
byte[] data = new byte[96];
System.arraycopy(peer.getData(), 0, data, 0, 32);
System.arraycopy(randomKey.getData(), 0, data, 32, 32);
_context.sha().calculateHash(data, 0, 64, data, 64);
return data[64] & 0x03;
private int getSubTier(Hash peer, long k0, long k1) {
return ((int) SipHashInline.hash24(k0, k1, peer.getData())) & 0x03;
}
public boolean isSelectable(Hash peer) {

View File

@@ -1,7 +1,6 @@
package net.i2p.router.tunnel.pool;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -15,6 +14,7 @@ import java.util.StringTokenizer;
import net.i2p.crypto.EncType;
import net.i2p.crypto.SHA256Generator;
import net.i2p.crypto.SigType;
import net.i2p.crypto.SipHashInline;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
@@ -26,7 +26,6 @@ import net.i2p.router.RouterContext;
import net.i2p.router.TunnelPoolSettings;
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
import net.i2p.router.transport.TransportUtil;
import net.i2p.router.util.HashDistance;
import net.i2p.util.Log;
import net.i2p.util.SystemVersion;
import net.i2p.util.VersionComparator;
@@ -653,34 +652,40 @@ public abstract class TunnelPeerSelector extends ConnectChecker {
* to maximize his chances of those two routers being at opposite
* ends of a tunnel.
*
* Previous:
* Previous Previous:
* d(l, h) - d(r, h)
*
* Now:
* Previous:
* d((H(l+h), h) - d(H(r+h), h)
*
* Now:
* SipHash using h to generate the SipHash keys
* then siphash(l) - siphash(r)
*/
private static class HashComparator implements Comparator<Hash>, Serializable {
private final Hash _hash, tmp;
private final byte[] data;
private final long k0, k1;
/** not thread safe */
/**
* not thread safe
*
* @param h container for sort keys, not used as a Hash
*/
private HashComparator(Hash h) {
_hash = h;
tmp = new Hash(new byte[Hash.HASH_LENGTH]);
data = new byte[2*Hash.HASH_LENGTH];
System.arraycopy(_hash.getData(), 0, data, Hash.HASH_LENGTH, Hash.HASH_LENGTH);
byte[] b = h.getData();
// we use the first half of the random key in ProfileOrganizer.getSubTier(),
// so use the last half here
k0 = DataHelper.fromLong8(b, 16);
k1 = DataHelper.fromLong8(b, 24);
}
public int compare(Hash l, Hash r) {
System.arraycopy(l.getData(), 0, data, 0, Hash.HASH_LENGTH);
byte[] tb = tmp.getData();
// don't use caching version of calculateHash()
SHA256Generator.getInstance().calculateHash(data, 0, 2*Hash.HASH_LENGTH, tb, 0);
BigInteger ll = HashDistance.getDistance(_hash, tmp);
System.arraycopy(r.getData(), 0, data, 0, Hash.HASH_LENGTH);
SHA256Generator.getInstance().calculateHash(data, 0, 2*Hash.HASH_LENGTH, tb, 0);
BigInteger rr = HashDistance.getDistance(_hash, tmp);
return ll.compareTo(rr);
long lh = SipHashInline.hash24(k0, k1, l.getData());
long rh = SipHashInline.hash24(k0, k1, r.getData());
if (lh > rh)
return 1;
if (lh < rh)
return -1;
return 0;
}
}