Merged latest develop.

Added Scanner implementation for BlockLoader.
This commit is contained in:
Adrian Tiberius 2015-06-17 14:50:47 +02:00
parent 40b0749304
commit a75f74f9f2
57 changed files with 2185 additions and 324 deletions

View File

@ -46,6 +46,7 @@ dependencies {
apt 'com.google.dagger:dagger-compiler:2.0'
compile fileTree(dir: 'libs', include: ['*.jar'])
compile (project(':ethereumj-core')) {
exclude group: 'commons-logging', module: 'commons-logging'
exclude group: "org.apache.commons", module: "commons-pool2"
exclude group: "org.slf4j", module: "slf4j-log4j12"
exclude group: "org.hibernate", module: "hibernate-core"

View File

@ -46,7 +46,7 @@ public class EthereumManager {
SystemProperties.CONFIG.activePeerPort(),
SystemProperties.CONFIG.activePeerNodeid());
} else {
duration = ethereum.getBlockLoader().loadBlocks();
ethereum.getBlockLoader().loadBlocks();
}
return duration;
}

View File

@ -260,11 +260,14 @@ public class OrmLiteBlockStoreDatabase extends OrmLiteSqliteOpenHelper implement
public boolean flush(final List<Block> blocks) {
reset();
try {
TransactionManager.callInTransaction(getBlockDao().getConnectionSource(),
new Callable<Void>() {
public Void call() throws Exception {
for (Block block : blocks) {
int lastIndex = blocks.size() - 1;
for (int i = 0; i < 1000; ++i){
Block block = blocks.get(lastIndex - i);
BlockVO blockVO = new BlockVO(block.getNumber(), block.getHash(), block.getEncoded(), block.getCumulativeDifficulty());
save(blockVO);
}

View File

@ -7,7 +7,6 @@ import com.j256.ormlite.android.apptools.OpenHelperManager;
import org.ethereum.android.datasource.LevelDbDataSource;
import org.ethereum.android.db.InMemoryBlockStore;
import org.ethereum.android.db.OrmLiteBlockStoreDatabase;
import org.ethereum.android.db.BlockStoreImpl;
import org.ethereum.config.SystemProperties;
import org.ethereum.core.BlockchainImpl;
import org.ethereum.core.Wallet;
@ -20,7 +19,7 @@ import org.ethereum.facade.Repository;
import org.ethereum.listener.CompositeEthereumListener;
import org.ethereum.listener.EthereumListener;
import org.ethereum.manager.AdminInfo;
import org.ethereum.manager.BlockLoader;
import org.ethereum.android.manager.BlockLoader;
import org.ethereum.manager.WorldManager;
import org.ethereum.net.MessageQueue;
import org.ethereum.net.client.PeerClient;

View File

@ -0,0 +1,85 @@
package org.ethereum.android.manager;
import org.ethereum.core.Block;
import org.ethereum.core.ImportResult;
import org.ethereum.facade.Blockchain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.io.FileInputStream;
import java.io.IOException;
import org.ethereum.android.util.Scanner;
import javax.inject.Inject;
import javax.inject.Singleton;
import static org.ethereum.config.SystemProperties.CONFIG;
@Singleton
public class BlockLoader extends org.ethereum.manager.BlockLoader {
private static final Logger logger = LoggerFactory.getLogger("BlockLoader");
Scanner scanner = null;
@Inject
public BlockLoader(Blockchain blockchain) {
super(blockchain);
}
public void loadBlocks(){
String fileSrc = CONFIG.blocksLoader();
try {
long startTime = System.currentTimeMillis();
FileInputStream inputStream = null;
inputStream = new FileInputStream(fileSrc);
scanner = new Scanner(inputStream);
System.out.println("Loading blocks: " + fileSrc);
while (scanner.hasNext()) {
byte[] blockRLPBytes = Hex.decode(scanner.nextLine());
Block block = new Block(blockRLPBytes);
long t1 = System.nanoTime();
if (block.getNumber() > blockchain.getBestBlock().getNumber()){
blockchain.tryToConnect(block);
long t1_ = System.nanoTime();
float elapsed = ((float)(t1_ - t1) / 1_000_000);
if (block.getNumber() % 1000 == 0 || elapsed > 10_000) {
String result = String.format("Imported block #%d took: [%02.2f msec]",
block.getNumber(), elapsed);
System.out.println(result);
}
} else {
if (block.getNumber() % 10000 == 0)
System.out.println("Skipping block #" + block.getNumber());
}
block = null;
blockRLPBytes = null;
}
long duration = System.currentTimeMillis() - startTime;
System.out.println("Finished loading blocks in " + (duration / 1000) + " seconds (" + (duration / 60000) + " minutes)");
//return duration;
} catch (IOException e) {
e.printStackTrace();
logger.error(e.getMessage(), e);
} catch (Exception e) {
logger.error(e.getMessage(), e);
System.out.println(e.getMessage());
}
//return 0;
}
}

View File

@ -0,0 +1,92 @@
package org.ethereum.android.util;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
class Locale {
final static int US=0;
}
public class Scanner {
private BufferedInputStream in;
int c;
int offset;
boolean atBeginningOfLine;
public Scanner(InputStream stream) {
in = new BufferedInputStream(stream);
try {
atBeginningOfLine = true;
c = (char)in.read();
} catch (IOException e) {
c = -1;
}
}
public boolean hasNext() {
if (!atBeginningOfLine)
throw new Error("hasNext only works "+
"after a call to nextLine");
return c != -1;
}
public String next() {
StringBuffer sb = new StringBuffer();
atBeginningOfLine = false;
try {
while (c <= ' ') {
c = in.read();
}
while (c > ' ') {
sb.append((char)c);
c = in.read();
}
} catch (IOException e) {
c = -1;
return "";
}
return sb.toString();
}
public String nextLine() {
StringBuffer sb = new StringBuffer();
atBeginningOfLine = true;
try {
while (c != '\n') {
sb.append((char)c);
c = in.read();
}
c = in.read();
} catch (IOException e) {
c = -1;
return "";
}
return sb.toString();
}
public int nextInt() {
String s = next();
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
return 0; //throw new Error("Malformed number " + s);
}
}
public double nextDouble() {
return new Double(next());
}
public long nextLong() {
return Long.parseLong(next());
}
public void useLocale(int l) {}
}

View File

@ -9,13 +9,11 @@ buildscript {
dependencies {
classpath 'me.champeau.gradle:antlr4-gradle-plugin:0.1'
classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:3.0.1'
//classpath "gradle.plugin.com.ewerk.gradle.plugins:dagger-plugin:1.0.0"
}
}
plugins {
id 'java'
// id "com.ewerk.gradle.plugins.dagger" version "1.0.0"
id 'application'
id 'jacoco'
id 'com.github.johnrengelman.shadow' version '1.2.1'
@ -32,7 +30,7 @@ repositories {
sourceCompatibility = 1.7
mainClassName = 'org.ethereum.Start'
applicationDefaultJvmArgs = ["-server", "-Xms3g", "-Xss32m"]
applicationDefaultJvmArgs = ["-server", "-Xss32m"]
ext.generatedSrcDir = file('src/gen/java')
@ -129,43 +127,41 @@ dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile('io.netty:netty-all:4.0.28.Final') {
exclude group: 'commons-logging', module: 'commons-logging'
}
compile "com.madgag.spongycastle:core:${scastleVersion}"
// for SHA3 and SECP256K1
compile "com.madgag.spongycastle:prov:${scastleVersion}"
// for SHA3 and SECP256K1
compile('io.netty:netty-all:4.0.28.Final')
compile "com.madgag.spongycastle:core:${scastleVersion}" // for SHA3 and SECP256K1
compile "com.madgag.spongycastle:prov:${scastleVersion}" // for SHA3 and SECP256K1
compile "org.iq80.leveldb:leveldb:${leveldbVersion}"
compile('com.cedarsoftware:java-util:1.8.0') {
exclude group: 'commons-logging', module: 'commons-logging'
} // for deep equals
compile "org.fusesource.leveldbjni:leveldbjni:1.8"
compile 'org.antlr:antlr4-runtime:4.5' // for serpent compilation
compile 'com.yuvalshavit:antlr-denter:1.1'
compile "org.slf4j:slf4j-log4j12:${slf4jVersion}"
compile 'org.codehaus.jackson:jackson-mapper-asl:1.9.13'
compile 'com.google.code.findbugs:jsr305:3.0.0'
compile 'com.fasterxml.jackson.core:jackson-databind:2.2.0'
compile 'org.apache.commons:commons-collections4:4.0'
compile 'commons-io:commons-io:2.4'
compile "com.cedarsoftware:java-util:1.8.0" // for deep equals
compile "org.antlr:antlr4-runtime:4.5" // for serpent compilation
compile "com.yuvalshavit:antlr-denter:1.1"
compile "org.javassist:javassist:3.15.0-GA"
compile "org.slf4j:slf4j-api:${slf4jVersion}"
compile "log4j:log4j:${log4jVersion}"
compile "org.codehaus.jackson:jackson-mapper-asl:1.9.13"
compile "com.google.code.findbugs:jsr305:3.0.0"
compile "com.fasterxml.jackson.core:jackson-databind:2.5.1"
compile "org.apache.commons:commons-collections4:4.0"
compile "commons-codec:commons-codec:1.10"
compile "com.h2database:h2:1.4.187"
compile "org.hibernate:hibernate-core:${hibernateVersion}"
compile "org.hibernate:hibernate-entitymanager:${hibernateVersion}"
compile "commons-dbcp:commons-dbcp:1.4"
compile "redis.clients:jedis:2.6.0"
compile('com.googlecode.json-simple:json-simple:1.1.1') {
compile "com.h2database:h2:1.4.187"
compile "org.slf4j:slf4j-log4j12:${slf4jVersion}"
compile "log4j:apache-log4j-extras:${log4jVersion}"
compile("com.googlecode.json-simple:json-simple:1.1.1") {
exclude group: 'junit', module: 'junit'
exclude group: 'xml-apis', module: 'xml-apis'
}
compile 'commons-io:commons-io:2.4'
testCompile "junit:junit:${junitVersion}"
testCompile 'com.google.dagger:dagger:2.1-SNAPSHOT'
testCompile 'com.google.dagger:dagger-compiler:2.0'
}

View File

@ -295,7 +295,8 @@ public class BlockchainImpl implements Blockchain {
storeBlock(block, receipts);
if (block.getNumber() % 20_000 == 0) {
if (adminInfo.isConsensus() &&
block.getNumber() % 5_000 == 0) {
repository.flush();
blockStore.flush();
}

View File

@ -324,9 +324,8 @@ public class TransactionExecutor {
track.delete(address.getLast20Bytes());
}
// Keep execution logs todo: that yet
//*cpp* if (m_ext)
// m_logs = m_ext->sub.logs;
if (result != null)
logs = result.getLogInfoList();
if (result.getLogInfoList() != null){

View File

@ -2,12 +2,15 @@ package org.ethereum.datasource;
import org.ethereum.config.SystemProperties;
import org.fusesource.leveldbjni.JniDBFactory;
import org.fusesource.leveldbjni.internal.JniDB;
import org.iq80.leveldb.CompressionType;
import org.iq80.leveldb.DB;
import org.iq80.leveldb.DBIterator;
import org.iq80.leveldb.Options;
import org.iq80.leveldb.WriteBatch;
import org.iq80.leveldb.impl.Iq80DBFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -46,6 +49,10 @@ public class LevelDbDataSource implements KeyValueDataSource {
Options options = new Options();
options.createIfMissing(true);
options.compressionType(CompressionType.NONE);
options.blockSize(10 * 1024);
options.writeBufferSize(10 * 1024);
options.cacheSize(0);
try {
logger.debug("Opening database");
File dbLocation = new File(System.getProperty("user.dir") + "/" +
@ -57,7 +64,23 @@ public class LevelDbDataSource implements KeyValueDataSource {
}
logger.debug("Initializing new or existing database: '{}'", name);
db = factory.open(fileLocation, options);
try {
db = JniDBFactory.factory.open(fileLocation, options);
} catch (Throwable e) {
System.out.println("No native version of LevelDB found");
}
String cpu = System.getProperty("sun.arch.data.model");
String os = System.getProperty("os.name");
if (db instanceof JniDB)
System.out.println("Native version of LevelDB loaded for: " + os + "." + cpu + "bit");
else{
System.out.println("Pure Java version of LevelDB loaded");
db = Iq80DBFactory.factory.open(fileLocation, options);
}
} catch (IOException ioe) {
logger.error(ioe.getMessage(), ioe);

View File

@ -2,6 +2,7 @@ package org.ethereum.db;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.util.HashMap;
import java.util.HashSet;
@ -35,6 +36,12 @@ public class DetailsDataStore {
details = new ContractDetailsImpl(data);
cache.put( wrap(key), details);
float out = ((float)data.length) / 1048576;
if (out > 10) {
String sizeFmt = String.format("%02.2f", out);
System.out.println("loaded: key: " + Hex.toHexString(key) + " size: " + sizeFmt + "MB");
}
}
return details;
@ -57,11 +64,13 @@ public class DetailsDataStore {
long t = System.nanoTime();
Map<byte[], byte[]> batch = new HashMap<>();
long totalSize = 0;
for (ByteArrayWrapper key : cache.keySet()){
ContractDetails contractDetails = cache.get(key);
byte[] value = contractDetails.getEncoded();
db.put(key.getData(), value);
batch.put(key.getData(), value);
totalSize += value.length;
}
db.getDb().updateBatch(batch);
@ -72,11 +81,18 @@ public class DetailsDataStore {
long keys = cache.size();
byte[] aKey = Hex.decode("b61662398570293e4f0d25525e2b3002b7fe0836");
ContractDetails aDetails = cache.get(wrap(aKey));
cache.clear();
removes.clear();
if (aDetails != null) cache.put(wrap(aKey), aDetails);
long t_ = System.nanoTime();
gLogger.info("Flush details in: {} ms, {} keys", ((float)(t_ - t) / 1_000_000), keys);
String sizeFmt = String.format("%02.2f", ((float)totalSize) / 1048576);
gLogger.info("Flush details in: {} ms, {} keys, {}MB",
((float)(t_ - t) / 1_000_000), keys, sizeFmt);
}

View File

@ -189,10 +189,19 @@ public class InMemoryBlockStore implements BlockStore{
long t_ = System.nanoTime();
Session s = sessionFactory.openSession();
// clear old blocks
s.beginTransaction();
for (Block block : blocks){
s.createQuery("delete from BlockVO").executeUpdate();
s.getTransaction().commit();
s.beginTransaction();
int lastIndex = blocks.size() - 1;
for (int i = 0; i < 1000; ++i){
Block block = blocks.get(lastIndex - i);
BlockVO blockVO = new BlockVO(block.getNumber(), block.getHash(), block.getEncoded(), block.getCumulativeDifficulty());
s.saveOrUpdate(blockVO);
s.save(blockVO);
}
s.getTransaction().commit();

View File

@ -171,12 +171,7 @@ public class RepositoryImpl implements Repository {
gLogger.info("flushing to disk");
dds.flush();
long t = System.nanoTime();
worldState.sync();
long t__ = System.nanoTime();
gLogger.info("Flush state in: {} ms", ((float)(t__ - t) / 1_000_000));
}

View File

@ -3,12 +3,12 @@ package org.ethereum.manager;
import org.ethereum.core.Block;
import org.ethereum.facade.Blockchain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
import java.util.Scanner;
import javax.inject.Inject;
@ -19,9 +19,7 @@ import static org.ethereum.config.SystemProperties.CONFIG;
@Singleton
public class BlockLoader {
private static final Logger logger = LoggerFactory.getLogger("BlockLoader");
private Blockchain blockchain;
protected Blockchain blockchain;
Scanner scanner = null;
@ -30,11 +28,11 @@ public class BlockLoader {
this.blockchain = blockchain;
}
public long loadBlocks(){
public void loadBlocks(){
String fileSrc = CONFIG.blocksLoader();
try {
long startTime = System.currentTimeMillis();
FileInputStream inputStream = null;
inputStream = new FileInputStream(fileSrc);
scanner = new Scanner(inputStream, "UTF-8");
@ -50,10 +48,15 @@ public class BlockLoader {
if (block.getNumber() > blockchain.getBestBlock().getNumber()){
blockchain.tryToConnect(block);
long t1_ = System.nanoTime();
float elapsed = ((float)(t1_ - t1) / 1_000_000);
if (block.getNumber() % 1000 == 0 || elapsed > 10_000) {
String result = String.format("Imported block #%d took: [%02.2f msec]",
block.getNumber(), ((float)(t1_ - t1) / 1_000_000));
block.getNumber(), elapsed);
System.out.println(result);
}
} else{
if (block.getNumber() % 10000 == 0)
@ -62,16 +65,9 @@ public class BlockLoader {
}
long duration = System.currentTimeMillis() - startTime;
System.out.println("Finished loading blocks in " + (duration / 1000) + " seconds (" + (duration / 60000) + " minutes)");
return duration;
} catch (IOException e) {
e.printStackTrace();
logger.error(e.getMessage(), e);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return 0;
}

View File

@ -71,6 +71,8 @@ public abstract class Message {
/* [2] Crate signature*/
ECKey.ECDSASignature signature = privKey.sign(forSig);
signature.v -= 27;
byte[] sigBytes =
merge(BigIntegers.asUnsignedByteArray(signature.r),
BigIntegers.asUnsignedByteArray(signature.s), new byte[]{signature.v});
@ -116,6 +118,12 @@ public abstract class Message {
return outKey;
}
public byte[] getNodeId() {
byte[] nodeID = new byte[64];
System.arraycopy(getKey().getPubKey(), 1, nodeID, 0, 64);
return nodeID;
}
public byte[] getPacket() {
return wire;
}

View File

@ -5,9 +5,10 @@ import org.ethereum.util.RLPList;
import org.spongycastle.util.encoders.Hex;
import java.nio.charset.Charset;
import java.util.Arrays;
import static org.ethereum.util.ByteUtil.byteArrayToInt;
import static org.ethereum.util.ByteUtil.intToBytesNoLeadZeroes;
import static org.ethereum.util.ByteUtil.intToBytes;
public class Node {
@ -28,9 +29,25 @@ public class Node {
byte[] hostB = nodeRLP.get(0).getRLPData();
byte[] portB = nodeRLP.get(1).getRLPData();
byte[] idB = nodeRLP.get(2).getRLPData();
byte[] idB;
String host = new String(hostB, Charset.forName("UTF-8"));
if (nodeRLP.size() > 3) {
idB = nodeRLP.get(3).getRLPData();
} else {
idB = nodeRLP.get(2).getRLPData();
}
StringBuilder sb = new StringBuilder();
sb.append(hostB[0] & 0xFF);
sb.append(".");
sb.append(hostB[1] & 0xFF);
sb.append(".");
sb.append(hostB[2] & 0xFF);
sb.append(".");
sb.append(hostB[3] & 0xFF);
// String host = new String(hostB, Charset.forName("UTF-8"));
String host = sb.toString();
int port = byteArrayToInt(portB);
this.host = host;
@ -66,7 +83,7 @@ public class Node {
public byte[] getRLP() {
byte[] rlphost = RLP.encodeElement(host.getBytes(Charset.forName("UTF-8")));
byte[] rlpPort = RLP.encodeElement(intToBytesNoLeadZeroes(port));
byte[] rlpPort = RLP.encodeElement(intToBytes(port));
byte[] rlpId = RLP.encodeElement(id);
byte[] data = RLP.encodeList(rlphost, rlpPort, rlpId);
@ -81,4 +98,26 @@ public class Node {
", id=" + Hex.toHexString(id) +
'}';
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (o == this) {
return true;
}
if (o instanceof Node) {
return Arrays.equals(((Node) o).getId(), this.getId());
}
return false;
}
}

View File

@ -5,7 +5,7 @@ import org.ethereum.util.ByteUtil;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList;
import org.spongycastle.util.encoders.Hex;
//import org.spongycastle.util.encoders.Hex;
import java.nio.charset.Charset;
@ -28,11 +28,20 @@ public class PingMessage extends Message {
byte[] tmpPort = longToBytes(port);
byte[] rlpPort = RLP.encodeElement(stripLeadingZeroes(tmpPort));
byte[] rlpIpTo = RLP.encodeElement(host.getBytes());
byte[] tmpPortTo = longToBytes(port);
byte[] rlpPortTo = RLP.encodeElement(stripLeadingZeroes(tmpPortTo));
byte[] tmpExp = longToBytes(expiration);
byte[] rlpExp = RLP.encodeElement(stripLeadingZeroes(tmpExp));
byte[] type = new byte[]{1};
byte[] data = RLP.encodeList(rlpIp, rlpPort, rlpExp);
byte[] version = new byte[]{4};
byte[] rlpVer = RLP.encodeElement(version);
byte[] rlpFromList = RLP.encodeList(rlpIp, rlpPort, rlpPort);
byte[] rlpToList = RLP.encodeList(rlpIpTo, rlpPortTo, rlpPortTo);
byte[] data = RLP.encodeList(rlpVer, rlpFromList, rlpToList, rlpExp);
PingMessage ping = new PingMessage();
ping.encode(type, data, privKey);
@ -48,14 +57,15 @@ public class PingMessage extends Message {
public void parse(byte[] data) {
RLPList list = RLP.decode2(data);
list = (RLPList) list.get(0);
RLPList dataList = (RLPList) list.get(0);
RLPList fromList = (RLPList) dataList.get(2);
byte[] ipB = list.get(0).getRLPData();
byte[] ipB = fromList.get(0).getRLPData();
this.host = new String(ipB, Charset.forName("UTF-8"));
this.port = ByteUtil.byteArrayToInt(list.get(1).getRLPData());
this.port = ByteUtil.byteArrayToInt(fromList.get(1).getRLPData());
RLPItem expires = (RLPItem) list.get(2);
RLPItem expires = (RLPItem) dataList.get(3);
this.expires = ByteUtil.byteArrayToLong(expires.getRLPData());
}

View File

@ -7,13 +7,42 @@ import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList;
import org.spongycastle.util.encoders.Hex;
import java.nio.charset.Charset;
//import java.nio.charset.Charset;
import static org.ethereum.util.ByteUtil.longToBytes;
import static org.ethereum.util.ByteUtil.stripLeadingZeroes;
public class PongMessage extends Message {
byte[] token; // token is the MDC of the ping
long expires;
public static PongMessage create(byte[] token, String host, int port, ECKey privKey) {
long expiration = 3 + System.currentTimeMillis() / 1000;
byte[] rlpIp = RLP.encodeElement(host.getBytes());
byte[] tmpPort = longToBytes(port);
byte[] rlpPort = RLP.encodeElement(stripLeadingZeroes(tmpPort));
byte[] rlpToList = RLP.encodeList(rlpIp, rlpPort, rlpPort);
/* RLP Encode data */
byte[] rlpToken = RLP.encodeElement(token);
byte[] tmpExp = longToBytes(expiration);
byte[] rlpExp = RLP.encodeElement(stripLeadingZeroes(tmpExp));
byte[] type = new byte[]{2};
byte[] data = RLP.encodeList(rlpToList, rlpToken, rlpExp);
PongMessage pong = new PongMessage();
pong.encode(type, data, privKey);
pong.token = token;
pong.expires = expiration;
return pong;
}
public static PongMessage create(byte[] token, ECKey privKey) {

View File

@ -0,0 +1,92 @@
package org.ethereum.net.rlpx.discover;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.socket.DatagramPacket;
import org.ethereum.crypto.ECKey;
import org.ethereum.net.rlpx.FindNodeMessage;
import org.ethereum.net.rlpx.Message;
import org.ethereum.net.rlpx.Node;
import org.ethereum.net.rlpx.discover.table.KademliaOptions;
import org.ethereum.net.rlpx.discover.table.NodeTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
public class DiscoverTask implements Runnable {
private static final Logger logger = LoggerFactory.getLogger("discover");
Channel channel;
NodeTable table;
ECKey key;
byte[] nodeId;
DiscoverTask(byte[] nodeId, Channel channel, ECKey key, NodeTable table) {
this.nodeId = nodeId;
this.channel = channel;
this.key = key;
this.table = table;
}
@Override
public void run() {
discover(nodeId, 0, new ArrayList<Node>());
}
public synchronized void discover(byte[] nodeId, int round, List<Node> prevTried) {
try {
// if (!channel.isOpen() || round == KademliaOptions.MAX_STEPS) {
// logger.info("{}", String.format("Nodes discovered %d ", table.getAllNodes().size()));
// return;
// }
if (round == KademliaOptions.MAX_STEPS) {
logger.info("{}", String.format("Terminating discover after %d rounds.", round));
logger.info("{}", String.format("Nodes discovered %d ", table.getNodesCount()));
return;
}
List<Node> closest = table.getClosestNodes(nodeId);
List<Node> tried = new ArrayList<>();
for (Node n : closest) {
if (!tried.contains(n) && !prevTried.contains(n)) {
try {
Message findNode = FindNodeMessage.create(nodeId, key);
DatagramPacket packet = new DatagramPacket(
Unpooled.copiedBuffer(findNode.getPacket()),
new InetSocketAddress(n.getHost(), n.getPort()));
channel.write(packet);
tried.add(n);
} catch (Exception ex) {
logger.info("{}", ex);
}
}
if (tried.size() == KademliaOptions.ALPHA) {
break;
}
}
channel.flush();
if (tried.isEmpty()) {
logger.info("{}", String.format("Terminating discover after %d rounds.", round));
logger.info("{}", String.format("Nodes discovered %d ", table.getNodesCount()));
return;
}
tried.addAll(prevTried);
discover(nodeId, round + 1, tried);
} catch (Exception ex) {
logger.info("{}", ex);
}
}
}

View File

@ -0,0 +1,32 @@
package org.ethereum.net.rlpx.discover;
import org.ethereum.net.rlpx.Message;
import java.net.InetSocketAddress;
public class DiscoveryEvent {
private Message message;
private InetSocketAddress address;
public DiscoveryEvent(Message m, InetSocketAddress a) {
message = m;
address = a;
}
public Message getMessage() {
return message;
}
public void setMessage(Message message) {
this.message = message;
}
public InetSocketAddress getAddress() {
return address;
}
public void setAddress(InetSocketAddress address) {
this.address = address;
}
}

View File

@ -0,0 +1,38 @@
package org.ethereum.net.rlpx.discover;
import io.netty.channel.Channel;
import org.ethereum.crypto.ECKey;
import org.ethereum.net.rlpx.discover.table.KademliaOptions;
import org.ethereum.net.rlpx.discover.table.NodeTable;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class DiscoveryExecutor {
Channel channel;
NodeTable table;
ECKey key;
ScheduledExecutorService discoverer = Executors.newSingleThreadScheduledExecutor();
ScheduledExecutorService refresher = Executors.newSingleThreadScheduledExecutor();
DiscoveryExecutor(Channel channel, NodeTable table, ECKey key) {
this.channel = channel;
this.table = table;
this.key = key;
}
public void discover() {
discoverer.scheduleWithFixedDelay(
new DiscoverTask(table.getNode().getId(), channel, key, table),
0, KademliaOptions.DISCOVER_CYCLE, TimeUnit.SECONDS);
refresher.scheduleWithFixedDelay(
new RefreshTask(channel, key, table),
0, KademliaOptions.BUCKET_REFRESH, TimeUnit.MILLISECONDS);
}
}

View File

@ -0,0 +1,187 @@
package org.ethereum.net.rlpx.discover;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import org.antlr.v4.runtime.misc.Triple;
import org.ethereum.crypto.ECKey;
import org.ethereum.net.rlpx.*;
import org.ethereum.net.rlpx.discover.table.KademliaOptions;
import org.ethereum.net.rlpx.discover.table.NodeTable;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.net.InetSocketAddress;
import java.util.*;
public class MessageHandler extends SimpleChannelInboundHandler<DiscoveryEvent> {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger("discover");
private ECKey key;
private NodeTable table;
private Map<Node, Node> evictedCandidates = new HashMap<>();
private Map<Node, Date> expectedPongs = new HashMap<>();
public MessageHandler(ECKey key, NodeTable table) {
this.key = key;
this.table = table;
}
@Override
public void channelRead0(ChannelHandlerContext ctx, DiscoveryEvent event) throws Exception {
Message m = event.getMessage();
InetSocketAddress sender = event.getAddress();
byte type = m.getType()[0];
switch (type) {
case 1:
handlePing(ctx, m, sender);
break;
case 2:
handlePong(ctx, m, sender);
break;
case 3:
handleFindNode(ctx, m, sender);
break;
case 4:
handleNeighbours(ctx, m, sender);
break;
}
}
private void handlePing(ChannelHandlerContext ctx, Message m, InetSocketAddress sender) {
PingMessage ping = (PingMessage) m;
logger.info("{}", String.format("PING from %s", sender.toString()));
Node n = new Node(ping.getNodeId(), sender.getHostName(), sender.getPort());
if (!table.getNode().equals(n)) {
update(ctx, n);
sendPong(ctx, ping.getMdc(), sender);
}
}
private void handlePong(ChannelHandlerContext ctx, Message m, InetSocketAddress sender) {
// logger.info("{}", String.format("PONG from %s", sender.toString()));
PongMessage pong = (PongMessage) m;
Node n = new Node(pong.getNodeId(), sender.getHostName(), sender.getPort());
update(ctx, n);
}
private void handleNeighbours(ChannelHandlerContext ctx, Message m, InetSocketAddress sender) {
NeighborsMessage neighborsMessage = (NeighborsMessage) m;
logger.info("{}", String.format("NEIGHBOURS from %s", sender.toString()));
update(ctx, new Node(neighborsMessage.getNodeId(), sender.getHostName(), sender.getPort()));
for (Node n : neighborsMessage.getNodes()) {
update(ctx, n);
}
}
private void update(ChannelHandlerContext ctx, Node n) {
if(table.getNode().equals(n)) {
return;
}
if (!table.contains(n)) {
if (expectedPongs.containsKey(n)) {
if (System.currentTimeMillis() - expectedPongs.get(n).getTime()
< KademliaOptions.REQ_TIMEOUT) {
if (evictedCandidates.containsKey(n)) {
logger.info("{}", String.format("Evicted node remains %s:%d, remove expected node %s:%d", n, evictedCandidates.get(n)));
expectedPongs.remove(n);
evictedCandidates.remove(n);
} else {
addNode(ctx, n);
}
} else {
if (evictedCandidates.containsKey(n)) {
logger.info("{}", String.format("Drop evicted %s:%d, add node %s:%d", n, evictedCandidates.get(n)));
dropNode(n);
addNode(ctx, evictedCandidates.get(n));
}
expectedPongs.remove(n);
evictedCandidates.remove(n);
}
} else {
expectedPongs.put(n, new Date());
sendPing(ctx, new InetSocketAddress(n.getHost(), n.getPort()));
}
} else {
table.touchNode(n);
}
Set<Node> expiredExpected = new HashSet<>();
long now = System.currentTimeMillis();
for (Map.Entry<Node, Date> e : expectedPongs.entrySet()) {
if (now - e.getValue().getTime() > KademliaOptions.REQ_TIMEOUT) {
if (evictedCandidates.containsKey(e.getKey())) {
Node evictionCandidate = e.getKey();
Node replacement = evictedCandidates.get(evictionCandidate);
logger.info("{}", String.format("Drop evicted %s:%d, add node %s:%d",
evictionCandidate.getHost(), evictionCandidate.getPort(),
replacement.getHost(), replacement.getPort()));
dropNode(evictionCandidate);
addNode(ctx, replacement);
}
expiredExpected.add(e.getKey());
}
}
expectedPongs.keySet().removeAll(expiredExpected);
evictedCandidates.keySet().removeAll(expiredExpected);
}
private void addNode(ChannelHandlerContext ctx, Node n) {
Node evictedCandidate = table.addNode(n);
if (evictedCandidate != null) {
expectedPongs.put(evictedCandidate, new Date());
evictedCandidates.put(evictedCandidate, n);
expectedPongs.remove(n);
sendPing(ctx, new InetSocketAddress(evictedCandidate.getHost(), evictedCandidate.getPort()));
}
}
private void dropNode(Node n) {
table.dropNode(n);
}
private void sendPong(ChannelHandlerContext ctx, byte[] mdc, InetSocketAddress address) {
Message pong = PongMessage.create(mdc, address.getHostName(), address.getPort(), key);
sendPacket(ctx, pong.getPacket(), address);
}
private void sendPing(ChannelHandlerContext ctx, InetSocketAddress address) {
Message ping = PingMessage.create(table.getNode().getHost(), table.getNode().getPort(), key);
// logger.info("{}", String.format("PING to %s:%d", address.getHostName(), address.getPort()));
sendPacket(ctx, ping.getPacket(), address);
}
private void handleFindNode(ChannelHandlerContext ctx, Message m, InetSocketAddress sender) {
logger.info("{}", String.format("FIND from %s", sender.toString()));
FindNodeMessage msg = (FindNodeMessage) m;
List<Node> closest = table.getClosestNodes(msg.getTarget());
sendNeighbours(ctx, closest, sender);
}
private void sendNeighbours(ChannelHandlerContext ctx, List<Node> closest, InetSocketAddress address) {
NeighborsMessage neighbors = NeighborsMessage.create(closest, key);
sendPacket(ctx, neighbors.getPacket(), address);
}
private void sendPacket(ChannelHandlerContext ctx, byte[] wire, InetSocketAddress address) {
DatagramPacket packet = new DatagramPacket(Unpooled.copiedBuffer(wire), address);
ctx.write(packet);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
// We don't close the channel because we can keep serving requests.
}
}

View File

@ -0,0 +1,26 @@
package org.ethereum.net.rlpx.discover;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageDecoder;
import org.ethereum.crypto.ECKey;
import org.ethereum.net.rlpx.Message;
import org.slf4j.LoggerFactory;
import java.util.List;
public class PacketDecoder extends MessageToMessageDecoder<DatagramPacket> {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger("discover");
@Override
public void decode(ChannelHandlerContext ctx, DatagramPacket packet, List<Object> out) throws Exception {
ByteBuf buf = packet.content();
byte[] encoded = new byte[buf.readableBytes()];
buf.readBytes(encoded);
Message msg = Message.decode(encoded);
DiscoveryEvent event = new DiscoveryEvent(msg, packet.sender());
out.add(event);
}
}

View File

@ -0,0 +1,31 @@
package org.ethereum.net.rlpx.discover;
import io.netty.channel.Channel;
import org.ethereum.crypto.ECKey;
import org.ethereum.net.rlpx.Node;
import org.ethereum.net.rlpx.discover.table.NodeTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Random;
public class RefreshTask extends DiscoverTask {
private static final Logger logger = LoggerFactory.getLogger("discover");
RefreshTask(Channel channel, ECKey key, NodeTable table) {
super(getNodeId(), channel, key, table);
}
public static byte[] getNodeId() {
Random gen = new Random();
byte[] id = new byte[64];
gen.nextBytes(id);
return id;
}
@Override
public void run() {
discover(getNodeId(), 0, new ArrayList<Node>());
}
}

View File

@ -0,0 +1,91 @@
package org.ethereum.net.rlpx.discover;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import org.ethereum.crypto.ECKey;
import org.ethereum.net.rlpx.Node;
import org.ethereum.net.rlpx.discover.table.NodeTable;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
public class UDPListener {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger("discover");
private final int port;
private final String address;
private final ECKey key;
private NodeTable table;
public UDPListener(String address, int port) {
this.address = address;
this.port = port;
key = ECKey.fromPrivate(BigInteger.TEN).decompress();
}
public void start() throws Exception {
NioEventLoopGroup group = new NioEventLoopGroup();
byte[] nodeID = new byte[64];
System.arraycopy(key.getPubKey(), 1, nodeID, 0, 64);
Node homeNode = new Node(nodeID, address, port);
table = new NodeTable(homeNode);
//add default node on local to connect
// byte[] peerId = Hex.decode("621168019b7491921722649cd1aa9608f23f8857d782e7495fb6765b821002c4aac6ba5da28a5c91b432e5fcc078931f802ffb5a3ababa42adee7a0c927ff49e");
// Node p = new Node(peerId, "127.0.0.1", 30303);
// table.addNode(p);
//Persist the list of known nodes with their reputation
byte[] cppId = Hex.decode("487611428e6c99a11a9795a6abe7b529e81315ca6aad66e2a2fc76e3adf263faba0d35466c2f8f68d561dbefa8878d4df5f1f2ddb1fbeab7f42ffb8cd328bd4a");
Node cppBootstrap = new Node(cppId, "5.1.83.226", 30303);
table.addNode(cppBootstrap);
byte[] cpp2Id = Hex.decode("1637a970d987ddb8fd18c5ca01931210cd2ac5d2fe0f42873c0b31f110c5cbedf68589ec608ec5421e1d259b06cba224127c6bbddbb7c26eaaea56423a23bd31");
Node cpp2Bootstrap = new Node(cpp2Id, "69.140.163.94", 30320);
table.addNode(cpp2Bootstrap);
try {
Bootstrap b = new Bootstrap();
b.group(group)
.option(ChannelOption.SO_TIMEOUT, 1000)
.channel(NioDatagramChannel.class)
.handler(new ChannelInitializer<NioDatagramChannel>() {
@Override
public void initChannel(NioDatagramChannel ch)
throws Exception {
ch.pipeline().addLast(new PacketDecoder());
ch.pipeline().addLast(new MessageHandler(key, table));
}
});
Channel channel = b.bind(address, port).sync().channel();
DiscoveryExecutor discoveryExecutor = new DiscoveryExecutor(channel, table, key);
discoveryExecutor.discover();
channel.closeFuture().sync();
} catch (Exception e) {
logger.error("{}", e);
} finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception {
String address = "0.0.0.0";
int port = 30303;
if (args.length >= 2) {
address = args[0];
port = Integer.parseInt(args[1]);
}
new UDPListener(address, port).start();
}
}

View File

@ -0,0 +1,28 @@
package org.ethereum.net.rlpx.discover.table;
import java.util.Comparator;
/**
* Created by kest on 5/26/15.
*/
public class DistanceComparator implements Comparator<NodeEntry> {
byte[] targetId;
DistanceComparator(byte[] targetId) {
this.targetId = targetId;
}
@Override
public int compare(NodeEntry e1, NodeEntry e2) {
int d1 = NodeEntry.distance(targetId, e1.getNode().getId());
int d2 = NodeEntry.distance(targetId, e2.getNode().getId());
if (d1 > d2) {
return 1;
} else if (d1 < d2) {
return -1;
} else {
return 0;
}
}
}

View File

@ -0,0 +1,15 @@
package org.ethereum.net.rlpx.discover.table;
/**
* Created by kest on 5/25/15.
*/
public class KademliaOptions {
public static final int BUCKET_SIZE = 16;
public static final int ALPHA = 3;
public static final int BINS = 256;
public static final int MAX_STEPS = 8;
public static final long REQ_TIMEOUT = 300;
public static final long BUCKET_REFRESH = 7200; //bucket refreshing interval in millis
public static final long DISCOVER_CYCLE = 30; //discovery cycle interval in seconds
}

View File

@ -0,0 +1,62 @@
package org.ethereum.net.rlpx.discover.table;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* Created by kest on 5/25/15.
*/
public class NodeBucket {
private final int depth;
private List<NodeEntry> nodes = new ArrayList<>();
NodeBucket(int depth) {
this.depth = depth;
}
public int getDepth() {
return depth;
}
public synchronized NodeEntry addNode(NodeEntry e) {
if (!nodes.contains(e)) {
if (nodes.size() >= KademliaOptions.BUCKET_SIZE) {
return getLastSeen();
} else {
nodes.add(e);
}
}
return null;
}
private NodeEntry getLastSeen() {
List<NodeEntry> sorted = nodes;
Collections.sort(sorted, new TimeComparator());
return sorted.get(0);
}
public synchronized void dropNode(NodeEntry entry) {
for (NodeEntry e : nodes) {
if (e.getId().equals(entry.getId())) {
nodes.remove(e);
break;
}
}
}
public int getNodesCount() {
return nodes.size();
}
public List<NodeEntry> getNodes() {
List<NodeEntry> nodes = new ArrayList<>();
for (NodeEntry e : this.nodes) {
nodes.add(e);
}
return nodes;
}
}

View File

@ -0,0 +1,111 @@
package org.ethereum.net.rlpx.discover.table;
import org.ethereum.net.rlpx.Node;
import static org.ethereum.crypto.HashUtil.sha3;
/**
* Created by kest on 5/25/15.
*/
public class NodeEntry {
private byte[] ownerId;
Node node;
private String entryId;
private int distance;
private long modified;
public NodeEntry(Node n) {
this.node = n;
this.ownerId = n.getId();
entryId = n.toString();
distance = distance(ownerId, n.getId());
touch();
}
public NodeEntry(byte[] ownerId, Node n) {
this.node = n;
this.ownerId = ownerId;
entryId = n.toString();
distance = distance(ownerId, n.getId());
touch();
}
public void touch() {
modified = System.currentTimeMillis();
}
public int getDistance() {
return distance;
}
public String getId() {
return entryId;
}
public Node getNode() {
return node;
}
public long getModified() {
return modified;
}
@Override
public boolean equals(Object o) {
boolean ret = false;
if (o instanceof NodeEntry){
NodeEntry e = (NodeEntry) o;
ret = this.getId().equals(e.getId());
}
return ret;
}
@Override
public int hashCode() {
return this.node.hashCode();
}
public static int distance(byte[] ownerId, byte[] targetId) {
byte[] h1 = sha3(targetId);
byte[] h2 = sha3(ownerId);
byte[] hash = new byte[Math.min(h1.length, h2.length)];
for (int i = 0; i < hash.length; i++) {
hash[i] = (byte) (((int) h1[i]) ^ ((int) h2[i]));
}
int d = KademliaOptions.BINS;
for (byte b : hash)
{
if (b == 0)
{
d -= 8;
}
else
{
int count = 0;
for (int i = 7; i >= 0; i--)
{
boolean a = (b & (1 << i)) == 0;
if (a)
{
count++;
}
else
{
break;
}
}
d -= count;
break;
}
}
return d;
}
}

View File

@ -0,0 +1,129 @@
package org.ethereum.net.rlpx.discover.table;
import org.ethereum.net.rlpx.Node;
import java.util.*;
/**
* Created by kest on 5/25/15.
*/
public class NodeTable {
private final Node node; // our node
private transient NodeBucket[] buckets;
private transient List<NodeEntry> nodes;
private Map<Node, Node> evictedCandidates = new HashMap<>();
private Map<Node, Date> expectedPongs = new HashMap<>();
public NodeTable(Node n) {
this.node = n;
initialize();
addNode(this.node);
}
public Node getNode() {
return node;
}
public final void initialize()
{
nodes = new ArrayList<>();
buckets = new NodeBucket[KademliaOptions.BINS];
for (int i = 0; i < KademliaOptions.BINS; i++)
{
buckets[i] = new NodeBucket(i);
}
}
public synchronized Node addNode(Node n) {
NodeEntry e = new NodeEntry(node.getId(), n);
NodeEntry lastSeen = buckets[getBucketId(e)].addNode(e);
if (lastSeen != null) {
return lastSeen.getNode();
}
if (!nodes.contains(e)) {
nodes.add(e);
}
return null;
}
public synchronized void dropNode(Node n) {
NodeEntry e = new NodeEntry(node.getId(), n);
buckets[getBucketId(e)].dropNode(e);
nodes.remove(e);
}
public synchronized boolean contains(Node n) {
NodeEntry e = new NodeEntry(node.getId(), n);
for (NodeBucket b : buckets) {
if (b.getNodes().contains(e)) {
return true;
}
}
return false;
}
public synchronized void touchNode(Node n) {
NodeEntry e = new NodeEntry(node.getId(), n);
for (NodeBucket b : buckets) {
if (b.getNodes().contains(e)) {
b.getNodes().get(b.getNodes().indexOf(e)).touch();
break;
}
}
}
public int getBucketsCount() {
int i = 0;
for (NodeBucket b : buckets) {
if (b.getNodesCount() > 0) {
i++;
}
}
return i;
}
public synchronized NodeBucket[] getBuckets() {
return buckets;
}
public int getBucketId(NodeEntry e) {
int id = e.getDistance() - 1;
return id < 0 ? 0 : id;
}
public synchronized int getNodesCount() {
return nodes.size();
}
public synchronized List<NodeEntry> getAllNodes()
{
List<NodeEntry> nodes = new ArrayList<>();
for (NodeBucket b : buckets)
{
for (NodeEntry e : b.getNodes())
{
if (!e.getNode().equals(node)) {
nodes.add(e);
}
}
}
return nodes;
}
public synchronized List<Node> getClosestNodes(byte[] targetId) {
List<NodeEntry> closestEntries = getAllNodes();
List<Node> closestNodes = new ArrayList<>();
Collections.sort(closestEntries, new DistanceComparator(targetId));
if (closestEntries.size() > KademliaOptions.BUCKET_SIZE) {
closestEntries = closestEntries.subList(0, KademliaOptions.BUCKET_SIZE);
}
for (NodeEntry e : closestEntries) {
closestNodes.add(e.getNode());
}
return closestNodes;
}
}

View File

@ -0,0 +1,23 @@
package org.ethereum.net.rlpx.discover.table;
import java.util.Comparator;
/**
* Created by kest on 5/26/15.
*/
public class TimeComparator implements Comparator<NodeEntry> {
@Override
public int compare(NodeEntry e1, NodeEntry e2) {
long t1 = e1.getModified();
long t2 = e2.getModified();
if (t1 < t2) {
return 1;
} else if (t1 >= t2) {
return -1;
} else {
return 0;
}
}
}

View File

@ -0,0 +1,200 @@
package org.ethereum.net.shh;
import org.ethereum.crypto.ECKey;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPElement;
import org.ethereum.util.RLPList;
import org.spongycastle.util.encoders.Hex;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
import static org.ethereum.net.shh.ShhMessageCodes.MESSAGE;
import static org.ethereum.crypto.HashUtil.sha3;
/**
* Created by kest on 6/12/15.
*/
public class Envelope extends ShhMessage {
private long expire;
private long ttl;
private Topic[] topics;
private byte[] data;
private int nonce = 0;
public Envelope(byte[] encoded) {
super(encoded);
}
public Envelope(long ttl, Topic[] topics, Message msg) {
this.expire = System.currentTimeMillis() + ttl;
this.ttl = ttl;
this.topics = topics;
this.data = msg.getBytes();
this.nonce = 0;
}
public Message open(ECKey privKey) {
if (!parsed) {
parse();
}
byte[] data = this.data;
long sent = this.expire - this.ttl;
Message m = new Message(data[0], sent, this.ttl, hash());
if ((m.getFlags() & Message.SIGNATURE_FLAG) == Message.SIGNATURE_FLAG) {
if (data.length < Message.SIGNATURE_LENGTH) {
throw new Error("Unable to open the envelope. First bit set but len(data) < len(signature)");
}
byte[] signature = new byte[Message.SIGNATURE_LENGTH];
System.arraycopy(data, 1, signature, 0, Message.SIGNATURE_LENGTH);
m.setSignature(signature);
byte[] payload = new byte[data.length - Message.SIGNATURE_LENGTH - 1];
System.arraycopy(data, Message.SIGNATURE_LENGTH + 1, payload, 0, payload.length);
m.setPayload(payload);
} else {
byte[] payload = new byte[data.length - 1];
System.arraycopy(data, 1, payload, 0, payload.length);
m.setPayload(payload);
}
if (privKey == null) {
return m;
}
m.decrypt(privKey);
return m;
}
private void parse() {
if (encoded == null) encode();
RLPList paramsList = (RLPList) RLP.decode2(encoded).get(0);
this.expire = ByteUtil.byteArrayToLong(paramsList.get(0).getRLPData());
this.ttl = ByteUtil.byteArrayToLong(paramsList.get(1).getRLPData());
List<Topic> topics = new ArrayList<>();
RLPList topicsList = (RLPList) RLP.decode2(paramsList.get(2).getRLPData()).get(0);
for (RLPElement e : topicsList) {
topics.add(new Topic(e.getRLPData()));
}
this.topics = new Topic[topics.size()];
topics.toArray(this.topics);
this.data = paramsList.get(3).getRLPData();
this.nonce = ByteUtil.byteArrayToInt(paramsList.get(4).getRLPData());
this.parsed = true;
}
private void encode() {
byte[] expire = RLP.encode(this.expire);
byte[] ttl = RLP.encode(this.expire);
List<byte[]> topics = new Vector<>();
for (Topic t : this.topics) {
topics.add(RLP.encodeElement(t.getBytes()));
}
byte[][] topicsArray = topics.toArray(new byte[topics.size()][]);
byte[] encodedTopics = RLP.encodeList(topicsArray);
byte[] data = RLP.encodeElement(this.data);
byte[] nonce = RLP.encodeInt(this.nonce);
this.encoded = RLP.encodeList(expire, ttl, encodedTopics, data, nonce);
}
private byte[] encodeWithoutNonce() {
byte[] expire = RLP.encode(this.expire);
byte[] ttl = RLP.encode(this.expire);
List<byte[]> topics = new Vector<>();
for (Topic t : this.topics) {
topics.add(RLP.encodeElement(t.getBytes()));
}
byte[][] topicsArray = topics.toArray(new byte[topics.size()][]);
byte[] encodedTopics = RLP.encodeList(topicsArray);
byte[] data = RLP.encodeElement(this.data);
return RLP.encodeList(expire, ttl, encodedTopics, data);
}
//TODO: complete the nonce implementation
public void seal(long pow) {
byte[] d = new byte[64];
Arrays.fill(d, (byte) 0);
byte[] rlp = encodeWithoutNonce();
long then = System.currentTimeMillis() + pow;
this.nonce = 0;
for (int bestBit = 0; System.currentTimeMillis() < then; ) {
for (int i = 0; i < 1024; ++i, ++bestBit) {
}
}
}
private byte[] hash() {
if (encoded == null) encode();
return sha3(encoded);
}
public long getExpire() {
if(!parsed) {
parse();
}
return expire;
}
public long getTtl() {
if(!parsed) {
parse();
}
return ttl;
}
public Topic[] getTopics() {
if(!parsed) {
parse();
}
return topics;
}
public byte[] getData() {
if(!parsed) {
parse();
}
return data;
}
@Override
public ShhMessageCodes getCommand() {
return MESSAGE;
}
@Override
public byte[] getEncoded() {
if (encoded == null) encode();
return encoded;
}
@Override
public Class<?> getAnswerMessage() {
return null;
}
@Override
public String toString() {
return this.toString();
}
}

View File

@ -0,0 +1,186 @@
package org.ethereum.net.shh;
import org.ethereum.crypto.ECIESCoder;
import org.ethereum.crypto.ECKey;
import org.spongycastle.util.BigIntegers;
import org.spongycastle.math.ec.ECPoint;
import java.security.SignatureException;
import java.util.Random;
import static org.ethereum.crypto.HashUtil.sha3;
import static org.ethereum.net.shh.ShhMessageCodes.MESSAGE;
import static org.ethereum.util.ByteUtil.merge;
/**
* Created by kest on 6/12/15.
*/
public class Message extends ShhMessage {
private byte flags;
private byte[] signature;
private byte[] payload;
private long sent;
private long ttl;
private byte[] envelopeHash;
public static final byte SIGNATURE_FLAG = 127;
public static final int SIGNATURE_LENGTH = 65;
// public Message(byte[] encoded) {
// super(encoded);
// }
public Message(byte[] payload) {
super(null);
Random r = new Random();
byte[] randByte = new byte[1];
r.nextBytes(randByte);
flags = randByte[0];
if (flags < 0) {
flags = (byte)(flags & 0xF);
}
flags &= ~SIGNATURE_FLAG;
this.sent = System.currentTimeMillis();
this.payload = payload;
}
public Message(byte flags, long sent, long ttl, byte[] envelopeHash) {
this.flags = flags;
this.sent = sent;
this.ttl = ttl;
this.envelopeHash = envelopeHash;
}
public Envelope wrap(long pow, Options options) {
//check ttl is not null
if (options.getPrivateKey() != null) {
sign(options.getPrivateKey());
}
if (options.getToPublicKey() != null) {
encrypt(options.getToPublicKey());
}
Envelope e = new Envelope(options.getTtl(), options.getTopics(), this);
return e;
}
public byte getFlags() {
return flags;
}
public void setPayload(byte[] payload) {
this.payload = payload;
}
public byte[] getPayload() {
return payload;
}
public byte[] getSignature() {
return signature;
}
public void setSignature(byte[] signature) {
this.signature = signature;
}
public byte[] getBytes() {
if (signature != null) {
return merge(new byte[]{flags}, signature, payload);
} else {
return merge(new byte[]{flags}, payload);
}
}
private void encrypt(byte[] toPublicKey) {
try {
ECKey key = ECKey.fromPublicOnly(toPublicKey);
ECPoint pubKeyPoint = key.getPubKeyPoint();
payload = ECIESCoder.encrypt(pubKeyPoint, payload);
} catch (Exception e) {
}
}
public boolean decrypt(ECKey privateKey) {
try {
payload = ECIESCoder.decrypt(privateKey.getPrivKey(), payload);
return true;
} catch (Exception e) {
System.out.println("The message payload isn't encrypted or something is wrong");
} catch (Throwable e) {
}
return false;
}
private void sign(ECKey privateKey) {
flags |= SIGNATURE_FLAG;
byte[] forSig = hash();
ECKey.ECDSASignature signature = privateKey.sign(forSig);
this.signature =
merge(BigIntegers.asUnsignedByteArray(signature.r),
BigIntegers.asUnsignedByteArray(signature.s), new byte[]{signature.v});
}
public ECKey recover() {
if (signature == null) {
return null;
}
byte[] r = new byte[32];
byte[] s = new byte[32];
byte v = signature[64];
if (v == 1) v = 28;
if (v == 0) v = 27;
System.arraycopy(signature, 0, r, 0, 32);
System.arraycopy(signature, 32, s, 0, 32);
ECKey.ECDSASignature signature = ECKey.ECDSASignature.fromComponents(r, s, v);
byte[] msgHash = hash();
ECKey outKey = null;
try {
outKey = ECKey.signatureToKey(msgHash, signature.toBase64());
} catch (SignatureException e) {
e.printStackTrace();
}
return outKey;
}
private byte[] hash() {
return sha3(merge(new byte[]{flags}, payload));
}
@Override
public ShhMessageCodes getCommand() {
return MESSAGE;
}
@Override
public byte[] getEncoded() {
return encoded;
}
@Override
public Class<?> getAnswerMessage() {
return null;
}
@Override
public String toString() {
return this.toString();
}
}

View File

@ -0,0 +1,52 @@
package org.ethereum.net.shh;
import org.ethereum.crypto.ECKey;
/**
* Created by kest on 6/13/15.
*/
public class Options {
private ECKey privateKey;
private byte[] toPublicKey;
private Topic[] topics;
private long ttl;
public Options(ECKey privateKey, byte[] toPublicKey, Topic[] topics, long ttl) {
this.privateKey = privateKey;
this.toPublicKey = toPublicKey;
this.topics = topics;
this.ttl = ttl;
}
public ECKey getPrivateKey() {
return privateKey;
}
public void setPrivateKey(ECKey privateKey) {
this.privateKey = privateKey;
}
public byte[] getToPublicKey() {
return toPublicKey;
}
public void setToPublicKey(byte[] toPublicKey) {
this.toPublicKey = toPublicKey;
}
public Topic[] getTopics() {
return topics;
}
public void setTopics(Topic[] topics) {
this.topics = topics;
}
public long getTtl() {
return ttl;
}
public void setTtl(long ttl) {
this.ttl = ttl;
}
}

View File

@ -0,0 +1,25 @@
package org.ethereum.net.shh;
import static org.ethereum.crypto.HashUtil.sha3;
/**
* Created by kest on 6/12/15.
*/
public class Topic {
private byte[] topic = new byte[4];
public Topic(byte[] data) {
byte[] topic = sha3(data);
System.arraycopy(topic, 0, this.topic, 0, 4);
}
public Topic(String data) {
this(data.getBytes());
}
public byte[] getBytes() {
return topic;
}
}

View File

@ -4,18 +4,24 @@ import org.ethereum.crypto.HashUtil;
import org.ethereum.datasource.KeyValueDataSource;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.util.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.ethereum.util.ByteUtil.wrap;
/**
* @author Nick Savers
* @since 20.05.2014
*/
public class Cache {
private static final Logger gLogger = LoggerFactory.getLogger("general");
private final KeyValueDataSource dataSource;
private Map<ByteArrayWrapper, Node> nodes = new ConcurrentHashMap<>();
private boolean isDirty;
@ -34,8 +40,8 @@ public class Cache {
Value value = new Value(o);
byte[] enc = value.encode();
if (enc.length >= 32) {
byte[] sha = HashUtil.sha3(enc);
this.nodes.put(new ByteArrayWrapper(sha), new Node(value, true));
byte[] sha = value.hash();
this.nodes.put(wrap(sha), new Node(value, true));
this.isDirty = true;
return sha;
}
@ -68,6 +74,7 @@ public class Cache {
public void commit() {
long t = System.nanoTime();
if (dataSource == null) return;
// Don't try to commit if it isn't dirty
@ -76,17 +83,31 @@ public class Cache {
}
long size = 0;
long keys = 0;
Map<byte[], byte[]> batch = new HashMap<>();
for (ByteArrayWrapper key : this.nodes.keySet()) {
Node node = this.nodes.get(key);
if (node.isDirty()) {
batch.put(key.getData(), node.getValue().encode());
byte[] value = node.getValue().encode();
batch.put(key.getData(), value);
node.setDirty(false);
size += value.length;
keys += 1;
}
}
dataSource.updateBatch(batch);
this.isDirty = false;
this.nodes.clear();
long t_ = System.nanoTime();
String sizeFmt = String.format("%02.2f", ((float)size) / 1048576);
gLogger.info("Flush state in: {} ms, {} nodes, {}MB",
((float)(t_ - t) / 1_000_000), keys, sizeFmt);
}
public void undo() {

View File

@ -163,8 +163,7 @@ public class TrieImpl implements Trie {
return (byte[]) this.getRoot();
} else {
Value rootValue = new Value(this.getRoot());
byte[] val = rootValue.encode();
return HashUtil.sha3(val);
return rootValue.hash();
}
}

View File

@ -115,17 +115,30 @@ public class ByteUtil {
return stripLeadingZeroes(data);
}
public static byte[] intToBytes(int val){
/**
* Converts a int value into a byte array.
*
* @param val - int value to convert
* @return decimal value with leading byte that are zeroes striped
*/
public static byte[] intToBytesNoLeadZeroes(int val) {
return longToBytesNoLeadZeroes((long)val);
if (val == 0) return EMPTY_BYTE_ARRAY;
int lenght = 0;
int tmpVal = val;
while (tmpVal != 0){
tmpVal = tmpVal >> 8;
++lenght;
}
byte[] result = new byte[lenght];
int index = result.length - 1;
while(val != 0){
result[index] = (byte)(val & 0xFF);
val = val >> 8;
index -= 1;
}
return result;
}
/**

View File

@ -7,9 +7,7 @@ import java.nio.ByteBuffer;
import java.util.*;
import static java.util.Arrays.copyOfRange;
import static org.ethereum.util.ByteUtil.byteArrayToInt;
import static org.ethereum.util.ByteUtil.isNullOrZeroArray;
import static org.ethereum.util.ByteUtil.isSingleZero;
import static org.ethereum.util.ByteUtil.*;
import static org.spongycastle.util.Arrays.concatenate;
import static org.spongycastle.util.BigIntegers.asUnsignedByteArray;
@ -687,7 +685,7 @@ public class RLP {
} else if (length < MAX_ITEM_LENGTH) {
byte[] binaryLength;
if (length > 0xFF)
binaryLength = BigInteger.valueOf(length).toByteArray();
binaryLength = intToBytes(length);
else
binaryLength = new byte[]{(byte) length};
byte firstByte = (byte) (binaryLength.length + offset + SIZE_THRESHOLD - 1);
@ -700,7 +698,7 @@ public class RLP {
public static byte[] encodeByte(byte singleByte) {
if ((singleByte & 0xFF) == 0) {
return new byte[]{(byte) OFFSET_SHORT_ITEM};
} else if ((singleByte & 0xFF) < 0x7F) {
} else if ((singleByte & 0xFF) <= 0x7F) {
return new byte[]{singleByte};
} else {
return new byte[]{(byte) (OFFSET_SHORT_ITEM + 1), singleByte};

View File

@ -2,6 +2,7 @@ package org.ethereum.util;
import com.cedarsoftware.util.DeepEquals;
import org.ethereum.crypto.HashUtil;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
@ -15,6 +16,8 @@ import java.util.List;
public class Value {
private Object value;
private byte[] rlp;
private byte[] sha3;
public static Value fromRlpEncoded(byte[] data) {
if (data != null && data.length != 0) {
@ -120,7 +123,15 @@ public class Value {
* *****************/
public byte[] encode() {
return RLP.encode(value);
if (rlp == null)
rlp = RLP.encode(value);
return rlp;
}
public byte[] hash(){
if (sha3 == null)
sha3 = HashUtil.sha3(encode());
return sha3;
}
public boolean cmp(Value o) {

View File

@ -296,6 +296,17 @@ public class DataWord implements Comparable<DataWord> {
return Hex.toHexString(data);
}
public String toPrefixString() {
byte[] pref = getNoLeadZeroesData();
if (pref.length == 0) return "";
if (pref.length < 7)
return Hex.toHexString(pref);
return Hex.toHexString(pref).substring(0, 6);
}
public String shortHex() {
String hexValue = Hex.toHexString(getNoLeadZeroesData()).toUpperCase();
return "0x" + hexValue.replaceFirst("^0+(?!$)", "");

View File

@ -53,13 +53,21 @@ public class Memory implements ProgramTraceListenerAware {
return data;
}
public void write(int address, byte[] data) {
public void write(int address, byte[] data, boolean limited) {
if (!limited)
extend(address, data.length);
int chunkIndex = address / CHUNK_SIZE;
int chunkOffset = address % CHUNK_SIZE;
int toCapture = data.length;
int toCapture = 0;
if (limited)
toCapture = (address + data.length > softSize) ? softSize - address : data.length;
else
toCapture = data.length;
int start = 0;
while (toCapture > 0) {
@ -79,7 +87,7 @@ public class Memory implements ProgramTraceListenerAware {
public void extendAndWrite(int address, int allocSize, byte[] data) {
extend(address, allocSize);
write(address, data);
write(address, data, false);
}
public void extend(int address, int size) {

View File

@ -215,11 +215,15 @@ public class Program {
}
public void memorySave(DataWord addrB, DataWord value) {
memory.write(addrB.intValue(), value.getData());
memory.write(addrB.intValue(), value.getData(), false);
}
public void memorySaveLimited(int addr, byte[] value) {
memory.write(addr, value, true);
}
public void memorySave(int addr, byte[] value) {
memory.write(addr, value);
memory.write(addr, value, false);
}
public void memoryExpand(DataWord outDataOffs, DataWord outDataSize) {
@ -241,6 +245,8 @@ public class Program {
memory.extendAndWrite(addr, allocSize, value);
}
public DataWord memoryLoad(DataWord addr) {
return memory.readWord(addr.intValue());
}
@ -493,15 +499,8 @@ public class Program {
// 3. APPLY RESULTS: result.getHReturn() into out_memory allocated
if (result != null) {
byte[] buffer = result.getHReturn();
int allocSize = msg.getOutDataSize().intValue();
if (buffer != null && allocSize > 0) {
int retSize = buffer.length;
int offset = msg.getOutDataOffs().intValue();
if (retSize > allocSize)
this.memorySave(offset, buffer);
else
this.memorySave(offset, allocSize, buffer);
}
this.memorySaveLimited(offset, buffer);
}
// 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK
@ -1024,7 +1023,7 @@ public class Program {
* used mostly for testing reasons
*/
public void initMem(byte[] data) {
this.memory.write(0, data);
this.memory.write(0, data, false);
}

View File

@ -914,7 +914,7 @@ public class VM {
DataWord value = program.stackPop();
if (logger.isInfoEnabled())
hint = "addr: " + addr + " value: " + value;
hint = "[" + program.programAddress.toPrefixString() + "] key: " + addr + " value: " + value;
program.storageSave(addr, value);
program.step();

View File

@ -1,89 +0,0 @@
package org.ethereum.blockstore;
import org.ethereum.core.Block;
import org.ethereum.db.InMemoryBlockStore;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
import static org.junit.Assert.*;
/**
* @author: Roman Mandeleil
* Created on: 30/01/2015 11:04
*/
@Ignore
public class InMemoryBlockStoreTest {
private static final Logger logger = LoggerFactory.getLogger("test");
private InMemoryBlockStore blockStore;
@Before
public void setup() throws URISyntaxException, IOException {
blockStore = new InMemoryBlockStore();
URL scenario1 = ClassLoader
.getSystemResource("blockstore/load.dmp");
File file = new File(scenario1.toURI());
List<String> strData = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
for (String blockRLP : strData) {
Block block = new Block(
Hex.decode(blockRLP));
logger.trace("adding block.hash: [{}] block.number: [{}]",
block.getShortHash(),
block.getNumber());
blockStore.saveBlock(block, null);
}
}
@Test
public void testSaving8003Blocks() {
Block bestBlock = blockStore.getBestBlock();
Long bestIndex = blockStore.getBestBlock().getNumber();
Long firstIndex = bestIndex - 1000;//InMemoryBlockStore.MAX_BLOCKS;
assertTrue(bestIndex == 8003);
assertTrue(firstIndex == 7003);
assertTrue(blockStore.getBlockByNumber(7000) == null);
assertTrue(blockStore.getBlockByNumber(8004) == null);
Block byHashBlock = blockStore.getBlockByHash(bestBlock.getHash());
assertTrue(bestBlock.getNumber() == byHashBlock.getNumber());
byte[] hashFor7500 = blockStore.getBlockByNumber(7500).getHash();
Block block7500 = blockStore.getBlockByHash(hashFor7500);
assertTrue(block7500.getNumber() == 7500);
}
@Test
public void testListOfHashes(){
Block block = blockStore.getBlockByNumber(7500);
byte[] hash = block.getHash();
List<byte[]> hashes = blockStore.getListOfHashesStartFrom(hash, 700);
byte[] lastHash = hashes.get(hashes.size() - 1);
assertEquals(Hex.toHexString(blockStore.getBestBlock().getHash()),
Hex.toHexString(lastHash));
assertTrue(hashes.size() == 504);
}
}

View File

@ -29,7 +29,7 @@ import static org.junit.Assert.*;
* @author: Roman Mandeleil
* Created on: 30/01/2015 11:04
*/
@Ignore
public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
private static final Logger logger = LoggerFactory.getLogger("test");
@ -64,7 +64,6 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
logger.info("total difficulty: {}", cumDifficulty);
}
@Ignore
@Test
public void testEmpty(){
BlockStore blockStore = new InMemoryBlockStore();
@ -72,7 +71,6 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
assertNull(blockStore.getBestBlock());
}
@Ignore
@Test
public void testFlush(){
BlockStore blockStore = new InMemoryBlockStore();
@ -85,7 +83,6 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
blockStore.flush();
}
@Ignore
@Test
public void testSimpleLoad(){
@ -107,7 +104,6 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
assertTrue(blockStore.getBestBlock().getNumber() == 8003);
}
@Ignore
@Test
public void testFlushEach1000(){
@ -125,7 +121,7 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
}
}
@Ignore
@Test
public void testBlockHashByNumber(){
@ -158,7 +154,6 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
assertTrue(hash.startsWith("820aa7"));
}
@Ignore
@Test
public void testBlockByNumber(){
@ -191,7 +186,7 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
assertTrue(hash.startsWith("820aa7"));
}
@Ignore
@Test
public void testGetBlockByNumber() {
@ -209,7 +204,7 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
assertEquals("4312750101", blockStore.getTotalDifficulty().toString());
}
@Ignore
@Test
public void testDbGetBlockByHash(){

View File

@ -0,0 +1,119 @@
package org.ethereum.net.rlpx;
import org.ethereum.net.rlpx.discover.table.KademliaOptions;
import org.ethereum.net.rlpx.discover.table.NodeBucket;
import org.ethereum.net.rlpx.discover.table.NodeEntry;
import org.ethereum.net.rlpx.discover.table.NodeTable;
import org.junit.Ignore;
import org.junit.Test;
import java.util.List;
import java.util.Random;
import static org.junit.Assert.*;
public class KademliaTest {
@Ignore
@Test
public void test1() {
//init table with one home node
NodeTable t = getTestNodeTable(0);
Node homeNode = t.getNode();
//table should contain the home node only
assertEquals(t.getAllNodes().size(), 1);
Node bucketNode = t.getAllNodes().get(0).getNode();
assertEquals(homeNode, bucketNode);
}
@Test
public void test2() {
NodeTable t = getTestNodeTable(0);
Node n = getNode();
t.addNode(n);
assertTrue(containsNode(t, n));
t.dropNode(n);
assertFalse(containsNode(t, n));
}
@Test
public void test3() {
NodeTable t = getTestNodeTable(1000);
showBuckets(t);
List<Node> closest1 = t.getClosestNodes(t.getNode().getId());
List<Node> closest2 = t.getClosestNodes(getNodeId());
assertNotEquals(closest1, closest2);
}
@Test
public void test4() {
NodeTable t = getTestNodeTable(0);
Node homeNode = t.getNode();
//t.getBucketsCount() returns non empty buckets
assertEquals(t.getBucketsCount(), 1);
//creates very close nodes
for (int i = 1; i < KademliaOptions.BUCKET_SIZE; i++) {
Node n= getNode(homeNode.getId(), i);
t.addNode(n);
}
assertEquals(t.getBucketsCount(), 1);
assertEquals(t.getBuckets()[0].getNodesCount(), KademliaOptions.BUCKET_SIZE);
}
public static byte[] getNodeId() {
Random gen = new Random();
byte[] id = new byte[64];
gen.nextBytes(id);
return id;
}
public static Node getNode(byte[] id, int i) {
id[0] += (byte) i;
Node n = getNode();
n.setId(id);
return n;
}
public static Node getNode() {
return new Node(getNodeId(), "127.0.0.1", 30303);
}
public static NodeTable getTestNodeTable(int nodesQuantity) {
NodeTable testTable = new NodeTable(getNode());
for (int i = 0; i < nodesQuantity; i++) {
testTable.addNode(getNode());
}
return testTable;
}
public static void showBuckets(NodeTable t) {
for (NodeBucket b : t.getBuckets()) {
if (b.getNodesCount() > 0) {
System.out.println(String.format("Bucket %d nodes %d depth %d", b.getDepth(), b.getNodesCount(), b.getDepth()));
}
}
}
public static boolean containsNode(NodeTable t, Node n) {
for (NodeEntry e : t.getAllNodes()) {
if (e.getNode().toString().equals(n.toString())) {
return true;
}
}
return false;
}
}

View File

@ -1,9 +1,6 @@
package org.ethereum.net.rlpx;
import org.ethereum.crypto.ECKey;
import org.ethereum.net.rlpx.*;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPList;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
@ -15,7 +12,6 @@ import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import org.junit.Ignore;
import static org.ethereum.crypto.HashUtil.sha3;
import static org.ethereum.util.ByteUtil.merge;
import static org.junit.Assert.assertEquals;
@ -25,6 +21,7 @@ public class RLPXTest {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger("test");
@Ignore
@Test // ping test
public void test1() {
@ -65,6 +62,7 @@ public class RLPXTest {
assertEquals(key.toString(), key2.toString());
}
@Ignore
@Test // neighbors message
public void test3() {
@ -133,6 +131,7 @@ public class RLPXTest {
}
@Ignore
@Test
public void test6() {
@ -153,7 +152,7 @@ public class RLPXTest {
}
@Ignore
@Test // Neighbors parse data
public void test7() {

View File

@ -0,0 +1,103 @@
package org.ethereum.net.shh;
import org.ethereum.crypto.ECKey;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPList;
import org.junit.Before;
import org.junit.Test;
import java.math.BigInteger;
import org.spongycastle.util.encoders.Hex;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
/**
* Created by kest on 6/13/15.
*/
public class ShhTest {
private byte[] payload = "Hello whisper!".getBytes();
private ECKey privKey = ECKey.fromPrivate(BigInteger.TEN);
private byte[] pubKey = privKey.decompress().getPubKey();
private long ttl = 10000;
private Topic[] topics = new Topic[]{
new Topic("topic 1"),
new Topic("topic 2"),
new Topic("topic 3")};
@Test /* Tests whether a message can be wrapped without any identity or encryption. */
public void test1() {
Message sent = new Message(payload);
Options options = new Options(null, null, topics, ttl);
Envelope e = sent.wrap(0, options);
RLPList rlpList = RLP.decode2(e.getEncoded());
RLPList.recursivePrint(rlpList);
System.out.println();
assertEquals(Hex.toHexString(e.getData()), Hex.toHexString(sent.getBytes()));
assertEquals(Hex.toHexString(sent.getPayload()), Hex.toHexString(payload));
assertTrue(sent.getSignature() == null);
Message received = e.open(null);
ECKey recovered = received.recover();
assertTrue(recovered == null);
}
@Test /* Tests whether a message can be signed, and wrapped in plain-text. */
public void test2() {
Message sent = new Message(payload);
Options options = new Options(privKey, null, topics, ttl);
Envelope e = sent.wrap(0, options);
assertEquals(Hex.toHexString(e.getData()), Hex.toHexString(sent.getBytes()));
assertEquals(Hex.toHexString(sent.getPayload()), Hex.toHexString(payload));
assertTrue(sent.getSignature() != null);
Message received = e.open(null);
ECKey recovered = received.recover();
assertEquals(Hex.toHexString(pubKey), Hex.toHexString(recovered.decompress().getPubKey()));
}
@Test /* Tests whether a message can be encrypted and decrypted using an anonymous sender (i.e. no signature).*/
public void test3() {
Message sent = new Message(payload);
Options options = new Options(null, pubKey, topics, ttl);
Envelope e = sent.wrap(0, options);
assertEquals(Hex.toHexString(e.getData()), Hex.toHexString(sent.getBytes()));
assertNotEquals(Hex.toHexString(sent.getPayload()), Hex.toHexString(payload));
assertTrue(sent.getSignature() == null);
Message received = e.open(null);
assertEquals(Hex.toHexString(sent.getBytes()), Hex.toHexString(received.getBytes()));
ECKey recovered = received.recover();
assertTrue(recovered == null);
}
@Test /* Tests whether a message can be properly signed and encrypted. */
public void test4() {
Message sent = new Message(payload);
Options options = new Options(privKey, pubKey, topics, ttl);
Envelope e = sent.wrap(0, options);
assertEquals(Hex.toHexString(e.getData()), Hex.toHexString(sent.getBytes()));
assertNotEquals(Hex.toHexString(sent.getPayload()), Hex.toHexString(payload));
assertTrue(sent.getSignature() != null);
Message received = e.open(privKey);
ECKey recovered = received.recover();
sent.decrypt(privKey);
assertEquals(Hex.toHexString(sent.getBytes()), Hex.toHexString(received.getBytes()));
assertEquals(Hex.toHexString(sent.getPayload()), Hex.toHexString(payload));
assertEquals(Hex.toHexString(pubKey), Hex.toHexString(recovered.decompress().getPubKey()));
}
}

View File

@ -128,7 +128,7 @@ public class RLPTest {
data = RLP.encodeByte((byte) 120);
assertArrayEquals(expected2, data);
byte[] expected3 = {(byte) 0x81, (byte) 0x7F};
byte[] expected3 = {(byte) 0x7F};
data = RLP.encodeByte((byte) 127);
assertArrayEquals(expected3, data);
}
@ -145,7 +145,7 @@ public class RLPTest {
data = RLP.encodeShort((byte) 120);
assertArrayEquals(expected2, data);
byte[] expected3 = {(byte) 0x81, (byte) 0x7F};
byte[] expected3 = { (byte) 0x7F};
data = RLP.encodeShort((byte) 127);
assertArrayEquals(expected3, data);
@ -170,7 +170,7 @@ public class RLPTest {
data = RLP.encodeInt(120);
assertArrayEquals(expected2, data);
byte[] expected3 = {(byte) 0x81, (byte) 0x7F};
byte[] expected3 = {(byte) 0x7F};
data = RLP.encodeInt(127);
assertArrayEquals(expected3, data);
@ -1029,6 +1029,21 @@ public class RLPTest {
assertEquals("c0", Hex.toHexString(setEncoded));
}
@Test
public void testEncodeInt_7f(){
String result = Hex.toHexString(RLP.encodeInt(0x7f));
String expected = "7f";
assertEquals(expected, result);
}
@Test
public void testEncodeInt_80(){
String result = Hex.toHexString(RLP.encodeInt(0x80));
String expected = "8180";
assertEquals(expected, result);
}
}

View File

@ -9,6 +9,7 @@ import java.util.Arrays;
import static java.lang.Math.ceil;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class MemoryTest {
@ -44,18 +45,18 @@ public class MemoryTest {
Memory memoryBuffer = new Memory();
byte[] data = {1, 1, 1, 1};
memoryBuffer.write(0, data);
memoryBuffer.write(0, data, false);
Assert.assertTrue(1 == memoryBuffer.getChunks().size());
assertTrue(1 == memoryBuffer.getChunks().size());
byte[] chunk = memoryBuffer.getChunks().get(0);
Assert.assertTrue(chunk[0] == 1);
Assert.assertTrue(chunk[1] == 1);
Assert.assertTrue(chunk[2] == 1);
Assert.assertTrue(chunk[3] == 1);
Assert.assertTrue(chunk[4] == 0);
assertTrue(chunk[0] == 1);
assertTrue(chunk[1] == 1);
assertTrue(chunk[2] == 1);
assertTrue(chunk[3] == 1);
assertTrue(chunk[4] == 0);
Assert.assertTrue(memoryBuffer.size() == 32);
assertTrue(memoryBuffer.size() == 32);
}
@Test
@ -64,19 +65,19 @@ public class MemoryTest {
Memory memoryBuffer = new Memory();
byte[] data = Hex.decode("0101010101010101010101010101010101010101010101010101010101010101");
memoryBuffer.write(0, data);
memoryBuffer.write(0, data, false);
Assert.assertTrue(1 == memoryBuffer.getChunks().size());
assertTrue(1 == memoryBuffer.getChunks().size());
byte[] chunk = memoryBuffer.getChunks().get(0);
Assert.assertTrue(chunk[0] == 1);
Assert.assertTrue(chunk[1] == 1);
assertTrue(chunk[0] == 1);
assertTrue(chunk[1] == 1);
Assert.assertTrue(chunk[30] == 1);
Assert.assertTrue(chunk[31] == 1);
Assert.assertTrue(chunk[32] == 0);
assertTrue(chunk[30] == 1);
assertTrue(chunk[31] == 1);
assertTrue(chunk[32] == 0);
Assert.assertTrue(memoryBuffer.size() == 32);
assertTrue(memoryBuffer.size() == 32);
}
@Test
@ -85,20 +86,20 @@ public class MemoryTest {
Memory memoryBuffer = new Memory();
byte[] data = Hex.decode("010101010101010101010101010101010101010101010101010101010101010101");
memoryBuffer.write(0, data);
memoryBuffer.write(0, data, false);
Assert.assertTrue(1 == memoryBuffer.getChunks().size());
assertTrue(1 == memoryBuffer.getChunks().size());
byte[] chunk = memoryBuffer.getChunks().get(0);
Assert.assertTrue(chunk[0] == 1);
Assert.assertTrue(chunk[1] == 1);
assertTrue(chunk[0] == 1);
assertTrue(chunk[1] == 1);
Assert.assertTrue(chunk[30] == 1);
Assert.assertTrue(chunk[31] == 1);
Assert.assertTrue(chunk[32] == 1);
Assert.assertTrue(chunk[33] == 0);
assertTrue(chunk[30] == 1);
assertTrue(chunk[31] == 1);
assertTrue(chunk[32] == 1);
assertTrue(chunk[33] == 0);
Assert.assertTrue(memoryBuffer.size() == 64);
assertTrue(memoryBuffer.size() == 64);
}
@Test
@ -108,18 +109,18 @@ public class MemoryTest {
byte[] data = new byte[1024];
Arrays.fill(data, (byte) 1);
memoryBuffer.write(0, data);
memoryBuffer.write(0, data, false);
Assert.assertTrue(1 == memoryBuffer.getChunks().size());
assertTrue(1 == memoryBuffer.getChunks().size());
byte[] chunk = memoryBuffer.getChunks().get(0);
Assert.assertTrue(chunk[0] == 1);
Assert.assertTrue(chunk[1] == 1);
assertTrue(chunk[0] == 1);
assertTrue(chunk[1] == 1);
Assert.assertTrue(chunk[1022] == 1);
Assert.assertTrue(chunk[1023] == 1);
assertTrue(chunk[1022] == 1);
assertTrue(chunk[1023] == 1);
Assert.assertTrue(memoryBuffer.size() == 1024);
assertTrue(memoryBuffer.size() == 1024);
}
@Test
@ -130,22 +131,22 @@ public class MemoryTest {
byte[] data = new byte[1025];
Arrays.fill(data, (byte) 1);
memoryBuffer.write(0, data);
memoryBuffer.write(0, data, false);
Assert.assertTrue(2 == memoryBuffer.getChunks().size());
assertTrue(2 == memoryBuffer.getChunks().size());
byte[] chunk1 = memoryBuffer.getChunks().get(0);
Assert.assertTrue(chunk1[0] == 1);
Assert.assertTrue(chunk1[1] == 1);
assertTrue(chunk1[0] == 1);
assertTrue(chunk1[1] == 1);
Assert.assertTrue(chunk1[1022] == 1);
Assert.assertTrue(chunk1[1023] == 1);
assertTrue(chunk1[1022] == 1);
assertTrue(chunk1[1023] == 1);
byte[] chunk2 = memoryBuffer.getChunks().get(1);
Assert.assertTrue(chunk2[0] == 1);
Assert.assertTrue(chunk2[1] == 0);
assertTrue(chunk2[0] == 1);
assertTrue(chunk2[1] == 0);
Assert.assertTrue(memoryBuffer.size() == 1056);
assertTrue(memoryBuffer.size() == 1056);
}
@Test
@ -159,26 +160,26 @@ public class MemoryTest {
byte[] data2 = new byte[1024];
Arrays.fill(data2, (byte) 2);
memoryBuffer.write(0, data1);
memoryBuffer.write(1024, data2);
memoryBuffer.write(0, data1, false);
memoryBuffer.write(1024, data2, false);
Assert.assertTrue(2 == memoryBuffer.getChunks().size());
assertTrue(2 == memoryBuffer.getChunks().size());
byte[] chunk1 = memoryBuffer.getChunks().get(0);
Assert.assertTrue(chunk1[0] == 1);
Assert.assertTrue(chunk1[1] == 1);
assertTrue(chunk1[0] == 1);
assertTrue(chunk1[1] == 1);
Assert.assertTrue(chunk1[1022] == 1);
Assert.assertTrue(chunk1[1023] == 1);
assertTrue(chunk1[1022] == 1);
assertTrue(chunk1[1023] == 1);
byte[] chunk2 = memoryBuffer.getChunks().get(1);
Assert.assertTrue(chunk2[0] == 2);
Assert.assertTrue(chunk2[1] == 2);
assertTrue(chunk2[0] == 2);
assertTrue(chunk2[1] == 2);
Assert.assertTrue(chunk2[1022] == 2);
Assert.assertTrue(chunk2[1023] == 2);
assertTrue(chunk2[1022] == 2);
assertTrue(chunk2[1023] == 2);
Assert.assertTrue(memoryBuffer.size() == 2048);
assertTrue(memoryBuffer.size() == 2048);
}
@Test
@ -195,30 +196,30 @@ public class MemoryTest {
byte[] data3 = new byte[1];
Arrays.fill(data3, (byte) 3);
memoryBuffer.write(0, data1);
memoryBuffer.write(1024, data2);
memoryBuffer.write(2048, data3);
memoryBuffer.write(0, data1, false);
memoryBuffer.write(1024, data2, false);
memoryBuffer.write(2048, data3, false);
Assert.assertTrue(3 == memoryBuffer.getChunks().size());
assertTrue(3 == memoryBuffer.getChunks().size());
byte[] chunk1 = memoryBuffer.getChunks().get(0);
Assert.assertTrue(chunk1[0] == 1);
Assert.assertTrue(chunk1[1] == 1);
assertTrue(chunk1[0] == 1);
assertTrue(chunk1[1] == 1);
Assert.assertTrue(chunk1[1022] == 1);
Assert.assertTrue(chunk1[1023] == 1);
assertTrue(chunk1[1022] == 1);
assertTrue(chunk1[1023] == 1);
byte[] chunk2 = memoryBuffer.getChunks().get(1);
Assert.assertTrue(chunk2[0] == 2);
Assert.assertTrue(chunk2[1] == 2);
assertTrue(chunk2[0] == 2);
assertTrue(chunk2[1] == 2);
Assert.assertTrue(chunk2[1022] == 2);
Assert.assertTrue(chunk2[1023] == 2);
assertTrue(chunk2[1022] == 2);
assertTrue(chunk2[1023] == 2);
byte[] chunk3 = memoryBuffer.getChunks().get(2);
Assert.assertTrue(chunk3[0] == 3);
assertTrue(chunk3[0] == 3);
Assert.assertTrue(memoryBuffer.size() == 2080);
assertTrue(memoryBuffer.size() == 2080);
}
@Test
@ -237,8 +238,8 @@ public class MemoryTest {
if (memoryBuffer.readByte(i) == 0) ++zeroes;
}
Assert.assertTrue(ones == zeroes);
Assert.assertTrue(256 == memoryBuffer.size());
assertTrue(ones == zeroes);
assertTrue(256 == memoryBuffer.size());
}
@ -248,9 +249,9 @@ public class MemoryTest {
Memory memoryBuffer = new Memory();
DataWord value = memoryBuffer.readWord(100);
Assert.assertTrue(value.intValue() == 0);
Assert.assertTrue(memoryBuffer.getChunks().size() == 1);
Assert.assertTrue(memoryBuffer.size() == 32 * 5);
assertTrue(value.intValue() == 0);
assertTrue(memoryBuffer.getChunks().size() == 1);
assertTrue(memoryBuffer.size() == 32 * 5);
}
@Test
@ -258,9 +259,9 @@ public class MemoryTest {
Memory memoryBuffer = new Memory();
DataWord value = memoryBuffer.readWord(2015);
Assert.assertTrue(value.intValue() == 0);
Assert.assertTrue(memoryBuffer.getChunks().size() == 2);
Assert.assertTrue(memoryBuffer.size() == 2048);
assertTrue(value.intValue() == 0);
assertTrue(memoryBuffer.getChunks().size() == 2);
assertTrue(memoryBuffer.size() == 2048);
}
@Test
@ -268,9 +269,9 @@ public class MemoryTest {
Memory memoryBuffer = new Memory();
DataWord value = memoryBuffer.readWord(2016);
Assert.assertTrue(value.intValue() == 0);
Assert.assertTrue(memoryBuffer.getChunks().size() == 2);
Assert.assertTrue(memoryBuffer.size() == 2048);
assertTrue(value.intValue() == 0);
assertTrue(memoryBuffer.getChunks().size() == 2);
assertTrue(memoryBuffer.size() == 2048);
}
@Test
@ -278,9 +279,9 @@ public class MemoryTest {
Memory memoryBuffer = new Memory();
DataWord value = memoryBuffer.readWord(2017);
Assert.assertTrue(value.intValue() == 0);
Assert.assertTrue(memoryBuffer.getChunks().size() == 3);
Assert.assertTrue(memoryBuffer.size() == 2080);
assertTrue(value.intValue() == 0);
assertTrue(memoryBuffer.getChunks().size() == 3);
assertTrue(memoryBuffer.size() == 2080);
}
@Test
@ -294,11 +295,11 @@ public class MemoryTest {
byte[] data2 = new byte[1024];
Arrays.fill(data2, (byte) 2);
memoryBuffer.write(0, data1);
memoryBuffer.write(1024, data2);
memoryBuffer.write(0, data1, false);
memoryBuffer.write(1024, data2, false);
Assert.assertTrue(memoryBuffer.getChunks().size() == 2);
Assert.assertTrue(memoryBuffer.size() == 2048);
assertTrue(memoryBuffer.getChunks().size() == 2);
assertTrue(memoryBuffer.size() == 2048);
DataWord val1 = memoryBuffer.readWord(0x3df);
DataWord val2 = memoryBuffer.readWord(0x3e0);
@ -315,7 +316,7 @@ public class MemoryTest {
assertArrayEquals(
Hex.decode("0101010101010101010101010101010101010101010101010101010101010102"),
val3.getData());
Assert.assertTrue(memoryBuffer.size() == 2048);
assertTrue(memoryBuffer.size() == 2048);
}
@ -329,8 +330,8 @@ public class MemoryTest {
byte[] data2 = new byte[32];
Arrays.fill(data2, (byte) 2);
memoryBuffer.write(0, data1);
memoryBuffer.write(32, data2);
memoryBuffer.write(0, data1, false);
memoryBuffer.write(32, data2, false);
byte[] data = memoryBuffer.read(0, 64);
@ -351,8 +352,8 @@ public class MemoryTest {
byte[] data1 = new byte[32];
Arrays.fill(data1, (byte) 1);
memoryBuffer.write(0, data1);
Assert.assertTrue(32 == memoryBuffer.size());
memoryBuffer.write(0, data1, false);
assertTrue(32 == memoryBuffer.size());
byte[] data = memoryBuffer.read(0, 64);
@ -376,8 +377,8 @@ public class MemoryTest {
byte[] data2 = new byte[1024];
Arrays.fill(data2, (byte) 2);
memoryBuffer.write(0, data1);
memoryBuffer.write(1024, data2);
memoryBuffer.write(0, data1, false);
memoryBuffer.write(1024, data2, false);
byte[] data = memoryBuffer.read(0, 2048);
@ -387,8 +388,8 @@ public class MemoryTest {
if (data[i] == 2) ++twos;
}
Assert.assertTrue(ones == twos);
Assert.assertTrue(2048 == memoryBuffer.size());
assertTrue(ones == twos);
assertTrue(2048 == memoryBuffer.size());
}
@Test
@ -402,8 +403,8 @@ public class MemoryTest {
byte[] data2 = new byte[1024];
Arrays.fill(data2, (byte) 2);
memoryBuffer.write(0, data1);
memoryBuffer.write(1024, data2);
memoryBuffer.write(0, data1, false);
memoryBuffer.write(1024, data2, false);
byte[] data = memoryBuffer.read(0, 2049);
@ -414,11 +415,40 @@ public class MemoryTest {
if (data[i] == 0) ++zero;
}
Assert.assertTrue(zero == 1);
Assert.assertTrue(ones == twos);
Assert.assertTrue(2080 == memoryBuffer.size());
assertTrue(zero == 1);
assertTrue(ones == twos);
assertTrue(2080 == memoryBuffer.size());
}
@Test
public void memoryWriteLimited_1(){
Memory memoryBuffer = new Memory();
memoryBuffer.extend(0, 3072);
byte[] data1 = new byte[6272];
Arrays.fill(data1, (byte) 1);
memoryBuffer.write(2720, data1, true);
byte lastZero = memoryBuffer.readByte(2719);
byte firstOne = memoryBuffer.readByte(2721);
assertTrue(memoryBuffer.internalSize() == 3072);
assertTrue(lastZero == 0);
assertTrue(firstOne == 1);
byte[] data = memoryBuffer.read(2720, 352);
int ones = 0; int zero = 0;
for (int i = 0; i < data.length; ++i){
if (data[i] == 1) ++ones;
if (data[i] == 0) ++zero;
}
assertTrue(ones == data.length);
assertTrue(zero == 0);
}
}

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-rc-4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip