diff --git a/app/src/main/java/org/ethereum/android_app/MainActivity.java b/app/src/main/java/org/ethereum/android_app/MainActivity.java index 17b2a209..9fbac52a 100644 --- a/app/src/main/java/org/ethereum/android_app/MainActivity.java +++ b/app/src/main/java/org/ethereum/android_app/MainActivity.java @@ -1,118 +1,165 @@ -package org.ethereum.android_app; - -import android.content.Context; -import android.os.AsyncTask; -import android.support.v4.view.ViewPager; -import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; -import android.support.v7.widget.Toolbar; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; - -import org.ethereum.android.EthereumManager; - - -public class MainActivity extends ActionBarActivity { - - private static final String TAG = "MyActivity"; - private static Integer quit = 0; - - private Toolbar toolbar; - private ViewPager viewPager; - private SlidingTabLayout tabs; - private TabsPagerAdapter adapter; - - public EthereumManager ethereumManager = null; - - @Override - protected void onCreate(Bundle savedInstanceState) { - - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - toolbar = (Toolbar) findViewById(R.id.tool_bar); - setSupportActionBar(toolbar); - - ethereumManager = new EthereumManager(this); - - adapter = new TabsPagerAdapter(getSupportFragmentManager(), ethereumManager); - viewPager = (ViewPager) findViewById(R.id.pager); - viewPager.setAdapter(adapter);; - - tabs = (SlidingTabLayout) findViewById(R.id.tabs); - tabs.setDistributeEvenly(true); - tabs.setViewPager(viewPager); - - //StrictMode.enableDefaults(); - - new PostTask().execute(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - //return super.onCreateOptionsMenu(menu); - getMenuInflater().inflate(R.menu.menu_main, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - int id = item.getItemId(); - Log.v(TAG, Integer.valueOf(id).toString()); - //noinspection SimplifiableIfStatement - if (id == R.id.action_settings) { - return true; - } - - return super.onOptionsItemSelected(item); - } - - // The definition of our task class - private class PostTask extends AsyncTask { - - - @Override - protected void onPreExecute() { - super.onPreExecute(); - } - - @Override - protected String doInBackground(Context... params) { - Log.v(TAG, "111"); - - Log.v(TAG, "222"); - ethereumManager.connect(); - Log.v(TAG, "333"); - while(true) { - - try { - Thread.sleep(50); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - if (quit == 1) { - Log.v(TAG, "Ending background process."); - return "All Done!"; - } - - //publishProgress(1111); - } - } - - @Override - protected void onProgressUpdate(Integer... values) { - super.onProgressUpdate(values); - Log.v(TAG, values[0].toString()); - } - - @Override - protected void onPostExecute(String result) { - super.onPostExecute(result); - } - } -} +package org.ethereum.android_app; + +import android.content.Context; +import android.os.AsyncTask; +import android.support.v4.view.ViewPager; +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.os.Build; + +import org.ethereum.android.EthereumManager; + + +public class MainActivity extends ActionBarActivity { + + private static final String TAG = "MyActivity"; + private static Integer quit = 0; + + private Toolbar toolbar; + private ViewPager viewPager; + private SlidingTabLayout tabs; + private TabsPagerAdapter adapter; + + public EthereumManager ethereumManager = null; + + @Override + protected void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + toolbar = (Toolbar) findViewById(R.id.tool_bar); + setSupportActionBar(toolbar); + + ethereumManager = new EthereumManager(this); + + adapter = new TabsPagerAdapter(getSupportFragmentManager(), ethereumManager); + viewPager = (ViewPager) findViewById(R.id.pager); + viewPager.setAdapter(adapter);; + + tabs = (SlidingTabLayout) findViewById(R.id.tabs); + tabs.setDistributeEvenly(true); + tabs.setViewPager(viewPager); + + //StrictMode.enableDefaults(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) + new PostTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + else + new PostTask().execute(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) + new JsonRpcTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + else + new JsonRpcTask().execute(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + //return super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + Log.v(TAG, Integer.valueOf(id).toString()); + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + ethereumManager.onDestroy(); + } + + // The definition of our task class + private class PostTask extends AsyncTask { + + + @Override + protected void onPreExecute() { + super.onPreExecute(); + } + + @Override + protected String doInBackground(Context... params) { + Log.v(TAG, "111"); + + Log.v(TAG, "222"); + ethereumManager.connect(); + Log.v(TAG, "333"); + while(true) { + + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + if (quit == 1) { + Log.v(TAG, "Ending background process."); + return "All Done!"; + } + + //publishProgress(1111); + } + } + + @Override + protected void onProgressUpdate(Integer... values) { + super.onProgressUpdate(values); + Log.v(TAG, values[0].toString()); + } + + @Override + protected void onPostExecute(String result) { + super.onPostExecute(result); + } + } + + + // The definition of our task class + private class JsonRpcTask extends AsyncTask { + + @Override + protected void onPreExecute() { + super.onPreExecute(); + } + + @Override + protected String doInBackground(Context... params) { + Log.v(TAG, "444"); + try { + ethereumManager.startJsonRpc(); + } catch (Exception e) { + } + Log.v(TAG, "555"); + return "done"; + } + + @Override + protected void onProgressUpdate(Integer... values) { + super.onProgressUpdate(values); + Log.v(TAG, values[0].toString()); + } + + @Override + protected void onPostExecute(String result) { + super.onPostExecute(result); + } + } +} diff --git a/ethereumj-core-android/build.gradle b/ethereumj-core-android/build.gradle index 86cd8034..c56d5e6e 100644 --- a/ethereumj-core-android/build.gradle +++ b/ethereumj-core-android/build.gradle @@ -1,60 +1,62 @@ -apply plugin: 'com.android.library' -apply plugin: 'com.neenbedankt.android-apt' - -buildscript { - repositories { - jcenter() - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:1.2.3' - classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' - } -} - -repositories { - jcenter() - mavenCentral() -} - -android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" - - defaultConfig { - minSdkVersion 14 - targetSdkVersion 22 - versionCode 1 - versionName "1.0" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -tasks.withType(JavaCompile){ - options.warnings = false -} - -dependencies { - apt 'com.google.dagger:dagger-compiler:2.0' - compile fileTree(dir: 'libs', include: ['*.jar']) - compile (project(':ethereumj-core')) { - exclude group: "org.apache.commons", module: "commons-pool2" - exclude group: "org.slf4j", module: "slf4j-log4j12" - exclude group: "org.hibernate", module: "hibernate-core" - exclude group: "org.hibernate", module: "hibernate-entitymanager" - exclude group: "redis.clients", module: "jedis" - exclude group: "org.antlr", module: "antlr4-runtime" - } - - //compile "com.google.dagger:dagger:2.1-SNAPSHOT" - compile "com.j256.ormlite:ormlite-android:4.48" - compile "org.glassfish:javax.annotation:10.0-b28" - compile "org.iq80.leveldb:leveldb:0.7" - compile 'org.slf4j:slf4j-android:1.7.12' - compile 'javax.persistence:persistence-api:1.0.2' -} +apply plugin: 'com.android.library' +apply plugin: 'com.neenbedankt.android-apt' + +buildscript { + repositories { + jcenter() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.2.3' + classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' + } +} + +repositories { + jcenter() + mavenCentral() +} + +android { + compileSdkVersion 22 + buildToolsVersion "22.0.1" + + defaultConfig { + minSdkVersion 14 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +tasks.withType(JavaCompile){ + options.warnings = false +} + +dependencies { + apt 'com.google.dagger:dagger-compiler:2.0' + compile fileTree(dir: 'libs', include: ['*.jar']) + compile (project(':ethereumj-core')) { + exclude group: "org.apache.commons", module: "commons-pool2" + exclude group: "org.slf4j", module: "slf4j-log4j12" + exclude group: "org.hibernate", module: "hibernate-core" + exclude group: "org.hibernate", module: "hibernate-entitymanager" + exclude group: "redis.clients", module: "jedis" + exclude group: "org.antlr", module: "antlr4-runtime" + } + + //compile "com.google.dagger:dagger:2.1-SNAPSHOT" + compile "com.j256.ormlite:ormlite-android:4.48" + compile "org.glassfish:javax.annotation:10.0-b28" + compile "org.iq80.leveldb:leveldb:0.7" + compile 'org.slf4j:slf4j-android:1.7.12' + compile 'javax.persistence:persistence-api:1.0.2' + + compile group: 'com.thetransactioncompany', name: 'jsonrpc2-server', version: '1.11' +} diff --git a/ethereumj-core-android/src/main/java/org/ethereum/android/EthereumManager.java b/ethereumj-core-android/src/main/java/org/ethereum/android/EthereumManager.java index 5256d55d..644237f4 100644 --- a/ethereumj-core-android/src/main/java/org/ethereum/android/EthereumManager.java +++ b/ethereumj-core-android/src/main/java/org/ethereum/android/EthereumManager.java @@ -1,50 +1,66 @@ -package org.ethereum.android; - -import android.content.Context; - -import org.ethereum.android.di.modules.EthereumModule; -import org.ethereum.android.di.components.DaggerEthereumComponent; -import org.ethereum.config.SystemProperties; -import org.ethereum.facade.Ethereum; -import org.ethereum.listener.EthereumListenerAdapter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class EthereumManager { - - private static final Logger logger = LoggerFactory.getLogger("manager"); - - public static Ethereum ethereum = null; - - - public EthereumManager(Context context) { - System.setProperty("sun.arch.data.model", "32"); - System.setProperty("leveldb.mmap", "false"); - ethereum = DaggerEthereumComponent.builder() - .ethereumModule(new EthereumModule(context)) - .build().ethereum(); - } - - public void start() { - - } - - public void connect() { - - ethereum.connect(SystemProperties.CONFIG.activePeerIP(), - SystemProperties.CONFIG.activePeerPort(), - SystemProperties.CONFIG.activePeerNodeid()); - //ethereum.getBlockchain(); - } - - public void startPeerDiscovery() { - - ethereum.startPeerDiscovery(); - } - - public void addListener(EthereumListenerAdapter listener) { - - ethereum.addListener(listener); - } - -} +package org.ethereum.android; + +import android.content.Context; + +import com.j256.ormlite.android.apptools.OpenHelperManager; + +import org.ethereum.android.di.modules.EthereumModule; +import org.ethereum.android.di.components.DaggerEthereumComponent; +import org.ethereum.config.SystemProperties; +import org.ethereum.facade.Ethereum; +import org.ethereum.listener.EthereumListenerAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.ethereum.android.jsonrpc.JsonRpcServer; + +public class EthereumManager { + + private static final Logger logger = LoggerFactory.getLogger("manager"); + + public static Ethereum ethereum = null; + + private JsonRpcServer jsonRpcServer; + + + public EthereumManager(Context context) { + System.setProperty("sun.arch.data.model", "32"); + System.setProperty("leveldb.mmap", "false"); + ethereum = DaggerEthereumComponent.builder() + .ethereumModule(new EthereumModule(context)) + .build().ethereum(); + + jsonRpcServer = new JsonRpcServer(ethereum); + } + + public void start() { + + } + + public void connect() { + + ethereum.connect(SystemProperties.CONFIG.activePeerIP(), + SystemProperties.CONFIG.activePeerPort(), + SystemProperties.CONFIG.activePeerNodeid()); + //ethereum.getBlockchain(); + } + + public void startPeerDiscovery() { + + ethereum.startPeerDiscovery(); + } + + public void addListener(EthereumListenerAdapter listener) { + + ethereum.addListener(listener); + } + + public void startJsonRpc() throws Exception { + + jsonRpcServer.start(); + } + + public void onDestroy() { + OpenHelperManager.releaseHelper(); + } + +} diff --git a/ethereumj-core-android/src/main/java/org/ethereum/android/db/BlockDatabaseHelper.java b/ethereumj-core-android/src/main/java/org/ethereum/android/db/BlockDatabaseHelper.java deleted file mode 100644 index 45aa8c3b..00000000 --- a/ethereumj-core-android/src/main/java/org/ethereum/android/db/BlockDatabaseHelper.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.ethereum.android.db; - - -import java.sql.SQLException; - -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.util.Log; - -import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; -import com.j256.ormlite.dao.Dao; -import com.j256.ormlite.support.ConnectionSource; -import com.j256.ormlite.table.TableUtils; - -import org.ethereum.db.BlockVO; - -/** - * Database helper class used to manage the creation and upgrading of your database. This class also usually provides - * the DAOs used by the other classes. - */ -public class BlockDatabaseHelper extends OrmLiteSqliteOpenHelper { - - private static final String DATABASE_NAME = "blocks.db"; - private static final int DATABASE_VERSION = 1; - - // the DAO object we use to access the SimpleData table - private Dao blockDao = null; - - public BlockDatabaseHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - /** - * This is called when the database is first created. Usually you should call createTable statements here to create - * the tables that will store your data. - */ - @Override - public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) { - try { - Log.i(BlockDatabaseHelper.class.getName(), "onCreate"); - TableUtils.createTable(connectionSource, BlockVO.class); - } catch (SQLException e) { - Log.e(BlockDatabaseHelper.class.getName(), "Can't create database", e); - throw new RuntimeException(e); - } - } - - /** - * This is called when your application is upgraded and it has a higher version number. This allows you to adjust - * the various data to match the new version number. - */ - @Override - public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) { - try { - Log.i(BlockDatabaseHelper.class.getName(), "onUpgrade"); - TableUtils.dropTable(connectionSource, BlockVO.class, true); - // after we drop the old databases, we create the new ones - onCreate(db, connectionSource); - } catch (SQLException e) { - Log.e(BlockDatabaseHelper.class.getName(), "Can't drop databases", e); - throw new RuntimeException(e); - } - } - - /** - * Returns the Database Access Object (DAO) for our SimpleData class. It will create it or just give the cached - * value. - */ - public Dao getBlockDao() throws SQLException { - if (blockDao == null) { - blockDao = getDao(BlockVO.class); - } - return blockDao; - } - - /** - * Close the database connections and clear any cached DAOs. - */ - @Override - public void close() { - super.close(); - blockDao = null; - } -} diff --git a/ethereumj-core-android/src/main/java/org/ethereum/android/db/BlockStoreDatabase.java b/ethereumj-core-android/src/main/java/org/ethereum/android/db/BlockStoreDatabase.java new file mode 100644 index 00000000..f9b3ed59 --- /dev/null +++ b/ethereumj-core-android/src/main/java/org/ethereum/android/db/BlockStoreDatabase.java @@ -0,0 +1,34 @@ +package org.ethereum.android.db; + +import org.ethereum.core.Block; +import org.ethereum.core.TransactionReceipt; + +import java.math.BigInteger; +import java.util.List; + +public interface BlockStoreDatabase { + + public List getByNumber(Long number); + + public List getByHash(byte[] hash); + + public List getHashListByNumberLimit(Long from, Long to); + + public void deleteBlocksSince(long number); + + public void save(BlockVO block); + + public BigInteger getTotalDifficultySince(long number); + + public BigInteger getTotalDifficulty(); + + public Block getBestBlock(); + + public List getAllBlocks(); + + public void reset(); + + public void save(TransactionReceiptVO transactionReceiptVO); + + public TransactionReceipt getTransactionReceiptByHash(byte[] hash); +} diff --git a/ethereumj-core-android/src/main/java/org/ethereum/android/db/BlockStoreImpl.java b/ethereumj-core-android/src/main/java/org/ethereum/android/db/BlockStoreImpl.java index 997ff208..bed633c9 100644 --- a/ethereumj-core-android/src/main/java/org/ethereum/android/db/BlockStoreImpl.java +++ b/ethereumj-core-android/src/main/java/org/ethereum/android/db/BlockStoreImpl.java @@ -6,17 +6,16 @@ import org.ethereum.db.BlockStore; import org.ethereum.util.ByteUtil; import java.math.BigInteger; +import java.util.ArrayList; import java.util.List; public class BlockStoreImpl implements BlockStore { - private BlockDatabaseHelper blockDao; - private TransactionDatabaseHelper transactionDao; + private BlockStoreDatabase database; - public BlockStoreImpl(BlockDatabaseHelper blockDao, TransactionDatabaseHelper transactionDao) { + public BlockStoreImpl(BlockStoreDatabase database) { - this.blockDao = blockDao; - this.transactionDao = transactionDao; + this.database = database; } public byte[] getBlockHashByNumber(long blockNumber) { @@ -29,65 +28,86 @@ public class BlockStoreImpl implements BlockStore { public Block getBlockByNumber(long blockNumber) { - /* - List result = sessionFactory.getCurrentSession(). - createQuery("from BlockVO where number = :number"). - setParameter("number", blockNumber).list(); + + List result = database.getByNumber(blockNumber); + if (result.size() == 0) return null; + BlockVO vo = (BlockVO) result.get(0); + + return new Block(vo.rlp); + } + + public Block getBlockByHash(byte[] hash) { + + List result = database.getByHash(hash); if (result.size() == 0) return null; BlockVO vo = (BlockVO) result.get(0); return new Block(vo.rlp); - */ - return null; } - public Block getBlockByHash(byte[] hash) { - - return null; - } - - @SuppressWarnings("unchecked") public List getListOfHashesStartFrom(byte[] hash, int qty) { - return null; + List hashes = new ArrayList(); + + // find block number of that block hash + Block block = getBlockByHash(hash); + if (block == null) return hashes; + + hashes = database.getHashListByNumberLimit(block.getNumber(), block.getNumber() - qty); + + return hashes; } public void deleteBlocksSince(long number) { + database.deleteBlocksSince(number); } public void saveBlock(Block block, List receipts) { + BlockVO blockVO = new BlockVO(block.getNumber(), block.getHash(), + block.getEncoded(), block.getCumulativeDifficulty()); + + for (TransactionReceipt receipt : receipts) { + + byte[] hash = receipt.getTransaction().getHash(); + byte[] rlp = receipt.getEncoded(); + + TransactionReceiptVO transactionReceiptVO = new TransactionReceiptVO(hash, rlp); + database.save(transactionReceiptVO); + } + + database.save(blockVO); } public BigInteger getTotalDifficultySince(long number) { - return null; + return database.getTotalDifficultySince(number); } public BigInteger getTotalDifficulty() { - return null; + return database.getTotalDifficulty(); } public Block getBestBlock() { - return null; + return database.getBestBlock(); } - @SuppressWarnings("unchecked") public List getAllBlocks() { - return null; + return database.getAllBlocks(); } public void reset() { + database.reset(); } public TransactionReceipt getTransactionReceiptByHash(byte[] hash) { - return null; + return database.getTransactionReceiptByHash(hash); } } diff --git a/ethereumj-core-android/src/main/java/org/ethereum/android/db/BlockVO.java b/ethereumj-core-android/src/main/java/org/ethereum/android/db/BlockVO.java new file mode 100644 index 00000000..6d5fba17 --- /dev/null +++ b/ethereumj-core-android/src/main/java/org/ethereum/android/db/BlockVO.java @@ -0,0 +1,66 @@ +package org.ethereum.android.db; + +import com.j256.ormlite.field.DataType; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +import java.math.BigInteger; + +@DatabaseTable(tableName = "block") +public class BlockVO { + + @DatabaseField(index = true, dataType = DataType.BYTE_ARRAY) + byte[] hash; + + @DatabaseField(index = true, dataType = DataType.LONG_OBJ) + Long number; + + @DatabaseField(columnName = "cumulativedifficulty", dataType = DataType.BIG_INTEGER) + BigInteger cumulativeDifficulty; + + @DatabaseField(dataType = DataType.BYTE_ARRAY) + byte[] rlp; + + public BlockVO() { + } + + public BlockVO(Long number, byte[] hash, byte[] rlp, BigInteger cumulativeDifficulty) { + this.number = number; + this.hash = hash; + this.rlp = rlp; + this.cumulativeDifficulty = cumulativeDifficulty; + } + + public byte[] getHash() { + return hash; + } + + public void setHash(byte[] hash) { + this.hash = hash; + } + + public Long getIndex() { + return number; + } + + public void setIndex(Long number) { + this.number = number; + } + + public byte[] getRlp() { + return rlp; + } + + public void setRlp(byte[] rlp) { + this.rlp = rlp; + } + + public BigInteger getCumulativeDifficulty() { + return cumulativeDifficulty; + } + + public void setCumulativeDifficulty(BigInteger cumulativeDifficulty) { + this.cumulativeDifficulty = cumulativeDifficulty; + } + +} diff --git a/ethereumj-core-android/src/main/java/org/ethereum/android/db/OrmLiteBlockStoreDatabase.java b/ethereumj-core-android/src/main/java/org/ethereum/android/db/OrmLiteBlockStoreDatabase.java new file mode 100644 index 00000000..7c8ef683 --- /dev/null +++ b/ethereumj-core-android/src/main/java/org/ethereum/android/db/OrmLiteBlockStoreDatabase.java @@ -0,0 +1,255 @@ +package org.ethereum.android.db; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.util.Log; + +import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; +import com.j256.ormlite.dao.Dao; +import com.j256.ormlite.dao.GenericRawResults; +import com.j256.ormlite.stmt.DeleteBuilder; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.TableUtils; + +import org.ethereum.core.Block; +import org.ethereum.core.TransactionReceipt; + +import java.math.BigInteger; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public class OrmLiteBlockStoreDatabase extends OrmLiteSqliteOpenHelper implements BlockStoreDatabase { + + private static final String DATABASE_NAME = "blockchain.db"; + private static final int DATABASE_VERSION = 1; + + private Dao blockDao = null; + private Dao transactionDao = null; + + public OrmLiteBlockStoreDatabase(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + /** + * This is called when the database is first created. Usually you should call createTable statements here to create + * the tables that will store your data. + */ + @Override + public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) { + + try { + Log.i(OrmLiteBlockStoreDatabase.class.getName(), "onCreate"); + TableUtils.createTable(connectionSource, BlockVO.class); + TableUtils.createTable(connectionSource, TransactionReceiptVO.class); + } catch (SQLException e) { + Log.e(OrmLiteBlockStoreDatabase.class.getName(), "Can't create database", e); + throw new RuntimeException(e); + } + } + + /** + * This is called when your application is upgraded and it has a higher version number. This allows you to adjust + * the various data to match the new version number. + */ + @Override + public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) { + + try { + Log.i(OrmLiteBlockStoreDatabase.class.getName(), "onUpgrade"); + TableUtils.dropTable(connectionSource, BlockVO.class, true); + TableUtils.dropTable(connectionSource, TransactionReceiptVO.class, true); + // after we drop the old databases, we create the new ones + onCreate(db, connectionSource); + } catch (SQLException e) { + Log.e(OrmLiteBlockStoreDatabase.class.getName(), "Can't drop databases", e); + throw new RuntimeException(e); + } + } + + /** + * Returns the Database Access Object (DAO) for our SimpleData class. It will create it or just give the cached + * value. + */ + public Dao getBlockDao() throws SQLException { + + if (blockDao == null) { + blockDao = getDao(BlockVO.class); + } + return blockDao; + } + + /** + * Returns the Database Access Object (DAO) for our SimpleData class. It will create it or just give the cached + * value. + */ + public Dao getTransactionDao() throws SQLException { + if (transactionDao == null) { + transactionDao = getDao(TransactionReceiptVO.class); + } + return transactionDao; + } + + /** + * Close the database connections and clear any cached DAOs. + */ + @Override + public void close() { + + super.close(); + blockDao = null; + transactionDao = null; + } + + public List getByNumber(Long number) { + + List list = new ArrayList(); + try { + list = getBlockDao().queryForEq("number", number); + } catch(java.sql.SQLException e) { + Log.e(OrmLiteBlockStoreDatabase.class.getName(), "Error querying for number", e); + } + + return list; + } + + public List getByHash(byte[] hash) { + + List list = new ArrayList(); + try { + list = getBlockDao().queryForEq("hash", hash); + } catch(java.sql.SQLException e) { + Log.e(OrmLiteBlockStoreDatabase.class.getName(), "Error querying for hash", e); + } + + return list; + } + + public List getHashListByNumberLimit(Long from, Long to) { + + List results = new ArrayList(); + try { + List list = new ArrayList(); + list = getBlockDao().queryBuilder().orderBy("number", false).limit(to - from).where().between("number", from, to).query(); + for (BlockVO block : list) { + results.add(block.hash); + } + } catch(java.sql.SQLException e) { + Log.e(OrmLiteBlockStoreDatabase.class.getName(), "Error querying for hash list", e); + } + + return results; + } + + public void deleteBlocksSince(long number) { + + try { + DeleteBuilder deleteBuilder = getBlockDao().deleteBuilder(); + deleteBuilder.where().gt("number", number); + deleteBuilder.delete(); + } catch(java.sql.SQLException e) { + Log.e(OrmLiteBlockStoreDatabase.class.getName(), "Error deleting blocks since", e); + } + } + + public void save(BlockVO block) { + + try { + getBlockDao().create(block); + } catch(java.sql.SQLException e) { + Log.e(OrmLiteBlockStoreDatabase.class.getName(), "Error saving block", e); + } + } + + public BigInteger getTotalDifficultySince(long number) { + + try { + GenericRawResults rawResults = getBlockDao().queryRaw("select sum(cumulativedifficulty) from block where number > " + number); + List results = rawResults.getResults(); + return new BigInteger(results.get(0)[0]); + } catch(java.sql.SQLException e) { + Log.e(OrmLiteBlockStoreDatabase.class.getName(), "Error getting total difficulty since", e); + } + return null; + } + + public BigInteger getTotalDifficulty() { + + try { + GenericRawResults rawResults = getBlockDao().queryRaw("select sum(cumulativedifficulty) from block"); + List results = rawResults.getResults(); + return new BigInteger(results.get(0)[0]); + } catch(java.sql.SQLException e) { + Log.e(OrmLiteBlockStoreDatabase.class.getName(), "Error getting total difficulty", e); + } + return null; + } + + public Block getBestBlock() { + + Long bestNumber = null; + try { + GenericRawResults rawResults = getBlockDao().queryRaw("select max(number) from block"); + List results = rawResults.getResults(); + if (results.size() > 0 && results.get(0).length > 0) { + bestNumber = Long.valueOf(results.get(0)[0]); + } + } catch(java.sql.SQLException e) { + Log.e(OrmLiteBlockStoreDatabase.class.getName(), "Sql Error getting best block", e); + } catch (Exception e) { + Log.e(OrmLiteBlockStoreDatabase.class.getName(), "Error getting best block", e); + } + + if (bestNumber == null) return null; + List result = getByNumber(bestNumber); + + if (result.isEmpty()) return null; + BlockVO vo = (BlockVO) result.get(0); + + return new Block(vo.rlp); + } + + public List getAllBlocks() { + + ArrayList blocks = new ArrayList<>(); + try { + for (BlockVO blockVO : getBlockDao()) { + blocks.add(new Block(blockVO.getRlp())); + } + } catch(java.sql.SQLException e) { + Log.e(OrmLiteBlockStoreDatabase.class.getName(), "Error getting all blocks", e); + } + + return blocks; + } + + public void reset() { + + deleteBlocksSince(Long.valueOf(0)); + } + + public void save(TransactionReceiptVO transactionReceiptVO) { + + try { + getTransactionDao().create(transactionReceiptVO); + } catch(java.sql.SQLException e) { + Log.e(OrmLiteBlockStoreDatabase.class.getName(), "Error saving transaction", e); + } + } + + public TransactionReceipt getTransactionReceiptByHash(byte[] hash) { + + List list = new ArrayList(); + try { + list = getTransactionDao().queryForEq("hash", hash); + } catch(java.sql.SQLException e) { + Log.e(OrmLiteBlockStoreDatabase.class.getName(), "Error querying for hash", e); + } + + if (list.size() == 0) return null; + TransactionReceiptVO vo = list.get(0); + + return new TransactionReceipt(vo.rlp); + + } +} diff --git a/ethereumj-core-android/src/main/java/org/ethereum/android/db/TransactionDatabaseHelper.java b/ethereumj-core-android/src/main/java/org/ethereum/android/db/TransactionDatabaseHelper.java deleted file mode 100644 index ae68de70..00000000 --- a/ethereumj-core-android/src/main/java/org/ethereum/android/db/TransactionDatabaseHelper.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.ethereum.android.db; - - -import java.sql.SQLException; - -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.util.Log; - -import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; -import com.j256.ormlite.dao.Dao; -import com.j256.ormlite.support.ConnectionSource; -import com.j256.ormlite.table.TableUtils; - -import org.ethereum.db.TransactionReceiptVO; - -/** - * Database helper class used to manage the creation and upgrading of your database. This class also usually provides - * the DAOs used by the other classes. - */ -public class TransactionDatabaseHelper extends OrmLiteSqliteOpenHelper { - - private static final String DATABASE_NAME = "transactions.db"; - private static final int DATABASE_VERSION = 1; - - // the DAO object we use to access the SimpleData table - private Dao dao = null; - - public TransactionDatabaseHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - /** - * This is called when the database is first created. Usually you should call createTable statements here to create - * the tables that will store your data. - */ - @Override - public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) { - try { - Log.i(BlockDatabaseHelper.class.getName(), "onCreate"); - TableUtils.createTable(connectionSource, TransactionReceiptVO.class); - } catch (SQLException e) { - Log.e(BlockDatabaseHelper.class.getName(), "Can't create database", e); - throw new RuntimeException(e); - } - } - - /** - * This is called when your application is upgraded and it has a higher version number. This allows you to adjust - * the various data to match the new version number. - */ - @Override - public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) { - try { - Log.i(BlockDatabaseHelper.class.getName(), "onUpgrade"); - TableUtils.dropTable(connectionSource, TransactionReceiptVO.class, true); - // after we drop the old databases, we create the new ones - onCreate(db, connectionSource); - } catch (SQLException e) { - Log.e(BlockDatabaseHelper.class.getName(), "Can't drop databases", e); - throw new RuntimeException(e); - } - } - - /** - * Returns the Database Access Object (DAO) for our SimpleData class. It will create it or just give the cached - * value. - */ - public Dao getBlockDao() throws SQLException { - if (dao == null) { - dao = getDao(TransactionReceiptVO.class); - } - return dao; - } - - /** - * Close the database connections and clear any cached DAOs. - */ - @Override - public void close() { - super.close(); - dao = null; - } -} diff --git a/ethereumj-core-android/src/main/java/org/ethereum/android/db/TransactionReceiptVO.java b/ethereumj-core-android/src/main/java/org/ethereum/android/db/TransactionReceiptVO.java new file mode 100644 index 00000000..341fbb1c --- /dev/null +++ b/ethereumj-core-android/src/main/java/org/ethereum/android/db/TransactionReceiptVO.java @@ -0,0 +1,49 @@ +package org.ethereum.android.db; + +import com.j256.ormlite.field.DataType; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Lob; +import javax.persistence.Table; + +/** + * @author Roman Mandeleil + * @since 14.11.2014 + */ +@DatabaseTable(tableName = "transaction_receipt") +public class TransactionReceiptVO { + + @DatabaseField(index = true, dataType = DataType.BYTE_ARRAY) + byte[] hash; + + @DatabaseField(dataType = DataType.BYTE_ARRAY) + byte[] rlp; + + public TransactionReceiptVO() { + } + + public TransactionReceiptVO(byte[] hash, byte[] rlp) { + this.hash = hash; + this.rlp = rlp; + } + + public byte[] getHash() { + return hash; + } + + public void setHash(byte[] hash) { + this.hash = hash; + } + + public byte[] getRlp() { + return rlp; + } + + public void setRlp(byte[] rlp) { + this.rlp = rlp; + } + +} diff --git a/ethereumj-core-android/src/main/java/org/ethereum/android/di/modules/EthereumModule.java b/ethereumj-core-android/src/main/java/org/ethereum/android/di/modules/EthereumModule.java index 793f0abd..c5818731 100644 --- a/ethereumj-core-android/src/main/java/org/ethereum/android/di/modules/EthereumModule.java +++ b/ethereumj-core-android/src/main/java/org/ethereum/android/di/modules/EthereumModule.java @@ -2,12 +2,15 @@ package org.ethereum.android.di.modules; import android.content.Context; +import com.j256.ormlite.android.apptools.OpenHelperManager; + import org.ethereum.android.datasource.LevelDbDataSource; +import org.ethereum.android.db.OrmLiteBlockStoreDatabase; +import org.ethereum.android.db.BlockStoreImpl; import org.ethereum.config.SystemProperties; import org.ethereum.core.BlockchainImpl; import org.ethereum.core.Wallet; import org.ethereum.db.BlockStore; -import org.ethereum.db.InMemoryBlockStore; import org.ethereum.db.RepositoryImpl; import org.ethereum.facade.Blockchain; import org.ethereum.facade.Ethereum; @@ -72,7 +75,8 @@ public class EthereumModule { @Provides @Singleton BlockStore provideBlockStore() { - return new InMemoryBlockStore(); + OrmLiteBlockStoreDatabase database = OpenHelperManager.getHelper(context, OrmLiteBlockStoreDatabase.class); + return new BlockStoreImpl(database); } @Provides diff --git a/ethereumj-core-android/src/main/java/org/ethereum/android/jsonrpc/JsonRpcServer.java b/ethereumj-core-android/src/main/java/org/ethereum/android/jsonrpc/JsonRpcServer.java new file mode 100644 index 00000000..0c3cbceb --- /dev/null +++ b/ethereumj-core-android/src/main/java/org/ethereum/android/jsonrpc/JsonRpcServer.java @@ -0,0 +1,159 @@ +package org.ethereum.android.jsonrpc; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.multipart.HttpPostStandardRequestDecoder; +import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory; +import io.netty.handler.codec.http.HttpObject; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.LastHttpContent; +import io.netty.buffer.ByteBuf; +import io.netty.util.CharsetUtil; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.channel.ChannelFuture; +import org.ethereum.facade.Ethereum; +import com.thetransactioncompany.jsonrpc2.*; +import com.thetransactioncompany.jsonrpc2.server.*; +import io.netty.handler.codec.http.HttpHeaders; +import static io.netty.handler.codec.http.HttpHeaders.Names.*; + +import org.ethereum.android.jsonrpc.method.*; + + +public final class JsonRpcServer { + + static final int PORT = 8545; + + private Ethereum ethereum; + private Dispatcher dispatcher; + + public JsonRpcServer(Ethereum ethereum) { + this.ethereum = ethereum; + + this.dispatcher = new Dispatcher(); + this.dispatcher.register(new eth_coinbase(this.ethereum)); + } + + public void start() throws Exception { + + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + ServerBootstrap b = new ServerBootstrap(); + b.option(ChannelOption.SO_BACKLOG, 1024); +// b.localAddress(InetAddress.getLocalHost(), PORT); + b.localAddress(PORT); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .childHandler(new JsonRpcServerInitializer()); + + Channel ch = b.bind().sync().channel(); + + ch.closeFuture().sync(); + } finally { + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + } + + class JsonRpcServerInitializer extends ChannelInitializer { + @Override + public void initChannel(SocketChannel ch) { + ChannelPipeline p = ch.pipeline(); + p.addLast(new HttpServerCodec()); + p.addLast(new JsonRpcServerHandler()); + } + } + + class JsonRpcServerHandler extends SimpleChannelInboundHandler { + + private HttpRequest request; + private final StringBuilder responseContent = new StringBuilder(); + private HttpPostStandardRequestDecoder decoder; + private String postData; + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + if (decoder != null) { + decoder.destroy(); + } + } + @Override + public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception { + if (msg instanceof HttpRequest) { + HttpRequest req = this.request = (HttpRequest) msg; + if (!req.getUri().equals("/") || !request.getMethod().equals(HttpMethod.POST)) { + responseContent.append("Hi, how are you?!!"); + return; + } else { + decoder = new HttpPostStandardRequestDecoder(new DefaultHttpDataFactory(false), req); + postData = ""; + } + } + + if (decoder != null) { + if (msg instanceof HttpContent) { + HttpContent chunk = (HttpContent) msg; + decoder.offer(chunk); + postData += chunk.content().toString(0, chunk.content().capacity(), CharsetUtil.UTF_8); + + if (chunk instanceof LastHttpContent) { + JSONRPC2Request req = JSONRPC2Request.parse(postData); + JSONRPC2Response resp = dispatcher.process(req, null); + responseContent.append(resp); + writeResponse(ctx); + request = null; + decoder.destroy(); + decoder = null; + } + } + } else { + writeResponse(ctx); + } + } + + private void writeResponse(ChannelHandlerContext ctx) { + ByteBuf buf = Unpooled.copiedBuffer(responseContent.toString(), CharsetUtil.UTF_8); + responseContent.setLength(0); + + boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request.headers().get(CONNECTION)) + || request.getProtocolVersion().equals(HttpVersion.HTTP_1_0) + && !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request.headers().get(CONNECTION)); + FullHttpResponse response = new DefaultFullHttpResponse( + HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf); + response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); + + if (!close) { + response.headers().set(CONTENT_LENGTH, buf.readableBytes()); + } + + ChannelFuture future = ctx.writeAndFlush(response); + if (close) { + future.addListener(ChannelFutureListener.CLOSE); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.printStackTrace(); + ctx.close(); + } + } +} \ No newline at end of file diff --git a/ethereumj-core-android/src/main/java/org/ethereum/android/jsonrpc/method/eth_coinbase.java b/ethereumj-core-android/src/main/java/org/ethereum/android/jsonrpc/method/eth_coinbase.java new file mode 100644 index 00000000..d1cea209 --- /dev/null +++ b/ethereumj-core-android/src/main/java/org/ethereum/android/jsonrpc/method/eth_coinbase.java @@ -0,0 +1,29 @@ +package org.ethereum.android.jsonrpc.method; + +import com.thetransactioncompany.jsonrpc2.*; +import com.thetransactioncompany.jsonrpc2.server.*; +import org.ethereum.facade.Ethereum; + +public class eth_coinbase implements RequestHandler { + + private String name = ""; + private Ethereum ethereum; + + public eth_coinbase(Ethereum ethereum) { + this.ethereum = ethereum; + name = this.getClass().getSimpleName(); + } + + public String[] handledRequests() { + return new String[]{name}; + } + + public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) { + if (req.getMethod().equals(name)) { + //TODO: place business logic here + return null; + } else { + return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, req.getID()); + } + } +} \ No newline at end of file diff --git a/ethereumj-core/build.gradle b/ethereumj-core/build.gradle index d95af860..b27b2eaf 100644 --- a/ethereumj-core/build.gradle +++ b/ethereumj-core/build.gradle @@ -100,7 +100,7 @@ test { logger.lifecycle("Running test: ${descriptor}") } - jvmArgs '-Xss32m' + jvmArgs += [ "-Xss32m", "-XX:MaxPermSize=256m" ] testLogging { events "failed" diff --git a/gradle.properties b/gradle.properties index 1d3591c8..a31c0f9d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,4 +15,6 @@ # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true \ No newline at end of file +# org.gradle.parallel=true + +org.gradle.jvmargs=-XX:MaxPermSize=512m \ No newline at end of file