Merged poc-9 branch.

Some minimalistic console ui implementation.
This commit is contained in:
Adrian Tiberius 2015-05-14 13:38:26 +02:00
parent ff71583142
commit 0b5f6f9c02
82 changed files with 2021 additions and 1138 deletions

View File

@ -31,6 +31,9 @@ android {
exclude 'META-INF/NOTICE.txt'
exclude 'LICENSE'
exclude 'NOTICE'
exclude 'META-INF/ASL2.0'
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
}
dexOptions {
javaMaxHeapSize "4g"

View File

@ -5,12 +5,12 @@
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name="android.support.multidex.MultiDexApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:name="android.support.multidex.MultiDexApplication"
android:largeHeap="true">
android:largeHeap="true"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >

View File

@ -1,29 +1,92 @@
package org.ethereum.ethereum_android;
import android.os.AsyncTask;
import android.os.StrictMode;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import org.ethereum.MyClass;
import org.robospring.RoboSpring;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.view.View.OnClickListener;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainActivity extends ActionBarActivity {
public class MainActivity extends ActionBarActivity implements OnClickListener {
public static ApplicationContext context = null;
private static final String TAG = "MyActivity";
private static Integer quit = 0;
private TextView text1;
private Button consoleButton;
private Button walletButton;
public EthereumManager ethereumManager = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text1 = (TextView) findViewById(R.id.text1);
text1.setMovementMethod(new ScrollingMovementMethod());
consoleButton = (Button) findViewById(R.id.consoleButton);
consoleButton.setOnClickListener(this);
walletButton = (Button) findViewById(R.id.walletButton);
walletButton.setOnClickListener(this);
StrictMode.enableDefaults();
//context = RoboSpring.getContext("applicationContext.xml");//new ClassPathXmlApplicationContext("applicationContext.xml"/*, clazz*/);
//RoboSpring.autowire(this);
System.setProperty("sun.arch.data.model", "32");
System.setProperty("leveldb.mmap", "false");
new PostTask().execute(getApplicationContext());
Thread t = new Thread() {
@Override
public void run() {
try {
while (!isInterrupted()) {
Thread.sleep(1000);
runOnUiThread(new Runnable() {
@Override
public void run() {
if (ethereumManager != null) {
text1.append(ethereumManager.getLog());
}
}
});
}
quit = 1;
} catch (InterruptedException e) {
}
}
};
t.start();
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.consoleButton: {
// do something for button 1 click
break;
}
case R.id.walletButton: {
// do something for button 2 click
break;
}
//.... etc
}
}
@ -40,7 +103,7 @@ public class MainActivity extends ActionBarActivity {
// 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, new Integer(id).toString());
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
@ -51,6 +114,8 @@ public class MainActivity extends ActionBarActivity {
// The definition of our task class
private class PostTask extends AsyncTask<android.content.Context, Integer, String> {
@Override
protected void onPreExecute() {
super.onPreExecute();
@ -60,23 +125,30 @@ public class MainActivity extends ActionBarActivity {
@Override
protected String doInBackground(android.content.Context... params) {
android.content.Context context=params[0];
MyClass.start(context);
for (int i = 0; i <= 100; i += 5) {
Log.v(TAG, "111");
ethereumManager = new EthereumManager(context);
Log.v(TAG, "222");
ethereumManager.connect();
Log.v(TAG, "333");
while(true) {
try {
Thread.sleep(50);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(i);
if (quit == 1) {
return "All Done!";
}
publishProgress(1111);
}
return "All Done!";
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// updateProgressBar(values[0]);
Log.v(TAG, values[0].toString());
}
@Override

View File

@ -6,6 +6,20 @@
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<TextView android:text="@string/hello_world" android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:scrollbars = "vertical" android:gravity="bottom"
android:fontFamily="Arial" android:typeface="serif" android:textSize="8sp"
android:id="@+id/text1" android:layout_height="wrap_content" />
<LinearLayout android:id="@+id/footer" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:orientation="horizontal"
android:layout_alignParentBottom="true" style="@android:style/ButtonBar">
<Button android:id="@+id/consoleButton" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_weight="1"
android:text="@string/menu_console" />
<Button android:id="@+id/walletButton" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_weight="1"
android:text="@string/menu_wallet" />
</LinearLayout>
</RelativeLayout>

View File

@ -2,4 +2,8 @@
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<!-- Per the design guidelines, navigation drawers should be between 240dp and 320dp:
https://developer.android.com/design/patterns/navigation-drawer.html -->
<dimen name="navigation_drawer_width">240dp</dimen>
</resources>

View File

@ -3,4 +3,14 @@
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
<string name="title_activity_menu">MenuActivity</string>
<string name="menu_console">Console</string>
<string name="menu_wallet">Wallet</string>
<string name="title_section3">Section 3</string>
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>
<string name="action_example">Example action</string>
</resources>

View File

@ -126,7 +126,7 @@ dependencies {
//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.fasterxml.jackson.core:jackson-databind:2.2.0'
compile 'org.apache.commons:commons-collections4:4.0'

View File

@ -3,15 +3,8 @@ package org.ethereum.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.io.*;
import java.util.*;
/**
* Utility class to retrieve property values from the system.properties files
@ -24,8 +17,9 @@ public class SystemProperties {
private static Logger logger = LoggerFactory.getLogger("general");
private final static int DEFAULT_TX_APPROVE_TIMEOUT = 10;
private final static String DEFAULT_DISCOVERY_PEER_LIST = "poc-7.ethdev.com:30303";
private final static String DEFAULT_ACTIVE_PEER_IP = "poc-7.ethdev.com";
private final static String DEFAULT_DISCOVERY_PEER_LIST = "poc-9.ethdev.com:30303";
private final static String DEFAULT_ACTIVE_PEER_NODEID = ""; // FIXME
private final static String DEFAULT_ACTIVE_PEER_IP = "poc-9.ethdev.com";
private final static int DEFAULT_ACTIVE_PORT = 30303;
private final static String DEFAULT_SAMPLES_DIR = "samples";
private final static String DEFAULT_COINBASE_SECRET = "monkey";
@ -134,6 +128,14 @@ public class SystemProperties {
prop.setProperty("database.reset", reset.toString());
}
public String activePeerNodeid() {
return prop.getProperty("peer.active.nodeid", DEFAULT_ACTIVE_PEER_NODEID);
}
public void setActivePeerNodeid(String nodeId) {
prop.setProperty("peer.active.nodeid", nodeId);
}
public String activePeerIP() {
return prop.getProperty("peer.active.ip", DEFAULT_ACTIVE_PEER_IP);
}
@ -258,9 +260,9 @@ public class SystemProperties {
public String getKeyValueDataSource() {
return prop.getProperty("keyvalue.datasource", DEFAULT_KEY_VALUE_DATA_SOURCE);
}
public boolean isRedisEnabled() {
return boolProperty("redis.enabled", DEFAULT_REDIS_ENABLED);
return boolProperty("redis.enabled", DEFAULT_REDIS_ENABLED);
}
public void setListenPort(Integer port) {

View File

@ -109,4 +109,4 @@ public class Account {
pendingTransactions.clear();
}
}
}
}

View File

@ -4,21 +4,14 @@ import org.ethereum.crypto.HashUtil;
import org.ethereum.crypto.SHA3Helper;
import org.ethereum.trie.Trie;
import org.ethereum.trie.TrieImpl;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.FastByteComparisons;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPElement;
import org.ethereum.util.RLPList;
import org.ethereum.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.Arrays;
import org.spongycastle.util.BigIntegers;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@ -66,13 +59,55 @@ public class Block {
this.parsed = false;
}
public Block(BlockHeader header, List<Transaction> transactionsList, List<BlockHeader> uncleList) {
this(header.getParentHash(),
header.getUnclesHash(),
header.getCoinbase(),
header.getLogsBloom(),
header.getDifficulty(),
header.getNumber(),
header.getGasLimit(),
header.getGasUsed(),
header.getTimestamp(),
header.getExtraData(),
header.getMixHash(),
header.getNonce(),
header.getReceiptsRoot(),
header.getTxTrieRoot(),
header.getStateRoot(),
transactionsList,
uncleList);
}
public Block(byte[] parentHash, byte[] unclesHash, byte[] coinbase, byte[] logsBloom,
byte[] difficulty, long number, long gasLimit,
long gasUsed, long timestamp, byte[] extraData, byte[] nonce,
long gasUsed, long timestamp, byte[] extraData,
byte[] mixHash, byte[] nonce, byte[] receiptsRoot,
byte[] transactionsRoot, byte[] stateRoot,
List<Transaction> transactionsList, List<BlockHeader> uncleList) {
this(parentHash, unclesHash, coinbase, logsBloom, difficulty, number, gasLimit,
gasUsed, timestamp, extraData, mixHash, nonce, transactionsList, uncleList);
this.header.setTransactionsRoot(calcTxTrie(transactionsList));
if (!Hex.toHexString(transactionsRoot).
equals(Hex.toHexString(this.header.getTxTrieRoot())))
logger.error("Transaction root miss-calculate, block: {}", getNumber());
this.header.setStateRoot(stateRoot);
this.header.setReceiptsRoot(receiptsRoot);
}
public Block(byte[] parentHash, byte[] unclesHash, byte[] coinbase, byte[] logsBloom,
byte[] difficulty, long number, long gasLimit,
long gasUsed, long timestamp,
byte[] extraData, byte[] mixHash, byte[] nonce,
List<Transaction> transactionsList, List<BlockHeader> uncleList) {
this.header = new BlockHeader(parentHash, unclesHash, coinbase, logsBloom,
difficulty, number, gasLimit, gasUsed,
timestamp, extraData, nonce);
timestamp, extraData, mixHash, nonce);
this.transactionsList = transactionsList;
if (this.transactionsList == null) {
@ -168,6 +203,12 @@ public class Block {
return this.header.getTxTrieRoot();
}
public byte[] getReceiptsRoot() {
if (!parsed) parseRLP();
return this.header.getReceiptsRoot();
}
public byte[] getLogBloom() {
if (!parsed) parseRLP();
return this.header.getLogsBloom();
@ -178,6 +219,12 @@ public class Block {
return this.header.getDifficulty();
}
public BigInteger getDifficultyBI() {
if (!parsed) parseRLP();
return this.header.getDifficultyBI();
}
public BigInteger getCumulativeDifficulty() {
if (!parsed) parseRLP();
BigInteger calcDifficulty = new BigInteger(1, this.header.getDifficulty());
@ -207,11 +254,18 @@ public class Block {
return this.header.getGasUsed();
}
public byte[] getExtraData() {
if (!parsed) parseRLP();
return this.header.getExtraData();
}
public byte[] getMixHash() {
if (!parsed) parseRLP();
return this.header.getMixHash();
}
public byte[] getNonce() {
if (!parsed) parseRLP();
return this.header.getNonce();
@ -277,7 +331,20 @@ public class Block {
}
private void parseTxs(byte[] expectedRoot, RLPList txTransactions) {
private byte[] calcTxTrie(List<Transaction> transactions){
this.txsState = new TrieImpl(null);
if (transactions == null || transactions.isEmpty())
return HashUtil.EMPTY_TRIE_HASH;
for (int i = 0; i < transactions.size(); i++) {
this.txsState.update(RLP.encodeInt(i), transactions.get(i).getEncoded());
}
return txsState.getRootHash();
}
private void parseTxs(RLPList txTransactions) {
this.txsState = new TrieImpl(null);
for (int i = 0; i < txTransactions.size(); i++) {
@ -285,7 +352,12 @@ public class Block {
this.transactionsList.add(new Transaction(transactionRaw.getRLPData()));
this.txsState.update(RLP.encodeInt(i), transactionRaw.getRLPData());
}
}
private void parseTxs(byte[] expectedRoot, RLPList txTransactions) {
parseTxs(txTransactions);
String calculatedRoot = Hex.toHexString(txsState.getRootHash());
if (!calculatedRoot.equals(Hex.toHexString(expectedRoot)))
logger.error("Added tx receipts don't match the given txsStateRoot");

View File

@ -6,6 +6,7 @@ import org.ethereum.util.RLPList;
import org.ethereum.util.Utils;
import java.math.BigInteger;
import java.util.List;
import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH;
import static org.ethereum.util.ByteUtil.toHexString;
@ -54,6 +55,10 @@ public class BlockHeader {
private long gasLimit;
/* A scalar value equal to the total gas used in transactions in this block */
private long gasUsed;
private byte[] mixHash;
/* An arbitrary byte array containing data relevant to this block.
* With the exception of the genesis block, this must be 32 bytes or fewer */
private byte[] extraData;
@ -91,14 +96,14 @@ public class BlockHeader {
this.timestamp = tsBytes == null ? 0 : (new BigInteger(1, tsBytes)).longValue();
this.extraData = rlpHeader.get(12).getRLPData();
this.nonce = rlpHeader.get(13).getRLPData();
this.mixHash = rlpHeader.get(13).getRLPData();
this.nonce = rlpHeader.get(14).getRLPData();
}
public BlockHeader(byte[] parentHash, byte[] unclesHash, byte[] coinbase,
byte[] logsBloom, byte[] difficulty, long number,
long gasLimit, long gasUsed, long timestamp,
byte[] extraData, byte[] nonce) {
byte[] extraData, byte[] mixHash, byte[] nonce) {
this.parentHash = parentHash;
this.unclesHash = unclesHash;
this.coinbase = coinbase;
@ -109,6 +114,7 @@ public class BlockHeader {
this.gasUsed = gasUsed;
this.timestamp = timestamp;
this.extraData = extraData;
this.mixHash = mixHash;
this.nonce = nonce;
this.stateRoot = HashUtil.EMPTY_TRIE_HASH;
}
@ -123,7 +129,7 @@ public class BlockHeader {
public byte[] calcDifficulty() {
if (this.isGenesis())
return Genesis.DIFFICULTY;
else { //todo find the right way to calc difficulty
else {
// Block parent = this.getParent();
// long parentDifficulty = new BigInteger(1, parent.getDifficulty()).longValue();
// long newDifficulty = this.getTimestamp() < parent.getTimestamp() + 5 ? parentDifficulty - (parentDifficulty >> 10) : (parentDifficulty + (parentDifficulty >> 10));
@ -141,10 +147,6 @@ public class BlockHeader {
return parentHash;
}
public void setParentHash(byte[] parentHash) {
this.parentHash = parentHash;
}
public byte[] getUnclesHash() {
return unclesHash;
}
@ -173,26 +175,32 @@ public class BlockHeader {
return txTrieRoot;
}
public void setTxTrieRoot(byte[] txTrieRoot) {
this.txTrieRoot = txTrieRoot;
public void setReceiptsRoot(byte[] receiptTrieRoot) {
this.receiptTrieRoot = receiptTrieRoot;
}
public byte[] getReceiptTrieRoot() {
public byte[] getReceiptsRoot() {
return receiptTrieRoot;
}
public void setTransactionsRoot(byte[] stateRoot) {
this.txTrieRoot = stateRoot;
}
public byte[] getLogsBloom() {
return logsBloom;
}
public void setReceiptTrieRoot(byte[] receiptTrieRoot) {
this.receiptTrieRoot = receiptTrieRoot;
}
public byte[] getDifficulty() {
return difficulty;
}
public BigInteger getDifficultyBI() {
return new BigInteger(1, difficulty);
}
public void setDifficulty(byte[] difficulty) {
this.difficulty = difficulty;
}
@ -229,12 +237,12 @@ public class BlockHeader {
this.gasUsed = gasUsed;
}
public byte[] getExtraData() {
return extraData;
public byte[] getMixHash() {
return mixHash;
}
public void setExtraData(byte[] extraData) {
this.extraData = extraData;
public byte[] getExtraData() {
return extraData;
}
public byte[] getNonce() {
@ -273,19 +281,32 @@ public class BlockHeader {
byte[] gasLimit = RLP.encodeBigInteger(BigInteger.valueOf(this.gasLimit));
byte[] gasUsed = RLP.encodeBigInteger(BigInteger.valueOf(this.gasUsed));
byte[] timestamp = RLP.encodeBigInteger(BigInteger.valueOf(this.timestamp));
byte[] extraData = RLP.encodeElement(this.extraData);
byte[] mixHash = RLP.encodeElement(this.mixHash);
if (withNonce) {
byte[] nonce = RLP.encodeElement(this.nonce);
return RLP.encodeList(parentHash, unclesHash, coinbase,
stateRoot, txTrieRoot, receiptTrieRoot, logsBloom, difficulty, number,
gasLimit, gasUsed, timestamp, extraData, nonce);
gasLimit, gasUsed, timestamp, extraData, mixHash, nonce);
} else {
return RLP.encodeList(parentHash, unclesHash, coinbase,
stateRoot, txTrieRoot, receiptTrieRoot, logsBloom, difficulty, number,
gasLimit, gasUsed, timestamp, extraData);
gasLimit, gasUsed, timestamp, extraData, mixHash);
}
}
public byte[] getUnclesEncoded(List<BlockHeader> uncleList) {
byte[][] unclesEncoded = new byte[uncleList.size()][];
int i = 0;
for (BlockHeader uncle : uncleList) {
unclesEncoded[i] = uncle.getEncoded();
++i;
}
return RLP.encodeList(unclesEncoded);
}
public String toString() {
return toStringWithSuffix("\n");
}
@ -304,6 +325,7 @@ public class BlockHeader {
toStringBuff.append(" gasUsed=").append(gasUsed).append(suffix);
toStringBuff.append(" timestamp=").append(timestamp).append(" (").append(Utils.longToDateTime(timestamp)).append(")").append(suffix);
toStringBuff.append(" extraData=").append(toHexString(extraData)).append(suffix);
toStringBuff.append(" mixHash=").append(toHexString(mixHash)).append(suffix);
toStringBuff.append(" nonce=").append(toHexString(nonce)).append(suffix);
return toStringBuff.toString();
}

View File

@ -1,20 +1,22 @@
package org.ethereum.core;
import org.ethereum.config.Constants;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.BlockStore;
import org.ethereum.db.RepositoryImpl;
import org.ethereum.facade.Blockchain;
import org.ethereum.facade.Repository;
import org.ethereum.listener.EthereumListener;
import org.ethereum.manager.AdminInfo;
import org.ethereum.net.BlockQueue;
import org.ethereum.net.server.ChannelManager;
import org.ethereum.util.AdvancedDeviceUtils;
import org.ethereum.trie.Trie;
import org.ethereum.trie.TrieImpl;
import org.ethereum.util.*;
import org.ethereum.vm.ProgramInvokeFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
//import org.springframework.stereotype.Component;
@ -26,13 +28,16 @@ import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.math.BigDecimal;
import java.util.*;
import static org.ethereum.config.Constants.*;
import static org.ethereum.config.SystemProperties.CONFIG;
import static org.ethereum.core.Denomination.SZABO;
import static org.ethereum.core.ImportResult.EXIST;
import static org.ethereum.core.ImportResult.NO_PARENT;
import static org.ethereum.core.ImportResult.SUCCESS;
/**
* The Ethereum blockchain is in many ways similar to the Bitcoin blockchain,
@ -66,8 +71,6 @@ import static org.ethereum.core.Denomination.SZABO;
//@Component
public class BlockchainImpl implements Blockchain {
/* A scalar value equal to the minimum limit of gas expenditure per block */
private static final long MIN_GAS_LIMIT = 125000L;
private static final Logger logger = LoggerFactory.getLogger("blockchain");
private static final Logger stateLogger = LoggerFactory.getLogger("state");
@ -75,7 +78,9 @@ public class BlockchainImpl implements Blockchain {
// to avoid using minGasPrice=0 from Genesis for the wallet
private static final long INITIAL_MIN_GAS_PRICE = 10 * SZABO.longValue();
private Set<Transaction> pendingTransactions;
// @Resource
// @Qualifier("pendingTransactions")
private Set<Transaction> pendingTransactions = new HashSet<>();
@Autowired
private Repository repository;
@ -110,10 +115,23 @@ public class BlockchainImpl implements Blockchain {
private List<Chain> altChains = new ArrayList<>();
private List<Block> garbage = new ArrayList<>();
public BlockchainImpl(final Set<Transaction> pendingTransactions) {
this.pendingTransactions = Collections.synchronizedSet(pendingTransactions);
}
//todo: autowire over constructor
public BlockchainImpl(BlockStore blockStore, Repository repository,
Wallet wallet, AdminInfo adminInfo,
EthereumListener listener) {
this.blockStore = blockStore;
this.repository = repository;
this.wallet = wallet;
this.adminInfo = adminInfo;
this.listener = listener;
}
@Override
public byte[] getBestBlockHash() {
return getBestBlock().getHash();
@ -145,12 +163,25 @@ public class BlockchainImpl implements Blockchain {
return blockStore.getListOfHashesStartFrom(hash, qty);
}
public void tryToConnect(Block block) {
private byte[] calcTxTrie(List<Transaction> transactions){
Trie txsState = new TrieImpl(null);
if (transactions == null || transactions.isEmpty())
return HashUtil.EMPTY_TRIE_HASH;
for (int i = 0; i < transactions.size(); i++) {
txsState.update(RLP.encodeInt(i), transactions.get(i).getEncoded());
}
return txsState.getRootHash();
}
public ImportResult tryToConnect(Block block) {
recordBlock(block);
if (logger.isDebugEnabled())
logger.debug("Try connect block hash: {}, number: {}",
if (logger.isInfoEnabled())
logger.info("Try connect block hash: {}, number: {}",
Hex.toHexString(block.getHash()).substring(0, 6),
block.getNumber());
@ -162,16 +193,23 @@ public class BlockchainImpl implements Blockchain {
block.getNumber());
// retry of well known block
return;
return EXIST;
}
// The simple case got the block
// to connect to the main chain
if (bestBlock.isParentOf(block)) {
add(block);
return;
return SUCCESS;
} else {
if (1 == 1) // FIXME: WORKARROUND
return NO_PARENT;
}
//TODO POC9 add rollback support
if (1 == 1)
return SUCCESS; // todo: temporary cancel the rollback
// cut on the chain got lastBlock + 1 > n
if (block.getNumber() > bestBlock.getNumber() + 1) {
channelManager.ethSync();
@ -180,7 +218,7 @@ public class BlockchainImpl implements Blockchain {
if (!hasParentOnTheChain(block) && block.getNumber() > bestBlock.getNumber()) {
if (1 == 1)
return; // todo: temporary cancel the rollback
return SUCCESS; // todo: temporary cancel the rollback
logger.info("*** Blockchain will rollback and resynchronise now ");
@ -198,19 +236,25 @@ public class BlockchainImpl implements Blockchain {
blockQueue.clear();
channelManager.ethSync();
return;
return SUCCESS;
}
// provisional, by the garbage will be
// defined how to deal with it in the
// future.
garbage.add(block);
return SUCCESS;
}
@Override
public void add(Block block) {
if(!isValid(block)){
logger.warn("Invalid block with number: {}", block.getNumber());
return;
}
track = repository.startTracking();
if (block == null)
return;
@ -224,9 +268,28 @@ public class BlockchainImpl implements Blockchain {
}
List<TransactionReceipt> receipts = processBlock(block);
stateLogger.info("applied reward for block: [{}] \n state: [{}]",
block.getNumber(),
Hex.toHexString(repository.getRoot()));
// Sanity checks
String receiptHash = Hex.toHexString( block.getReceiptsRoot() );
String receiptListHash = Hex.toHexString(calcReceiptsTrie(receipts));
if( !receiptHash.equals(receiptListHash) ) {
logger.error("Block's given Receipt Hash doesn't match: {} != {}", receiptHash, receiptListHash);
//return false;
}
String logBloomHash = Hex.toHexString( block.getLogBloom() );
String logBloomListHash = Hex.toHexString(calcLogBloom(receipts));
if( !logBloomHash.equals(logBloomListHash) ) {
logger.error("Block's given logBloom Hash doesn't match: {} != {}", logBloomHash, logBloomListHash);
//track.rollback();
//return;
}
//DEBUG
//System.out.println(" Receipts root is: " + receiptHash + " logbloomhash is " + logBloomHash);
//System.out.println(" Receipts listroot is: " + receiptListHash + " logbloomlisthash is " + logBloomListHash);
track.commit();
repository.flush(); // saving to the disc
@ -242,7 +305,8 @@ public class BlockchainImpl implements Blockchain {
listener.onBlock(block);
listener.onBlockReciepts(receipts);
if (blockQueue.size() == 0 &&
if (blockQueue != null &&
blockQueue.size() == 0 &&
!syncDoneCalled &&
channelManager.isAllSync()) {
@ -252,33 +316,87 @@ public class BlockchainImpl implements Blockchain {
}
}
private byte[] calcReceiptsTrie(List<TransactionReceipt> receipts){
//TODO Fix Trie hash for receipts - doesnt match cpp
Trie receiptsTrie = new TrieImpl(null);
if (receipts == null || receipts.isEmpty())
return HashUtil.EMPTY_TRIE_HASH;
for (int i = 0; i < receipts.size(); i++) {
receiptsTrie.update(RLP.encodeInt(i), receipts.get(i).getEncoded());
}
return receiptsTrie.getRootHash();
}
private byte[] calcLogBloom( List<TransactionReceipt> receipts ) {
Bloom retBloomFilter = new Bloom();
if (receipts == null || receipts.isEmpty())
return retBloomFilter.getData();
for (int i = 0; i < receipts.size(); i++) {
retBloomFilter.or(receipts.get(i).getBloomFilter());
}
return retBloomFilter.getData();
}
public Block getParent(BlockHeader header) {
return blockStore.getBlockByHash(header.getParentHash());
}
/**
* Calculate GasLimit
* See Yellow Paper: http://www.gavwood.com/Paper.pdf - page 5, 4.3.4 (25)
*
* @return long value of the gasLimit
*/
public long calcGasLimit(BlockHeader header) {
if (header.isGenesis())
return Genesis.GAS_LIMIT;
Block parent = getParent(header);
return Math.max(MIN_GAS_LIMIT, (parent.getGasLimit() * (1024 - 1) + (parent.getGasUsed() * 6 / 5)) / 1024);
}
public boolean isValid(BlockHeader header) {
return header.getDifficulty() == header.calcDifficulty() // difficulty meets requirements
&& header.getGasLimit() == calcGasLimit(header) // gasLimit meets requirements
&& header.getTimestamp() > getParent(header).getTimestamp() // timestamp meets requirements
&& (header.getExtraData() == null || header.getExtraData().length <= 1024); // extraData doesn't exceed 1024 bytes
Block parentBlock = getParent(header);
BigInteger parentDifficulty = parentBlock.getDifficultyBI();
long parentTimestamp = parentBlock.getTimestamp();
BigInteger minDifficulty = header.getTimestamp() >= parentTimestamp + DURATION_LIMIT ?
parentBlock.getDifficultyBI().subtract(parentDifficulty.divide(BigInteger.valueOf(Constants.DIFFICULTY_BOUND_DIVISOR))) :
parentBlock.getDifficultyBI().add(parentDifficulty.divide(BigInteger.valueOf(Constants.DIFFICULTY_BOUND_DIVISOR)));
BigInteger difficulty = new BigInteger(1, header.getDifficulty());
if (header.getNumber() != (parentBlock.getNumber() + 1) ) {
logger.error("Block invalid: block number is not parentBlock number + 1, ");
return false;
}
if (header.getGasLimit() < header.getGasUsed()) {
logger.error("Block invalid: header.getGasLimit() < header.getGasUsed()");
return false;
}
if (difficulty.compareTo(minDifficulty) == -1) {
logger.error("Block invalid: difficulty < minDifficulty");
return false;
}
if (header.getGasLimit() < MIN_GAS_LIMIT) {
logger.error("Block invalid: header.getGasLimit() < MIN_GAS_LIMIT");
return false;
}
if (header.getExtraData() != null && header.getExtraData().length > MAXIMUM_EXTRA_DATA_SIZE) {
logger.error("Block invalid: header.getExtraData().length > MAXIMUM_EXTRA_DATA_SIZE");
return false;
}
if (header.getGasLimit() < Constants.MIN_GAS_LIMIT ||
header.getGasLimit() < parentBlock.getGasLimit() * (GAS_LIMIT_BOUND_DIVISOR - 1) / GAS_LIMIT_BOUND_DIVISOR ||
header.getGasLimit() > parentBlock.getGasLimit() * (GAS_LIMIT_BOUND_DIVISOR + 1) / GAS_LIMIT_BOUND_DIVISOR){
logger.error("Block invalid: gas limit exceeds parentBlock.getGasLimit() (+-) GAS_LIMIT_BOUND_DIVISOR");
return false;
}
return true;
}
/**
@ -291,49 +409,72 @@ public class BlockchainImpl implements Blockchain {
private boolean isValid(Block block) {
boolean isValid = true;
if (isValid) return (isValid); // todo get back to the real header validation
if (!block.isGenesis()) {
isValid = isValid(block.getHeader());
// Sanity checks
String trieHash = Hex.toHexString( block.getTxTrieRoot() );
String trieListHash = Hex.toHexString(calcTxTrie(block.getTransactionsList()));
/* FIXME: temporary comment out tx.trie validation
if( !trieHash.equals(trieListHash) ) {
logger.error("Block's given Trie Hash doesn't match: {} != {}", trieHash, trieListHash);
return false;
}
*/
String unclesHash = Hex.toHexString(block.getHeader().getUnclesHash());
String unclesListHash = Hex.toHexString( HashUtil.sha3(block.getHeader().getUnclesEncoded( block.getUncleList() ) ) );
if( !unclesHash.equals(unclesListHash) ) {
logger.error("Block's given Uncle Hash doesn't match: {} != {}", unclesHash, unclesListHash);
return false;
}
if (block.getUncleList().size() > UNCLE_LIST_LIMIT) {
logger.error("Uncle list to big: block.getUncleList().size() > UNCLE_LIST_LIMIT");
return false;
};
for (BlockHeader uncle : block.getUncleList()) {
// - They are valid headers (not necessarily valid blocks)
isValid = isValid(uncle);
// - Their parent is a kth generation ancestor for k in {2, 3, 4, 5, 6, 7}
long generationGap = block.getNumber() - getParent(uncle).getNumber();
isValid = generationGap > 1 && generationGap < 8;
// - They were not uncles of the kth generation ancestor for k in {1, 2, 3, 4, 5, 6}
generationGap = block.getNumber() - uncle.getNumber();
isValid = generationGap > 0 && generationGap < 7;
if (!isValid(uncle)) return false;
//if uncle's parent's number is not less than currentBlock - UNCLE_GEN_LIMIT, mark invalid
isValid = !(getParent(uncle).getNumber() < (block.getNumber() - UNCLE_GENERATION_LIMIT) );
if (!isValid){
logger.error("Uncle too old: generationGap must be under UNCLE_GENERATION_LIMIT");
return false;
}
}
}
if (!isValid)
logger.warn("WARNING: Invalid - {}", this);
return isValid;
return isValid;
}
private List<TransactionReceipt> processBlock(Block block) {
List<TransactionReceipt> receipts = new ArrayList<>();
if (isValid(block)) {
if (!block.isGenesis()) {
if (!CONFIG.blockChainOnly()) {
wallet.addTransactions(block.getTransactionsList());
receipts = applyBlock(block);
wallet.processBlock(block);
}
if (!block.isGenesis()) {
if (!CONFIG.blockChainOnly()) {
wallet.addTransactions(block.getTransactionsList());
receipts = applyBlock(block);
wallet.processBlock(block);
}
storeBlock(block, receipts);
} else {
logger.warn("Invalid block with nr: {}", block.getNumber());
}
storeBlock(block, receipts);
return receipts;
}
private List<TransactionReceipt> applyBlock(Block block) {
logger.info("applyBlock: block: [{}] tx.list: [{}]", block.getNumber(), block.getTransactionsList().size());
long saveTime = System.nanoTime();
int i = 1;
long totalGasUsed = 0;
@ -344,13 +485,17 @@ public class BlockchainImpl implements Blockchain {
TransactionExecutor executor = new TransactionExecutor(tx, block.getCoinbase(),
track, blockStore,
programInvokeFactory, block, listener);
executor.execute();
programInvokeFactory, block, listener, totalGasUsed);
TransactionReceipt receipt = executor.getReceipt();
totalGasUsed += receipt.getCumulativeGasLong();
executor.init();
executor.execute2();
executor.go();
executor.finalization();
totalGasUsed += executor.getGasUsed();
track.commit();
TransactionReceipt receipt = new TransactionReceipt();
receipt.setCumulativeGas(totalGasUsed);
receipt.setPostTxState(repository.getRoot());
receipt.setTransaction(tx);
@ -374,6 +519,11 @@ public class BlockchainImpl implements Blockchain {
track.commit();
stateLogger.info("applied reward for block: [{}] \n state: [{}]",
block.getNumber(),
Hex.toHexString(repository.getRoot()));
if (block.getNumber() >= CONFIG.traceStartBlock())
repository.dumpState(block, totalGasUsed, 0, null);
@ -398,12 +548,15 @@ public class BlockchainImpl implements Blockchain {
// Add extra rewards based on number of uncles
if (block.getUncleList().size() > 0) {
for (BlockHeader uncle : block.getUncleList()) {
track.addBalance(uncle.getCoinbase(), Block.UNCLE_REWARD);
track.addBalance(uncle.getCoinbase(),
new BigDecimal(block.BLOCK_REWARD).multiply(BigDecimal.valueOf(8 + uncle.getNumber() - block.getNumber()).divide(new BigDecimal(8))).toBigInteger());
totalBlockReward = totalBlockReward.add(Block.INCLUSION_REWARD);
}
totalBlockReward = totalBlockReward.add(Block.INCLUSION_REWARD
.multiply(BigInteger.valueOf(block.getUncleList().size())));
}
track.addBalance(block.getCoinbase(), totalBlockReward);
}
@Override
@ -577,4 +730,5 @@ public class BlockchainImpl implements Blockchain {
track.commit();
}
}

View File

@ -1,7 +1,6 @@
package org.ethereum.core;
import org.ethereum.util.ByteUtil;
import org.spongycastle.util.encoders.Hex;
import java.util.Arrays;
@ -15,7 +14,12 @@ import java.util.Arrays;
public class Bloom {
byte[] data = new byte[64];
final static int _8STEPS = 8;
final static int _3LOW_BITS = 7;
final static int ENSURE_BYTE = 255;
byte[] data = new byte[256];
public Bloom() {
}
@ -25,11 +29,12 @@ public class Bloom {
}
public static Bloom create(byte[] toBloom) {
int mov1 = ((255 & toBloom[0 + 1]) + 256 * ((255 & toBloom[0]) & 1));
int mov2 = ((255 & toBloom[2 + 1]) + 256 * ((255 & toBloom[2]) & 1));
int mov3 = ((255 & toBloom[4 + 1]) + 256 * ((255 & toBloom[4]) & 1));
byte[] data = new byte[64];
int mov1 = (((toBloom[0] & ENSURE_BYTE) & (_3LOW_BITS)) << _8STEPS) + ((toBloom[1]) & ENSURE_BYTE);
int mov2 = (((toBloom[2] & ENSURE_BYTE) & (_3LOW_BITS)) << _8STEPS) + ((toBloom[3]) & ENSURE_BYTE);
int mov3 = (((toBloom[4] & ENSURE_BYTE) & (_3LOW_BITS)) << _8STEPS) + ((toBloom[5]) & ENSURE_BYTE);
byte[] data = new byte[256];
Bloom bloom = new Bloom(data);
ByteUtil.setBit(data, mov1, 1);

View File

@ -1,22 +1,26 @@
package org.ethereum.core;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.trie.SecureTrie;
import org.ethereum.trie.Trie;
import org.ethereum.trie.TrieImpl;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static org.ethereum.core.Denomination.FINNEY;
import static org.ethereum.core.Denomination.WEI;
import static org.ethereum.crypto.HashUtil.*;
import static org.ethereum.crypto.HashUtil.EMPTY_LIST_HASH;
import static org.ethereum.util.ByteUtil.wrap;
/**
* The genesis block is the first block in the chain and has fixed values according to
* the protocol specification. The genesis block is 13 items, and is specified thus:
*
* <p>
* ( zerohash_256 , SHA3 RLP () , zerohash_160 , stateRoot, 0, 2^22 , 0, 0, 1000000, 0, 0, 0, SHA3 (42) , (), () )
*
* <p>
* - Where zerohash_256 refers to the parent hash, a 256-bit hash which is all zeroes;
* - zerohash_160 refers to the coinbase address, a 160-bit hash which is all zeroes;
* - 2^22 refers to the difficulty;
@ -25,66 +29,98 @@ import static org.ethereum.crypto.HashUtil.*;
* - The sequences of both uncles and transactions are empty and represented by ().
* - SHA3 (42) refers to the SHA3 hash of a byte array of length one whose first and only byte is of value 42.
* - SHA3 RLP () value refers to the hash of the uncle lists in RLP, both empty lists.
*
* <p>
* See Yellow Paper: http://www.gavwood.com/Paper.pdf (Appendix I. Genesis Block)
*/
public class Genesis extends Block {
public final static BigInteger PREMINE_AMOUNT = BigInteger.valueOf(2).pow(200);
// Genesis reference: https://github.com/ethereum/cpp-ethereum/blob/[#branch#]/libethereum/GenesisInfo.cpp
static String GENESIS_JSON =
"{" +
"'0000000000000000000000000000000000000001': { 'wei': '1' }" +
"'0000000000000000000000000000000000000002': { 'wei': '1' }" +
"'0000000000000000000000000000000000000003': { 'wei': '1' }" +
"'0000000000000000000000000000000000000004': { 'wei': '1' }" +
"'dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6': { 'wei': '1606938044258990275541962092341162602522202993782792835301376' }" +
/*(J) */ "'e6716f9544a56c530d868e4bfbacb172315bdead': { 'wei': '1606938044258990275541962092341162602522202993782792835301376' }" +
/*(V) */ "'b9c015918bdaba24b4ff057a92a3873d6eb201be': { 'wei': '1606938044258990275541962092341162602522202993782792835301376' }" +
/*(A) */ "'1a26338f0d905e295fccb71fa9ea849ffa12aaf4': { 'wei': '1606938044258990275541962092341162602522202993782792835301376' }" +
/*(M) */ "'2ef47100e0787b915105fd5e3f4ff6752079d5cb': { 'wei': '1606938044258990275541962092341162602522202993782792835301376' }" +
/*(R) */ "'cd2a3d9f938e13cd947ec05abc7fe734df8dd826': { 'wei': '1606938044258990275541962092341162602522202993782792835301376' }" +
/*(HH)*/ "'6c386a4b26f73c802f34673f7248bb118f97424a': { 'wei': '1606938044258990275541962092341162602522202993782792835301376' }" +
/*(CH)*/ "'e4157b34ea9615cfbde6b4fda419828124b70c78': { 'wei': '1606938044258990275541962092341162602522202993782792835301376' }" +
"}";
private static PremineRaw[] premine = new PremineRaw[]{
new PremineRaw(Hex.decode("dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6"), PREMINE_AMOUNT, WEI),
new PremineRaw(Hex.decode("e6716f9544a56c530d868e4bfbacb172315bdead"), PREMINE_AMOUNT, WEI),
new PremineRaw(Hex.decode("b9c015918bdaba24b4ff057a92a3873d6eb201be"), PREMINE_AMOUNT, WEI),
new PremineRaw(Hex.decode("1a26338f0d905e295fccb71fa9ea849ffa12aaf4"), PREMINE_AMOUNT, WEI),
new PremineRaw(Hex.decode("2ef47100e0787b915105fd5e3f4ff6752079d5cb"), PREMINE_AMOUNT, WEI),
new PremineRaw(Hex.decode("cd2a3d9f938e13cd947ec05abc7fe734df8dd826"), PREMINE_AMOUNT, WEI),
new PremineRaw(Hex.decode("6c386a4b26f73c802f34673f7248bb118f97424a"), PREMINE_AMOUNT, WEI),
new PremineRaw(Hex.decode("e4157b34ea9615cfbde6b4fda419828124b70c78"), PREMINE_AMOUNT, WEI),
new PremineRaw(Hex.decode("b0afc46d9ce366d06ab4952ca27db1d9557ae9fd"), new BigInteger("154162184"), FINNEY),
new PremineRaw(Hex.decode("f6b1e9dc460d4d62cc22ec5f987d726929c0f9f0"), new BigInteger("102774789"), FINNEY),
new PremineRaw(Hex.decode("cc45122d8b7fa0b1eaa6b29e0fb561422a9239d0"), new BigInteger("51387394"), FINNEY),
new PremineRaw(Hex.decode("b7576e9d314df41ec5506494293afb1bd5d3f65d"), new BigInteger("69423399"), FINNEY),
};
static {
GENESIS_JSON = GENESIS_JSON.replace("'", "\"");
}
private Map<ByteArrayWrapper, AccountState> premine = new HashMap<>();
private static byte[] zeroHash256 = new byte[32];
private static byte[] zeroHash160 = new byte[20];
private static byte[] zeroHash512 = new byte[64];
private static byte[] zeroHash2048 = new byte[256];
public static byte[] PARENT_HASH = zeroHash256;
public static byte[] UNCLES_HASH = EMPTY_LIST_HASH;
public static byte[] COINBASE = zeroHash160;
public static byte[] LOG_BLOOM = zeroHash512;
public static byte[] LOG_BLOOM = zeroHash2048;
public static byte[] DIFFICULTY = BigInteger.valueOf(2).pow(17).toByteArray();
public static long NUMBER = 0;
public static long GAS_LIMIT = 1000000;
public static long GAS_LIMIT = 0x2FEFD8;
public static long GAS_USED = 0;
public static long TIMESTAMP = 0;
public static byte[] EXTRA_DATA = new byte[0];
public static byte[] NONCE = sha3(new byte[]{42});
public static byte[] MIX_HASH = zeroHash256;
public static byte[] NONCE = Hex.decode("000000000000002A");
private static Block instance;
private Genesis() {
super(PARENT_HASH, UNCLES_HASH, COINBASE, LOG_BLOOM, DIFFICULTY,
NUMBER, GAS_LIMIT, GAS_USED, TIMESTAMP,
EXTRA_DATA, NONCE, null, null);
EXTRA_DATA, MIX_HASH, NONCE, null, null);
Trie state = new TrieImpl(null);
// The proof-of-concept series include a development pre-mine, making the state root hash
// some value stateRoot. The latest documentation should be consulted for the value of the state root.
Trie state = parseGenesis();
setStateRoot(state.getRootHash());
}
for (PremineRaw raw : premine) {
AccountState acctState = new AccountState(BigInteger.ZERO,
raw.getValue().multiply(raw.getDenomination().value()));
state.update(raw.getAddr(), acctState.getEncoded());
private Trie parseGenesis() {
JSONParser parser = new JSONParser();
JSONObject genesisMap = null;
try {
genesisMap = (JSONObject) parser.parse(GENESIS_JSON);
} catch (ParseException e) {
e.printStackTrace();
System.exit(-1);
}
setStateRoot(state.getRootHash());
Set<String> keys = genesisMap.keySet();
Trie state = new SecureTrie(null);
for (String key : keys) {
JSONObject val = (JSONObject) genesisMap.get(key);
String denom = (String) val.keySet().toArray()[0];
String value = (String) val.values().toArray()[0];
BigInteger wei = Denomination.valueOf(denom.toUpperCase()).value().
multiply(new BigInteger(value));
AccountState acctState = new AccountState(BigInteger.ZERO, wei);
byte[] keyB = Hex.decode(key);
state.update(keyB, acctState.getEncoded());
premine.put(wrap(keyB), acctState);
}
return state;
}
public static Block getInstance() {
@ -94,7 +130,8 @@ public class Genesis extends Block {
return instance;
}
public final static PremineRaw[] getPremine() {
public Map<ByteArrayWrapper, AccountState> getPremine() {
return premine;
}

View File

@ -16,6 +16,7 @@ import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.security.SignatureException;
import java.util.Arrays;
import static org.ethereum.util.ByteUtil.*;
@ -96,10 +97,18 @@ public class Transaction {
this.receiveAddress = ByteUtil.EMPTY_BYTE_ARRAY;
}
getEncoded();
parsed = true;
}
public Transaction(byte[] nonce, byte[] gasPrice, byte[] gasLimit, byte[] receiveAddress, byte[] value, byte[] data, byte[] r, byte[] s, byte v) {
this(nonce, gasPrice, gasLimit, receiveAddress, value, data);
ECDSASignature signature = new ECDSASignature(new BigInteger(r), new BigInteger(s));
signature.v = v;
this.signature = signature;
}
public void rlpParse() {
RLPList decodedTxList = RLP.decode2(rlpEncoded);
@ -208,7 +217,7 @@ public class Transaction {
public boolean isContractCreation() {
if (!parsed) rlpParse();
return this.receiveAddress == null || this.receiveAddress == ByteUtil.EMPTY_BYTE_ARRAY;
return this.receiveAddress == null || Arrays.equals(this.receiveAddress,ByteUtil.EMPTY_BYTE_ARRAY);
}
/*
@ -342,7 +351,7 @@ public class Transaction {
return tx.hashCode() == this.hashCode();
}
public static Transaction createDefault(String from, String to, BigInteger ammount, BigInteger nonce){
public static Transaction createDefault(String to, BigInteger ammount, BigInteger nonce){
return new Transaction(BigIntegers.asUnsignedByteArray(nonce),
BigIntegers.asUnsignedByteArray(DEFAULT_GAS_PRICE),
@ -351,4 +360,6 @@ public class Transaction {
BigIntegers.asUnsignedByteArray(ammount),
null);
}
}
}

View File

@ -4,28 +4,19 @@ import org.ethereum.db.BlockStore;
import org.ethereum.facade.Repository;
import org.ethereum.listener.EthereumListener;
import org.ethereum.listener.EthereumListenerAdapter;
import org.ethereum.vm.DataWord;
import org.ethereum.vm.GasCost;
import org.ethereum.vm.LogInfo;
import org.ethereum.vm.Program;
import org.ethereum.vm.ProgramInvoke;
import org.ethereum.vm.ProgramInvokeFactory;
import org.ethereum.vm.ProgramResult;
import org.ethereum.vm.VM;
import org.ethereum.vm.*;
import org.ethereum.vmtrace.ProgramTrace;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.lang.Long;
import java.nio.ByteBuffer;
import java.util.List;
import static java.nio.ByteBuffer.wrap;
import static org.ethereum.config.SystemProperties.CONFIG;
import static org.ethereum.util.BIUtil.*;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
/**
@ -39,42 +30,252 @@ public class TransactionExecutor {
private Transaction tx;
private Repository track;
private Repository cacheTrack;
private BlockStore blockStore;
private final long gasUsedInTheBlock;
private boolean readyToExecute = false;
private ProgramInvokeFactory programInvokeFactory;
private byte[] coinbase;
private TransactionReceipt receipt;
private ProgramResult result;
private ProgramResult result = new ProgramResult();
private Block currentBlock;
private final EthereumListener listener;
private VM vm;
private Program program;
long m_endGas = 0;
long basicTxCost = 0;
public TransactionExecutor(Transaction tx, byte[] coinbase, Repository track, BlockStore blockStore,
ProgramInvokeFactory programInvokeFactory, Block currentBlock) {
this(tx, coinbase, track, blockStore, programInvokeFactory, currentBlock, new EthereumListenerAdapter());
this(tx, coinbase, track, blockStore, programInvokeFactory, currentBlock, new EthereumListenerAdapter(), 0);
}
public TransactionExecutor(Transaction tx, byte[] coinbase, Repository track, BlockStore blockStore,
ProgramInvokeFactory programInvokeFactory, Block currentBlock, EthereumListener listener) {
ProgramInvokeFactory programInvokeFactory, Block currentBlock,
EthereumListener listener, long gasUsedInTheBlock) {
this.tx = tx;
this.coinbase = coinbase;
this.track = track;
this.cacheTrack = track.startTracking();
this.blockStore = blockStore;
this.programInvokeFactory = programInvokeFactory;
this.currentBlock = currentBlock;
this.listener = listener;
this.gasUsedInTheBlock = gasUsedInTheBlock;
}
/* jeff:
execution happens like this:
create account, transfer value (if any), create a snapshot,
run INIT code, if err, rollback to snapshot, if success set return value.
*/
// https://github.com/ethereum/cpp-ethereum/blob/develop/libethereum/Executive.cpp#L55
/**
* Do all the basic validation, if the executor
* will be ready to run the transaction at the end
* set readyToExecute = true
*/
public void init(){
long txGasLimit = toBI(tx.getGasLimit()).longValue();
boolean cumulativeGasReached = (gasUsedInTheBlock + txGasLimit > currentBlock.getGasLimit());
if (cumulativeGasReached){
if (logger.isWarnEnabled())
logger.warn("Too much gas used in this block: Require: {} Got: {}", currentBlock.getGasLimit() - toBI(tx.getGasLimit()).longValue(), toBI(tx.getGasLimit()).longValue() );
// TODO: save reason for failure
return;
}
basicTxCost = GasCost.TRANSACTION + (tx.getData() == null ? 0 :
tx.nonZeroDataBytes() * GasCost.TX_NO_ZERO_DATA + tx.zeroDataBytes() * GasCost.TX_ZERO_DATA);
boolean basicCostCovered = (txGasLimit >= basicTxCost);
if (!basicCostCovered){
if (logger.isWarnEnabled())
logger.warn("Too much gas used in this block: Require: {} Got: {}", txGasLimit, basicTxCost);
// TODO: save reason for failure
return;
}
BigInteger reqNonce = track.getNonce(tx.getSender());
BigInteger txNonce = toBI(tx.getNonce());
boolean validNonce = (txNonce.compareTo(reqNonce) == 0);
if (!validNonce){
if (logger.isWarnEnabled())
logger.warn("Invalid nonce: required: {} , tx.nonce: {}", reqNonce, txNonce);
// TODO: save reason for failure
return;
}
BigInteger txGasCost = toBI(tx.getGasPrice()).multiply(toBI(txGasLimit));
BigInteger totalCost = toBI(tx.getValue()).add(txGasCost);
BigInteger senderBalance = track.getBalance(tx.getSender());
boolean canAfford = isCovers(senderBalance, totalCost);
if (!canAfford){
if (logger.isWarnEnabled())
logger.warn("Not enough cash: Require: {}, Sender cash: {}", totalCost, senderBalance);
// TODO: save reason for failure
return;
}
readyToExecute = true;
}
public void execute2(){
if (!readyToExecute) return;
track.increaseNonce(tx.getSender());
long txGasLimit = toBI(tx.getGasLimit()).longValue();
BigInteger txGasCost = toBI(tx.getGasPrice()).multiply(toBI(txGasLimit));
track.addBalance(tx.getSender(), txGasCost.negate());
//if (logger.isInfoEnabled())
// logger.info("Paying: txGasCost: [{}], gasPrice: [{}], gasLimit: [{}]", txGasCost, toBI(tx.getGasPrice()), txGasLimit);
if (tx.isContractCreation())
create();
else
call();
}
private void call() {
if (!readyToExecute) return;
byte[] targetAddress = tx.getReceiveAddress();
byte[] code = track.getCode(targetAddress);
if (code.length > 0){
ProgramInvoke programInvoke =
programInvokeFactory.createProgramInvoke(tx, currentBlock, cacheTrack, blockStore);
this.vm = new VM();
this.program = new Program(code, programInvoke);
} else {
m_endGas = toBI(tx.getGasLimit()).longValue() - basicTxCost;
}
BigInteger endowment = toBI(tx.getValue());
transfer(cacheTrack, tx.getSender(), targetAddress, endowment);
}
private void create() {
byte[] newContractAddress = tx.getContractAddress();
if (!(tx.getData().length == 0)){
ProgramInvoke programInvoke =
programInvokeFactory.createProgramInvoke(tx, currentBlock, cacheTrack, blockStore);
this.vm = new VM();
this.program = new Program(tx.getData(), programInvoke);
}
BigInteger endowment = toBI(tx.getValue());
transfer(cacheTrack, tx.getSender(), newContractAddress, endowment);
}
public void go(){
if (!readyToExecute) return;
// TODO: transaction call for pre-compiled contracts
if (vm == null) return;
try {
if (CONFIG.playVM())
vm.play(program);
result = program.getResult();
m_endGas = toBI(tx.getGasLimit()).subtract( toBI(result.getGasUsed()) ).longValue();
if (tx.isContractCreation()){
byte[] initCode = EMPTY_BYTE_ARRAY;
if (result.getHReturn().length * GasCost.CREATE_DATA <= m_endGas){
BigInteger returnDataGasValue = BigInteger.valueOf(result.getHReturn().length * GasCost.CREATE_DATA);
m_endGas -= returnDataGasValue.longValue();
initCode = result.getHReturn();
cacheTrack.saveCode(tx.getContractAddress(), initCode);
}
}
if (result.getException() != null)
throw result.getException();
} catch (Throwable e) {
// TODO: catch whatever they will throw on you !!!
// https://github.com/ethereum/cpp-ethereum/blob/develop/libethereum/Executive.cpp#L241
cacheTrack.rollback();
m_endGas = 0;
}
}
public void finalization(){
if (!readyToExecute) return;
cacheTrack.commit();
// Accumulate refunds for suicides
if (result != null)
result.futureRefundGas(
GasCost.SUICIDE_REFUND * (result.getDeleteAccounts() == null ? 0 : result.getDeleteAccounts().size()));
// SSTORE refunds...
// must be done before the miner gets the fees.
long gasLimit = toBI(tx.getGasLimit()).longValue();
if (result != null)
m_endGas += Math.min(result.getFutureRefund(), result.getGasUsed() / 2);
BigInteger endGasBI = toBI(m_endGas);
BigInteger gasPrice = toBI(tx.getGasPrice());
BigInteger futureRefundVal = endGasBI.multiply(gasPrice);
// Refund for gas leftover
track.addBalance(tx.getSender(), futureRefundVal);
logger.info("Pay total refund to sender: [{}], refund val: [{}]", Hex.toHexString(tx.getSender()), futureRefundVal);
// Transfer fees to miner
BigInteger feesEarned = toBI(gasLimit).subtract(endGasBI).multiply(gasPrice);
track.addBalance(coinbase, feesEarned);
logger.info("Pay fees to miner: [{}], feesEarned: [{}]", Hex.toHexString(coinbase), feesEarned);
// Traverse list of suicides
if (result.getDeleteAccounts() != null)
for (DataWord address : result.getDeleteAccounts()) {
track.delete(address.getLast20Bytes());
}
// Keep execution logs todo: that yet
//*cpp* if (m_ext)
// m_logs = m_ext->sub.logs;
}
public void execute() {
@ -90,49 +291,17 @@ public class TransactionExecutor {
// AccountState senderAccount = repository.getAccountState(senderAddress);
logger.info("tx.sender: [{}]", Hex.toHexString(tx.getSender()));
// VALIDATE THE NONCE
BigInteger nonce = track.getNonce(senderAddress);
BigInteger txNonce = new BigInteger(1, tx.getNonce());
if (nonce.compareTo(txNonce) != 0) {
if (stateLogger.isWarnEnabled())
stateLogger.warn("Invalid nonce account.nonce={} tx.nonce={}",
nonce, txNonce);
receipt.setCumulativeGas(0);
this.receipt = receipt;
return;
}
//Insert gas cost protection
BigInteger gasLimit = new BigInteger(1, tx.getGasLimit());
if (gasLimit.compareTo(BigInteger.ZERO) == 0) {
logger.debug("No gas limit set on transaction: hash={}", txHash);
receipt.setCumulativeGas(0);
this.receipt = receipt;
return;
}
BigInteger startGasUsed = new BigInteger( Long.toString( this.currentBlock.getGasUsed() ) );
BigInteger startGasLimit = new BigInteger( Long.toString( this.currentBlock.getGasLimit() ) );
if( startGasUsed.add(gasLimit).compareTo( startGasLimit ) == 1) {
logger.debug("Too much gas used in this block: require={}", startGasLimit.toString());
receipt.setCumulativeGas(0);
this.receipt = receipt;
return;
}
// GET TOTAL ETHER VALUE AVAILABLE FOR TX FEE
BigInteger gasPrice = new BigInteger(1, tx.getGasPrice());
BigInteger gasDebit = new BigInteger(1, tx.getGasLimit()).multiply(gasPrice);
logger.info("Gas price limited to [{} wei]", gasDebit.toString());
BigInteger gasPrice = toBI(tx.getGasPrice());
BigInteger gasDebit = toBI(tx.getGasLimit()).multiply(gasPrice);
logger.info("Gas price limited to [{} units]", gasDebit.toString());
// Debit the actual total gas value from the sender
// the purchased gas will be available for
// the contract in the execution state,
// it can be retrieved using GAS op
BigInteger txValue = new BigInteger(1, tx.getValue());
BigInteger txValue = toBI(tx.getValue());
if (track.getBalance(senderAddress).compareTo(gasDebit.add(txValue)) == -1) {
logger.debug("No gas to start the execution: sender={}",
Hex.toHexString(senderAddress));
@ -149,10 +318,6 @@ public class TransactionExecutor {
if (isContractCreation) {
receiverAddress = tx.getContractAddress();
code = tx.getData(); // init code
// on CREATE the contract is created event if it will rollback
track.addBalance(receiverAddress, BigInteger.ZERO);
} else {
receiverAddress = tx.getReceiveAddress();
code = track.getCode(receiverAddress);
@ -165,24 +330,26 @@ public class TransactionExecutor {
}
// THE SIMPLE VALUE/BALANCE CHANGE
if (track.getBalance(senderAddress).compareTo(txValue) >= 0) {
if (!(isContractCreation || code != EMPTY_BYTE_ARRAY)) // if code invoke transfer will be done latter
// for rollback purposes
if (isCovers(track.getBalance(senderAddress), txValue)) {
track.addBalance(receiverAddress, txValue); // balance will be read again below
track.addBalance(senderAddress, txValue.negate());
transfer(track, senderAddress, receiverAddress, txValue);
/*if (stateLogger.isDebugEnabled())
stateLogger.debug("Update value balance \n "
+ "sender={}, receiver={}, value={}",
Hex.toHexString(senderAddress),
Hex.toHexString(receiverAddress),
new BigInteger(tx.getValue()));
*/
}
/*if (stateLogger.isDebugEnabled())
stateLogger.debug("Update value balance \n "
+ "sender={}, receiver={}, value={}",
Hex.toHexString(senderAddress),
Hex.toHexString(receiverAddress),
new BigInteger(tx.getValue()));
*/
}
// UPDATE THE NONCE
track.increaseNonce(senderAddress);
logger.info("increased nonce to: [{}], addr: [{}]", track.getNonce(senderAddress), Hex.toHexString(senderAddress));
logger.info("increased nonce to: [{}], addr: [{}]",
track.getNonce(senderAddress), Hex.toHexString(senderAddress));
// CHARGE FOR GAS
track.addBalance(senderAddress, gasDebit.negate());
@ -197,6 +364,16 @@ public class TransactionExecutor {
+ "\n sender={} \n gas_debit= {}",
Hex.toHexString(senderAddress), gasDebit);
//Check: Do not execute if transaction has debit amount of 0 and there is code
if (isZero(gasDebit) && tx.getData() != null) {
logger.debug("Transaction gas debits are zero! Cannot execute any code: sender={}",
Hex.toHexString(senderAddress));
receipt.setCumulativeGas(0);
this.receipt = receipt;
return;
}
// CREATE AND/OR EXECUTE CONTRACT
long gasUsed = 0;
if (isContractCreation || code != EMPTY_BYTE_ARRAY) {
@ -205,7 +382,21 @@ public class TransactionExecutor {
Program program = null;
Repository trackTx = track.startTracking();
trackTx.addBalance(receiverAddress, BigInteger.ZERO); // the contract created for anycase but SUICIDE call
// THE SIMPLE VALUE/BALANCE CHANGE
if (isCovers(trackTx.getBalance(senderAddress), txValue)) {
transfer(trackTx, senderAddress, receiverAddress, txValue);
/* if (stateLogger.isDebugEnabled())
stateLogger.debug("Update value balance \n "
+ "sender={}, receiver={}, value={}",
Hex.toHexString(senderAddress),
Hex.toHexString(receiverAddress),
new BigInteger(tx.getValue()));
*/
}
logger.info("Start tracking VM run");
try {
@ -226,11 +417,12 @@ public class TransactionExecutor {
vm.play(program);
program.saveProgramTraceToFile(txHash);
result = program.getResult();
applyProgramResult(result, gasDebit, gasPrice, trackTx,
gasUsed = applyProgramResult(result, gasDebit, gasPrice, trackTx,
senderAddress, receiverAddress, coinbase, isContractCreation);
postExecute(gasUsed);
List<LogInfo> logs = result.getLogInfoList();
receipt.setLogInfoList(logs);
@ -244,9 +436,8 @@ public class TransactionExecutor {
if (CONFIG.vmTrace()) {
String traceAsJson = "{}";
if (program != null) {
ProgramResult result = program.getResult();
ProgramTrace trace = program.getProgramTrace();
trace.setResult(result.getHReturn());
trace.setResult(wrap(result.getHReturn()));
trace.setError(result.getException());
traceAsJson = trace.asJsonString();
}
@ -255,31 +446,39 @@ public class TransactionExecutor {
}
trackTx.commit();
} else {
// REFUND GASDEBIT EXCEPT FOR FEE (500 + 5*TX_NO_ZERO_DATA)
// REFUND GAS_DEBIT EXCEPT FOR FEE (500 + 5*TX_NO_ZERO_DATA)
long dataCost = tx.getData() == null ? 0 :
tx.nonZeroDataBytes() * GasCost.TX_NO_ZERO_DATA +
tx.zeroDataBytes() * GasCost.TX_ZERO_DATA;
gasUsed = GasCost.TRANSACTION + dataCost;
BigInteger refund = gasDebit.subtract(BigInteger.valueOf(gasUsed).multiply(gasPrice));
if (refund.signum() > 0) {
track.addBalance(senderAddress, refund);
track.addBalance(coinbase, refund.negate());
}
BigInteger refund = gasDebit.subtract(toBI(gasUsed).multiply(gasPrice));
if (isPositive(refund))
transfer(track, coinbase, senderAddress, refund);
}
receipt.setCumulativeGas(gasUsed);
this.receipt = receipt;
}
private void postExecute(long gasUsed) {
BigInteger gasPrice = toBI(tx.getGasPrice());
BigInteger feesEarned = toBI(gasUsed).multiply(gasPrice);
transfer(track, tx.getSender(), currentBlock.getCoinbase(), feesEarned);
}
/**
* After any contract code finish the run the certain result should take place,
* according to the given circumstances.
*/
private void applyProgramResult(ProgramResult result, BigInteger gasDebit,
private long applyProgramResult(ProgramResult result, BigInteger gasDebit,
BigInteger gasPrice, Repository repository, byte[] senderAddress,
byte[] contractAddress, byte[] coinbase, boolean initResults) {
long gasUsed = result.getGasUsed();
if (result.getException() != null) {
stateLogger.debug("contract run halted by Exception: contract: [{}], exception: [{}]",
Hex.toHexString(contractAddress),
@ -287,46 +486,48 @@ public class TransactionExecutor {
throw result.getException();
}
BigInteger refund = gasDebit.subtract(toBI(result.getGasUsed()).multiply(gasPrice));
BigInteger refund = gasDebit.subtract(BigInteger.valueOf(
result.getGasUsed()).multiply(gasPrice));
// accumulate refunds for suicides
result.futureRefundGas(
GasCost.SUICIDE_REFUND * (result.getDeleteAccounts() == null ? 0 : result.getDeleteAccounts().size()));
if (refund.signum() > 0) {
if (isPositive(refund)) {
/*if (stateLogger.isDebugEnabled())
stateLogger
.debug("After contract execution the sender address refunded with gas leftover, "
+ "\n sender={} \n contract={} \n gas_refund= {}",
Hex.toHexString(senderAddress),
Hex.toHexString(contractAddress), refund);
*/
*/
// gas refund
repository.addBalance(senderAddress, refund);
repository.addBalance(coinbase, refund.negate());
transfer(repository, coinbase, senderAddress, refund);
}
if (result.getFutureRefund() > 0) {
long futureRefund = Math.min(result.getFutureRefund(), result.getGasUsed() / 2);
BigInteger futureRefundBI = BigInteger.valueOf(futureRefund);
//TODO #POC9 add getGasFree() as method to ProgramResult?
BigInteger gasFree = gasDebit.subtract(toBI(result.getGasUsed()));
long futureRefund = Math.min(result.getFutureRefund(), gasDebit.subtract(gasFree).longValue() / 2);
BigInteger futureRefundBI = toBI(futureRefund);
BigInteger futureRefundVal = futureRefundBI.multiply(gasPrice);
/* if (stateLogger.isDebugEnabled())
/* if (stateLogger.isDebugEnabled())
stateLogger
.debug("After contract execution the sender address refunded with storage save refunds, "
+ "\n sender={} \n contract={} \n gas_refund= {}",
Hex.toHexString(senderAddress),
Hex.toHexString(contractAddress), futureRefundVal);
*/
repository.addBalance(senderAddress, futureRefundVal);
repository.addBalance(coinbase, futureRefundVal.negate());
*/
transfer(repository, coinbase, senderAddress, futureRefundVal);
}
if (initResults) {
// Save the code created by init
byte[] bodyCode = null;
if (result.getHReturn() != null && result.getHReturn().array().length > 0) {
bodyCode = result.getHReturn().array();
if (result.getHReturn() != null && result.getHReturn().length > 0) {
bodyCode = result.getHReturn();
}
if (bodyCode != null) {
@ -336,27 +537,29 @@ public class TransactionExecutor {
Hex.toHexString(contractAddress),
Hex.toHexString(bodyCode));
BigInteger storageCost = gasPrice.multiply(BigInteger.valueOf(bodyCode.length * GasCost
.CREATE_DATA_BYTE));
BigInteger returnDataGasValue = BigInteger.valueOf(bodyCode.length * GasCost.CREATE_DATA);
gasUsed += returnDataGasValue.longValue();
BigInteger storageCost = gasPrice.multiply(returnDataGasValue);
BigInteger balance = repository.getBalance(senderAddress);
// check if can be charged for the contract data save
if (storageCost.compareTo(balance) > 1) {
if (isCovers(balance, storageCost))
transfer(repository, senderAddress, coinbase, storageCost);
else
bodyCode = EMPTY_BYTE_ARRAY;
} else {
repository.addBalance(coinbase, storageCost);
repository.addBalance(senderAddress, storageCost.negate());
}
repository.saveCode(contractAddress, bodyCode);
}
}
// delete the marked to die accounts
if (result.getDeleteAccounts() == null) return;
for (DataWord address : result.getDeleteAccounts()) {
repository.delete(address.getNoLeadZeroesData());
}
if (result.getDeleteAccounts() != null)
for (DataWord address : result.getDeleteAccounts()) {
repository.delete(address.getLast20Bytes());
}
return gasUsed;
}
@ -367,4 +570,9 @@ public class TransactionExecutor {
public ProgramResult getResult() {
return result;
}
public long getGasUsed(){
return toBI(tx.getGasLimit()).longValue() - m_endGas;
}
}

View File

@ -1,10 +1,8 @@
package org.ethereum.crypto;
import com.google.common.base.Throwables;
import org.ethereum.ConcatKDFBytesGenerator;
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
import org.spongycastle.crypto.BufferedBlockCipher;
import org.spongycastle.crypto.InvalidCipherTextException;
import org.spongycastle.crypto.KeyGenerationParameters;
import org.spongycastle.crypto.*;
import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
import org.spongycastle.crypto.digests.SHA256Digest;
import org.spongycastle.crypto.engines.AESFastEngine;
@ -28,7 +26,7 @@ public class ECIESCoder {
public static final int KEY_SIZE = 128;
public static byte[] decrypt(BigInteger privKey, byte[] cipher) throws Throwable {
public static byte[] decrypt(BigInteger privKey, byte[] cipher) throws IOException, InvalidCipherTextException {
byte[] plaintext;
@ -47,7 +45,7 @@ public class ECIESCoder {
return plaintext;
}
public static byte[] decrypt(ECPoint ephem, BigInteger prv, byte[] IV, byte[] cipher) throws Throwable {
public static byte[] decrypt(ECPoint ephem, BigInteger prv, byte[] IV, byte[] cipher) throws InvalidCipherTextException {
AESFastEngine aesFastEngine = new AESFastEngine();
EthereumIESEngine iesEngine = new EthereumIESEngine(
@ -71,7 +69,7 @@ public class ECIESCoder {
}
public static byte[] encrypt(ECPoint toPub, byte[] plaintext) throws InvalidCipherTextException, IOException {
public static byte[] encrypt(ECPoint toPub, byte[] plaintext) {
ECKeyPairGenerator eGen = new ECKeyPairGenerator();
SecureRandom random = new SecureRandom();
@ -95,12 +93,19 @@ public class ECIESCoder {
ECKeyPairGenerator gen = new ECKeyPairGenerator();
gen.init(new ECKeyGenerationParameters(ECKey.CURVE, random));
byte[] cipher = iesEngine.processBlock(plaintext, 0, plaintext.length);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(pub.getEncoded(false));
bos.write(IV);
bos.write(cipher);
return bos.toByteArray();
byte[] cipher;
try {
cipher = iesEngine.processBlock(plaintext, 0, plaintext.length);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(pub.getEncoded(false));
bos.write(IV);
bos.write(cipher);
return bos.toByteArray();
} catch (InvalidCipherTextException e) {
throw Throwables.propagate(e);
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
@ -125,4 +130,8 @@ public class ECIESCoder {
return iesEngine;
}
public static int getOverhead() {
// 256 bit EC public key, IV, 256 bit MAC
return 65 + KEY_SIZE/8 + 32;
}
}

View File

@ -23,16 +23,16 @@ import org.spongycastle.asn1.x9.X9ECParameters;
import org.spongycastle.asn1.x9.X9IntegerConverter;
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
import org.spongycastle.crypto.digests.SHA256Digest;
import org.spongycastle.crypto.engines.AESFastEngine;
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
import org.spongycastle.crypto.params.ECDomainParameters;
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
import org.spongycastle.crypto.params.ECPublicKeyParameters;
import org.spongycastle.crypto.modes.SICBlockCipher;
import org.spongycastle.crypto.params.*;
import org.spongycastle.crypto.signers.ECDSASigner;
import org.spongycastle.crypto.signers.HMacDSAKCalculator;
import org.spongycastle.math.ec.ECAlgorithms;
import org.spongycastle.math.ec.ECCurve;
import org.spongycastle.math.ec.ECPoint;
import org.spongycastle.util.BigIntegers;
import org.spongycastle.util.encoders.Base64;
import org.spongycastle.util.encoders.Hex;
@ -468,6 +468,48 @@ public class ECKey implements Serializable {
return key;
}
/**
* Decrypt cipher by AES in SIC(also know as CTR) mode
* @param cipher -proper cipher
* @return decrypted cipher, equal length to the cipher.
*/
public byte[] decryptAES(byte[] cipher){
if (priv == null)
throw new MissingPrivateKeyException();
AESFastEngine engine = new AESFastEngine();
SICBlockCipher ctrEngine = new SICBlockCipher(engine);
KeyParameter key = new KeyParameter(BigIntegers.asUnsignedByteArray(priv));
ParametersWithIV params = new ParametersWithIV(key, new byte[16]);
ctrEngine.init(false, params);
int i = 0;
byte[] out = new byte[cipher.length];
while(i < cipher.length){
ctrEngine.processBlock(cipher, i, out, i);
i += engine.getBlockSize();
if (cipher.length - i < engine.getBlockSize())
break;
}
// process left bytes
if (cipher.length - i > 0){
byte[] tmpBlock = new byte[16];
System.arraycopy(cipher, i, tmpBlock, 0, cipher.length - i);
ctrEngine.processBlock(tmpBlock, 0, tmpBlock, 0);
System.arraycopy(tmpBlock, 0, out, i, cipher.length - i);
}
return out;
}
/**
* <p>Verifies the given ECDSA signature against the message bytes using the public key bytes.</p>
*
@ -654,6 +696,8 @@ public class ECKey implements Serializable {
return (bits[0] & 0xFF) | ((bits[1] & 0xFF) << 8) | ((bits[2] & 0xFF) << 16) | ((bits[3] & 0xFF) << 24);
}
@SuppressWarnings("serial")
public static class MissingPrivateKeyException extends RuntimeException {
}
@ -661,4 +705,5 @@ public class ECKey implements Serializable {
private static void check(boolean test, String message) {
if (!test) throw new IllegalArgumentException(message);
}
}

View File

@ -25,6 +25,10 @@ public class SHA3Helper {
return sha3(message, new SHA3Digest(DEFAULT_SIZE), true);
}
public static byte[] sha3(byte[] m1, byte[] m2) {
return sha3(m1, m2, new SHA3Digest(DEFAULT_SIZE), true);
}
public static byte[] sha3(byte[] message, int start, int length) {
return sha3(message, start, length, new SHA3Digest(DEFAULT_SIZE), true);
}
@ -70,6 +74,10 @@ public class SHA3Helper {
return doSha3(message, digest, bouncyencoder);
}
private static byte[] sha3(byte[] m1, byte[] m2, SHA3Digest digest, boolean bouncyencoder) {
return doSha3(m1, m2, digest, bouncyencoder);
}
private static byte[] sha3(byte[] message, int start, int length, SHA3Digest digest, boolean bouncyencoder) {
byte[] hash = new byte[digest.getDigestSize()];
@ -91,6 +99,15 @@ public class SHA3Helper {
return hash;
}
private static byte[] doSha3(byte[] m1, byte[] m2, SHA3Digest digest, boolean bouncyencoder) {
byte[] hash = new byte[digest.getDigestSize()];
digest.update(m1, 0, m1.length);
digest.update(m2, 0, m2.length);
digest.doFinal(hash, 0);
return hash;
}
public enum Size {
S224(224),

View File

@ -144,4 +144,4 @@ public class LevelDbDataSource implements KeyValueDataSource {
logger.error("Failed to find the db file on the close: {} ", name);
}
}
}
}

View File

@ -108,4 +108,4 @@ public class RedisConnectionImpl implements RedisConnection {
public KeyValueDataSource createDataSource(String name) {
return new RedisDataSource(name, jedisPool);
}
}
}

View File

@ -50,4 +50,4 @@ public interface BlockStore {
void reset();
TransactionReceipt getTransactionReceiptByHash(byte[] hash);
}
}

View File

@ -8,9 +8,9 @@ import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
//import org.springframework.stereotype.Repository;
//import org.springframework.transaction.annotation.Propagation;
//import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigInteger;
@ -23,11 +23,10 @@ import java.util.List;
*/
public class BlockStoreImpl implements BlockStore {
@Autowired
private SessionFactory sessionFactory;
public BlockStoreImpl() {
public BlockStoreImpl(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
@ -39,7 +38,7 @@ public class BlockStoreImpl implements BlockStore {
}
@Override
//@Transactional(readOnly = true)
@Transactional(readOnly = true)
public Block getBlockByNumber(long blockNumber) {
List result = sessionFactory.getCurrentSession().
@ -53,7 +52,7 @@ public class BlockStoreImpl implements BlockStore {
}
@Override
//@Transactional(readOnly = true)
@Transactional(readOnly = true)
public Block getBlockByHash(byte[] hash) {
List result = sessionFactory.getCurrentSession().
@ -67,7 +66,7 @@ public class BlockStoreImpl implements BlockStore {
}
@Override
//@Transactional(readOnly = true)
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public List<byte[]> getListOfHashesStartFrom(byte[] hash, int qty) {
@ -90,7 +89,7 @@ public class BlockStoreImpl implements BlockStore {
}
@Override
//@Transactional
@Transactional
public void deleteBlocksSince(long number) {
sessionFactory.getCurrentSession().
@ -101,7 +100,7 @@ public class BlockStoreImpl implements BlockStore {
@Override
//@Transactional
@Transactional
public void saveBlock(Block block, List<TransactionReceipt> receipts) {
BlockVO blockVO = new BlockVO(block.getNumber(), block.getHash(),
@ -120,7 +119,7 @@ public class BlockStoreImpl implements BlockStore {
}
@Override
//@Transactional(readOnly = true)
@Transactional(readOnly = true)
public BigInteger getTotalDifficultySince(long number) {
return (BigInteger) sessionFactory.getCurrentSession().
@ -131,7 +130,7 @@ public class BlockStoreImpl implements BlockStore {
@Override
//@Transactional(readOnly = true)
@Transactional(readOnly = true)
public BigInteger getTotalDifficulty() {
return (BigInteger) sessionFactory.getCurrentSession().
@ -140,7 +139,7 @@ public class BlockStoreImpl implements BlockStore {
@Override
//@Transactional(readOnly = true)
@Transactional(readOnly = true)
public Block getBestBlock() {
Long bestNumber = (Long)
@ -155,7 +154,7 @@ public class BlockStoreImpl implements BlockStore {
}
@Override
//@Transactional(readOnly = true)
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public List<Block> getAllBlocks() {
@ -171,7 +170,7 @@ public class BlockStoreImpl implements BlockStore {
}
@Override
//@Transactional
@Transactional
public void reset() {
sessionFactory.getCurrentSession().
createQuery("delete from BlockVO").executeUpdate();

View File

@ -13,11 +13,13 @@ import java.util.Arrays;
public class ByteArrayWrapper implements Comparable<ByteArrayWrapper> {
private final byte[] data;
private int hashCode = 0;
public ByteArrayWrapper(byte[] data) {
if (data == null)
throw new NullPointerException("Data must not be null");
this.data = data;
this.hashCode = Arrays.hashCode(data);
}
public boolean equals(Object other) {
@ -31,7 +33,7 @@ public class ByteArrayWrapper implements Comparable<ByteArrayWrapper> {
@Override
public int hashCode() {
return Arrays.hashCode(data);
return hashCode;
}
@Override

View File

@ -1,5 +1,6 @@
package org.ethereum.db;
import org.ethereum.trie.SecureTrie;
import org.ethereum.trie.Trie;
import org.ethereum.trie.TrieImpl;
import org.ethereum.util.ByteUtil;
@ -33,7 +34,7 @@ public class ContractDetails {
private boolean dirty = false;
private boolean deleted = false;
private Trie storageTrie = new TrieImpl(null);
private Trie storageTrie = new SecureTrie(null);
public ContractDetails() {
}
@ -96,7 +97,7 @@ public class ContractDetails {
public byte[] getStorageHash() {
storageTrie = new TrieImpl(null);
storageTrie = new SecureTrie(null);
// calc the trie for root hash
for (int i = 0; i < storageKeys.size(); ++i) {
storageTrie.update(storageKeys.get(i).getData(), RLP
@ -198,7 +199,7 @@ public class ContractDetails {
public void setStorage(Map<DataWord, DataWord> storage) {
List<DataWord> keys = new ArrayList<>();
keys.addAll(storageKeys);
keys.addAll(storage.keySet());
List<DataWord> values = new ArrayList<>();
for (DataWord key : keys) {

View File

@ -3,11 +3,13 @@ package org.ethereum.db;
import org.ethereum.core.AccountState;
import org.ethereum.core.Block;
import org.ethereum.datasource.KeyValueDataSource;
import org.ethereum.datasource.HashMapDB;
import org.ethereum.facade.Repository;
import org.ethereum.json.EtherObjectMapper;
import org.ethereum.json.JSONHelper;
import org.ethereum.trie.SecureTrie;
import org.ethereum.trie.Trie;
import org.ethereum.trie.TrieImpl;
import org.ethereum.vm.DataWord;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
@ -65,31 +67,23 @@ public class RepositoryImpl implements Repository {
public RepositoryImpl(KeyValueDataSource detailsDS, KeyValueDataSource stateDS) {
logger.debug("test1");
detailsDS.setName(DETAILS_DB);
logger.debug("test2");
detailsDS.init();
logger.debug("test3");
this.detailsDS = detailsDS;
stateDS.setName(STATE_DB);
logger.debug("test4");
stateDS.init();
logger.debug("test5");
this.stateDS = stateDS;
detailsDB = new DatabaseImpl(detailsDS);
logger.debug("test6");
stateDB = new DatabaseImpl(stateDS);
logger.debug("test7");
worldState = new TrieImpl(stateDB.getDb());
logger.debug("test8");
worldState = new SecureTrie(stateDB.getDb());
}
public RepositoryImpl(String detailsDbName, String stateDbName) {
detailsDB = new DatabaseImpl(detailsDbName);
stateDB = new DatabaseImpl(stateDbName);
worldState = new TrieImpl(stateDB.getDb());
worldState = new SecureTrie(stateDB.getDb());
}
@ -102,7 +96,7 @@ public class RepositoryImpl implements Repository {
stateDS.init();
stateDB = new DatabaseImpl(stateDS);
worldState = new TrieImpl(stateDB.getDb());
worldState = new SecureTrie(stateDB.getDb());
}
@Override
@ -156,7 +150,6 @@ public class RepositoryImpl implements Repository {
}
}
}
stateCache.clear();
@ -255,6 +248,10 @@ public class RepositoryImpl implements Repository {
}
}
public String getTrieDump(){
return worldState.getTrieDump();
}
public void dumpTrie(Block block) {
if (!(CONFIG.dumpFull() || CONFIG.dumpBlock() == block.getNumber()))

View File

@ -60,6 +60,7 @@ public class RepositoryTrack implements Repository {
if (accountState == null) {
repository.loadAccount(addr, cacheAccounts, cacheDetails);
accountState = cacheAccounts.get(wrap(addr));
}
return accountState;
@ -71,12 +72,9 @@ public class RepositoryTrack implements Repository {
AccountState accountState = cacheAccounts.get(wrap(addr));
if (accountState != null) return !accountState.isDeleted();
accountState = repository.getAccountState(addr);
return accountState != null && !accountState.isDeleted();
return repository.isExist(addr);
}
@Override
public ContractDetails getContractDetails(byte[] addr) {
@ -235,10 +233,10 @@ public class RepositoryTrack implements Repository {
@Override
public void commit() {
logger.debug("commit changes");
repository.updateBatch(cacheAccounts, cacheDetails);
cacheAccounts.clear();
cacheDetails.clear();
logger.debug("committed changes");
}

View File

@ -16,7 +16,7 @@ public interface Blockchain {
public void add(Block block);
public void tryToConnect(Block block);
public ImportResult tryToConnect(Block block);
public void storeBlock(Block block, List<TransactionReceipt> receipts);

View File

@ -122,4 +122,4 @@ public class CommonConfig {
}
*/
}
}

View File

@ -28,4 +28,4 @@ public class DefaultConfig {
return new InMemoryBlockStore();
}
*/
}
}

View File

@ -6,6 +6,7 @@ import org.ethereum.listener.EthereumListener;
import org.ethereum.manager.AdminInfo;
import org.ethereum.net.client.PeerClient;
import org.ethereum.net.peerdiscovery.PeerInfo;
import org.ethereum.net.rlpx.FrameCodec;
import org.ethereum.net.server.ChannelManager;
import org.springframework.context.ApplicationContext;
@ -64,15 +65,15 @@ public interface Ethereum {
*/
public Set<PeerInfo> getPeers();
public void setContext(ApplicationContext context);
public void setContext(ApplicationContext context);
public void startPeerDiscovery();
public void stopPeerDiscovery();
public void connect(InetAddress addr, int port);
public void connect(InetAddress addr, int port, String remoteId);
public void connect(String ip, int port);
public void connect(String ip, int port, String remoteId);
public Blockchain getBlockchain();
@ -138,4 +139,5 @@ public interface Ethereum {
public Set<Transaction> getPendingTransactions();
}

View File

@ -7,8 +7,8 @@ import org.ethereum.manager.AdminInfo;
import org.ethereum.manager.WorldManager;
import org.ethereum.net.client.PeerClient;
import org.ethereum.net.peerdiscovery.PeerInfo;
import org.ethereum.net.rlpx.FrameCodec;
import org.ethereum.net.server.ChannelManager;
import org.ethereum.net.server.EthereumChannelInitializer;
import org.ethereum.net.server.PeerServer;
import org.ethereum.net.submit.TransactionExecutor;
import org.ethereum.net.submit.TransactionTask;
@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
//import org.springframework.stereotype.Component;
//import javax.annotation.PostConstruct;
import java.math.BigInteger;
import java.net.InetAddress;
import java.util.HashSet;
@ -48,7 +49,7 @@ public class EthereumImpl implements Ethereum {
@Autowired
ChannelManager channelManager;
@Autowired
PeerServer peerServer;
@Autowired
@ -56,24 +57,24 @@ public class EthereumImpl implements Ethereum {
public EthereumImpl() {
System.out.println();
logger.info("EthereumImpl constructor");
//RoboSpring.autowire(ctx);
logger.info("EthereumImpl constructor");
}
//@PostConstruct
public void init() {
worldManager.loadBlockchain();
if (CONFIG.listenPort() > 0) {
Executors.newSingleThreadExecutor().submit(
new Runnable() {
public void run() {
peerServer.start(CONFIG.listenPort());
// peerServer.start(CONFIG.listenPort());
}
}
);
}
}
public void setContext(ApplicationContext ctx) {
public void setContext(ApplicationContext ctx) {
this.ctx = ctx;
}
@ -146,12 +147,12 @@ public class EthereumImpl implements Ethereum {
}
@Override
public void connect(InetAddress addr, int port) {
connect(addr.getHostName(), port);
public void connect(InetAddress addr, int port, String remoteId) {
connect(addr.getHostName(), port, remoteId);
}
@Override
public void connect(String ip, int port) {
public void connect(String ip, int port, String remoteId) {
logger.info("Connecting to: {}:{}", ip, port);
PeerClient peerClient = worldManager.getActivePeer();
@ -159,7 +160,7 @@ public class EthereumImpl implements Ethereum {
peerClient = ctx.getBean(PeerClient.class);
worldManager.setActivePeer(peerClient);
peerClient.connect(ip, port);
peerClient.connect(ip, port, remoteId);
}
@Override
@ -187,6 +188,7 @@ public class EthereumImpl implements Ethereum {
PeerClient peer = worldManager.getActivePeer();
if (peer == null) {
peer = new PeerClient();
worldManager.setActivePeer(peer);
}
@ -250,4 +252,6 @@ public class EthereumImpl implements Ethereum {
public Set<Transaction> getPendingTransactions() {
return getBlockchain().getPendingTransactions();
}
}

View File

@ -31,4 +31,4 @@ public class RemoteConfig {
return new InMemoryBlockStore();
}
*/
}
}

View File

@ -21,22 +21,33 @@ public class Env {
private final byte[] previousHash;
public Env(byte[] currentCoinbase, byte[] currentDifficulty, byte[]
currentGasLimit, byte[] currentNumber, byte[]
currentTimestamp, byte[] previousHash) {
this.currentCoinbase = currentCoinbase;
this.currentDifficulty = currentDifficulty;
this.currentGasLimit = currentGasLimit;
this.currentNumber = currentNumber;
this.currentTimestamp = currentTimestamp;
this.previousHash = previousHash;
}
/*
e.g:
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
*/
e.g:
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
*/
public Env(JSONObject env) {
String coinbase = env.get("currentCoinbase").toString();
String difficulty = env.get("currentDifficulty").toString();
String timestamp = env.get("currentTimestamp").toString();
String number = env.get("currentNumber").toString();
String gasLimit = env.get("currentGasLimit").toString();
String gasLimit = Utils.parseUnidentifiedBase( env.get("currentGasLimit").toString() );
String prevHash = env.get("previousHash").toString();
this.currentCoinbase = Hex.decode(coinbase);

View File

@ -37,19 +37,18 @@ public class JSONReader {
String json = "";
if (!SystemProperties.CONFIG.vmTestLoadLocal())
json = getFromUrl("https://raw.githubusercontent.com/ethereum/tests/" + shacommit + "/" + filename);
if (!json.isEmpty()) json = json.replaceAll("//", "data");
return json.isEmpty() ? getFromLocal(filename) : json;
}
public static String getFromLocal(String filename) {
System.out.println("Loading local file: " + filename);
try {
if (System.getProperty("ETHEREUM_TEST_PATH") == null) {
System.out.println("ETHEREUM_TEST_PATH is not passed as a VM argument, please make sure you pass it " +
"with the correct path");
return "";
File vmTestFile = new File(filename);
if (!vmTestFile.exists()){
System.out.println(" Error: no file: " +filename);
System.exit(1);
}
System.out.println("From: " + System.getProperty("ETHEREUM_TEST_PATH"));
File vmTestFile = new File(System.getProperty("ETHEREUM_TEST_PATH") + filename);
//return new String(Files.readAllBytes(vmTestFile.toPath()));
return new String(FileUtils.readFileToByteArray(vmTestFile));
} catch (IOException e) {

View File

@ -1,123 +1,81 @@
package org.ethereum.jsontestsuite;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.jsontestsuite.model.AccountTck;
import org.ethereum.jsontestsuite.model.EnvTck;
import org.ethereum.jsontestsuite.model.LogTck;
import org.ethereum.jsontestsuite.model.TransactionTck;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;
import org.spongycastle.util.encoders.Hex;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Roman Mandeleil
* @since 15.12.2014
*/
public class StateTestCase {
private String name = "";
private Env env;
private Logs logs;
private byte[] out;
private EnvTck env;
private List<LogTck> logs;
private String out;
private Map<String, AccountTck> pre;
private String postStateRoot;
private Map<String, AccountTck> post;
private TransactionTck transaction;
// "pre": { ... },
private Map<ByteArrayWrapper, AccountState> pre = new HashMap<>();
// "post": { ... },
private Map<ByteArrayWrapper, AccountState> post = new HashMap<>();
private Transaction transaction;
public StateTestCase(String name, JSONObject testCaseJSONObj) throws ParseException {
this(testCaseJSONObj);
this.name = name;
public StateTestCase() {
}
public StateTestCase(JSONObject testCaseJSONObj) throws ParseException {
try {
JSONObject envJSON = (JSONObject) testCaseJSONObj.get("env");
JSONArray logsJSON = (JSONArray) testCaseJSONObj.get("logs");
String outStr = testCaseJSONObj.get("out").toString();
JSONObject txJSON = (JSONObject) testCaseJSONObj.get("transaction");
JSONObject preJSON = (JSONObject) testCaseJSONObj.get("pre");
JSONObject postJSON = (JSONObject) testCaseJSONObj.get("post");
this.env = new Env(envJSON);
this.logs = new Logs(logsJSON);
this.out = Utils.parseData(outStr);
this.transaction = new Transaction(txJSON);
for (Object key : preJSON.keySet()) {
byte[] keyBytes = Hex.decode(key.toString());
AccountState accountState =
new AccountState(keyBytes, (JSONObject) preJSON.get(key));
pre.put(new ByteArrayWrapper(keyBytes), accountState);
}
for (Object key : postJSON.keySet()) {
byte[] keyBytes = Hex.decode(key.toString());
AccountState accountState =
new AccountState(keyBytes, (JSONObject) postJSON.get(key));
post.put(new ByteArrayWrapper(keyBytes), accountState);
}
} catch (Throwable e) {
throw new ParseException(0, e);
}
}
public String getName() {
return name;
}
public Env getEnv() {
public EnvTck getEnv() {
return env;
}
public Logs getLogs() {
public void setEnv(EnvTck env) {
this.env = env;
}
public List<LogTck> getLogs() {
return logs;
}
public byte[] getOut() {
public void setLogs(List<LogTck> logs) {
this.logs = logs;
}
public String getOut() {
return out;
}
public Map<ByteArrayWrapper, AccountState> getPre() {
public void setOut(String out) {
this.out = out;
}
public Map<String, AccountTck> getPre() {
return pre;
}
public Map<ByteArrayWrapper, AccountState> getPost() {
public void setPre(Map<String, AccountTck> pre) {
this.pre = pre;
}
public String getPostStateRoot() {
return postStateRoot;
}
public void setPostStateRoot(String postStateRoot) {
this.postStateRoot = postStateRoot;
}
public Map<String, AccountTck> getPost() {
return post;
}
public Transaction getTransaction() {
public void setPost(Map<String, AccountTck> post) {
this.post = post;
}
public TransactionTck getTransaction() {
return transaction;
}
@Override
public String toString() {
return "StateTestCase{" +
"name='" + name + '\'' +
", env=" + env +
", logs=" + logs +
", out=" + Arrays.toString(out) +
", pre=" + pre +
", post=" + post +
", transaction=" + transaction +
'}';
public void setTransaction(TransactionTck transaction) {
this.transaction = transaction;
}
}

View File

@ -1,47 +1,37 @@
package org.ethereum.jsontestsuite;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.JavaType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author Roman Mandeleil
* @since 10.07.2014
*/
public class StateTestSuite {
private Logger logger = LoggerFactory.getLogger("TCK-Test");
Map<String, StateTestCase> testCases = new HashMap<>();
public StateTestSuite(JSONObject testCaseJSONObj) throws ParseException {
public StateTestSuite(String json) throws IOException {
for (Object key : testCaseJSONObj.keySet()) {
ObjectMapper mapper = new ObjectMapper();
JavaType type = mapper.getTypeFactory().
constructMapType(HashMap.class, String.class, StateTestCase.class);
Object testCaseJSON = testCaseJSONObj.get(key);
StateTestCase testCase = new StateTestCase(key.toString(), (JSONObject) testCaseJSON);
testCases.put(key.toString(), testCase);
}
testCases = new ObjectMapper().readValue(json, type);
}
public StateTestCase getTestCase(String name) {
StateTestCase testCase = testCases.get(name);
if (testCase == null) throw new NullPointerException("Test cases doesn't exist: " + name);
return testCase;
public Map<String, StateTestCase> getTestCases() {
return testCases;
}
public Collection<StateTestCase> getAllTests() {
return testCases.values();
@Override
public String toString() {
return "StateTestSuite{" +
"testCases=" + testCases +
'}';
}
}

View File

@ -112,8 +112,11 @@ public class TestCase {
this.callCreateList.add(cc);
}
this.env = new Env(envJSON);
this.exec = new Exec(execJSON);
if (testCaseJSONObj.containsKey("env"))
this.env = new Env(envJSON);
if (testCaseJSONObj.containsKey("exec"))
this.exec = new Exec(execJSON);
} catch (Throwable e) {
throw new ParseException(0, e);

View File

@ -21,7 +21,7 @@ public class TestProgramInvokeFactory implements ProgramInvokeFactory {
private final Env env;
TestProgramInvokeFactory(Env env) {
public TestProgramInvokeFactory(Env env) {
this.env = env;
}

View File

@ -1,37 +1,29 @@
package org.ethereum.jsontestsuite;
import org.ethereum.core.BlockchainImpl;
import org.ethereum.core.Block;
import org.ethereum.core.TransactionExecutor;
import org.ethereum.core.BlockchainImpl;
import org.ethereum.core.TransactionReceipt;
import org.ethereum.core.Wallet;
import org.ethereum.db.*;
import org.ethereum.facade.Repository;
import org.ethereum.jsontestsuite.builder.BlockBuilder;
import org.ethereum.jsontestsuite.builder.RepositoryBuilder;
import org.ethereum.jsontestsuite.model.BlockTck;
import org.ethereum.listener.CompositeEthereumListener;
import org.ethereum.listener.EthereumListener;
import org.ethereum.manager.AdminInfo;
import org.ethereum.util.ByteUtil;
import org.ethereum.vm.DataWord;
import org.ethereum.vm.LogInfo;
import org.ethereum.vm.Program;
import org.ethereum.vm.ProgramInvoke;
import org.ethereum.vm.ProgramInvokeFactory;
import org.ethereum.vm.ProgramInvokeImpl;
import org.ethereum.vm.VM;
import org.ethereum.vm.*;
import org.ethereum.vmtrace.ProgramTrace;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.ethereum.util.ByteUtil.*;
import static org.ethereum.jsontestsuite.Utils.parseData;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
/**
* @author Roman Mandeleil
@ -41,6 +33,8 @@ public class TestRunner {
private Logger logger = LoggerFactory.getLogger("TCK-Test");
private ProgramTrace trace = null;
private boolean setNewStateRoot;
private String bestStateRoot;
public List<String> runTestSuite(TestSuite testSuite) {
@ -59,106 +53,93 @@ public class TestRunner {
return resultCollector;
}
public List<String> runTestCase(StateTestCase testCase) {
List<String> results = new ArrayList<>();
logger.info("\n***");
logger.info(" Running test case: [" + testCase.getName() + "]");
logger.info("***\n");
logger.info("--------- PRE ---------");
RepositoryImpl repository = loadRepository(new RepositoryDummy(), testCase.getPre());
public List<String> runTestCase(BlockTestCase testCase) {
logger.info("loaded repository");
/* 1 */ // Create genesis + init pre state
Block genesis = BlockBuilder.build(testCase.getGenesisBlockHeader(), null, null);
Repository repository = RepositoryBuilder.build(testCase.getPre());
org.ethereum.core.Transaction tx = createTransaction(testCase.getTransaction());
logger.info("transaction: {}", tx.toString());
BlockStore blockStore = new InMemoryBlockStore();
blockStore.saveBlock(genesis, new ArrayList<TransactionReceipt>());
byte[] secretKey = testCase.getTransaction().secretKey;
logger.info("sign tx with: {}", Hex.toHexString(secretKey));
tx.sign(secretKey);
Wallet wallet = new Wallet();
AdminInfo adminInfo = new AdminInfo();
EthereumListener listener = new CompositeEthereumListener();
ProgramInvokeFactoryImpl programInvokeFactory = new ProgramInvokeFactoryImpl();
BlockchainImpl blockchain = new BlockchainImpl(new HashSet<org.ethereum.core.Transaction>());
blockchain.setRepository(repository);
BlockchainImpl blockchain = new BlockchainImpl(blockStore, repository, wallet, adminInfo, listener);
byte[] coinbase = testCase.getEnv().getCurrentCoinbase();
ProgramInvokeFactory invokeFactory = new TestProgramInvokeFactory(testCase.getEnv());
blockchain.setBestBlock(genesis);
blockchain.setTotalDifficulty(BigInteger.ZERO);
blockchain.setProgramInvokeFactory(programInvokeFactory);
programInvokeFactory.setBlockchain(blockchain);
Block block = new Block(
ByteUtil.EMPTY_BYTE_ARRAY,
ByteUtil.EMPTY_BYTE_ARRAY,
coinbase,
ByteUtil.EMPTY_BYTE_ARRAY,
testCase.getEnv().getCurrentDifficulty(),
new BigInteger(1, testCase.getEnv().getCurrentNumber()).longValue(),
new BigInteger(1, testCase.getEnv().getCurrentGasLimit()).longValue(),
0L,
new BigInteger(1, testCase.getEnv().getCurrentTimestamp()).longValue(),
ByteUtil.ZERO_BYTE_ARRAY,
ByteUtil.ZERO_BYTE_ARRAY,
null, null);
blockchain.setBestBlock(block);
blockchain.setProgramInvokeFactory(invokeFactory);
blockchain.startTracking();
// todo: validate root of the genesis *!!!*
Repository track = repository.startTracking();
TransactionExecutor executor =
new TransactionExecutor(tx, coinbase, track, new BlockStoreDummy(),
invokeFactory, blockchain.getBestBlock());
executor.execute();
track.commit();
logger.info("compare results");
bestStateRoot = Hex.toHexString(genesis.getStateRoot());
/* 2 */ // Create block traffic list
List<Block> blockTraffic = new ArrayList<>();
for (BlockTck blockTck : testCase.getBlocks()) {
Block block = BlockBuilder.build(blockTck.getBlockHeader(),
blockTck.getTransactions(),
blockTck.getUncleHeaders());
List<LogInfo> logs = null;
if (executor.getResult() != null)
logs = executor.getResult().getLogInfoList();
setNewStateRoot = !((blockTck.getTransactions() == null)
&& (blockTck.getUncleHeaders() == null)
&& (blockTck.getBlockHeader() == null));
List<String> logResults = testCase.getLogs().compareToReal(logs);
results.addAll(logResults);
//DEBUG System.out.println(" --> " + setNewStateRoot);
Block tBlock = null;
try {
byte[] rlp = parseData(blockTck.getRlp());
tBlock = new Block(rlp);
Set<ByteArrayWrapper> fullAddressSet = repository.getFullAddressSet();
int repoSize = 0;
for (ByteArrayWrapper addrWrapped : fullAddressSet) {
// ArrayList<String> outputSummary =
// BlockHeaderValidator.valid(tBlock.getHeader(), block.getHeader());
byte[] addr = addrWrapped.getData();
// if (!outputSummary.isEmpty()){
// for (String output : outputSummary)
// logger.error("%s", output);
//
// System.exit(-1);
// }
if(setNewStateRoot)
bestStateRoot = Hex.toHexString(tBlock.getStateRoot());
org.ethereum.core.AccountState accountState = repository.getAccountState(addr);
ContractDetails contractDetails = repository.getContractDetails(addr);
/*
logger.info("{} \n{} \n{}", Hex.toHexString(addr),
accountState.toString(), contractDetails.toString());
*/
logger.info("");
AccountState expectedAccountState = testCase.getPost().get(wrap(addr));
if (expectedAccountState == null) {
String formattedString = String.format("Unexpected account state: address: %s", Hex.toHexString(addr));
results.add(formattedString);
continue;
blockTraffic.add(tBlock);
} catch (Exception e) {
System.out.println("*** Exception");
}
List<String> result = expectedAccountState.compareToReal(accountState, contractDetails);
results.addAll(result);
++repoSize;
}
int postRepoSize = testCase.getPost().size();
if (postRepoSize > repoSize) {
results.add("ERROR: Expected 'Post' repository contains more accounts than executed repository ");
logger.info("Full address set: " + fullAddressSet);
/* 3 */ // Inject blocks to the blockchain execution
for (Block block : blockTraffic) {
//DEBUG System.out.println(" Examine block: "); System.out.println(block.toString());
blockchain.tryToConnect(block);
}
//Check state root matches last valid block
List<String> results = new ArrayList<>();
String currRoot = Hex.toHexString(repository.getRoot());
if (!bestStateRoot.equals(currRoot)){
String formattedString = String.format("Root hash doesn't match best: expected: %s current: %s",
bestStateRoot, currRoot);
results.add(formattedString);
}
Repository postRepository = RepositoryBuilder.build(testCase.getPostState());
//Uncomment this if you want POST debugging checks enabled
//results.addAll(RepositoryValidator.rootValid(repository, postRepository));
return results;
}
public List<String> runTestCase(TestCase testCase) {
logger.info("\n***");
@ -168,7 +149,7 @@ public class TestRunner {
logger.info("--------- PRE ---------");
RepositoryImpl repository = loadRepository(new RepositoryVMTestDummy(), testCase.getPre());
RepositoryImpl repository = loadRepository(new RepositoryVMTestDummy(), testCase.getPre());
try {
@ -484,7 +465,7 @@ public class TestRunner {
byte[] expectedHReturn = testCase.getOut();
byte[] actualHReturn = EMPTY_BYTE_ARRAY;
if (program.getResult().getHReturn() != null) {
actualHReturn = program.getResult().getHReturn().array();
actualHReturn = program.getResult().getHReturn();
}
if (!Arrays.equals(expectedHReturn, actualHReturn)) {

View File

@ -33,8 +33,8 @@ public class Transaction {
public Transaction(JSONObject callCreateJSON) {
String dataStr = callCreateJSON.get("data").toString();
String gasLimitStr = callCreateJSON.get("gasLimit").toString();
String gasPriceStr = callCreateJSON.get("gasPrice").toString();
String gasLimitStr = Utils.parseUnidentifiedBase(callCreateJSON.get("gasLimit").toString());
String gasPriceStr = Utils.parseUnidentifiedBase(callCreateJSON.get("gasPrice").toString());
String nonceStr = callCreateJSON.get("nonce").toString();
String secretKeyStr = callCreateJSON.get("secretKey").toString();
String toStr = callCreateJSON.get("to").toString();

View File

@ -4,20 +4,54 @@ import org.ethereum.util.ByteUtil;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
/**
* @author Roman Mandeleil
* @since 15.12.2014
*/
public class Utils {
public static byte[] parseVarData(String data){
if (data == null || data.equals("")) return EMPTY_BYTE_ARRAY;
if (data.startsWith("0x")) {
data = data.substring(2);
if (data.equals("")) return EMPTY_BYTE_ARRAY;
return Hex.decode(data);
}
return parseNumericData(data);
}
public static byte[] parseData(String data) {
if (data == null) return ByteUtil.EMPTY_BYTE_ARRAY;
if (data == null) return EMPTY_BYTE_ARRAY;
if (data.startsWith("0x")) data = data.substring(2);
return Hex.decode(data);
}
public static byte[] parseNumericData(String data){
if (data == null || data.equals("")) return EMPTY_BYTE_ARRAY;
byte[] dataB = new BigInteger(data, 10).toByteArray();
return ByteUtil.stripLeadingZeroes(dataB);
}
public static long parseLong(String data) {
return data.equals("") ? 0 : Long.parseLong(data);
}
public static byte parseByte(String data) {
return data.equals("") ? 0 : Byte.parseByte(data);
}
public static String parseUnidentifiedBase(String number) {
if (number.startsWith("0x"))
number = new BigInteger(number.substring(2), 16).toString(10);
return number;
}
}

View File

@ -2,6 +2,7 @@ package org.ethereum.manager;
//import org.springframework.stereotype.Component;
//import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@ -19,6 +20,7 @@ public class AdminInfo {
private List<Long> blockExecTime = new LinkedList<>();
// @PostConstruct
public void init() {
startupTimeStamp = System.currentTimeMillis();
}

View File

@ -3,6 +3,7 @@ package org.ethereum.manager;
import org.ethereum.core.*;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.BlockStore;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.facade.Blockchain;
import org.ethereum.facade.Repository;
import org.ethereum.listener.EthereumListener;
@ -27,6 +28,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
//import javax.annotation.PostConstruct;
//import javax.annotation.PreDestroy;
import static org.ethereum.config.SystemProperties.CONFIG;
/**
@ -49,6 +53,7 @@ public class WorldManager {
@Autowired
private Wallet wallet;
// @Autowired
private PeerClient activePeer;
@Autowired
@ -67,10 +72,11 @@ public class WorldManager {
@Autowired
private EthereumListener listener;
public WorldManager() {
public WorldManager() {
logger.info("World manager instantiated");
}
// @PostConstruct
public void init() {
byte[] cowAddr = HashUtil.sha3("cow".getBytes());
wallet.importKey(cowAddr);
@ -142,15 +148,16 @@ public class WorldManager {
if (bestBlock == null) {
logger.info("DB is empty - adding Genesis");
for (PremineRaw raw : Genesis.getPremine()) {
repository.createAccount(raw.getAddr());
repository.addBalance(raw.getAddr(), raw.getValue().multiply(raw.getDenomination().value()));
Genesis genesis = (Genesis)Genesis.getInstance();
for (ByteArrayWrapper key : genesis.getPremine().keySet()) {
repository.createAccount(key.getData());
repository.addBalance(key.getData(), genesis.getPremine().get(key).getBalance());
}
blockStore.saveBlock(Genesis.getInstance(), new ArrayList<TransactionReceipt>());
blockchain.setBestBlock(Genesis.getInstance());
blockchain.setTotalDifficulty(BigInteger.ZERO);
blockchain.setTotalDifficulty(Genesis.getInstance().getCumulativeDifficulty());
listener.onBlock(Genesis.getInstance());
repository.dumpState(Genesis.getInstance(), 0, 0, null);
@ -199,6 +206,7 @@ public class WorldManager {
}
// @PreDestroy
public void close() {
stopPeerDiscovery();
repository.close();

View File

@ -1,30 +1,24 @@
package org.ethereum.net;
import org.ethereum.core.Block;
import org.ethereum.core.ImportResult;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.facade.Blockchain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.stereotype.Component;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Deque;
import java.util.List;
import java.util.ListIterator;
import java.util.Queue;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentLinkedQueue;
import static java.lang.Thread.sleep;
import static org.ethereum.config.SystemProperties.CONFIG;
import static org.ethereum.core.ImportResult.NO_PARENT;
import static org.ethereum.core.ImportResult.SUCCESS;
/**
* The processing queue for blocks to be validated and added to the blockchain.
@ -48,7 +42,7 @@ public class BlockQueue {
/**
* Queue with blocks to be validated and added to the blockchain
*/
private Queue<Block> blockReceivedQueue = new ConcurrentLinkedQueue<>();
private PriorityBlockingQueue<Block> blockReceivedQueue = new PriorityBlockingQueue<>(1000, new BlockByNumberComparator());
/**
* Highest known total difficulty, representing the heaviest chain on the network
@ -66,30 +60,47 @@ public class BlockQueue {
Blockchain blockchain;
public BlockQueue() {
timer.scheduleAtFixedRate(new TimerTask() {
Runnable queueProducer = new Runnable(){
@Override
public void run() {
nudgeQueue();
produceQueue();
}
}, 10, 10);
};
Thread t=new Thread (queueProducer);
t.start();
}
/**
* Processing the queue adding blocks to the chain.
*/
private void nudgeQueue() {
try {
if (blockReceivedQueue.isEmpty())
return;
private void produceQueue() {
logger.info("BlockQueue size: {}", blockReceivedQueue.size());
while (!blockReceivedQueue.isEmpty()) {
Block block = blockReceivedQueue.poll();
while (1==1){
logger.info("Processing block index: {}", block.getNumber());
blockchain.tryToConnect(block);
try {
Block block = blockReceivedQueue.take();
logger.info("BlockQueue size: {}", blockReceivedQueue.size());
ImportResult importResult = blockchain.tryToConnect(block);
// In case we don't have a parent on the chain
// return the try and wait for more blocks to come.
if (importResult == NO_PARENT){
logger.info("No parent on the chain for block.number: [{}]", block.getNumber());
blockReceivedQueue.add(block);
sleep(2000);
}
if (importResult == SUCCESS)
logger.info("Success importing: block number: {}", block.getNumber());
} catch (Throwable e) {
logger.error("Error: {} ", e);
}
} catch (Throwable e) {
logger.error("Error: {} ", e.getMessage());
}
}
@ -105,7 +116,9 @@ public class BlockQueue {
*/
public void addBlocks(List<Block> blockList) {
blockReceivedQueue.addAll(blockList);
for (Block block : blockList)
blockReceivedQueue.put(block);
lastBlock = blockList.get(blockList.size() - 1);
logger.info("Blocks waiting to be proceed: queue.size: [{}] lastBlock.number: [{}]",
@ -168,15 +181,27 @@ public class BlockQueue {
blockHashQueue.addLast(hash);
if (logger.isTraceEnabled()) {
logger.trace("Adding hash to a hashQueue: [{}]", Hex.toHexString(hash));
logger.trace("Adding hash to a hashQueue: [{}], hash queue size: {} ",
Hex.toHexString(hash).substring(0, 6),
blockHashQueue.size());
}
}
public void returnHashes(List<byte[]> hashes) {
public void returnHashes(List<ByteArrayWrapper> hashes) {
if (hashes.isEmpty()) return;
logger.info("Hashes remained uncovered: hashes.size: [{}]", hashes.size());
ListIterator iterator = hashes.listIterator(hashes.size());
while (iterator.hasPrevious())
blockHashQueue.addLast((byte[]) iterator.previous());
while (iterator.hasPrevious()) {
byte[] hash = ((ByteArrayWrapper) iterator.previous()).getData();
if (logger.isDebugEnabled())
logger.debug("Return hash: [{}]", Hex.toHexString(hash));
blockHashQueue.addLast(hash);
}
}
public void addNewBlockHash(byte[] hash) {
@ -203,7 +228,7 @@ public class BlockQueue {
logger.info("Block hashes list size: [{}]", blockHashQueue.size());
}
private class BlockByIndexComparator implements Comparator<Block> {
private class BlockByNumberComparator implements Comparator<Block> {
@Override
public int compare(Block o1, Block o2) {
@ -254,4 +279,6 @@ public class BlockQueue {
timer.cancel();
timer.purge();
}
}

View File

@ -16,6 +16,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
//import org.springframework.context.annotation.Scope;
//import org.springframework.stereotype.Component;
@ -34,25 +35,26 @@ public class PeerClient {
private boolean peerDiscoveryMode = false;
@Autowired
private ApplicationContext ctx;
@Autowired
WorldManager worldManager;
@Autowired
public ChannelManager channelManager;
@Autowired
public EthereumChannelInitializer ethereumChannelInitializer;
public PeerClient() {
public PeerClient() {
logger.info("Peer client instantiated");
}
public void connect(String host, int port) {
public void connect(String host, int port, String remoteId) {
EventLoopGroup workerGroup = new NioEventLoopGroup();
worldManager.getListener().trace("Connecting to: " + host + ":" + port);
EthereumChannelInitializer ethereumChannelInitializer = (EthereumChannelInitializer)ctx.getBean("ethereumChannelInitializer", EthereumChannelInitializer.class);
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);

View File

@ -32,7 +32,7 @@ public class BlocksMessage extends EthMessage {
RLPList paramsList = (RLPList) RLP.decode2(encoded).get(0);
blocks = new ArrayList<>();
for (int i = 1; i < paramsList.size(); ++i) {
for (int i = 0; i < paramsList.size(); ++i) {
RLPList rlpData = ((RLPList) paramsList.get(i));
Block blockData = new Block(rlpData.getRLPData());
blocks.add(blockData);

View File

@ -3,6 +3,7 @@ package org.ethereum.net.eth;
import org.ethereum.core.Block;
import org.ethereum.core.Genesis;
import org.ethereum.core.Transaction;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.facade.Blockchain;
import org.ethereum.manager.WorldManager;
import org.ethereum.net.BlockQueue;
@ -24,17 +25,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import java.util.*;
import static org.ethereum.config.SystemProperties.CONFIG;
import static org.ethereum.net.message.StaticMessages.GET_TRANSACTIONS_MESSAGE;
import static org.ethereum.util.ByteUtil.wrap;
/**
* Process the messages between peers with 'eth' capability on the network.
@ -54,7 +49,8 @@ import static org.ethereum.net.message.StaticMessages.GET_TRANSACTIONS_MESSAGE;
//@Scope("prototype")
public class EthHandler extends SimpleChannelInboundHandler<EthMessage> {
public final static byte VERSION = 54;
public final static byte VERSION = 60;
public final static byte NETWORK_ID = 0x0;
private final static Logger logger = LoggerFactory.getLogger("net");
@ -66,7 +62,6 @@ public class EthHandler extends SimpleChannelInboundHandler<EthMessage> {
private MessageQueue msgQueue = null;
private SyncStatus syncStatus = SyncStatus.INIT;
private boolean active = false;
private StatusMessage handshakeStatusMessage = null;
private BigInteger totalDifficulty = Genesis.getInstance().getCumulativeDifficulty();
@ -81,7 +76,7 @@ public class EthHandler extends SimpleChannelInboundHandler<EthMessage> {
@Autowired
private WorldManager worldManager;
private List<byte[]> sentHashes;
private List<ByteArrayWrapper> sentHashes;
private Block lastBlock = Genesis.getInstance();
public EthHandler() {
@ -96,8 +91,6 @@ public class EthHandler extends SimpleChannelInboundHandler<EthMessage> {
public void activate() {
logger.info("ETH protocol activated");
worldManager.getListener().trace("ETH protocol activated");
active = true;
sendStatus();
}
@ -106,15 +99,10 @@ public class EthHandler extends SimpleChannelInboundHandler<EthMessage> {
}
public boolean isActive() {
return active;
}
@Override
public void channelRead0(final ChannelHandlerContext ctx, EthMessage msg) throws InterruptedException {
if (!isActive()) return;
if (EthMessageCodes.inRange(msg.getCommand().asByte()))
logger.info("EthHandler invoke: [{}]", msg.getCommand());
@ -186,7 +174,6 @@ public class EthHandler extends SimpleChannelInboundHandler<EthMessage> {
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
logger.debug("handlerRemoved: kill timers in EthHandler");
active = false;
this.killTimers();
}
@ -255,6 +242,7 @@ public class EthHandler extends SimpleChannelInboundHandler<EthMessage> {
// or peer doesn't have the best hash anymore
if (receivedHashes.isEmpty()
|| !this.peerId.equals(hashRetrievalLock)) {
chainQueue.addHash( blockchain.getBestBlockHash() );
sendGetBlocks(); // start getting blocks from hash queue
return;
}
@ -267,6 +255,7 @@ public class EthHandler extends SimpleChannelInboundHandler<EthMessage> {
chainQueue.addHash(foundHash); // store unknown hashes in queue until known hash is found
} else {
chainQueue.addHash(blockchain.getBestBlockHash());
logger.trace("Catch up with the hashes until: {[]}", foundHash);
// if known hash is found, ignore the rest
sendGetBlocks(); // start getting blocks from hash queue
@ -288,15 +277,14 @@ public class EthHandler extends SimpleChannelInboundHandler<EthMessage> {
lastBlock = blockList.get(blockList.size() - 1);
}
// check if you got less blocks than you asked
if (blockList.size() < sentHashes.size()) {
for (int i = 0; i < blockList.size(); ++i)
sentHashes.remove(0);
logger.info("Got less blocks: [{}], return [{}] hashes to the queue",
blockList.size(), sentHashes.size());
blockchain.getQueue().returnHashes(sentHashes);
// check if you got less blocks than you asked,
// and keep the missing to ask again
sentHashes.remove(wrap(Genesis.getInstance().getHash()));
for (Block block : blockList){
ByteArrayWrapper hash = wrap(block.getHash());
sentHashes.remove(hash);
}
blockchain.getQueue().returnHashes(sentHashes);
if (blockchain.getQueue().isHashesEmpty()) {
logger.info(" The peer sync process fully complete");
@ -410,13 +398,20 @@ public class EthHandler extends SimpleChannelInboundHandler<EthMessage> {
// save them locally in case the remote peer
// will return less blocks than requested.
List<byte[]> hashes = queue.getHashes();
this.sentHashes = hashes;
this.sentHashes = new ArrayList<>();
for (byte[] hash : hashes)
this.sentHashes.add(wrap(hash));
if (hashes.isEmpty()) {
return;
}
Collections.shuffle(hashes);
GetBlocksMessage msg = new GetBlocksMessage(hashes);
if (logger.isDebugEnabled())
logger.debug(msg.getDetailedString());
msgQueue.sendMessage(msg);
}

View File

@ -38,17 +38,16 @@ public class GetBlockHashesMessage extends EthMessage {
}
private void encode() {
byte[] command = RLP.encodeByte(GET_BLOCK_HASHES.asByte());
byte[] hash = RLP.encodeElement(this.bestHash);
byte[] maxBlocks = RLP.encodeInt(this.maxBlocks);
this.encoded = RLP.encodeList(command, hash, maxBlocks);
this.encoded = RLP.encodeList(hash, maxBlocks);
}
private void parse() {
RLPList paramsList = (RLPList) RLP.decode2(encoded).get(0);
this.bestHash = paramsList.get(1).getRLPData();
byte[] maxBlocksBytes = paramsList.get(2).getRLPData();
this.bestHash = paramsList.get(0).getRLPData();
byte[] maxBlocksBytes = paramsList.get(1).getRLPData();
this.maxBlocks = ByteUtil.byteArrayToInt(maxBlocksBytes);
parsed = true;

View File

@ -3,6 +3,7 @@ package org.ethereum.net.eth;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPList;
import org.ethereum.util.Utils;
import org.spongycastle.util.encoders.Hex;
import java.util.ArrayList;
import java.util.List;
@ -71,6 +72,18 @@ public class GetBlocksMessage extends EthMessage {
return EthMessageCodes.GET_BLOCKS;
}
public String getDetailedString(){
StringBuilder stringBuilder = new StringBuilder();
for (byte[] hash : getBlockHashes()){
stringBuilder.append(Hex.toHexString(hash)).append("\n");
}
return "[" + this.getCommand().name() + "\n" + stringBuilder.toString() + "]";
}
public String toString() {
final String hashListShort = Utils.getHashListShort(getBlockHashes());
return "[" + this.getCommand().name() + hashListShort + "] (" + blockHashes.size() + ")";

View File

@ -27,20 +27,19 @@ public class NewBlockMessage extends EthMessage {
}
private void encode() {
byte[] command = RLP.encodeByte(this.getCommand().asByte());
byte[] block = this.block.getEncoded();
byte[] diff = RLP.encodeElement(this.difficulty);
this.encoded = RLP.encodeList(command, block, diff);
this.encoded = RLP.encodeList(block, diff);
parsed = true;
}
private void parse() {
RLPList paramsList = (RLPList) RLP.decode2(encoded).get(0);
RLPList blockRLP = ((RLPList) paramsList.get(1));
RLPList blockRLP = ((RLPList) paramsList.get(0));
block = new Block(blockRLP.getRLPData());
difficulty = paramsList.get(2).getRLPData();
difficulty = paramsList.get(1).getRLPData();
parsed = true;
}

View File

@ -48,25 +48,25 @@ public class StatusMessage extends EthMessage {
private void parse() {
RLPList paramsList = (RLPList) RLP.decode2(encoded).get(0);
this.protocolVersion = paramsList.get(1).getRLPData()[0];
byte[] networkIdBytes = paramsList.get(2).getRLPData();
this.protocolVersion = paramsList.get(0).getRLPData()[0];
byte[] networkIdBytes = paramsList.get(1).getRLPData();
this.networkId = networkIdBytes == null ? 0 : networkIdBytes[0];
this.totalDifficulty = paramsList.get(3).getRLPData();
this.bestHash = paramsList.get(4).getRLPData();
this.genesisHash = paramsList.get(5).getRLPData();
this.totalDifficulty = paramsList.get(2).getRLPData();
this.bestHash = paramsList.get(3).getRLPData();
this.genesisHash = paramsList.get(4).getRLPData();
parsed = true;
}
private void encode() {
byte[] command = RLP.encodeByte(STATUS.asByte());
byte[] protocolVersion = RLP.encodeByte(this.protocolVersion);
byte[] networkId = RLP.encodeByte(this.networkId);
byte[] totalDifficulty = RLP.encodeElement(this.totalDifficulty);
byte[] bestHash = RLP.encodeElement(this.bestHash);
byte[] genesisHash = RLP.encodeElement(this.genesisHash);
this.encoded = RLP.encodeList(command, protocolVersion, networkId,
this.encoded = RLP.encodeList( protocolVersion, networkId,
totalDifficulty, bestHash, genesisHash);
}

View File

@ -35,4 +35,11 @@ public abstract class Message {
* @return A string with all attributes of the message
*/
public abstract String toString();
public abstract Enum getCommand();
public byte getCode() {
return code;
}
}

View File

@ -21,8 +21,7 @@ import org.ethereum.util.RLP;
*/
public class MessageFactory {
public static Message createMessage(byte[] encoded) {
byte code = RLP.getCommandCode(encoded);
public static Message createMessage(byte code, byte[] encoded) {
if (P2pMessageCodes.inRange(code)) {

View File

@ -43,7 +43,8 @@ public class StaticMessages {
byte p2pVersion = P2pHandler.VERSION;
List<Capability> capabilities = Arrays.asList(
new Capability(Capability.ETH, EthHandler.VERSION),
new Capability(Capability.SHH, ShhHandler.VERSION));
new Capability(Capability.SHH, ShhHandler.VERSION)
);
int listenPort = SystemProperties.CONFIG.listenPort();
return new HelloMessage(p2pVersion, helloAnnouncement,

View File

@ -28,7 +28,7 @@ public class DisconnectMessage extends P2pMessage {
private void parse() {
RLPList paramsList = (RLPList) RLP.decode2(encoded).get(0);
byte[] reasonBytes = paramsList.get(1).getRLPData();
byte[] reasonBytes = paramsList.get(0).getRLPData();
if (reasonBytes == null)
this.reason = REQUESTED;
else

View File

@ -65,13 +65,13 @@ public class HelloMessage extends P2pMessage {
// The message does not distinguish between 0 and null,
// so we check command code for null.
byte[] p2pVersionBytes = paramsList.get(1).getRLPData();
byte[] p2pVersionBytes = paramsList.get(0).getRLPData();
this.p2pVersion = p2pVersionBytes != null ? p2pVersionBytes[0] : 0;
byte[] clientIdBytes = paramsList.get(2).getRLPData();
byte[] clientIdBytes = paramsList.get(1).getRLPData();
this.clientId = new String(clientIdBytes != null ? clientIdBytes : EMPTY_BYTE_ARRAY);
RLPList capabilityList = (RLPList) paramsList.get(3);
RLPList capabilityList = (RLPList) paramsList.get(2);
this.capabilities = new ArrayList<>();
for (Object aCapabilityList : capabilityList) {
@ -85,16 +85,15 @@ public class HelloMessage extends P2pMessage {
this.capabilities.add(cap);
}
byte[] peerPortBytes = paramsList.get(4).getRLPData();
byte[] peerPortBytes = paramsList.get(3).getRLPData();
this.listenPort = ByteUtil.byteArrayToInt(peerPortBytes);
byte[] peerIdBytes = paramsList.get(5).getRLPData();
byte[] peerIdBytes = paramsList.get(4).getRLPData();
this.peerId = Hex.toHexString(peerIdBytes);
this.parsed = true;
}
private void encode() {
byte[] command = RLP.encodeByte(HELLO.asByte());
byte[] p2pVersion = RLP.encodeByte(this.p2pVersion);
byte[] clientId = RLP.encodeString(this.clientId);
byte[][] capabilities = new byte[this.capabilities.size()][];
@ -108,7 +107,7 @@ public class HelloMessage extends P2pMessage {
byte[] peerPort = RLP.encodeInt(this.listenPort);
byte[] peerId = RLP.encodeElement(Hex.decode(this.peerId));
this.encoded = RLP.encodeList(command, p2pVersion, clientId,
this.encoded = RLP.encodeList(p2pVersion, clientId,
capabilityList, peerPort, peerId);
}
@ -148,6 +147,9 @@ public class HelloMessage extends P2pMessage {
return P2pMessageCodes.HELLO;
}
public void setPeerId(String peerId) {
this.peerId = peerId;
}
@Override
public Class<?> getAnswerMessage() {

View File

@ -1,5 +1,6 @@
package org.ethereum.net.p2p;
import io.netty.buffer.ByteBuf;
import org.ethereum.core.Block;
import org.ethereum.core.Transaction;
import org.ethereum.manager.WorldManager;
@ -12,6 +13,8 @@ import org.ethereum.net.eth.TransactionsMessage;
import org.ethereum.net.message.ReasonCode;
import org.ethereum.net.message.StaticMessages;
import org.ethereum.net.peerdiscovery.PeerInfo;
import org.ethereum.net.rlpx.FrameCodec;
import org.ethereum.net.server.Channel;
import org.ethereum.net.shh.ShhHandler;
import org.ethereum.net.shh.ShhMessageCodes;
@ -21,10 +24,12 @@ import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.annotation.Scope;
//import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@ -64,7 +69,6 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
private MessageQueue msgQueue;
private boolean tearDown = false;
private boolean active = false;
private boolean peerDiscoveryMode = false;
private HelloMessage handshakeHelloMessage = null;
@ -72,6 +76,7 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
@Autowired
WorldManager worldManager;
private Channel channel;
public P2pHandler() {
@ -91,32 +96,19 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
this.peerDiscoveryMode = peerDiscoveryMode;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
active = true;
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
logger.info("P2P protocol activated");
msgQueue.activate(ctx);
// Send HELLO once when channel connection has been established
msgQueue.sendMessage(HELLO_MESSAGE);
worldManager.getListener().trace("P2P protocol activated");
startTimers();
}
public void activate() {
// logger.info("Incoming connection from: {}", ch.remoteAddress().toString());
logger.info("P2P protocol activated");
worldManager.getListener().trace("P2P protocol activated");
}
public boolean isActive() {
return active;
}
@Override
public void channelRead0(final ChannelHandlerContext ctx, P2pMessage msg) throws InterruptedException {
if (!isActive()) return;
if (P2pMessageCodes.inRange(msg.getCommand().asByte()))
logger.info("P2PHandler invoke: [{}]", msg.getCommand());
@ -126,7 +118,7 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
case HELLO:
msgQueue.receivedMessage(msg);
setHandshake((HelloMessage) msg, ctx);
sendGetPeers();
// sendGetPeers();
break;
case DISCONNECT:
msgQueue.receivedMessage(msg);
@ -163,7 +155,6 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
logger.info("channel inactive: ", ctx.toString());
active = false;
this.killTimers();
}
@ -171,7 +162,6 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
logger.error(cause.getCause().toString());
super.exceptionCaught(ctx, cause);
active = false;
ctx.close();
killTimers();
}
@ -204,7 +194,8 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
}
private void setHandshake(HelloMessage msg, ChannelHandlerContext ctx) {
public void setHandshake(HelloMessage msg, ChannelHandlerContext ctx) {
this.handshakeHelloMessage = msg;
if (msg.getP2PVersion() != VERSION) {
@ -214,21 +205,22 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
List<Capability> capInCommon = new ArrayList<>();
for (Capability capability : msg.getCapabilities()) {
if (HELLO_MESSAGE.getCapabilities().contains(capability)) {
if (capability.getName().equals(Capability.ETH)) {
if (capability.getName().equals(Capability.ETH) &&
capability.getVersion() == EthHandler.VERSION) {
// Activate EthHandler for this peer
EthHandler ethHandler =
(EthHandler) ctx.pipeline().get(Capability.ETH);
EthHandler ethHandler = channel.getEthHandler();
ethHandler.setPeerId(msg.getPeerId());
ctx.pipeline().addLast(Capability.ETH, ethHandler);
ethHandler.activate();
}
else if (capability.getName().equals(Capability.SHH)) {
} else if
(capability.getName().equals(Capability.SHH) &&
capability.getVersion() == ShhHandler.VERSION) {
// Activate ShhHandler for this peer
ShhHandler shhHandler =
(ShhHandler) ctx.pipeline().get(Capability.SHH);
shhHandler.activate();
// ShhHandler shhHandler =
// (ShhHandler) ctx.pipeline().get(Capability.SHH);
// shhHandler.activate();
}
capInCommon.add(capability);
}
@ -321,4 +313,8 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
public void setMsgQueue(MessageQueue msgQueue) {
this.msgQueue = msgQueue;
}
public void setChannel(Channel channel) {
this.channel = channel;
}
}

View File

@ -89,4 +89,5 @@ public enum P2pMessageCodes {
public byte asByte() {
return (byte) (cmd);
}
}

View File

@ -12,7 +12,7 @@ public class PingMessage extends P2pMessage {
/**
* Ping message is always a the same single command payload
*/
private final static byte[] FIXED_PAYLOAD = Hex.decode("C102");
private final static byte[] FIXED_PAYLOAD = Hex.decode("C0");
public byte[] getEncoded() {
return FIXED_PAYLOAD;

View File

@ -12,7 +12,7 @@ public class PongMessage extends P2pMessage {
/**
* Pong message is always a the same single command payload
*/
private final static byte[] FIXED_PAYLOAD = Hex.decode("C103");
private final static byte[] FIXED_PAYLOAD = Hex.decode("C0");
@Override
public byte[] getEncoded() {

View File

@ -1,5 +1,10 @@
package org.ethereum.net.peerdiscovery;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.ethereum.manager.WorldManager;
import org.ethereum.net.MessageQueue;
import org.ethereum.net.client.Capability;
@ -8,23 +13,9 @@ import org.ethereum.net.eth.StatusMessage;
import org.ethereum.net.p2p.HelloMessage;
import org.ethereum.net.p2p.P2pHandler;
import org.ethereum.net.shh.ShhHandler;
import org.ethereum.net.wire.MessageDecoder;
import org.ethereum.net.wire.MessageEncoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.DefaultMessageSizeEstimator;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.FixedRecvByteBufAllocator;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.ethereum.net.wire.MessageCodec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
//import org.springframework.context.annotation.Scope;
@ -88,15 +79,13 @@ public class DiscoveryChannel {
p2pHandler.setMsgQueue(messageQueue);
p2pHandler.setPeerDiscoveryMode(true);
p2pHandler.activate();
ethHandler.setMsgQueue(messageQueue);
ethHandler.setPeerDiscoveryMode(true);
shhHandler.setMsgQueue(messageQueue);
final MessageDecoder decoder = ctx.getBean(MessageDecoder.class);
final MessageEncoder encoder = ctx.getBean(MessageEncoder.class);
final MessageCodec decoder = ctx.getBean(MessageCodec.class);
b.handler(
@ -108,8 +97,8 @@ public class DiscoveryChannel {
ch.pipeline().addLast("readTimeoutHandler",
new ReadTimeoutHandler(CONFIG.peerChannelReadTimeout(), TimeUnit.SECONDS));
ch.pipeline().addLast("out encoder", encoder);
ch.pipeline().addLast("in encoder", decoder);
ch.pipeline().addLast("initiator", decoder.getInitiator());
ch.pipeline().addLast("messageCodec", decoder);
ch.pipeline().addLast(Capability.P2P, p2pHandler);
ch.pipeline().addLast(Capability.ETH, ethHandler);
ch.pipeline().addLast(Capability.SHH, shhHandler);

View File

@ -1,23 +1,29 @@
package org.ethereum.net.server;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import org.ethereum.core.Block;
import org.ethereum.core.Transaction;
import org.ethereum.net.MessageQueue;
import org.ethereum.net.client.Capability;
import org.ethereum.net.eth.EthHandler;
import org.ethereum.net.p2p.HelloMessage;
import org.ethereum.net.p2p.P2pHandler;
import org.ethereum.net.rlpx.FrameCodec;
import org.ethereum.net.shh.ShhHandler;
import org.ethereum.net.wire.MessageDecoder;
import org.ethereum.net.wire.MessageEncoder;
import org.ethereum.net.wire.MessageCodec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.annotation.Scope;
//import org.springframework.stereotype.Component;
import java.io.IOException;
import java.math.BigInteger;
import java.net.InetSocketAddress;
import static org.ethereum.net.message.StaticMessages.HELLO_MESSAGE;
/**
* @author Roman Mandeleil
* @since 01.11.2014
@ -26,6 +32,8 @@ import java.net.InetSocketAddress;
//@Scope("prototype")
public class Channel {
private final static Logger logger = LoggerFactory.getLogger("net");
@Autowired
ChannelManager channelManager;
@ -42,10 +50,7 @@ public class Channel {
ShhHandler shhHandler;
@Autowired
MessageDecoder messageDecoder;
@Autowired
MessageEncoder messageEncoder;
MessageCodec messageCodec;
InetSocketAddress inetSocketAddress;
@ -56,7 +61,11 @@ public class Channel {
public Channel() {
}
public void init() {
public void init(String remoteId) {
messageCodec.setRemoteId(remoteId, this);
//messageCodec.setMsgQueue(msgQueue);
p2pHandler.setMsgQueue(msgQueue);
ethHandler.setMsgQueue(msgQueue);
shhHandler.setMsgQueue(msgQueue);
@ -64,6 +73,34 @@ public class Channel {
startupTS = System.currentTimeMillis();
}
public void publicRLPxHandshakeFinished(ChannelHandlerContext ctx, FrameCodec frameCodec, HelloMessage helloRemote, byte[] nodeId) throws IOException, InterruptedException {
ctx.pipeline().addLast(Capability.P2P, p2pHandler);
p2pHandler.setChannel(this);
p2pHandler.setHandshake(helloRemote, ctx);
// ctx.pipeline().addLast(Capability.ETH, getEthHandler());
// ctx.pipeline().addLast(Capability.SHH, getShhHandler());
}
public void sendHelloMessage(ChannelHandlerContext ctx, FrameCodec frameCodec, String nodeId) throws IOException, InterruptedException {
HELLO_MESSAGE.setPeerId(nodeId);
byte[] payload = HELLO_MESSAGE.getEncoded();
ByteBuf byteBufMsg = ctx.alloc().buffer();
frameCodec.writeFrame(new FrameCodec.Frame(HELLO_MESSAGE.getCode(), payload), byteBufMsg);
ctx.writeAndFlush(byteBufMsg).sync();
if (logger.isInfoEnabled())
logger.info("To: \t{} \tSend: \t{}", ctx.channel().remoteAddress(), HELLO_MESSAGE);
}
public P2pHandler getP2pHandler() {
return p2pHandler;
}
@ -76,12 +113,8 @@ public class Channel {
return shhHandler;
}
public MessageDecoder getMessageDecoder() {
return messageDecoder;
}
public MessageEncoder getMessageEncoder() {
return messageEncoder;
public MessageCodec getMessageCodec() {
return messageCodec;
}
public void sendTransaction(Transaction tx) {

View File

@ -20,6 +20,7 @@ import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
//import javax.annotation.PostConstruct;
/**
* @author Roman Mandeleil
@ -42,6 +43,7 @@ public class ChannelManager {
}
// @PostConstruct
public void init() {
scheduleChannelCollector();
}
@ -105,10 +107,10 @@ public class ChannelManager {
Iterator<Channel> iter = channels.iterator();
while (iter.hasNext()) {
Channel channel = iter.next();
if (!channel.p2pHandler.isActive()) {
iter.remove();
logger.info("Channel removed: {}", channel.p2pHandler.getHandshakeHelloMessage());
}
// todo: identify how to remove channels
// iter.remove();
// logger.info("Channel removed: {}", channel.p2pHandler.getHandshakeHelloMessage());
}
if (channels.size() == 0) {

View File

@ -1,20 +1,17 @@
package org.ethereum.net.server;
import org.ethereum.facade.Blockchain;
import org.ethereum.manager.WorldManager;
import org.ethereum.net.client.Capability;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.FixedRecvByteBufAllocator;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.ethereum.facade.Blockchain;
import org.ethereum.manager.WorldManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
//import org.springframework.context.annotation.Scope;
//import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@ -26,6 +23,7 @@ import static org.ethereum.config.SystemProperties.CONFIG;
* @since 01.11.2014
*/
//@Component
//@Scope("prototype")
public class EthereumChannelInitializer extends ChannelInitializer<NioSocketChannel> {
private static final Logger logger = LoggerFactory.getLogger("net");
@ -42,31 +40,42 @@ public class EthereumChannelInitializer extends ChannelInitializer<NioSocketChan
@Autowired
WorldManager worldManager;
String remoteId;
public EthereumChannelInitializer() {
logger.info("Channel initializer instantiated");
this.remoteId = "";
}
public EthereumChannelInitializer(String remoteId) {
logger.info("Channel initializer instantiated");
this.remoteId = remoteId;
}
@Override
public void initChannel(NioSocketChannel ch) throws Exception {
logger.info("Open connection, channel: {}", ch.toString());
Channel channel = ctx.getBean(Channel.class);
channel.init();
channel.init(remoteId);
channelManager.addChannel(channel);
channel.getP2pHandler().activate();
ch.pipeline().addLast("readTimeoutHandler",
new ReadTimeoutHandler(CONFIG.peerChannelReadTimeout(), TimeUnit.SECONDS));
ch.pipeline().addLast("out encoder", channel.getMessageEncoder());
ch.pipeline().addLast("in encoder", channel.getMessageDecoder());
ch.pipeline().addLast(Capability.P2P, channel.getP2pHandler());
ch.pipeline().addLast(Capability.ETH, channel.getEthHandler());
ch.pipeline().addLast(Capability.SHH, channel.getShhHandler());
// ch.pipeline().addLast("in encoder", channel.getMessageDecoder());
// ch.pipeline().addLast("out encoder", channel.getMessageEncoder());
// ch.pipeline().addLast(Capability.P2P, channel.getP2pHandler());
// ch.pipeline().addLast(Capability.ETH, channel.getEthHandler());
// ch.pipeline().addLast(Capability.SHH, channel.getShhHandler());
ch.pipeline().addLast("initiator", channel.getMessageCodec().getInitiator());
ch.pipeline().addLast("messageCodec", channel.getMessageCodec());
// limit the size of receiving buffer to 1024
ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(32368));
ch.config().setOption(ChannelOption.SO_RCVBUF, 32368);
ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(16_777_216));
ch.config().setOption(ChannelOption.SO_RCVBUF, 16_777_216);
ch.config().setOption(ChannelOption.SO_BACKLOG, 1024);
}

View File

@ -33,8 +33,9 @@ public class PeerServer {
@Autowired
public ChannelManager channelManager;
@Autowired
// TODO: this was removed ???
@Autowired
public EthereumChannelInitializer ethereumChannelInitializer;
@Autowired

View File

@ -18,6 +18,7 @@ import java.util.Set;
import static java.util.Arrays.copyOfRange;
import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
import static org.ethereum.util.ByteUtil.matchingNibbleLength;
import static org.ethereum.util.CompactEncoder.*;
import static org.spongycastle.util.Arrays.concatenate;
@ -133,12 +134,12 @@ public class TrieImpl implements Trie {
* Delete a key/value pair from the trie.
*/
public void delete(String key) {
this.update(key.getBytes(), "".getBytes());
this.update(key.getBytes(), EMPTY_BYTE_ARRAY);
}
@Override
public void delete(byte[] key) {
delete(new String(key));
this.update(key, EMPTY_BYTE_ARRAY);
if (logger.isDebugEnabled()) {
logger.debug("Deleted value for key {}", Hex.toHexString(key));
logger.debug("New root-hash: {}", Hex.toHexString(this.getRootHash()));

View File

@ -12,6 +12,8 @@ import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class ByteUtil {
@ -401,4 +403,28 @@ public class ByteUtil {
}
return mergedArray;
}
public static boolean isNullOrZeroArray(byte[] array){
return (array == null) || (array.length == 0) || (array.length == 1 && array[0] == 0);
}
public static Set<byte[]> difference(Set<byte[]> setA, Set<byte[]> setB){
Set<byte[]> result = new HashSet<>();
for (byte[] elementA : setA){
boolean found = false;
for (byte[] elementB : setB){
if (Arrays.equals(elementA, elementB)){
found = true;
break;
}
}
if (!found) result.add(elementA);
}
return result;
}
}

View File

@ -4,6 +4,7 @@ import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@ -125,21 +126,29 @@ public class CompactEncoder {
* @return array with each individual nibble adding a terminator at the end
*/
public static byte[] binToNibbles(byte[] str) {
byte[] hexEncoded = encode(str);
ByteBuffer slice = ByteBuffer.allocate(hexEncoded.length + 1);
for (byte b : hexEncoded) {
slice.put(hexMap.get((char) b));
byte[] hexEncodedTerminated = Arrays.copyOf(hexEncoded, hexEncoded.length + 1);
for (int i = 0; i < hexEncoded.length; ++i){
byte b = hexEncodedTerminated[i];
hexEncodedTerminated[i] = hexMap.get((char) b);
}
slice.put(TERMINATOR);
return slice.array();
hexEncodedTerminated[hexEncodedTerminated.length - 1] = TERMINATOR;
return hexEncodedTerminated;
}
public static byte[] binToNibblesNoTerminator(byte[] str) {
byte[] hexEncoded = encode(str);
ByteBuffer slice = ByteBuffer.allocate(hexEncoded.length);
for (byte b : hexEncoded) {
slice.put(hexMap.get((char) b));
for (int i = 0; i < hexEncoded.length; ++i){
byte b = hexEncoded[i];
hexEncoded[i] = hexMap.get((char) b);
}
return slice.array();
return hexEncoded;
}
}

View File

@ -1,22 +1,18 @@
package org.ethereum.util;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Queue;
import java.util.*;
import static java.util.Arrays.copyOfRange;
import static org.ethereum.util.ByteUtil.byteArrayToInt;
import static org.ethereum.util.ByteUtil.isNullOrZeroArray;
import static org.spongycastle.util.Arrays.concatenate;
import static org.spongycastle.util.BigIntegers.asUnsignedByteArray;
/**
* Recursive Length Prefix (RLP) encoding.
*
* <p>
* The purpose of RLP is to encode arbitrarily nested arrays of binary data, and
* RLP is the main encoding method used to serialize objects in Ethereum. The
* only purpose of RLP is to encode structure; encoding specific atomic data
@ -26,18 +22,18 @@ import static org.spongycastle.util.BigIntegers.asUnsignedByteArray;
* canonical forms are to either use [[k1,v1],[k2,v2]...] with keys in
* lexicographic order or to use the higher-level Patricia Tree encoding as
* Ethereum does.
*
* <p>
* The RLP encoding function takes in an item. An item is defined as follows:
*
* <p>
* - A string (ie. byte array) is an item - A list of items is an item
*
* <p>
* For example, an empty string is an item, as is the string containing the word
* "cat", a list containing any number of strings, as well as more complex data
* structures like ["cat",["puppy","cow"],"horse",[[]],"pig",[""],"sheep"]. Note
* that in the context of the rest of this article, "string" will be used as a
* synonym for "a certain number of bytes of binary data"; no special encodings
* are used and no knowledge about the content of the strings is implied.
*
* <p>
* See: https://github.com/ethereum/wiki/wiki/%5BEnglish%5D-RLP
*
* @author Roman Mandeleil
@ -129,10 +125,12 @@ public class RLP {
}
public static int decodeInt(byte[] data, int index) {
int value = 0;
// NOTE: there are two ways zero can be encoded - 0x00 and OFFSET_SHORT_ITEM
if ((data[index] & 0xFF) > OFFSET_SHORT_ITEM
if ((data[index] & 0xFF) < OFFSET_SHORT_ITEM) {
return data[index];
} else if ((data[index] & 0xFF) >= OFFSET_SHORT_ITEM
&& (data[index] & 0xFF) < OFFSET_LONG_ITEM) {
byte length = (byte) (data[index] - OFFSET_SHORT_ITEM);
@ -256,13 +254,13 @@ public class RLP {
int offset = 1;
final byte[] result=new byte[4];
for ( int i =0 ; i<4 ; i++) {
final byte[] result = new byte[4];
for (int i = 0; i < 4; i++) {
result[i] = decodeOneByteItem(data, index + offset);
if ((data[index + offset] & 0xFF) > OFFSET_SHORT_ITEM)
offset += 2;
offset += 2;
else
offset += 1;
offset += 1;
}
// return IP address
@ -748,7 +746,7 @@ public class RLP {
public static byte[] encodeElement(byte[] srcData) {
if (srcData == null)
if (isNullOrZeroArray(srcData))
return new byte[]{(byte) OFFSET_SHORT_ITEM};
else if (srcData.length == 1 && (srcData[0] & 0xFF) < 0x80) {
return srcData;

View File

@ -16,6 +16,8 @@ import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;
//import javax.swing.*;
public class Utils {
private static SecureRandom random = new SecureRandom();
@ -46,7 +48,13 @@ public class Utils {
return formatter.format(date);
}
static BigInteger _1000_ = new BigInteger("1000");
/* public static ImageIcon getImageIcon(String resource) {
URL imageURL = ClassLoader.getSystemResource(resource);
ImageIcon image = new ImageIcon(imageURL);
return image;
}*/
static BigInteger _1000_ = new BigInteger("1000");
public static String getValueShortString(BigInteger number) {
BigInteger result = number;

View File

@ -107,6 +107,10 @@ public class DataWord implements Comparable<DataWord> {
return new BigInteger(data);
}
public String bigIntValue() {
return new BigInteger(data).toString();
}
public boolean isZero() {
for (byte tmp : data) {
if (tmp != 0) return false;

View File

@ -8,98 +8,58 @@ package org.ethereum.vm;
*/
public class GasCost {
/**
* Cost 1 gas
*/
/* backwards compatibility, remove eventually */
public final static int STEP = 1;
/**
* Cost 20 gas
*/
public final static int BALANCE = 20;
/**
* Cost 10 gas
*/
public final static int SHA3 = 10;
/**
* Cost 10 gas
*/
public final static int SHA3_WORD = 10;
/**
* Cost 20 gas
*/
public final static int SLOAD = 20;
/**
* Cost 0 gas
*/
public final static int STOP = 0;
/**
* Cost 0 gas
*/
public final static int SUICIDE = 0;
/**
* Cost 300 gas
*/
public final static int SSTORE = 300;
/**
* Cost 100 gas
*/
public final static int RESET_SSTORE = 100;
/**
* Cost 100 gas
*/
public final static int REFUND_SSTORE = 100;
/**
* Cost 100 gas
*/
public final static int CREATE = 100;
/**
* Cost 1 gas
*/
/* backwards compatibility, remove eventually */
public final static int ZEROSTEP = 0;
public final static int QUICKSTEP = 2;
public final static int FASTESTSTEP = 3;
public final static int FASTSTEP = 5;
public final static int MIDSTEP = 8;
public final static int SLOWSTEP = 10;
public final static int EXTSTEP = 20;
public final static int GENESISGASLIMIT = 1000000;
public final static int MINGASLIMIT = 125000;
public final static int BALANCE = 20;
public final static int SHA3 = 30;
public final static int SHA3_WORD = 6;
public final static int SLOAD = 50;
public final static int STOP = 0;
public final static int SUICIDE = 0;
public final static int CLEAR_SSTORE = 5000;
public final static int SET_SSTORE = 20000;
public final static int RESET_SSTORE = 5000;
public final static int REFUND_SSTORE = 15000;
public final static int CREATE = 32000;
public final static int JUMPDEST = 1;
public final static int CREATE_DATA_BYTE = 5;
/**
* Cost 20 gas
*/
public final static int CALL = 20;
/**
* Cost 1 gas
*/
public final static int MEMORY = 1;
/**
* Cost 5 gas
*/
public final static int TX_NO_ZERO_DATA = 5;
/**
* Cost 1 gas
*/
public final static int TX_ZERO_DATA = 1;
/**
* Cost 500 gas
*/
public final static int TRANSACTION = 500;
/**
* Cost 32 gas
*/
public static int LOG_GAS = 32;
/**
* Cost 1 gas
*/
public final static int LOG_DATA_GAS = 1;
/**
* Cost 32 gas
*/
public final static int LOG_TOPIC_GAS = 32;
/**
* Cost 1 gas
*/
public final static int COPY_GAS = 1;
/**
* Cost 1 gas
*/
public final static int EXP_GAS = 1;
/**
* Cost 1 gas
*/
public final static int EXP_BYTE_GAS = 1;
}
public final static int CALL = 40;
public final static int STIPEND_CALL = 2300;
public final static int VT_CALL = 9000; //value transfer call
public final static int NEW_ACCT_CALL = 25000; //new account call
public final static int MEMORY = 3;
public final static int SUICIDE_REFUND = 24000;
public final static int QUAD_COEFF_DIV = 512;
public final static int CREATE_DATA = 200;
public final static int TX_NO_ZERO_DATA = 68;
public final static int TX_ZERO_DATA = 4;
public final static int TRANSACTION = 21000;
public final static int LOG_GAS = 375;
public final static int LOG_DATA_GAS = 8;
public final static int LOG_TOPIC_GAS = 375;
public final static int COPY_GAS = 3;
public final static int EXP_GAS = 10;
public final static int EXP_BYTE_GAS = 10;
public final static int IDENTITY = 15;
public final static int IDENTITY_WORD = 3;
public final static int RIPEMD160 = 600;
public final static int RIPEMD160_WORD = 120;
public final static int SHA256 = 60;
public final static int SHA256_WORD = 12;
public final static int EC_RECOVER = 3000;
}

View File

@ -3,120 +3,123 @@ package org.ethereum.vm;
import java.util.HashMap;
import java.util.Map;
import static org.ethereum.vm.OpCode.Tier.*;
/**
* Instruction set for the Ethereum Virtual Machine
* See Yellow Paper: http://www.gavwood.com/Paper.pdf
* - Appendix G. Virtual Machine Specification
*/
public enum OpCode {
// TODO #POC9 Need to make tiers more accurate
/**
* Halts execution (0x00)
*/
STOP(0x00, 0),
STOP(0x00, 0, 0, ZeroTier),
/* Arithmetic Operations */
/**
* (0x01) Addition operation
*/
ADD(0x01, 2),
ADD(0x01, 2, 1, VeryLowTier),
/**
* (0x02) Multiplication operation
*/
MUL(0x02, 2),
MUL(0x02, 2, 1, LowTier),
/**
* (0x03) Subtraction operations
*/
SUB(0x03, 2),
SUB(0x03, 2, 1, VeryLowTier),
/**
* (0x04) Integer division operation
*/
DIV(0x04, 2),
DIV(0x04, 2, 1, LowTier),
/**
* (0x05) Signed integer division operation
*/
SDIV(0x05, 2),
SDIV(0x05, 2, 1, LowTier),
/**
* (0x06) Modulo remainder operation
*/
MOD(0x06, 2),
MOD(0x06, 2, 1, LowTier),
/**
* (0x07) Signed modulo remainder operation
*/
SMOD(0x07, 2),
SMOD(0x07, 2, 1, LowTier),
/**
* (0x08) Addition combined with modulo
* remainder operation
*/
ADDMOD(0x08, 3),
ADDMOD(0x08, 3, 1, MidTier),
/**
* (0x09) Multiplication combined with modulo
* remainder operation
*/
MULMOD(0x09, 3),
MULMOD(0x09, 3, 1, MidTier),
/**
* (0x0a) Exponential operation
*/
EXP(0x0a, 2),
EXP(0x0a, 2, 1, SpecialTier),
/**
* (0x0b) Extend length of signed integer
*/
SIGNEXTEND(0x0b, 2),
SIGNEXTEND(0x0b, 2, 1, LowTier),
/* Bitwise Logic & Comparison Operations */
/**
* (0x10) Less-than comparison
*/
LT(0X10, 2),
LT(0X10, 2, 1, VeryLowTier),
/**
* (0x11) Greater-than comparison
*/
GT(0X11, 2),
GT(0X11, 2, 1, VeryLowTier),
/**
* (0x12) Signed less-than comparison
*/
SLT(0X12, 2),
SLT(0X12, 2, 1, VeryLowTier),
/**
* (0x13) Signed greater-than comparison
*/
SGT(0X13, 2),
SGT(0X13, 2, 1, VeryLowTier),
/**
* (0x14) Equality comparison
*/
EQ(0X14, 2),
EQ(0X14, 2, 1, VeryLowTier),
/**
* (0x15) Negation operation
*/
ISZERO(0x15, 1),
ISZERO(0x15, 1, 1, VeryLowTier),
/**
* (0x16) Bitwise AND operation
*/
AND(0x16, 2),
AND(0x16, 2, 1, VeryLowTier),
/**
* (0x17) Bitwise OR operation
*/
OR(0x17, 2),
OR(0x17, 2, 1, VeryLowTier),
/**
* (0x18) Bitwise XOR operation
*/
XOR(0x18, 2),
XOR(0x18, 2, 1, VeryLowTier),
/**
* (0x19) Bitwise NOT operationr
*/
NOT(0x19, 1),
NOT(0x19, 1, 1, VeryLowTier),
/**
* (0x1a) Retrieve single byte from word
*/
BYTE(0x1a, 2),
BYTE(0x1a, 2, 1, VeryLowTier),
/* Cryptographic Operations */
/**
* (0x20) Compute SHA3-256 hash
*/
SHA3(0x20, 2),
SHA3(0x20, 2, 1, SpecialTier),
/* Environmental Information */
@ -124,65 +127,65 @@ public enum OpCode {
* (0x30) Get address of currently
* executing account
*/
ADDRESS(0x30, 0),
ADDRESS(0x30, 0, 1, BaseTier),
/**
* (0x31) Get balance of the given account
*/
BALANCE(0x31, 1),
BALANCE(0x31, 1, 1, ExtTier),
/**
* (0x32) Get execution origination address
*/
ORIGIN(0x32, 0),
ORIGIN(0x32, 0, 1, BaseTier),
/**
* (0x33) Get caller address
*/
CALLER(0x33, 0),
CALLER(0x33, 0, 1, BaseTier),
/**
* (0x34) Get deposited value by the
* instruction/transaction responsible
* for this execution
*/
CALLVALUE(0x34, 0),
CALLVALUE(0x34, 0, 1, BaseTier),
/**
* (0x35) Get input data of current
* environment
*/
CALLDATALOAD(0x35, 1),
CALLDATALOAD(0x35, 1, 1, VeryLowTier),
/**
* (0x36) Get size of input data in current
* environment
*/
CALLDATASIZE(0x36, 0),
CALLDATASIZE(0x36, 0, 1, BaseTier),
/**
* (0x37) Copy input data in current
* environment to memory
*/
CALLDATACOPY(0x37, 3),
CALLDATACOPY(0x37, 3, 0, VeryLowTier),
/**
* (0x38) Get size of code running in
* current environment
*/
CODESIZE(0x38, 0),
CODESIZE(0x38, 0, 1, BaseTier),
/**
* (0x39) Copy code running in current
* environment to memory
*/
CODECOPY(0x39, 3), // [len code_start mem_start CODECOPY]
CODECOPY(0x39, 3, 0, VeryLowTier), // [len code_start mem_start CODECOPY]
/**
* (0x3a) Get price of gas in current
* environment
*/
GASPRICE(0x3a, 0),
GASPRICE(0x3a, 0, 1, BaseTier),
/**
* (0x3b) Get size of code running in
* current environment with given offset
*/
EXTCODESIZE(0x3b, 1),
EXTCODESIZE(0x3b, 1, 1, ExtTier),
/**
* (0x3c) Copy code running in current
* environment to memory with given offset
*/
EXTCODECOPY(0x3c, 4),
EXTCODECOPY(0x3c, 4, 0, ExtTier),
/* Block Information */
@ -190,383 +193,385 @@ public enum OpCode {
* (0x40) Get hash of most recent
* complete block
*/
BLOCKHASH(0x40, 1),
BLOCKHASH(0x40, 1, 1, ExtTier),
/**
* (0x41) Get the blocks coinbase address
*/
COINBASE(0x41, 0),
COINBASE(0x41, 0, 1, BaseTier),
/**
* (x042) Get the blocks timestamp
*/
TIMESTAMP(0x42, 0),
TIMESTAMP(0x42, 0, 1, BaseTier),
/**
* (0x43) Get the blocks number
*/
NUMBER(0x43, 0),
NUMBER(0x43, 0, 1, BaseTier),
/**
* (0x44) Get the blocks difficulty
*/
DIFFICULTY(0x44, 0),
DIFFICULTY(0x44, 0, 1, BaseTier),
/**
* (0x45) Get the blocks gas limit
*/
GASLIMIT(0x45, 0),
GASLIMIT(0x45, 0, 1, BaseTier),
/* Memory, Storage and Flow Operations */
/**
* (0x50) Remove item from stack
*/
POP(0x50, 1),
POP(0x50, 1, 0, BaseTier),
/**
* (0x51) Load word from memory
*/
MLOAD(0x51, 1),
MLOAD(0x51, 1, 1, VeryLowTier),
/**
* (0x52) Save word to memory
*/
MSTORE(0x52, 2),
MSTORE(0x52, 2, 0, VeryLowTier),
/**
* (0x53) Save byte to memory
*/
MSTORE8(0x53, 2),
MSTORE8(0x53, 2, 0, VeryLowTier),
/**
* (0x54) Load word from storage
*/
SLOAD(0x54, 1),
SLOAD(0x54, 1, 1, SpecialTier),
/**
* (0x55) Save word to storage
*/
SSTORE(0x55, 2),
SSTORE(0x55, 2, 0, SpecialTier),
/**
* (0x56) Alter the program counter
*/
JUMP(0x56, 1),
JUMP(0x56, 1, 0, MidTier),
/**
* (0x57) Conditionally alter the program
* counter
*/
JUMPI(0x57, 2),
JUMPI(0x57, 2, 0, HighTier),
/**
* (0x58) Get the program counter
*/
PC(0x58, 0),
PC(0x58, 0, 1, BaseTier),
/**
* (0x59) Get the size of active memory
*/
MSIZE(0x59, 0),
MSIZE(0x59, 0, 1, BaseTier),
/**
* (0x5a) Get the amount of available gas
*/
GAS(0x5a, 0),
GAS(0x5a, 0, 1, BaseTier),
/**
* (0x5b)
*/
JUMPDEST(0x5b, 0),
JUMPDEST(0x5b, 0, 0, SpecialTier),
/* Push Operations */
/**
* (0x60) Place 1-byte item on stack
*/
PUSH1(0x60, 0),
PUSH1(0x60, 0, 1, VeryLowTier),
/**
* (0x61) Place 2-byte item on stack
*/
PUSH2(0x61, 0),
PUSH2(0x61, 0, 1, VeryLowTier),
/**
* (0x62) Place 3-byte item on stack
*/
PUSH3(0x62, 0),
PUSH3(0x62, 0, 1, VeryLowTier),
/**
* (0x63) Place 4-byte item on stack
*/
PUSH4(0x63, 0),
PUSH4(0x63, 0, 1, VeryLowTier),
/**
* (0x64) Place 5-byte item on stack
*/
PUSH5(0x64, 0),
PUSH5(0x64, 0, 1, VeryLowTier),
/**
* (0x65) Place 6-byte item on stack
*/
PUSH6(0x65, 0),
PUSH6(0x65, 0, 1, VeryLowTier),
/**
* (0x66) Place 7-byte item on stack
*/
PUSH7(0x66, 0),
PUSH7(0x66, 0, 1, VeryLowTier),
/**
* (0x67) Place 8-byte item on stack
*/
PUSH8(0x67, 0),
PUSH8(0x67, 0, 1, VeryLowTier),
/**
* (0x68) Place 9-byte item on stack
*/
PUSH9(0x68, 0),
PUSH9(0x68, 0, 1, VeryLowTier),
/**
* (0x69) Place 10-byte item on stack
*/
PUSH10(0x69, 0),
PUSH10(0x69, 0, 1, VeryLowTier),
/**
* (0x6a) Place 11-byte item on stack
*/
PUSH11(0x6a, 0),
PUSH11(0x6a, 0, 1, VeryLowTier),
/**
* (0x6b) Place 12-byte item on stack
*/
PUSH12(0x6b, 0),
PUSH12(0x6b, 0, 1, VeryLowTier),
/**
* (0x6c) Place 13-byte item on stack
*/
PUSH13(0x6c, 0),
PUSH13(0x6c, 0, 1, VeryLowTier),
/**
* (0x6d) Place 14-byte item on stack
*/
PUSH14(0x6d, 0),
PUSH14(0x6d, 0, 1, VeryLowTier),
/**
* (0x6e) Place 15-byte item on stack
*/
PUSH15(0x6e, 0),
PUSH15(0x6e, 0, 1, VeryLowTier),
/**
* (0x6f) Place 16-byte item on stack
*/
PUSH16(0x6f, 0),
PUSH16(0x6f, 0, 1, VeryLowTier),
/**
* (0x70) Place 17-byte item on stack
*/
PUSH17(0x70, 0),
PUSH17(0x70, 0, 1, VeryLowTier),
/**
* (0x71) Place 18-byte item on stack
*/
PUSH18(0x71, 0),
PUSH18(0x71, 0, 1, VeryLowTier),
/**
* (0x72) Place 19-byte item on stack
*/
PUSH19(0x72, 0),
PUSH19(0x72, 0, 1, VeryLowTier),
/**
* (0x73) Place 20-byte item on stack
*/
PUSH20(0x73, 0),
PUSH20(0x73, 0, 1, VeryLowTier),
/**
* (0x74) Place 21-byte item on stack
*/
PUSH21(0x74, 0),
PUSH21(0x74, 0, 1, VeryLowTier),
/**
* (0x75) Place 22-byte item on stack
*/
PUSH22(0x75, 0),
PUSH22(0x75, 0, 1, VeryLowTier),
/**
* (0x76) Place 23-byte item on stack
*/
PUSH23(0x76, 0),
PUSH23(0x76, 0, 1, VeryLowTier),
/**
* (0x77) Place 24-byte item on stack
*/
PUSH24(0x77, 0),
PUSH24(0x77, 0, 1, VeryLowTier),
/**
* (0x78) Place 25-byte item on stack
*/
PUSH25(0x78, 0),
PUSH25(0x78, 0, 1, VeryLowTier),
/**
* (0x79) Place 26-byte item on stack
*/
PUSH26(0x79, 0),
PUSH26(0x79, 0, 1, VeryLowTier),
/**
* (0x7a) Place 27-byte item on stack
*/
PUSH27(0x7a, 0),
PUSH27(0x7a, 0, 1, VeryLowTier),
/**
* (0x7b) Place 28-byte item on stack
*/
PUSH28(0x7b, 0),
PUSH28(0x7b, 0, 1, VeryLowTier),
/**
* (0x7c) Place 29-byte item on stack
*/
PUSH29(0x7c, 0),
PUSH29(0x7c, 0, 1, VeryLowTier),
/**
* (0x7d) Place 30-byte item on stack
*/
PUSH30(0x7d, 0),
PUSH30(0x7d, 0, 1, VeryLowTier),
/**
* (0x7e) Place 31-byte item on stack
*/
PUSH31(0x7e, 0),
PUSH31(0x7e, 0, 1, VeryLowTier),
/**
* (0x7f) Place 32-byte (full word)
* item on stack
*/
PUSH32(0x7f, 0),
PUSH32(0x7f, 0, 1, VeryLowTier),
/* Duplicate Nth item from the stack */
/**
* (0x80) Duplicate 1st item on stack
*/
DUP1(0x80, 1),
DUP1(0x80, 1, 2, VeryLowTier),
/**
* (0x81) Duplicate 2nd item on stack
*/
DUP2(0x81, 2),
DUP2(0x81, 2, 3, VeryLowTier),
/**
* (0x82) Duplicate 3rd item on stack
*/
DUP3(0x82, 3),
DUP3(0x82, 3, 4, VeryLowTier),
/**
* (0x83) Duplicate 4th item on stack
*/
DUP4(0x83, 4),
DUP4(0x83, 4, 5, VeryLowTier),
/**
* (0x84) Duplicate 5th item on stack
*/
DUP5(0x84, 5),
DUP5(0x84, 5, 6, VeryLowTier),
/**
* (0x85) Duplicate 6th item on stack
*/
DUP6(0x85, 6),
DUP6(0x85, 6, 7, VeryLowTier),
/**
* (0x86) Duplicate 7th item on stack
*/
DUP7(0x86, 7),
DUP7(0x86, 7, 8, VeryLowTier),
/**
* (0x87) Duplicate 8th item on stack
*/
DUP8(0x87, 8),
DUP8(0x87, 8, 9, VeryLowTier),
/**
* (0x88) Duplicate 9th item on stack
*/
DUP9(0x88, 9),
DUP9(0x88, 9, 10, VeryLowTier),
/**
* (0x89) Duplicate 10th item on stack
*/
DUP10(0x89, 10),
DUP10(0x89, 10, 11, VeryLowTier),
/**
* (0x8a) Duplicate 11th item on stack
*/
DUP11(0x8a, 11),
DUP11(0x8a, 11, 12, VeryLowTier),
/**
* (0x8b) Duplicate 12th item on stack
*/
DUP12(0x8b, 12),
DUP12(0x8b, 12, 13, VeryLowTier),
/**
* (0x8c) Duplicate 13th item on stack
*/
DUP13(0x8c, 13),
DUP13(0x8c, 13, 14, VeryLowTier),
/**
* (0x8d) Duplicate 14th item on stack
*/
DUP14(0x8d, 14),
DUP14(0x8d, 14, 15, VeryLowTier),
/**
* (0x8e) Duplicate 15th item on stack
*/
DUP15(0x8e, 15),
DUP15(0x8e, 15, 16, VeryLowTier),
/**
* (0x8f) Duplicate 16th item on stack
*/
DUP16(0x8f, 16),
DUP16(0x8f, 16, 17, VeryLowTier),
/* Swap the Nth item from the stack with the top */
/**
* (0x90) Exchange 2nd item from stack with the top
*/
SWAP1(0x90, 2),
SWAP1(0x90, 2, 2, VeryLowTier),
/**
* (0x91) Exchange 3rd item from stack with the top
*/
SWAP2(0x91, 3),
SWAP2(0x91, 3, 3, VeryLowTier),
/**
* (0x92) Exchange 4th item from stack with the top
*/
SWAP3(0x92, 4),
SWAP3(0x92, 4, 4, VeryLowTier),
/**
* (0x93) Exchange 5th item from stack with the top
*/
SWAP4(0x93, 5),
SWAP4(0x93, 5, 5, VeryLowTier),
/**
* (0x94) Exchange 6th item from stack with the top
*/
SWAP5(0x94, 6),
SWAP5(0x94, 6, 6, VeryLowTier),
/**
* (0x95) Exchange 7th item from stack with the top
*/
SWAP6(0x95, 7),
SWAP6(0x95, 7, 7, VeryLowTier),
/**
* (0x96) Exchange 8th item from stack with the top
*/
SWAP7(0x96, 8),
SWAP7(0x96, 8, 8, VeryLowTier),
/**
* (0x97) Exchange 9th item from stack with the top
*/
SWAP8(0x97, 9),
SWAP8(0x97, 9, 9, VeryLowTier),
/**
* (0x98) Exchange 10th item from stack with the top
*/
SWAP9(0x98, 10),
SWAP9(0x98, 10, 10, VeryLowTier),
/**
* (0x99) Exchange 11th item from stack with the top
*/
SWAP10(0x99, 11),
SWAP10(0x99, 11, 11,VeryLowTier),
/**
* (0x9a) Exchange 12th item from stack with the top
*/
SWAP11(0x9a, 12),
SWAP11(0x9a, 12, 12, VeryLowTier),
/**
* (0x9b) Exchange 13th item from stack with the top
*/
SWAP12(0x9b, 13),
SWAP12(0x9b, 13, 13, VeryLowTier),
/**
* (0x9c) Exchange 14th item from stack with the top
*/
SWAP13(0x9c, 14),
SWAP13(0x9c, 14, 14, VeryLowTier),
/**
* (0x9d) Exchange 15th item from stack with the top
*/
SWAP14(0x9d, 15),
SWAP14(0x9d, 15, 15, VeryLowTier),
/**
* (0x9e) Exchange 16th item from stack with the top
*/
SWAP15(0x9e, 16),
SWAP15(0x9e, 16, 16, VeryLowTier),
/**
* (0x9f) Exchange 17th item from stack with the top
*/
SWAP16(0x9f, 17),
SWAP16(0x9f, 17, 17, VeryLowTier),
/**
* (0xa[n]) log some data for some addres with 0..n tags [addr [tag0..tagn] data]
*/
LOG0(0xa0, 2),
LOG1(0xa1, 3),
LOG2(0xa2, 4),
LOG3(0xa3, 5),
LOG4(0xa4, 6),
LOG0(0xa0, 2, 0, SpecialTier),
LOG1(0xa1, 3, 0, SpecialTier),
LOG2(0xa2, 4, 0, SpecialTier),
LOG3(0xa3, 5, 0, SpecialTier),
LOG4(0xa4, 6, 0, SpecialTier),
/* System operations */
/**
* (0xf0) Create a new account with associated code
*/
CREATE(0xf0, 3), // [in_size] [in_offs] [gas_val] CREATE
CREATE(0xf0, 3, 1, SpecialTier), // [in_size] [in_offs] [gas_val] CREATE
/**
* (cxf1) Message-call into an account
*/
CALL(0xf1, 7), // [out_data_size] [out_data_start] [in_data_size] [in_data_start] [value] [to_addr]
CALL(0xf1, 7, 1, SpecialTier), // [out_data_size] [out_data_start] [in_data_size] [in_data_start] [value] [to_addr]
// [gas] CALL
/**
* (0xf2) Calls self, but grabbing the code from the
* TO argument instead of from one's own address
*/
CALLCODE(0xf2, 7),
CALLCODE(0xf2, 7, 1, SpecialTier),
/**
* (0xf3) Halt execution returning output data
*/
RETURN(0xf3, 2),
RETURN(0xf3, 2, 0, ZeroTier),
/**
* (0xff) Halt execution and register account for
* later deletion
*/
SUICIDE(0xff, 1);
SUICIDE(0xff, 1, 0, ZeroTier);
private final byte opcode;
private final int require;
private final Tier tier;
private final int ret;
private static final Map<Byte, OpCode> intToTypeMap = new HashMap<>();
private static final Map<String, Byte> stringToByteMap = new HashMap<>();
@ -578,9 +583,13 @@ public enum OpCode {
}
}
private OpCode(int op, int require) {
//require = required args
//return = required return
private OpCode(int op, int require, int ret, Tier tier) {
this.opcode = (byte) op;
this.require = require;
this.tier = tier;
this.ret = ret;
}
public byte val() {
@ -596,6 +605,10 @@ public enum OpCode {
return require;
}
public int ret() {
return ret;
}
public int asInt() {
return opcode;
}
@ -611,4 +624,36 @@ public enum OpCode {
public static OpCode code(byte code) {
return intToTypeMap.get(code);
}
public Tier getTier() {
return this.tier;
}
public enum Tier {
ZeroTier(0),
BaseTier(2),
VeryLowTier(3),
LowTier(5),
MidTier(8),
HighTier(10),
ExtTier(20),
SpecialTier(1), //TODO #POC9 is this correct?? "multiparam" from cpp
InvalidTier(0);
private final int level;
private Tier(int level) {
this.level = level;
}
public int asInt() {
return level;
}
}
;
}

View File

@ -44,8 +44,8 @@ public class PrecompiledContracts {
// gas charge for the execution:
// minimum 1 and additional 1 for each 32 bytes word (round up)
if (data == null) return 1;
return 1 + (data.length + 31) / 32 * 1;
if (data == null) return 15;
return 15 + (data.length + 31) / 32 * 3;
}
@Override
@ -62,8 +62,8 @@ public class PrecompiledContracts {
// gas charge for the execution:
// minimum 50 and additional 50 for each 32 bytes word (round up)
if (data == null) return 50;
return 50 + (data.length + 31) / 32 * 50;
if (data == null) return 60;
return 60 + (data.length + 31) / 32 * 12;
}
@Override
@ -81,10 +81,11 @@ public class PrecompiledContracts {
@Override
public long getGasForData(byte[] data) {
// TODO #POC9 Replace magic numbers with constants
// gas charge for the execution:
// minimum 50 and additional 50 for each 32 bytes word (round up)
if (data == null) return 50;
return 50 + (data.length + 31) / 32 * 50;
if (data == null) return 600;
return 600 + (data.length + 31) / 32 * 120;
}
@Override
@ -103,7 +104,7 @@ public class PrecompiledContracts {
@Override
public long getGasForData(byte[] data) {
return 500;
return 3000;
}
@Override

View File

@ -3,6 +3,7 @@ package org.ethereum.vm;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.ContractDetails;
import org.ethereum.facade.Repository;
import org.ethereum.util.BIUtil;
import org.ethereum.util.ByteUtil;
import org.ethereum.vm.MessageCall.MsgType;
import org.ethereum.vm.PrecompiledContracts.PrecompiledContract;
@ -22,7 +23,9 @@ import java.util.*;
import static java.lang.String.format;
import static org.ethereum.config.SystemProperties.CONFIG;
import static org.ethereum.util.BIUtil.*;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
//import static org.springframework.util.StringUtils.isEmpty;
import static org.springframework.util.StringUtils.hasLength;
/**
@ -42,6 +45,9 @@ public class Program {
*/
private static final int MAX_DEPTH = 1024;
//Max size for stack checks
private static final int MAX_STACKSIZE = 1024;
ProgramInvokeFactory programInvokeFactory = new ProgramInvokeFactoryImpl();
private int invokeHash;
@ -113,20 +119,21 @@ public class Program {
public void stackPush(byte[] data) {
DataWord stackWord = new DataWord(data);
stack.push(stackWord);
stackPush(stackWord);
}
public void stackPushZero() {
DataWord stackWord = new DataWord(0);
stack.push(stackWord);
stackPush(stackWord);
}
public void stackPushOne() {
DataWord stackWord = new DataWord(1);
stack.push(stackWord);
stackPush(stackWord);
}
public void stackPush(DataWord stackWord) {
stackMax(0, 1); //Sanity Check
stack.push(stackWord);
}
@ -195,6 +202,12 @@ public class Program {
}
}
public void stackMax(int argsReqs, int returnReqs) {
if ( (stack.size() - argsReqs + returnReqs) > MAX_STACKSIZE) {
throw new StackTooLargeException("Expected: overflow 1024 elements stack limit");
}
}
public int getMemSize() {
return memory != null ? memory.limit() : 0;
}
@ -288,19 +301,18 @@ public class Program {
}
public void suicide(DataWord obtainer) {
public void suicide(DataWord obtainerDW) {
byte[] owner = getOwnerAddress().getLast20Bytes();
byte[] obtainer = obtainerDW.getLast20Bytes();
BigInteger balance = result.getRepository().getBalance(owner);
DataWord balance = getBalance(this.getOwnerAddress());
// 1) pass full endowment to the obtainer
if (logger.isInfoEnabled())
logger.info("Transfer to: [{}] heritage: [{}]",
Hex.toHexString(obtainer.getLast20Bytes()),
balance.longValue());
Hex.toHexString(obtainer),
balance);
this.result.getRepository().addBalance(obtainer.getLast20Bytes(), balance.value());
this.result.getRepository().addBalance(this.getOwnerAddress().getLast20Bytes(), balance.value().negate());
// 2) mark the account as for delete
transfer(result.getRepository(), owner, obtainer, balance);
result.addDeleteAccount(this.getOwnerAddress());
}
@ -332,7 +344,6 @@ public class Program {
// [2] CREATE THE CONTRACT ADDRESS
byte[] nonce = result.getRepository().getNonce(senderAddress).toByteArray();
byte[] newAddress = HashUtil.calcNewAddr(this.getOwnerAddress().getLast20Bytes(), nonce);
result.getRepository().createAccount(newAddress);
if (invokeData.byTestingSuite()) {
// This keeps track of the contracts created for a test
@ -341,12 +352,6 @@ public class Program {
value.getNoLeadZeroesData());
}
// [4] TRANSFER THE BALANCE
result.getRepository().addBalance(senderAddress, endowment.negate());
BigInteger newBalance = BigInteger.ZERO;
if (!invokeData.byTestingSuite()) {
newBalance = result.getRepository().addBalance(newAddress, endowment);
}
// [3] UPDATE THE NONCE
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
@ -356,6 +361,19 @@ public class Program {
Repository track = result.getRepository().startTracking();
//In case of hashing collisions, check for any balance before createAccount()
BigInteger oldBalance = result.getRepository().getBalance(newAddress);
track.createAccount(newAddress);
track.addBalance(newAddress,oldBalance);
// [4] TRANSFER THE BALANCE
track.addBalance(senderAddress, endowment.negate());
BigInteger newBalance = BigInteger.ZERO;
if (!invokeData.byTestingSuite()) {
newBalance = track.addBalance(newAddress, endowment);
}
// [5] COOK THE INVOKE AND EXECUTE
ProgramInvoke programInvoke = programInvokeFactory.createProgramInvoke(
this, new DataWord(newAddress), DataWord.ZERO, gasLimit,
@ -390,7 +408,7 @@ public class Program {
}
// 4. CREATE THE CONTRACT OUT OF RETURN
byte[] code = result.getHReturn().array();
byte[] code = result.getHReturn();
long storageCost = code.length * GasCost.CREATE_DATA_BYTE;
long afterSpend = invokeData.getGas().longValue() - storageCost - result.getGasUsed();
@ -398,7 +416,7 @@ public class Program {
track.saveCode(newAddress, EMPTY_BYTE_ARRAY);
} else {
result.spendGas(code.length * GasCost.CREATE_DATA_BYTE);
result.spendGas(code.length * GasCost.CREATE_DATA);
track.saveCode(newAddress, code);
}
@ -449,28 +467,22 @@ public class Program {
logger.info(msg.getType().name() + " for existing contract: address: [{}], outDataOffs: [{}], outDataSize: [{}] ",
Hex.toHexString(contextAddress), msg.getOutDataOffs().longValue(), msg.getOutDataSize().longValue());
*/
// 2.1 PERFORM THE GAS VALUE TX
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
if (this.getGas().longValue() - msg.getGas().longValue() < 0) {
OutOfGasException ex = new OutOfGasException("Not enough gas for the internal call: fromAddress[%s], codeAddress[%s];",
Hex.toHexString(senderAddress), Hex.toHexString(codeAddress));
gasLogger.info(ex.getMessage());
throw ex;
}
Repository trackRepository = result.getRepository().startTracking();
BigInteger endowment = msg.getEndowment().value();
BigInteger senderBalance = result.getRepository().getBalance(senderAddress);
if (senderBalance.compareTo(endowment) < 0) {
// 2.1 PERFORM THE VALUE (endowment) PART
BigInteger endowment = msg.getEndowment().value(); //TODO #POC9 add 1024 stack check <=
BigInteger senderBalance = trackRepository.getBalance(senderAddress);
if (isNotCovers(senderBalance, endowment)) {
stackPushZero();
this.refundGas(msg.getGas().longValue(), "refund gas from message call");
return;
}
result.getRepository().addBalance(senderAddress, endowment.negate());
trackRepository .addBalance(senderAddress, endowment.negate());
BigInteger contextBalance = BigInteger.ZERO;
if (!invokeData.byTestingSuite()) {
contextBalance = result.getRepository().addBalance(contextAddress, endowment);
contextBalance = trackRepository.addBalance(contextAddress, endowment);
}
if (invokeData.byTestingSuite()) {
@ -480,10 +492,6 @@ public class Program {
msg.getEndowment().getNoLeadZeroesData());
}
// actual gas subtract
this.spendGas(msg.getGas().longValue(), "internal call");
Repository trackRepository = result.getRepository().startTracking();
ProgramInvoke programInvoke = programInvokeFactory.createProgramInvoke(
this, new DataWord(contextAddress), msg.getEndowment(),
msg.getGas(), contextBalance, data, trackRepository, this.invokeData.getBlockStore(), invokeData.byTestingSuite());
@ -498,6 +506,7 @@ public class Program {
this.getProgramTrace().merge(program.getProgramTrace());
this.result.addDeleteAccounts(result.getDeleteAccounts());
this.result.addLogInfos(result.getLogInfoList());
this.result.futureRefundGas(result.getFutureRefund());
}
if (result != null &&
@ -514,15 +523,15 @@ public class Program {
// 3. APPLY RESULTS: result.getHReturn() into out_memory allocated
if (result != null) {
ByteBuffer buffer = result.getHReturn();
byte[] buffer = result.getHReturn();
int allocSize = msg.getOutDataSize().intValue();
if (buffer != null && allocSize > 0) {
int retSize = buffer.limit();
int retSize = buffer.length;
int offset = msg.getOutDataOffs().intValue();
if (retSize > allocSize)
this.memorySave(offset, buffer.array());
this.memorySave(offset, buffer);
else
this.memorySave(offset, allocSize, buffer.array());
this.memorySave(offset, allocSize, buffer);
}
}
@ -532,8 +541,8 @@ public class Program {
// 5. REFUND THE REMAIN GAS
if (result != null) {
BigInteger refundGas = msg.getGas().value().subtract(BigInteger.valueOf(result.getGasUsed()));
if (refundGas.signum() == 1) {
BigInteger refundGas = msg.getGas().value().subtract(toBI(result.getGasUsed()));
if (isPositive(refundGas)) {
this.refundGas(refundGas.longValue(), "remaining gas from the internal call");
if (gasLogger.isInfoEnabled())
gasLogger.info("The remaining gas refunded, account: [{}], gas: [{}] ",
@ -565,9 +574,14 @@ public class Program {
}
public void futureRefundGas(long gasValue) {
logger.info("Future refund added: [{}]", gasValue);
result.futureRefundGas(gasValue);
}
public void resetFutureRefund() {
result.resetFutureRefund();
}
public void storageSave(DataWord word1, DataWord word2) {
storageSave(word1.getData(), word2.getData());
}
@ -793,7 +807,7 @@ public class Program {
if (result.getHReturn() != null)
globalOutput.append("\n HReturn: ").append(
Hex.toHexString(result.getHReturn().array()));
Hex.toHexString(result.getHReturn()));
// sophisticated assumption that msg.data != codedata
// means we are calling the contract not creating it
@ -842,7 +856,7 @@ public class Program {
fw = new FileWriter(dumpFile.getAbsoluteFile());
bw = new BufferedWriter(fw);
bw.write(programTrace.asJsonString(true));
bw.write(programTrace.asJsonString());
} catch (IOException e) {
logger.error(e.getMessage(), e);
} finally {
@ -874,17 +888,21 @@ public class Program {
}
public static String stringify(byte[] code, int index, String result) {
public static String stringify(byte[] code, int index, String result){
if (code == null || code.length == 0)
return result;
final byte opCode = code[index];
OpCode op = OpCode.code(opCode);
if (op == null) {
throw Program.Exception.invalidOpCode(opCode);
throw Program.Exception.invalidOpCode(opCode);
}
final byte[] continuedCode;
if (op == null) throw new IllegalOperationException("Invalid operation: " +
Hex.toHexString(code, index, 1));
switch(op) {
case PUSH1: case PUSH2: case PUSH3: case PUSH4: case PUSH5: case PUSH6: case PUSH7: case PUSH8:
case PUSH9: case PUSH10: case PUSH11: case PUSH12: case PUSH13: case PUSH14: case PUSH15: case PUSH16:
@ -919,8 +937,21 @@ public class Program {
public void callToPrecompiledAddress(MessageCall msg, PrecompiledContract contract) {
Repository track = this.getResult().getRepository().startTracking();
byte[] senderAddress = this.getOwnerAddress().getLast20Bytes();
byte[] codeAddress = msg.getCodeAddress().getLast20Bytes();
BigInteger endowment = msg.getEndowment().value();
BigInteger senderBalance = result.getRepository().getBalance(senderAddress);
if (senderBalance.compareTo(endowment) < 0) {
stackPushZero();
this.refundGas(msg.getGas().longValue(), "refund gas from message call");
return;
}
byte[] data = this.memoryChunk(msg.getInDataOffs(), msg.getInDataSize()).array();
this.result.getRepository().addBalance(this.getOwnerAddress().getLast20Bytes(), msg.getEndowment().value().negate());
transfer(track, senderAddress, codeAddress, msg.getEndowment().value());
if (invokeData.byTestingSuite()) {
// This keeps track of the calls created for a test
@ -934,20 +965,20 @@ public class Program {
}
this.result.getRepository().addBalance(msg.getCodeAddress().getLast20Bytes(), msg.getEndowment().value());
long requiredGas = contract.getGasForData(data);
if (requiredGas > msg.getGas().longValue()) {
this.spendGas(msg.getGas().longValue(), "call pre-compiled");
this.refundGas(0, "call pre-compiled"); //matches cpp logic
this.stackPushZero();
track.rollback();
} else {
this.spendGas(requiredGas, "call pre-compiled");
this.refundGas(msg.getGas().longValue() - requiredGas, "call pre-compiled");
byte[] out = contract.execute(data);
this.memorySave(msg.getOutDataOffs().intValue(), out);
this.stackPushOne();
track.commit();
}
}
@ -957,7 +988,7 @@ public class Program {
@SuppressWarnings("serial")
public static class OutOfGasException extends RuntimeException {
public OutOfGasException(String message, Object... args) {
super(format(message, args));
}
@ -965,7 +996,7 @@ public class Program {
@SuppressWarnings("serial")
public static class IllegalOperationException extends RuntimeException {
public IllegalOperationException(String message, Object... args) {
super(format(message, args));
}
@ -981,7 +1012,7 @@ public class Program {
@SuppressWarnings("serial")
public static class StackTooSmallException extends RuntimeException {
public StackTooSmallException(String message, Object... args) {
super(format(message, args));
}
@ -1021,6 +1052,13 @@ public class Program {
}
}
@SuppressWarnings("serial")
public class StackTooLargeException extends RuntimeException {
public StackTooLargeException(String message) {
super(message);
}
}
/**
* used mostly for testing reasons
*/

View File

@ -171,18 +171,22 @@ public class ProgramInvokeFactoryImpl implements ProgramInvokeFactory {
balance.toString(),
gasPrice.longValue(),
gas.longValue(),
callValue.toString(),
Hex.toHexString(callValue.getNoLeadZeroesData()),
data == null ? "" : Hex.toHexString(data),
Hex.toHexString(lastHash.getData()),
Hex.toHexString(coinbase.getLast20Bytes()),
timestamp.longValue(),
number.longValue(),
Hex.toHexString(difficulty.getNoLeadZeroesData()),
gasLimit.longValue());
gasLimit.bigIntValue());
}
*/
return new ProgramInvokeImpl(address, origin, caller, balance, gasPrice, gas, callValue,
data, lastHash, coinbase, timestamp, number, difficulty, gasLimit,
repository, program.invokeData.getCallDeep() + 1, blockStore, byTestingSuite);
}
public void setBlockchain(Blockchain blockchain) {
this.blockchain = blockchain;
}
}

View File

@ -1,12 +1,15 @@
package org.ethereum.vm;
import org.ethereum.facade.Repository;
import org.ethereum.util.ByteUtil;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
/**
* @author Roman Mandeleil
* @since 07.06.2014
@ -14,7 +17,7 @@ import java.util.List;
public class ProgramResult {
private long gasUsed = 0;
private ByteBuffer hReturn = null;
private byte[] hReturn = EMPTY_BYTE_ARRAY;
private RuntimeException exception;
private List<DataWord> deleteAccounts;
private List<LogInfo> logInfoList;
@ -38,13 +41,11 @@ public class ProgramResult {
}
public void setHReturn(byte[] hReturn) {
this.hReturn = ByteBuffer.allocate(hReturn.length);
this.hReturn.put(hReturn);
this.hReturn = hReturn;
}
public ByteBuffer getHReturn() {
if (hReturn == null)
this.setHReturn(new byte[] {});
public byte[] getHReturn() {
return hReturn;
}
@ -117,4 +118,8 @@ public class ProgramResult {
public long getFutureRefund() {
return futureRefund;
}
public void resetFutureRefund() {
futureRefund = 0;
}
}

View File

@ -79,8 +79,10 @@ public class VM {
throw Program.Exception.invalidOpCode(program.getCurrentOp());
}
program.setLastOp(op.val());
program.stackRequire(op.require());
program.stackMax(op.require(), op.ret()); //Check not exceeding stack limits
long oldMemSize = program.getMemSize();
BigInteger newMemSize = BigInteger.ZERO;
@ -89,10 +91,22 @@ public class VM {
String hint = "";
long callGas = 0, memWords = 0; // parameters for logging
long gasCost = GasCost.STEP;
long gasCost = op.getTier().asInt();
long gasBefore = program.getGas().longValue();
int stepBefore = program.getPC();
/*DEBUG #POC9 if( op.asInt() == 96 || op.asInt() == -128 || op.asInt() == 57 || op.asInt() == 115) {
//byte alphaone = 0x63;
//op = OpCode.code(alphaone);
gasCost = 3;
}
if( op.asInt() == -13 ) {
//byte alphaone = 0x63;
//op = OpCode.code(alphaone);
gasCost = 0;
}*/
// Calculate fees and spend gas
switch (op) {
case STOP:
@ -104,13 +118,13 @@ public class VM {
DataWord newValue = stack.get(stack.size() - 2);
DataWord oldValue = program.storageLoad(stack.peek());
if (oldValue == null && !newValue.isZero())
gasCost = GasCost.SSTORE;
gasCost = GasCost.SET_SSTORE;
else if (oldValue != null && newValue.isZero()) {
// todo: GASREFUND counter policy
// refund step cost policy.
program.futureRefundGas(GasCost.REFUND_SSTORE);
gasCost = 0;
gasCost = GasCost.CLEAR_SSTORE;
} else
gasCost = GasCost.RESET_SSTORE;
break;
@ -132,6 +146,7 @@ public class VM {
newMemSize = memNeeded(stack.peek(), new DataWord(32));
break;
case RETURN:
gasCost = GasCost.STOP; //rename?
newMemSize = memNeeded(stack.peek(), stack.get(stack.size() - 2));
break;
case SHA3:
@ -160,7 +175,19 @@ public class VM {
if (callGasWord.compareTo(program.getGas()) == 1) {
throw Program.Exception.notEnoughOpGas(op, callGasWord, program.getGas());
}
DataWord callAddressWord = stack.get(stack.size() - 2);
//check to see if account does not exist and is not a precompiled contract
if (op != CALLCODE && !program.result.getRepository().isExist(callAddressWord.getLast20Bytes()))
gasCost += GasCost.NEW_ACCT_CALL;
//TODO #POC9 Make sure this is converted to BigInteger (256num support)
if (stack.get(stack.size() - 3).intValue() > 0 )
gasCost += GasCost.VT_CALL;
callGas = callGasWord.longValue();
gasCost += callGas;
BigInteger in = memNeeded(stack.get(stack.size() - 4), stack.get(stack.size() - 5)); // in offset+size
BigInteger out = memNeeded(stack.get(stack.size() - 6), stack.get(stack.size() - 7)); // out offset+size
newMemSize = in.max(out);
@ -197,6 +224,8 @@ public class VM {
default:
break;
}
//DEBUG System.out.println(" OP IS " + op.name() + " GASCOST IS " + gasCost + " NUM IS " + op.asInt());
program.spendGas(gasCost, op.name());
// Avoid overflows
@ -207,14 +236,17 @@ public class VM {
// memory gas calc
long memoryUsage = (newMemSize.longValue() + 31) / 32 * 32;
if (memoryUsage > oldMemSize) {
memWords = (memoryUsage - oldMemSize) / 32;
long memGas = GasCost.MEMORY * (memWords + memWords * memWords / 1024);
memWords = (memoryUsage / 32);
long memWordsOld = (oldMemSize / 32);
//TODO #POC9 c_quadCoeffDiv = 512, this should be a constant, not magic number
long memGas = ( GasCost.MEMORY * memWords + memWords * memWords / 512)
- (GasCost.MEMORY * memWordsOld + memWordsOld * memWordsOld / 512);
program.spendGas(memGas, op.name() + " (memory usage)");
gasCost += memGas;
}
if (copySize > 0) {
long copyGas = GasCost.COPY_GAS * (copySize + 31) / 32;
long copyGas = GasCost.COPY_GAS * ((copySize + 31) / 32);
gasCost += copyGas;
program.spendGas(copyGas, op.name() + " (copy usage)");
}
@ -1024,23 +1056,28 @@ public class VM {
DataWord codeAddress = program.stackPop();
DataWord value = program.stackPop();
if( value.intValue() > 0)
gas = new DataWord(gas.intValue() + GasCost.STIPEND_CALL);
DataWord inDataOffs = program.stackPop();
DataWord inDataSize = program.stackPop();
DataWord outDataOffs = program.stackPop();
DataWord outDataSize = program.stackPop();
/*
if (logger.isInfoEnabled()) {
hint = "addr: " + Hex.toHexString(codeAddress.getLast20Bytes())
+ " gas: " + gas.shortHex()
+ " inOff: " + inDataOffs.shortHex()
+ " inSize: " + inDataSize.shortHex();
/*
logger.info(logString, program.getPC(),
String.format("%-12s", op.name()),
program.getGas().value(),
program.invokeData.getCallDeep(), hint);
}
*/
}
program.memoryExpand(outDataOffs, outDataSize);
MessageCall msg = new MessageCall(
@ -1101,6 +1138,7 @@ public class VM {
} catch (RuntimeException e) {
logger.warn("VM halted: [{}]", e.toString());
program.spendAllGas();
program.resetFutureRefund();
program.stop();
throw e;
} finally {

View File

@ -1,6 +1,8 @@
package org.ethereum.vmtrace;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.nio.ByteBuffer;
@ -16,11 +18,12 @@ import static org.ethereum.vmtrace.Serializers.serializeFieldsOnly;
*/
public class ProgramTrace {
private static final Logger LOGGER = LoggerFactory.getLogger("vmtrace");
@JsonIgnore
private byte[] txHash;
private List<Op> ops = new ArrayList<>();
private String result = "";
private String error = "";
private String result;
private String error;
public void setTxHash(byte[] txHash) {
this.txHash = txHash;
@ -31,7 +34,7 @@ public class ProgramTrace {
}
public void setError(Exception error) {
this.error = (error == null) ? "" : format("%s: %s", error.getClass().getSimpleName(), error.getMessage());
this.error = (error == null) ? "" : format("%s: %s", error.getClass(), error.getMessage());
}
public void addOp(Op op) {
@ -46,11 +49,7 @@ public class ProgramTrace {
this.ops.addAll(programTrace.ops);
}
public String asJsonString(boolean needPrettify) {
return serializeFieldsOnly(this, needPrettify);
}
public String asJsonString() {
return asJsonString(false);
return serializeFieldsOnly(this, true);
}
}

View File

@ -59,7 +59,7 @@ public class EtherSaleWallet {
'}';
}
public static byte[] hexStringToByteArray(String s) {
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {

View File

@ -93,7 +93,8 @@
<bean id="channel" class="org.ethereum.net.server.Channel" scope="prototype">
</bean>
<bean id="ethereumChannelInitializer" class="org.ethereum.net.server.EthereumChannelInitializer">
<bean id="ethereumChannelInitializer" class="org.ethereum.net.server.EthereumChannelInitializer" scope="prototype">
</bean>
<bean id="peerServer" class="org.ethereum.net.server.PeerServer">
@ -102,10 +103,7 @@
<bean id="shhHandler" class="org.ethereum.net.shh.ShhHandler" scope="prototype">
</bean>
<bean id="messageDecoder" class="org.ethereum.net.wire.MessageDecoder" scope="prototype">
</bean>
<bean id="messageEncoder" class="org.ethereum.net.wire.MessageEncoder" scope="prototype">
<bean id="messageCodec" class="org.ethereum.net.wire.MessageCodec" scope="prototype">
</bean>
<bean id="programInvokeFactory" class="org.ethereum.vm.ProgramInvokeFactoryImpl">