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'
|
apt 'com.google.dagger:dagger-compiler:2.0'
|
||||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
compile (project(':ethereumj-core')) {
|
compile (project(':ethereumj-core')) {
|
||||||
|
exclude group: 'commons-logging', module: 'commons-logging'
|
||||||
exclude group: "org.apache.commons", module: "commons-pool2"
|
exclude group: "org.apache.commons", module: "commons-pool2"
|
||||||
exclude group: "org.slf4j", module: "slf4j-log4j12"
|
exclude group: "org.slf4j", module: "slf4j-log4j12"
|
||||||
exclude group: "org.hibernate", module: "hibernate-core"
|
exclude group: "org.hibernate", module: "hibernate-core"
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class EthereumManager {
|
||||||
SystemProperties.CONFIG.activePeerPort(),
|
SystemProperties.CONFIG.activePeerPort(),
|
||||||
SystemProperties.CONFIG.activePeerNodeid());
|
SystemProperties.CONFIG.activePeerNodeid());
|
||||||
} else {
|
} else {
|
||||||
duration = ethereum.getBlockLoader().loadBlocks();
|
ethereum.getBlockLoader().loadBlocks();
|
||||||
}
|
}
|
||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,11 +260,14 @@ public class OrmLiteBlockStoreDatabase extends OrmLiteSqliteOpenHelper implement
|
||||||
|
|
||||||
public boolean flush(final List<Block> blocks) {
|
public boolean flush(final List<Block> blocks) {
|
||||||
|
|
||||||
|
reset();
|
||||||
try {
|
try {
|
||||||
TransactionManager.callInTransaction(getBlockDao().getConnectionSource(),
|
TransactionManager.callInTransaction(getBlockDao().getConnectionSource(),
|
||||||
new Callable<Void>() {
|
new Callable<Void>() {
|
||||||
public Void call() throws Exception {
|
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());
|
BlockVO blockVO = new BlockVO(block.getNumber(), block.getHash(), block.getEncoded(), block.getCumulativeDifficulty());
|
||||||
save(blockVO);
|
save(blockVO);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import com.j256.ormlite.android.apptools.OpenHelperManager;
|
||||||
import org.ethereum.android.datasource.LevelDbDataSource;
|
import org.ethereum.android.datasource.LevelDbDataSource;
|
||||||
import org.ethereum.android.db.InMemoryBlockStore;
|
import org.ethereum.android.db.InMemoryBlockStore;
|
||||||
import org.ethereum.android.db.OrmLiteBlockStoreDatabase;
|
import org.ethereum.android.db.OrmLiteBlockStoreDatabase;
|
||||||
import org.ethereum.android.db.BlockStoreImpl;
|
|
||||||
import org.ethereum.config.SystemProperties;
|
import org.ethereum.config.SystemProperties;
|
||||||
import org.ethereum.core.BlockchainImpl;
|
import org.ethereum.core.BlockchainImpl;
|
||||||
import org.ethereum.core.Wallet;
|
import org.ethereum.core.Wallet;
|
||||||
|
@ -20,7 +19,7 @@ import org.ethereum.facade.Repository;
|
||||||
import org.ethereum.listener.CompositeEthereumListener;
|
import org.ethereum.listener.CompositeEthereumListener;
|
||||||
import org.ethereum.listener.EthereumListener;
|
import org.ethereum.listener.EthereumListener;
|
||||||
import org.ethereum.manager.AdminInfo;
|
import org.ethereum.manager.AdminInfo;
|
||||||
import org.ethereum.manager.BlockLoader;
|
import org.ethereum.android.manager.BlockLoader;
|
||||||
import org.ethereum.manager.WorldManager;
|
import org.ethereum.manager.WorldManager;
|
||||||
import org.ethereum.net.MessageQueue;
|
import org.ethereum.net.MessageQueue;
|
||||||
import org.ethereum.net.client.PeerClient;
|
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 {
|
dependencies {
|
||||||
classpath 'me.champeau.gradle:antlr4-gradle-plugin:0.1'
|
classpath 'me.champeau.gradle:antlr4-gradle-plugin:0.1'
|
||||||
classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:3.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 {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
// id "com.ewerk.gradle.plugins.dagger" version "1.0.0"
|
|
||||||
id 'application'
|
id 'application'
|
||||||
id 'jacoco'
|
id 'jacoco'
|
||||||
id 'com.github.johnrengelman.shadow' version '1.2.1'
|
id 'com.github.johnrengelman.shadow' version '1.2.1'
|
||||||
|
@ -32,7 +30,7 @@ repositories {
|
||||||
sourceCompatibility = 1.7
|
sourceCompatibility = 1.7
|
||||||
|
|
||||||
mainClassName = 'org.ethereum.Start'
|
mainClassName = 'org.ethereum.Start'
|
||||||
applicationDefaultJvmArgs = ["-server", "-Xms3g", "-Xss32m"]
|
applicationDefaultJvmArgs = ["-server", "-Xss32m"]
|
||||||
|
|
||||||
ext.generatedSrcDir = file('src/gen/java')
|
ext.generatedSrcDir = file('src/gen/java')
|
||||||
|
|
||||||
|
@ -129,43 +127,41 @@ dependencies {
|
||||||
|
|
||||||
compile fileTree(include: ['*.jar'], dir: 'libs')
|
compile fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
|
|
||||||
compile('io.netty:netty-all:4.0.28.Final') {
|
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 "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 "org.iq80.leveldb:leveldb:${leveldbVersion}"
|
||||||
|
|
||||||
compile('com.cedarsoftware:java-util:1.8.0') {
|
compile "org.fusesource.leveldbjni:leveldbjni:1.8"
|
||||||
exclude group: 'commons-logging', module: 'commons-logging'
|
|
||||||
} // for deep equals
|
|
||||||
|
|
||||||
compile 'org.antlr:antlr4-runtime:4.5' // for serpent compilation
|
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 "com.yuvalshavit:antlr-denter:1.1"
|
||||||
|
compile "org.javassist:javassist:3.15.0-GA"
|
||||||
compile "org.slf4j:slf4j-log4j12:${slf4jVersion}"
|
compile "org.slf4j:slf4j-api:${slf4jVersion}"
|
||||||
|
compile "log4j:log4j:${log4jVersion}"
|
||||||
compile 'org.codehaus.jackson:jackson-mapper-asl:1.9.13'
|
compile "org.codehaus.jackson:jackson-mapper-asl:1.9.13"
|
||||||
compile 'com.google.code.findbugs:jsr305:3.0.0'
|
compile "com.google.code.findbugs:jsr305:3.0.0"
|
||||||
compile 'com.fasterxml.jackson.core:jackson-databind:2.2.0'
|
compile "com.fasterxml.jackson.core:jackson-databind:2.5.1"
|
||||||
compile 'org.apache.commons:commons-collections4:4.0'
|
compile "org.apache.commons:commons-collections4:4.0"
|
||||||
compile 'commons-io:commons-io:2.4'
|
|
||||||
compile "commons-codec:commons-codec:1.10"
|
compile "commons-codec:commons-codec:1.10"
|
||||||
compile "com.h2database:h2:1.4.187"
|
|
||||||
compile "org.hibernate:hibernate-core:${hibernateVersion}"
|
compile "org.hibernate:hibernate-core:${hibernateVersion}"
|
||||||
compile "org.hibernate:hibernate-entitymanager:${hibernateVersion}"
|
compile "org.hibernate:hibernate-entitymanager:${hibernateVersion}"
|
||||||
|
compile "commons-dbcp:commons-dbcp:1.4"
|
||||||
compile "redis.clients:jedis:2.6.0"
|
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: 'junit', module: 'junit'
|
||||||
exclude group: 'xml-apis', module: 'xml-apis'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compile 'commons-io:commons-io:2.4'
|
||||||
|
|
||||||
testCompile "junit:junit:${junitVersion}"
|
testCompile "junit:junit:${junitVersion}"
|
||||||
testCompile 'com.google.dagger:dagger:2.1-SNAPSHOT'
|
testCompile 'com.google.dagger:dagger:2.1-SNAPSHOT'
|
||||||
testCompile 'com.google.dagger:dagger-compiler:2.0'
|
testCompile 'com.google.dagger:dagger-compiler:2.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -295,7 +295,8 @@ public class BlockchainImpl implements Blockchain {
|
||||||
storeBlock(block, receipts);
|
storeBlock(block, receipts);
|
||||||
|
|
||||||
|
|
||||||
if (block.getNumber() % 20_000 == 0) {
|
if (adminInfo.isConsensus() &&
|
||||||
|
block.getNumber() % 5_000 == 0) {
|
||||||
repository.flush();
|
repository.flush();
|
||||||
blockStore.flush();
|
blockStore.flush();
|
||||||
}
|
}
|
||||||
|
|
|
@ -324,9 +324,8 @@ public class TransactionExecutor {
|
||||||
track.delete(address.getLast20Bytes());
|
track.delete(address.getLast20Bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep execution logs todo: that yet
|
if (result != null)
|
||||||
//*cpp* if (m_ext)
|
logs = result.getLogInfoList();
|
||||||
// m_logs = m_ext->sub.logs;
|
|
||||||
|
|
||||||
if (result.getLogInfoList() != null){
|
if (result.getLogInfoList() != null){
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,15 @@ package org.ethereum.datasource;
|
||||||
|
|
||||||
import org.ethereum.config.SystemProperties;
|
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.CompressionType;
|
||||||
import org.iq80.leveldb.DB;
|
import org.iq80.leveldb.DB;
|
||||||
import org.iq80.leveldb.DBIterator;
|
import org.iq80.leveldb.DBIterator;
|
||||||
import org.iq80.leveldb.Options;
|
import org.iq80.leveldb.Options;
|
||||||
import org.iq80.leveldb.WriteBatch;
|
import org.iq80.leveldb.WriteBatch;
|
||||||
|
|
||||||
|
import org.iq80.leveldb.impl.Iq80DBFactory;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -46,6 +49,10 @@ public class LevelDbDataSource implements KeyValueDataSource {
|
||||||
Options options = new Options();
|
Options options = new Options();
|
||||||
options.createIfMissing(true);
|
options.createIfMissing(true);
|
||||||
options.compressionType(CompressionType.NONE);
|
options.compressionType(CompressionType.NONE);
|
||||||
|
options.blockSize(10 * 1024);
|
||||||
|
options.writeBufferSize(10 * 1024);
|
||||||
|
options.cacheSize(0);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.debug("Opening database");
|
logger.debug("Opening database");
|
||||||
File dbLocation = new File(System.getProperty("user.dir") + "/" +
|
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);
|
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) {
|
} catch (IOException ioe) {
|
||||||
logger.error(ioe.getMessage(), ioe);
|
logger.error(ioe.getMessage(), ioe);
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.ethereum.db;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -35,6 +36,12 @@ public class DetailsDataStore {
|
||||||
|
|
||||||
details = new ContractDetailsImpl(data);
|
details = new ContractDetailsImpl(data);
|
||||||
cache.put( wrap(key), details);
|
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;
|
return details;
|
||||||
|
@ -57,11 +64,13 @@ public class DetailsDataStore {
|
||||||
long t = System.nanoTime();
|
long t = System.nanoTime();
|
||||||
|
|
||||||
Map<byte[], byte[]> batch = new HashMap<>();
|
Map<byte[], byte[]> batch = new HashMap<>();
|
||||||
|
long totalSize = 0;
|
||||||
for (ByteArrayWrapper key : cache.keySet()){
|
for (ByteArrayWrapper key : cache.keySet()){
|
||||||
ContractDetails contractDetails = cache.get(key);
|
ContractDetails contractDetails = cache.get(key);
|
||||||
byte[] value = contractDetails.getEncoded();
|
byte[] value = contractDetails.getEncoded();
|
||||||
db.put(key.getData(), value);
|
db.put(key.getData(), value);
|
||||||
batch.put(key.getData(), value);
|
batch.put(key.getData(), value);
|
||||||
|
totalSize += value.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
db.getDb().updateBatch(batch);
|
db.getDb().updateBatch(batch);
|
||||||
|
@ -72,11 +81,18 @@ public class DetailsDataStore {
|
||||||
|
|
||||||
long keys = cache.size();
|
long keys = cache.size();
|
||||||
|
|
||||||
|
byte[] aKey = Hex.decode("b61662398570293e4f0d25525e2b3002b7fe0836");
|
||||||
|
ContractDetails aDetails = cache.get(wrap(aKey));
|
||||||
|
|
||||||
cache.clear();
|
cache.clear();
|
||||||
removes.clear();
|
removes.clear();
|
||||||
|
|
||||||
|
if (aDetails != null) cache.put(wrap(aKey), aDetails);
|
||||||
|
|
||||||
long t_ = System.nanoTime();
|
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();
|
long t_ = System.nanoTime();
|
||||||
|
|
||||||
Session s = sessionFactory.openSession();
|
Session s = sessionFactory.openSession();
|
||||||
|
|
||||||
|
// clear old blocks
|
||||||
s.beginTransaction();
|
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());
|
BlockVO blockVO = new BlockVO(block.getNumber(), block.getHash(), block.getEncoded(), block.getCumulativeDifficulty());
|
||||||
s.saveOrUpdate(blockVO);
|
s.save(blockVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
s.getTransaction().commit();
|
s.getTransaction().commit();
|
||||||
|
|
|
@ -171,12 +171,7 @@ public class RepositoryImpl implements Repository {
|
||||||
gLogger.info("flushing to disk");
|
gLogger.info("flushing to disk");
|
||||||
|
|
||||||
dds.flush();
|
dds.flush();
|
||||||
|
|
||||||
long t = System.nanoTime();
|
|
||||||
worldState.sync();
|
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.core.Block;
|
||||||
import org.ethereum.facade.Blockchain;
|
import org.ethereum.facade.Blockchain;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
@ -19,9 +19,7 @@ import static org.ethereum.config.SystemProperties.CONFIG;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class BlockLoader {
|
public class BlockLoader {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger("BlockLoader");
|
protected Blockchain blockchain;
|
||||||
|
|
||||||
private Blockchain blockchain;
|
|
||||||
|
|
||||||
Scanner scanner = null;
|
Scanner scanner = null;
|
||||||
|
|
||||||
|
@ -30,11 +28,11 @@ public class BlockLoader {
|
||||||
this.blockchain = blockchain;
|
this.blockchain = blockchain;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long loadBlocks(){
|
public void loadBlocks(){
|
||||||
|
|
||||||
String fileSrc = CONFIG.blocksLoader();
|
String fileSrc = CONFIG.blocksLoader();
|
||||||
try {
|
try {
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
FileInputStream inputStream = null;
|
FileInputStream inputStream = null;
|
||||||
inputStream = new FileInputStream(fileSrc);
|
inputStream = new FileInputStream(fileSrc);
|
||||||
scanner = new Scanner(inputStream, "UTF-8");
|
scanner = new Scanner(inputStream, "UTF-8");
|
||||||
|
@ -50,11 +48,16 @@ public class BlockLoader {
|
||||||
if (block.getNumber() > blockchain.getBestBlock().getNumber()){
|
if (block.getNumber() > blockchain.getBestBlock().getNumber()){
|
||||||
blockchain.tryToConnect(block);
|
blockchain.tryToConnect(block);
|
||||||
long t1_ = System.nanoTime();
|
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);
|
float elapsed = ((float)(t1_ - t1) / 1_000_000);
|
||||||
} else {
|
|
||||||
|
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)
|
if (block.getNumber() % 10000 == 0)
|
||||||
System.out.println("Skipping block #" + block.getNumber());
|
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) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
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*/
|
/* [2] Crate signature*/
|
||||||
ECKey.ECDSASignature signature = privKey.sign(forSig);
|
ECKey.ECDSASignature signature = privKey.sign(forSig);
|
||||||
|
|
||||||
|
signature.v -= 27;
|
||||||
|
|
||||||
byte[] sigBytes =
|
byte[] sigBytes =
|
||||||
merge(BigIntegers.asUnsignedByteArray(signature.r),
|
merge(BigIntegers.asUnsignedByteArray(signature.r),
|
||||||
BigIntegers.asUnsignedByteArray(signature.s), new byte[]{signature.v});
|
BigIntegers.asUnsignedByteArray(signature.s), new byte[]{signature.v});
|
||||||
|
@ -116,6 +118,12 @@ public abstract class Message {
|
||||||
return outKey;
|
return outKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] getNodeId() {
|
||||||
|
byte[] nodeID = new byte[64];
|
||||||
|
System.arraycopy(getKey().getPubKey(), 1, nodeID, 0, 64);
|
||||||
|
return nodeID;
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] getPacket() {
|
public byte[] getPacket() {
|
||||||
return wire;
|
return wire;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,10 @@ import org.ethereum.util.RLPList;
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static org.ethereum.util.ByteUtil.byteArrayToInt;
|
import static org.ethereum.util.ByteUtil.byteArrayToInt;
|
||||||
import static org.ethereum.util.ByteUtil.intToBytesNoLeadZeroes;
|
import static org.ethereum.util.ByteUtil.intToBytes;
|
||||||
|
|
||||||
public class Node {
|
public class Node {
|
||||||
|
|
||||||
|
@ -28,9 +29,25 @@ public class Node {
|
||||||
|
|
||||||
byte[] hostB = nodeRLP.get(0).getRLPData();
|
byte[] hostB = nodeRLP.get(0).getRLPData();
|
||||||
byte[] portB = nodeRLP.get(1).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);
|
int port = byteArrayToInt(portB);
|
||||||
|
|
||||||
this.host = host;
|
this.host = host;
|
||||||
|
@ -66,7 +83,7 @@ public class Node {
|
||||||
public byte[] getRLP() {
|
public byte[] getRLP() {
|
||||||
|
|
||||||
byte[] rlphost = RLP.encodeElement(host.getBytes(Charset.forName("UTF-8")));
|
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[] rlpId = RLP.encodeElement(id);
|
||||||
|
|
||||||
byte[] data = RLP.encodeList(rlphost, rlpPort, rlpId);
|
byte[] data = RLP.encodeList(rlphost, rlpPort, rlpId);
|
||||||
|
@ -81,4 +98,26 @@ public class Node {
|
||||||
", id=" + Hex.toHexString(id) +
|
", 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.RLP;
|
||||||
import org.ethereum.util.RLPItem;
|
import org.ethereum.util.RLPItem;
|
||||||
import org.ethereum.util.RLPList;
|
import org.ethereum.util.RLPList;
|
||||||
import org.spongycastle.util.encoders.Hex;
|
//import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
@ -28,11 +28,20 @@ public class PingMessage extends Message {
|
||||||
byte[] tmpPort = longToBytes(port);
|
byte[] tmpPort = longToBytes(port);
|
||||||
byte[] rlpPort = RLP.encodeElement(stripLeadingZeroes(tmpPort));
|
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[] tmpExp = longToBytes(expiration);
|
||||||
byte[] rlpExp = RLP.encodeElement(stripLeadingZeroes(tmpExp));
|
byte[] rlpExp = RLP.encodeElement(stripLeadingZeroes(tmpExp));
|
||||||
|
|
||||||
byte[] type = new byte[]{1};
|
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();
|
PingMessage ping = new PingMessage();
|
||||||
ping.encode(type, data, privKey);
|
ping.encode(type, data, privKey);
|
||||||
|
@ -48,14 +57,15 @@ public class PingMessage extends Message {
|
||||||
public void parse(byte[] data) {
|
public void parse(byte[] data) {
|
||||||
|
|
||||||
RLPList list = RLP.decode2(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.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());
|
this.expires = ByteUtil.byteArrayToLong(expires.getRLPData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,42 @@ import org.ethereum.util.RLPItem;
|
||||||
import org.ethereum.util.RLPList;
|
import org.ethereum.util.RLPList;
|
||||||
import org.spongycastle.util.encoders.Hex;
|
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 {
|
public class PongMessage extends Message {
|
||||||
|
|
||||||
byte[] token; // token is the MDC of the ping
|
byte[] token; // token is the MDC of the ping
|
||||||
long expires;
|
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) {
|
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.datasource.KeyValueDataSource;
|
||||||
import org.ethereum.db.ByteArrayWrapper;
|
import org.ethereum.db.ByteArrayWrapper;
|
||||||
import org.ethereum.util.Value;
|
import org.ethereum.util.Value;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import static org.ethereum.util.ByteUtil.wrap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Nick Savers
|
* @author Nick Savers
|
||||||
* @since 20.05.2014
|
* @since 20.05.2014
|
||||||
*/
|
*/
|
||||||
public class Cache {
|
public class Cache {
|
||||||
|
|
||||||
|
private static final Logger gLogger = LoggerFactory.getLogger("general");
|
||||||
|
|
||||||
private final KeyValueDataSource dataSource;
|
private final KeyValueDataSource dataSource;
|
||||||
private Map<ByteArrayWrapper, Node> nodes = new ConcurrentHashMap<>();
|
private Map<ByteArrayWrapper, Node> nodes = new ConcurrentHashMap<>();
|
||||||
private boolean isDirty;
|
private boolean isDirty;
|
||||||
|
@ -34,8 +40,8 @@ public class Cache {
|
||||||
Value value = new Value(o);
|
Value value = new Value(o);
|
||||||
byte[] enc = value.encode();
|
byte[] enc = value.encode();
|
||||||
if (enc.length >= 32) {
|
if (enc.length >= 32) {
|
||||||
byte[] sha = HashUtil.sha3(enc);
|
byte[] sha = value.hash();
|
||||||
this.nodes.put(new ByteArrayWrapper(sha), new Node(value, true));
|
this.nodes.put(wrap(sha), new Node(value, true));
|
||||||
this.isDirty = true;
|
this.isDirty = true;
|
||||||
return sha;
|
return sha;
|
||||||
}
|
}
|
||||||
|
@ -68,6 +74,7 @@ public class Cache {
|
||||||
|
|
||||||
public void commit() {
|
public void commit() {
|
||||||
|
|
||||||
|
long t = System.nanoTime();
|
||||||
if (dataSource == null) return;
|
if (dataSource == null) return;
|
||||||
|
|
||||||
// Don't try to commit if it isn't dirty
|
// 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<>();
|
Map<byte[], byte[]> batch = new HashMap<>();
|
||||||
for (ByteArrayWrapper key : this.nodes.keySet()) {
|
for (ByteArrayWrapper key : this.nodes.keySet()) {
|
||||||
Node node = this.nodes.get(key);
|
Node node = this.nodes.get(key);
|
||||||
if (node.isDirty()) {
|
if (node.isDirty()) {
|
||||||
batch.put(key.getData(), node.getValue().encode());
|
|
||||||
|
byte[] value = node.getValue().encode();
|
||||||
|
batch.put(key.getData(), value);
|
||||||
node.setDirty(false);
|
node.setDirty(false);
|
||||||
|
|
||||||
|
size += value.length;
|
||||||
|
keys += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dataSource.updateBatch(batch);
|
dataSource.updateBatch(batch);
|
||||||
this.isDirty = false;
|
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() {
|
public void undo() {
|
||||||
|
|
|
@ -163,8 +163,7 @@ public class TrieImpl implements Trie {
|
||||||
return (byte[]) this.getRoot();
|
return (byte[]) this.getRoot();
|
||||||
} else {
|
} else {
|
||||||
Value rootValue = new Value(this.getRoot());
|
Value rootValue = new Value(this.getRoot());
|
||||||
byte[] val = rootValue.encode();
|
return rootValue.hash();
|
||||||
return HashUtil.sha3(val);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -115,19 +115,32 @@ public class ByteUtil {
|
||||||
return stripLeadingZeroes(data);
|
return stripLeadingZeroes(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] intToBytes(int val){
|
||||||
|
|
||||||
/**
|
if (val == 0) return EMPTY_BYTE_ARRAY;
|
||||||
* Converts a int value into a byte array.
|
|
||||||
*
|
int lenght = 0;
|
||||||
* @param val - int value to convert
|
|
||||||
* @return decimal value with leading byte that are zeroes striped
|
int tmpVal = val;
|
||||||
*/
|
while (tmpVal != 0){
|
||||||
public static byte[] intToBytesNoLeadZeroes(int val) {
|
tmpVal = tmpVal >> 8;
|
||||||
return longToBytesNoLeadZeroes((long)val);
|
++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>
|
* Convert a byte-array into a hex String.<br>
|
||||||
* Works similar to {@link Hex#toHexString}
|
* Works similar to {@link Hex#toHexString}
|
||||||
|
|
|
@ -7,9 +7,7 @@ import java.nio.ByteBuffer;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static java.util.Arrays.copyOfRange;
|
import static java.util.Arrays.copyOfRange;
|
||||||
import static org.ethereum.util.ByteUtil.byteArrayToInt;
|
import static org.ethereum.util.ByteUtil.*;
|
||||||
import static org.ethereum.util.ByteUtil.isNullOrZeroArray;
|
|
||||||
import static org.ethereum.util.ByteUtil.isSingleZero;
|
|
||||||
import static org.spongycastle.util.Arrays.concatenate;
|
import static org.spongycastle.util.Arrays.concatenate;
|
||||||
import static org.spongycastle.util.BigIntegers.asUnsignedByteArray;
|
import static org.spongycastle.util.BigIntegers.asUnsignedByteArray;
|
||||||
|
|
||||||
|
@ -687,7 +685,7 @@ public class RLP {
|
||||||
} else if (length < MAX_ITEM_LENGTH) {
|
} else if (length < MAX_ITEM_LENGTH) {
|
||||||
byte[] binaryLength;
|
byte[] binaryLength;
|
||||||
if (length > 0xFF)
|
if (length > 0xFF)
|
||||||
binaryLength = BigInteger.valueOf(length).toByteArray();
|
binaryLength = intToBytes(length);
|
||||||
else
|
else
|
||||||
binaryLength = new byte[]{(byte) length};
|
binaryLength = new byte[]{(byte) length};
|
||||||
byte firstByte = (byte) (binaryLength.length + offset + SIZE_THRESHOLD - 1);
|
byte firstByte = (byte) (binaryLength.length + offset + SIZE_THRESHOLD - 1);
|
||||||
|
@ -700,7 +698,7 @@ public class RLP {
|
||||||
public static byte[] encodeByte(byte singleByte) {
|
public static byte[] encodeByte(byte singleByte) {
|
||||||
if ((singleByte & 0xFF) == 0) {
|
if ((singleByte & 0xFF) == 0) {
|
||||||
return new byte[]{(byte) OFFSET_SHORT_ITEM};
|
return new byte[]{(byte) OFFSET_SHORT_ITEM};
|
||||||
} else if ((singleByte & 0xFF) < 0x7F) {
|
} else if ((singleByte & 0xFF) <= 0x7F) {
|
||||||
return new byte[]{singleByte};
|
return new byte[]{singleByte};
|
||||||
} else {
|
} else {
|
||||||
return new byte[]{(byte) (OFFSET_SHORT_ITEM + 1), singleByte};
|
return new byte[]{(byte) (OFFSET_SHORT_ITEM + 1), singleByte};
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.ethereum.util;
|
||||||
|
|
||||||
import com.cedarsoftware.util.DeepEquals;
|
import com.cedarsoftware.util.DeepEquals;
|
||||||
|
|
||||||
|
import org.ethereum.crypto.HashUtil;
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
@ -15,6 +16,8 @@ import java.util.List;
|
||||||
public class Value {
|
public class Value {
|
||||||
|
|
||||||
private Object value;
|
private Object value;
|
||||||
|
private byte[] rlp;
|
||||||
|
private byte[] sha3;
|
||||||
|
|
||||||
public static Value fromRlpEncoded(byte[] data) {
|
public static Value fromRlpEncoded(byte[] data) {
|
||||||
if (data != null && data.length != 0) {
|
if (data != null && data.length != 0) {
|
||||||
|
@ -120,7 +123,15 @@ public class Value {
|
||||||
* *****************/
|
* *****************/
|
||||||
|
|
||||||
public byte[] encode() {
|
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) {
|
public boolean cmp(Value o) {
|
||||||
|
|
|
@ -296,6 +296,17 @@ public class DataWord implements Comparable<DataWord> {
|
||||||
return Hex.toHexString(data);
|
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() {
|
public String shortHex() {
|
||||||
String hexValue = Hex.toHexString(getNoLeadZeroesData()).toUpperCase();
|
String hexValue = Hex.toHexString(getNoLeadZeroesData()).toUpperCase();
|
||||||
return "0x" + hexValue.replaceFirst("^0+(?!$)", "");
|
return "0x" + hexValue.replaceFirst("^0+(?!$)", "");
|
||||||
|
|
|
@ -53,13 +53,21 @@ public class Memory implements ProgramTraceListenerAware {
|
||||||
return data;
|
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 chunkIndex = address / CHUNK_SIZE;
|
||||||
int chunkOffset = 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;
|
int start = 0;
|
||||||
|
|
||||||
while (toCapture > 0) {
|
while (toCapture > 0) {
|
||||||
|
@ -79,7 +87,7 @@ public class Memory implements ProgramTraceListenerAware {
|
||||||
|
|
||||||
public void extendAndWrite(int address, int allocSize, byte[] data) {
|
public void extendAndWrite(int address, int allocSize, byte[] data) {
|
||||||
extend(address, allocSize);
|
extend(address, allocSize);
|
||||||
write(address, data);
|
write(address, data, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void extend(int address, int size) {
|
public void extend(int address, int size) {
|
||||||
|
|
|
@ -215,11 +215,15 @@ public class Program {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void memorySave(DataWord addrB, DataWord value) {
|
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) {
|
public void memorySave(int addr, byte[] value) {
|
||||||
memory.write(addr, value);
|
memory.write(addr, value, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void memoryExpand(DataWord outDataOffs, DataWord outDataSize) {
|
public void memoryExpand(DataWord outDataOffs, DataWord outDataSize) {
|
||||||
|
@ -241,6 +245,8 @@ public class Program {
|
||||||
memory.extendAndWrite(addr, allocSize, value);
|
memory.extendAndWrite(addr, allocSize, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public DataWord memoryLoad(DataWord addr) {
|
public DataWord memoryLoad(DataWord addr) {
|
||||||
return memory.readWord(addr.intValue());
|
return memory.readWord(addr.intValue());
|
||||||
}
|
}
|
||||||
|
@ -493,15 +499,8 @@ public class Program {
|
||||||
// 3. APPLY RESULTS: result.getHReturn() into out_memory allocated
|
// 3. APPLY RESULTS: result.getHReturn() into out_memory allocated
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
byte[] buffer = result.getHReturn();
|
byte[] buffer = result.getHReturn();
|
||||||
int allocSize = msg.getOutDataSize().intValue();
|
int offset = msg.getOutDataOffs().intValue();
|
||||||
if (buffer != null && allocSize > 0) {
|
this.memorySaveLimited(offset, buffer);
|
||||||
int retSize = buffer.length;
|
|
||||||
int offset = msg.getOutDataOffs().intValue();
|
|
||||||
if (retSize > allocSize)
|
|
||||||
this.memorySave(offset, buffer);
|
|
||||||
else
|
|
||||||
this.memorySave(offset, allocSize, buffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK
|
// 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK
|
||||||
|
@ -1024,7 +1023,7 @@ public class Program {
|
||||||
* used mostly for testing reasons
|
* used mostly for testing reasons
|
||||||
*/
|
*/
|
||||||
public void initMem(byte[] data) {
|
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();
|
DataWord value = program.stackPop();
|
||||||
|
|
||||||
if (logger.isInfoEnabled())
|
if (logger.isInfoEnabled())
|
||||||
hint = "addr: " + addr + " value: " + value;
|
hint = "[" + program.programAddress.toPrefixString() + "] key: " + addr + " value: " + value;
|
||||||
|
|
||||||
program.storageSave(addr, value);
|
program.storageSave(addr, value);
|
||||||
program.step();
|
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
|
* @author: Roman Mandeleil
|
||||||
* Created on: 30/01/2015 11:04
|
* Created on: 30/01/2015 11:04
|
||||||
*/
|
*/
|
||||||
|
@Ignore
|
||||||
public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
|
public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger("test");
|
private static final Logger logger = LoggerFactory.getLogger("test");
|
||||||
|
@ -64,7 +64,6 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
|
||||||
logger.info("total difficulty: {}", cumDifficulty);
|
logger.info("total difficulty: {}", cumDifficulty);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@Test
|
@Test
|
||||||
public void testEmpty(){
|
public void testEmpty(){
|
||||||
BlockStore blockStore = new InMemoryBlockStore();
|
BlockStore blockStore = new InMemoryBlockStore();
|
||||||
|
@ -72,7 +71,6 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
|
||||||
assertNull(blockStore.getBestBlock());
|
assertNull(blockStore.getBestBlock());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@Test
|
@Test
|
||||||
public void testFlush(){
|
public void testFlush(){
|
||||||
BlockStore blockStore = new InMemoryBlockStore();
|
BlockStore blockStore = new InMemoryBlockStore();
|
||||||
|
@ -85,7 +83,6 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
|
||||||
blockStore.flush();
|
blockStore.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleLoad(){
|
public void testSimpleLoad(){
|
||||||
|
|
||||||
|
@ -107,7 +104,6 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
|
||||||
assertTrue(blockStore.getBestBlock().getNumber() == 8003);
|
assertTrue(blockStore.getBestBlock().getNumber() == 8003);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@Test
|
@Test
|
||||||
public void testFlushEach1000(){
|
public void testFlushEach1000(){
|
||||||
|
|
||||||
|
@ -125,7 +121,7 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@Test
|
@Test
|
||||||
public void testBlockHashByNumber(){
|
public void testBlockHashByNumber(){
|
||||||
|
|
||||||
|
@ -158,7 +154,6 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
|
||||||
assertTrue(hash.startsWith("820aa7"));
|
assertTrue(hash.startsWith("820aa7"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@Test
|
@Test
|
||||||
public void testBlockByNumber(){
|
public void testBlockByNumber(){
|
||||||
|
|
||||||
|
@ -191,7 +186,7 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
|
||||||
assertTrue(hash.startsWith("820aa7"));
|
assertTrue(hash.startsWith("820aa7"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetBlockByNumber() {
|
public void testGetBlockByNumber() {
|
||||||
|
|
||||||
|
@ -209,7 +204,7 @@ public class InMemoryBlockStoreTest extends AbstractInMemoryBlockStoreTest {
|
||||||
assertEquals("4312750101", blockStore.getTotalDifficulty().toString());
|
assertEquals("4312750101", blockStore.getTotalDifficulty().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@Test
|
@Test
|
||||||
public void testDbGetBlockByHash(){
|
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;
|
package org.ethereum.net.rlpx;
|
||||||
|
|
||||||
import org.ethereum.crypto.ECKey;
|
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.Assert;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -15,7 +12,6 @@ import java.nio.charset.Charset;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import static org.ethereum.crypto.HashUtil.sha3;
|
import static org.ethereum.crypto.HashUtil.sha3;
|
||||||
import static org.ethereum.util.ByteUtil.merge;
|
import static org.ethereum.util.ByteUtil.merge;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
@ -25,6 +21,7 @@ public class RLPXTest {
|
||||||
|
|
||||||
private static final org.slf4j.Logger logger = LoggerFactory.getLogger("test");
|
private static final org.slf4j.Logger logger = LoggerFactory.getLogger("test");
|
||||||
|
|
||||||
|
@Ignore
|
||||||
@Test // ping test
|
@Test // ping test
|
||||||
public void test1() {
|
public void test1() {
|
||||||
|
|
||||||
|
@ -65,6 +62,7 @@ public class RLPXTest {
|
||||||
assertEquals(key.toString(), key2.toString());
|
assertEquals(key.toString(), key2.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore
|
||||||
@Test // neighbors message
|
@Test // neighbors message
|
||||||
public void test3() {
|
public void test3() {
|
||||||
|
|
||||||
|
@ -133,6 +131,7 @@ public class RLPXTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Ignore
|
||||||
@Test
|
@Test
|
||||||
public void test6() {
|
public void test6() {
|
||||||
|
|
||||||
|
@ -153,7 +152,7 @@ public class RLPXTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Ignore
|
||||||
@Test // Neighbors parse data
|
@Test // Neighbors parse data
|
||||||
public void test7() {
|
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);
|
data = RLP.encodeByte((byte) 120);
|
||||||
assertArrayEquals(expected2, data);
|
assertArrayEquals(expected2, data);
|
||||||
|
|
||||||
byte[] expected3 = {(byte) 0x81, (byte) 0x7F};
|
byte[] expected3 = {(byte) 0x7F};
|
||||||
data = RLP.encodeByte((byte) 127);
|
data = RLP.encodeByte((byte) 127);
|
||||||
assertArrayEquals(expected3, data);
|
assertArrayEquals(expected3, data);
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ public class RLPTest {
|
||||||
data = RLP.encodeShort((byte) 120);
|
data = RLP.encodeShort((byte) 120);
|
||||||
assertArrayEquals(expected2, data);
|
assertArrayEquals(expected2, data);
|
||||||
|
|
||||||
byte[] expected3 = {(byte) 0x81, (byte) 0x7F};
|
byte[] expected3 = { (byte) 0x7F};
|
||||||
data = RLP.encodeShort((byte) 127);
|
data = RLP.encodeShort((byte) 127);
|
||||||
assertArrayEquals(expected3, data);
|
assertArrayEquals(expected3, data);
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ public class RLPTest {
|
||||||
data = RLP.encodeInt(120);
|
data = RLP.encodeInt(120);
|
||||||
assertArrayEquals(expected2, data);
|
assertArrayEquals(expected2, data);
|
||||||
|
|
||||||
byte[] expected3 = {(byte) 0x81, (byte) 0x7F};
|
byte[] expected3 = {(byte) 0x7F};
|
||||||
data = RLP.encodeInt(127);
|
data = RLP.encodeInt(127);
|
||||||
assertArrayEquals(expected3, data);
|
assertArrayEquals(expected3, data);
|
||||||
|
|
||||||
|
@ -1029,6 +1029,21 @@ public class RLPTest {
|
||||||
assertEquals("c0", Hex.toHexString(setEncoded));
|
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 java.lang.Math.ceil;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class MemoryTest {
|
public class MemoryTest {
|
||||||
|
|
||||||
|
@ -44,18 +45,18 @@ public class MemoryTest {
|
||||||
Memory memoryBuffer = new Memory();
|
Memory memoryBuffer = new Memory();
|
||||||
byte[] data = {1, 1, 1, 1};
|
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);
|
byte[] chunk = memoryBuffer.getChunks().get(0);
|
||||||
Assert.assertTrue(chunk[0] == 1);
|
assertTrue(chunk[0] == 1);
|
||||||
Assert.assertTrue(chunk[1] == 1);
|
assertTrue(chunk[1] == 1);
|
||||||
Assert.assertTrue(chunk[2] == 1);
|
assertTrue(chunk[2] == 1);
|
||||||
Assert.assertTrue(chunk[3] == 1);
|
assertTrue(chunk[3] == 1);
|
||||||
Assert.assertTrue(chunk[4] == 0);
|
assertTrue(chunk[4] == 0);
|
||||||
|
|
||||||
Assert.assertTrue(memoryBuffer.size() == 32);
|
assertTrue(memoryBuffer.size() == 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -64,19 +65,19 @@ public class MemoryTest {
|
||||||
Memory memoryBuffer = new Memory();
|
Memory memoryBuffer = new Memory();
|
||||||
byte[] data = Hex.decode("0101010101010101010101010101010101010101010101010101010101010101");
|
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);
|
byte[] chunk = memoryBuffer.getChunks().get(0);
|
||||||
Assert.assertTrue(chunk[0] == 1);
|
assertTrue(chunk[0] == 1);
|
||||||
Assert.assertTrue(chunk[1] == 1);
|
assertTrue(chunk[1] == 1);
|
||||||
|
|
||||||
Assert.assertTrue(chunk[30] == 1);
|
assertTrue(chunk[30] == 1);
|
||||||
Assert.assertTrue(chunk[31] == 1);
|
assertTrue(chunk[31] == 1);
|
||||||
Assert.assertTrue(chunk[32] == 0);
|
assertTrue(chunk[32] == 0);
|
||||||
|
|
||||||
Assert.assertTrue(memoryBuffer.size() == 32);
|
assertTrue(memoryBuffer.size() == 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -85,20 +86,20 @@ public class MemoryTest {
|
||||||
Memory memoryBuffer = new Memory();
|
Memory memoryBuffer = new Memory();
|
||||||
byte[] data = Hex.decode("010101010101010101010101010101010101010101010101010101010101010101");
|
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);
|
byte[] chunk = memoryBuffer.getChunks().get(0);
|
||||||
Assert.assertTrue(chunk[0] == 1);
|
assertTrue(chunk[0] == 1);
|
||||||
Assert.assertTrue(chunk[1] == 1);
|
assertTrue(chunk[1] == 1);
|
||||||
|
|
||||||
Assert.assertTrue(chunk[30] == 1);
|
assertTrue(chunk[30] == 1);
|
||||||
Assert.assertTrue(chunk[31] == 1);
|
assertTrue(chunk[31] == 1);
|
||||||
Assert.assertTrue(chunk[32] == 1);
|
assertTrue(chunk[32] == 1);
|
||||||
Assert.assertTrue(chunk[33] == 0);
|
assertTrue(chunk[33] == 0);
|
||||||
|
|
||||||
Assert.assertTrue(memoryBuffer.size() == 64);
|
assertTrue(memoryBuffer.size() == 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -108,18 +109,18 @@ public class MemoryTest {
|
||||||
byte[] data = new byte[1024];
|
byte[] data = new byte[1024];
|
||||||
Arrays.fill(data, (byte) 1);
|
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);
|
byte[] chunk = memoryBuffer.getChunks().get(0);
|
||||||
Assert.assertTrue(chunk[0] == 1);
|
assertTrue(chunk[0] == 1);
|
||||||
Assert.assertTrue(chunk[1] == 1);
|
assertTrue(chunk[1] == 1);
|
||||||
|
|
||||||
Assert.assertTrue(chunk[1022] == 1);
|
assertTrue(chunk[1022] == 1);
|
||||||
Assert.assertTrue(chunk[1023] == 1);
|
assertTrue(chunk[1023] == 1);
|
||||||
|
|
||||||
Assert.assertTrue(memoryBuffer.size() == 1024);
|
assertTrue(memoryBuffer.size() == 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -130,22 +131,22 @@ public class MemoryTest {
|
||||||
byte[] data = new byte[1025];
|
byte[] data = new byte[1025];
|
||||||
Arrays.fill(data, (byte) 1);
|
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);
|
byte[] chunk1 = memoryBuffer.getChunks().get(0);
|
||||||
Assert.assertTrue(chunk1[0] == 1);
|
assertTrue(chunk1[0] == 1);
|
||||||
Assert.assertTrue(chunk1[1] == 1);
|
assertTrue(chunk1[1] == 1);
|
||||||
|
|
||||||
Assert.assertTrue(chunk1[1022] == 1);
|
assertTrue(chunk1[1022] == 1);
|
||||||
Assert.assertTrue(chunk1[1023] == 1);
|
assertTrue(chunk1[1023] == 1);
|
||||||
|
|
||||||
byte[] chunk2 = memoryBuffer.getChunks().get(1);
|
byte[] chunk2 = memoryBuffer.getChunks().get(1);
|
||||||
Assert.assertTrue(chunk2[0] == 1);
|
assertTrue(chunk2[0] == 1);
|
||||||
Assert.assertTrue(chunk2[1] == 0);
|
assertTrue(chunk2[1] == 0);
|
||||||
|
|
||||||
Assert.assertTrue(memoryBuffer.size() == 1056);
|
assertTrue(memoryBuffer.size() == 1056);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -159,26 +160,26 @@ public class MemoryTest {
|
||||||
byte[] data2 = new byte[1024];
|
byte[] data2 = new byte[1024];
|
||||||
Arrays.fill(data2, (byte) 2);
|
Arrays.fill(data2, (byte) 2);
|
||||||
|
|
||||||
memoryBuffer.write(0, data1);
|
memoryBuffer.write(0, data1, false);
|
||||||
memoryBuffer.write(1024, data2);
|
memoryBuffer.write(1024, data2, false);
|
||||||
|
|
||||||
Assert.assertTrue(2 == memoryBuffer.getChunks().size());
|
assertTrue(2 == memoryBuffer.getChunks().size());
|
||||||
|
|
||||||
byte[] chunk1 = memoryBuffer.getChunks().get(0);
|
byte[] chunk1 = memoryBuffer.getChunks().get(0);
|
||||||
Assert.assertTrue(chunk1[0] == 1);
|
assertTrue(chunk1[0] == 1);
|
||||||
Assert.assertTrue(chunk1[1] == 1);
|
assertTrue(chunk1[1] == 1);
|
||||||
|
|
||||||
Assert.assertTrue(chunk1[1022] == 1);
|
assertTrue(chunk1[1022] == 1);
|
||||||
Assert.assertTrue(chunk1[1023] == 1);
|
assertTrue(chunk1[1023] == 1);
|
||||||
|
|
||||||
byte[] chunk2 = memoryBuffer.getChunks().get(1);
|
byte[] chunk2 = memoryBuffer.getChunks().get(1);
|
||||||
Assert.assertTrue(chunk2[0] == 2);
|
assertTrue(chunk2[0] == 2);
|
||||||
Assert.assertTrue(chunk2[1] == 2);
|
assertTrue(chunk2[1] == 2);
|
||||||
|
|
||||||
Assert.assertTrue(chunk2[1022] == 2);
|
assertTrue(chunk2[1022] == 2);
|
||||||
Assert.assertTrue(chunk2[1023] == 2);
|
assertTrue(chunk2[1023] == 2);
|
||||||
|
|
||||||
Assert.assertTrue(memoryBuffer.size() == 2048);
|
assertTrue(memoryBuffer.size() == 2048);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -195,30 +196,30 @@ public class MemoryTest {
|
||||||
byte[] data3 = new byte[1];
|
byte[] data3 = new byte[1];
|
||||||
Arrays.fill(data3, (byte) 3);
|
Arrays.fill(data3, (byte) 3);
|
||||||
|
|
||||||
memoryBuffer.write(0, data1);
|
memoryBuffer.write(0, data1, false);
|
||||||
memoryBuffer.write(1024, data2);
|
memoryBuffer.write(1024, data2, false);
|
||||||
memoryBuffer.write(2048, data3);
|
memoryBuffer.write(2048, data3, false);
|
||||||
|
|
||||||
Assert.assertTrue(3 == memoryBuffer.getChunks().size());
|
assertTrue(3 == memoryBuffer.getChunks().size());
|
||||||
|
|
||||||
byte[] chunk1 = memoryBuffer.getChunks().get(0);
|
byte[] chunk1 = memoryBuffer.getChunks().get(0);
|
||||||
Assert.assertTrue(chunk1[0] == 1);
|
assertTrue(chunk1[0] == 1);
|
||||||
Assert.assertTrue(chunk1[1] == 1);
|
assertTrue(chunk1[1] == 1);
|
||||||
|
|
||||||
Assert.assertTrue(chunk1[1022] == 1);
|
assertTrue(chunk1[1022] == 1);
|
||||||
Assert.assertTrue(chunk1[1023] == 1);
|
assertTrue(chunk1[1023] == 1);
|
||||||
|
|
||||||
byte[] chunk2 = memoryBuffer.getChunks().get(1);
|
byte[] chunk2 = memoryBuffer.getChunks().get(1);
|
||||||
Assert.assertTrue(chunk2[0] == 2);
|
assertTrue(chunk2[0] == 2);
|
||||||
Assert.assertTrue(chunk2[1] == 2);
|
assertTrue(chunk2[1] == 2);
|
||||||
|
|
||||||
Assert.assertTrue(chunk2[1022] == 2);
|
assertTrue(chunk2[1022] == 2);
|
||||||
Assert.assertTrue(chunk2[1023] == 2);
|
assertTrue(chunk2[1023] == 2);
|
||||||
|
|
||||||
byte[] chunk3 = memoryBuffer.getChunks().get(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
|
@Test
|
||||||
|
@ -237,8 +238,8 @@ public class MemoryTest {
|
||||||
if (memoryBuffer.readByte(i) == 0) ++zeroes;
|
if (memoryBuffer.readByte(i) == 0) ++zeroes;
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.assertTrue(ones == zeroes);
|
assertTrue(ones == zeroes);
|
||||||
Assert.assertTrue(256 == memoryBuffer.size());
|
assertTrue(256 == memoryBuffer.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -248,9 +249,9 @@ public class MemoryTest {
|
||||||
|
|
||||||
Memory memoryBuffer = new Memory();
|
Memory memoryBuffer = new Memory();
|
||||||
DataWord value = memoryBuffer.readWord(100);
|
DataWord value = memoryBuffer.readWord(100);
|
||||||
Assert.assertTrue(value.intValue() == 0);
|
assertTrue(value.intValue() == 0);
|
||||||
Assert.assertTrue(memoryBuffer.getChunks().size() == 1);
|
assertTrue(memoryBuffer.getChunks().size() == 1);
|
||||||
Assert.assertTrue(memoryBuffer.size() == 32 * 5);
|
assertTrue(memoryBuffer.size() == 32 * 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -258,9 +259,9 @@ public class MemoryTest {
|
||||||
|
|
||||||
Memory memoryBuffer = new Memory();
|
Memory memoryBuffer = new Memory();
|
||||||
DataWord value = memoryBuffer.readWord(2015);
|
DataWord value = memoryBuffer.readWord(2015);
|
||||||
Assert.assertTrue(value.intValue() == 0);
|
assertTrue(value.intValue() == 0);
|
||||||
Assert.assertTrue(memoryBuffer.getChunks().size() == 2);
|
assertTrue(memoryBuffer.getChunks().size() == 2);
|
||||||
Assert.assertTrue(memoryBuffer.size() == 2048);
|
assertTrue(memoryBuffer.size() == 2048);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -268,9 +269,9 @@ public class MemoryTest {
|
||||||
|
|
||||||
Memory memoryBuffer = new Memory();
|
Memory memoryBuffer = new Memory();
|
||||||
DataWord value = memoryBuffer.readWord(2016);
|
DataWord value = memoryBuffer.readWord(2016);
|
||||||
Assert.assertTrue(value.intValue() == 0);
|
assertTrue(value.intValue() == 0);
|
||||||
Assert.assertTrue(memoryBuffer.getChunks().size() == 2);
|
assertTrue(memoryBuffer.getChunks().size() == 2);
|
||||||
Assert.assertTrue(memoryBuffer.size() == 2048);
|
assertTrue(memoryBuffer.size() == 2048);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -278,9 +279,9 @@ public class MemoryTest {
|
||||||
|
|
||||||
Memory memoryBuffer = new Memory();
|
Memory memoryBuffer = new Memory();
|
||||||
DataWord value = memoryBuffer.readWord(2017);
|
DataWord value = memoryBuffer.readWord(2017);
|
||||||
Assert.assertTrue(value.intValue() == 0);
|
assertTrue(value.intValue() == 0);
|
||||||
Assert.assertTrue(memoryBuffer.getChunks().size() == 3);
|
assertTrue(memoryBuffer.getChunks().size() == 3);
|
||||||
Assert.assertTrue(memoryBuffer.size() == 2080);
|
assertTrue(memoryBuffer.size() == 2080);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -294,11 +295,11 @@ public class MemoryTest {
|
||||||
byte[] data2 = new byte[1024];
|
byte[] data2 = new byte[1024];
|
||||||
Arrays.fill(data2, (byte) 2);
|
Arrays.fill(data2, (byte) 2);
|
||||||
|
|
||||||
memoryBuffer.write(0, data1);
|
memoryBuffer.write(0, data1, false);
|
||||||
memoryBuffer.write(1024, data2);
|
memoryBuffer.write(1024, data2, false);
|
||||||
|
|
||||||
Assert.assertTrue(memoryBuffer.getChunks().size() == 2);
|
assertTrue(memoryBuffer.getChunks().size() == 2);
|
||||||
Assert.assertTrue(memoryBuffer.size() == 2048);
|
assertTrue(memoryBuffer.size() == 2048);
|
||||||
|
|
||||||
DataWord val1 = memoryBuffer.readWord(0x3df);
|
DataWord val1 = memoryBuffer.readWord(0x3df);
|
||||||
DataWord val2 = memoryBuffer.readWord(0x3e0);
|
DataWord val2 = memoryBuffer.readWord(0x3e0);
|
||||||
|
@ -315,7 +316,7 @@ public class MemoryTest {
|
||||||
assertArrayEquals(
|
assertArrayEquals(
|
||||||
Hex.decode("0101010101010101010101010101010101010101010101010101010101010102"),
|
Hex.decode("0101010101010101010101010101010101010101010101010101010101010102"),
|
||||||
val3.getData());
|
val3.getData());
|
||||||
Assert.assertTrue(memoryBuffer.size() == 2048);
|
assertTrue(memoryBuffer.size() == 2048);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -329,8 +330,8 @@ public class MemoryTest {
|
||||||
byte[] data2 = new byte[32];
|
byte[] data2 = new byte[32];
|
||||||
Arrays.fill(data2, (byte) 2);
|
Arrays.fill(data2, (byte) 2);
|
||||||
|
|
||||||
memoryBuffer.write(0, data1);
|
memoryBuffer.write(0, data1, false);
|
||||||
memoryBuffer.write(32, data2);
|
memoryBuffer.write(32, data2, false);
|
||||||
|
|
||||||
byte[] data = memoryBuffer.read(0, 64);
|
byte[] data = memoryBuffer.read(0, 64);
|
||||||
|
|
||||||
|
@ -351,8 +352,8 @@ public class MemoryTest {
|
||||||
byte[] data1 = new byte[32];
|
byte[] data1 = new byte[32];
|
||||||
Arrays.fill(data1, (byte) 1);
|
Arrays.fill(data1, (byte) 1);
|
||||||
|
|
||||||
memoryBuffer.write(0, data1);
|
memoryBuffer.write(0, data1, false);
|
||||||
Assert.assertTrue(32 == memoryBuffer.size());
|
assertTrue(32 == memoryBuffer.size());
|
||||||
|
|
||||||
byte[] data = memoryBuffer.read(0, 64);
|
byte[] data = memoryBuffer.read(0, 64);
|
||||||
|
|
||||||
|
@ -376,8 +377,8 @@ public class MemoryTest {
|
||||||
byte[] data2 = new byte[1024];
|
byte[] data2 = new byte[1024];
|
||||||
Arrays.fill(data2, (byte) 2);
|
Arrays.fill(data2, (byte) 2);
|
||||||
|
|
||||||
memoryBuffer.write(0, data1);
|
memoryBuffer.write(0, data1, false);
|
||||||
memoryBuffer.write(1024, data2);
|
memoryBuffer.write(1024, data2, false);
|
||||||
|
|
||||||
byte[] data = memoryBuffer.read(0, 2048);
|
byte[] data = memoryBuffer.read(0, 2048);
|
||||||
|
|
||||||
|
@ -387,8 +388,8 @@ public class MemoryTest {
|
||||||
if (data[i] == 2) ++twos;
|
if (data[i] == 2) ++twos;
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.assertTrue(ones == twos);
|
assertTrue(ones == twos);
|
||||||
Assert.assertTrue(2048 == memoryBuffer.size());
|
assertTrue(2048 == memoryBuffer.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -402,8 +403,8 @@ public class MemoryTest {
|
||||||
byte[] data2 = new byte[1024];
|
byte[] data2 = new byte[1024];
|
||||||
Arrays.fill(data2, (byte) 2);
|
Arrays.fill(data2, (byte) 2);
|
||||||
|
|
||||||
memoryBuffer.write(0, data1);
|
memoryBuffer.write(0, data1, false);
|
||||||
memoryBuffer.write(1024, data2);
|
memoryBuffer.write(1024, data2, false);
|
||||||
|
|
||||||
byte[] data = memoryBuffer.read(0, 2049);
|
byte[] data = memoryBuffer.read(0, 2049);
|
||||||
|
|
||||||
|
@ -414,11 +415,40 @@ public class MemoryTest {
|
||||||
if (data[i] == 0) ++zero;
|
if (data[i] == 0) ++zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.assertTrue(zero == 1);
|
assertTrue(zero == 1);
|
||||||
Assert.assertTrue(ones == twos);
|
assertTrue(ones == twos);
|
||||||
Assert.assertTrue(2080 == memoryBuffer.size());
|
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
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
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