Merged latest develop.
Added Scanner implementation for BlockLoader.
This commit is contained in:
parent
40b0749304
commit
a75f74f9f2
|
@ -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"
|
||||
|
|
|
@ -46,7 +46,7 @@ public class EthereumManager {
|
|||
SystemProperties.CONFIG.activePeerPort(),
|
||||
SystemProperties.CONFIG.activePeerNodeid());
|
||||
} else {
|
||||
duration = ethereum.getBlockLoader().loadBlocks();
|
||||
ethereum.getBlockLoader().loadBlocks();
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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) {}
|
||||
}
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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){
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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,11 +48,16 @@ public class BlockLoader {
|
|||
if (block.getNumber() > blockchain.getBestBlock().getNumber()){
|
||||
blockchain.tryToConnect(block);
|
||||
long t1_ = System.nanoTime();
|
||||
String result = String.format("Imported block #%d took: [%02.2f msec]",
|
||||
block.getNumber(), ((float)(t1_ - t1) / 1_000_000));
|
||||
|
||||
System.out.println(result);
|
||||
} else {
|
||||
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());
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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.
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -115,19 +115,32 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a byte-array into a hex String.<br>
|
||||
* Works similar to {@link Hex#toHexString}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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+(?!$)", "");
|
||||
|
|
|
@ -28,7 +28,7 @@ public class Memory implements ProgramTraceListenerAware {
|
|||
|
||||
public byte[] read(int address, int size) {
|
||||
if (size <= 0) return EMPTY_BYTE_ARRAY;
|
||||
|
||||
|
||||
extend(address, size);
|
||||
byte[] data = new byte[size];
|
||||
|
||||
|
@ -53,13 +53,21 @@ public class Memory implements ProgramTraceListenerAware {
|
|||
return data;
|
||||
}
|
||||
|
||||
public void write(int address, byte[] data) {
|
||||
extend(address, data.length);
|
||||
|
||||
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) {
|
||||
|
@ -73,18 +81,18 @@ public class Memory implements ProgramTraceListenerAware {
|
|||
toCapture -= captured;
|
||||
start += captured;
|
||||
}
|
||||
|
||||
|
||||
if (traceListener != null) traceListener.onMemoryWrite(address, data);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (size <= 0) return;
|
||||
|
||||
|
||||
final int newSize = address + size;
|
||||
|
||||
int toAllocate = newSize - internalSize();
|
||||
|
@ -96,7 +104,7 @@ public class Memory implements ProgramTraceListenerAware {
|
|||
if (toAllocate > 0) {
|
||||
toAllocate = (int) ceil((double) toAllocate / WORD_SIZE) * WORD_SIZE;
|
||||
softSize += toAllocate;
|
||||
|
||||
|
||||
if (traceListener != null) traceListener.onMemoryExtend(toAllocate);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
int offset = msg.getOutDataOffs().intValue();
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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(){
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue