diff --git a/ethereumj-core/src/main/java/org/ethereum/net/dht/Bucket.java b/ethereumj-core/src/main/java/org/ethereum/net/dht/Bucket.java new file mode 100644 index 00000000..5c7e06b1 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/net/dht/Bucket.java @@ -0,0 +1,130 @@ +package org.ethereum.net.dht; + +import java.util.ArrayList; +import java.util.List; + +public class Bucket { + + public static int MAX_KADEMLIA_K = 5; + + // if bit = 1 go left + Bucket left; + + // if bit = 0 go right + Bucket right; + + String name; + + List peerIds = new ArrayList<>(); + + + public Bucket(String name) { + this.name = name; + } + + + public void add(PeerId peerId) { + + if (peerId == null) throw new Error("Not a leaf"); + + if ( peerIds == null){ + + if (peerId.nextBit(name) == 1) + left.add(peerId); + else + right.add(peerId); + + return; + } + + peerIds.add(peerId); + + if (peerIds.size() > MAX_KADEMLIA_K) + splitBucket(); + } + + public void splitBucket() { + left = new Bucket(name + "1"); + right = new Bucket(name + "0"); + + for (PeerId id : peerIds) { + if (id.nextBit(name) == 1) + left.add(id); + else + right.add(id); + } + + this.peerIds = null; + } + + + public Bucket left() { + return left; + } + + public Bucket right() { + return right; + } + + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + + sb.append(name).append("\n"); + + if (peerIds == null) return sb.toString(); + + for (PeerId id : peerIds) + sb.append(id.toBinaryString()).append("\n"); + + return sb.toString(); + } + + + public void traverseTree(DoOnTree doOnTree) { + + if (left != null) left.traverseTree(doOnTree); + if (right != null) right.traverseTree(doOnTree); + + doOnTree.call(this); + } + + + /********************/ + // tree operations // + /********************/ + + public interface DoOnTree { + + public void call(Bucket bucket); + } + + + public static class SaveLeaf implements DoOnTree { + + List leafs = new ArrayList<>(); + + @Override + public void call(Bucket bucket) { + if (bucket.peerIds != null) leafs.add(bucket); + } + + public List getLeafs() { + return leafs; + } + + public void setLeafs(List leafs) { + this.leafs = leafs; + } + } + + public String getName(){ + return name; + } + + public List getPeerIds() { + return peerIds; + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/net/dht/DHTUtils.java b/ethereumj-core/src/main/java/org/ethereum/net/dht/DHTUtils.java new file mode 100644 index 00000000..27282717 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/net/dht/DHTUtils.java @@ -0,0 +1,23 @@ +package org.ethereum.net.dht; + +import java.util.List; + +import static org.ethereum.net.dht.Bucket.*; + +public class DHTUtils { + + public static void printAllLeafs(Bucket root){ + SaveLeaf saveLeaf = new SaveLeaf(); + root.traverseTree(saveLeaf); + + for (Bucket bucket : saveLeaf.getLeafs()) + System.out.println(bucket); + } + + public static List getAllLeafs(Bucket root){ + SaveLeaf saveLeaf = new SaveLeaf(); + root.traverseTree(saveLeaf); + + return saveLeaf.getLeafs(); + } +} diff --git a/ethereumj-core/src/main/java/org/ethereum/net/dht/PeerId.java b/ethereumj-core/src/main/java/org/ethereum/net/dht/PeerId.java new file mode 100644 index 00000000..557c97fd --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/net/dht/PeerId.java @@ -0,0 +1,62 @@ +package org.ethereum.net.dht; + +import org.ethereum.crypto.HashUtil; +import org.spongycastle.util.BigIntegers; +import org.spongycastle.util.encoders.Hex; + +import java.math.BigInteger; + +public class PeerId { + byte[] data; + + public PeerId(byte[] data) { + this.data = data; + } + + public PeerId() { + HashUtil.randomPeerId(); + } + + public byte nextBit(String startPatern) { + + if (this.toBinaryString().startsWith(startPatern + "1")) + return 1; + else + return 0; + } + + public byte[] calcDistance(PeerId toPeerId) { + + BigInteger aPeer = new BigInteger(data); + BigInteger bPeer = new BigInteger(toPeerId.data); + + BigInteger distance = aPeer.xor(bPeer); + return BigIntegers.asUnsignedByteArray(distance); + } + + + public byte[] getData() { + return data; + } + + public void setData(byte[] data) { + this.data = data; + } + + @Override + public String toString() { + return "PeerId{" + + "data=" + Hex.toHexString(data) + + '}'; + } + + public String toBinaryString() { + + BigInteger bi = new BigInteger(1, data); + String out = String.format("%512s", bi.toString(2)); + out = out.replace(' ', '0'); + + return out; + } + +}