mirror of
https://github.com/status-im/ethereumj-personal.git
synced 2025-02-10 18:56:29 +00:00
Merged poc-9 branch.
Some minimalistic console ui implementation.
This commit is contained in:
parent
ff71583142
commit
0b5f6f9c02
@ -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"
|
||||
|
@ -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" >
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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'
|
||||
|
@ -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) {
|
||||
|
@ -109,4 +109,4 @@ public class Account {
|
||||
pendingTransactions.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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");
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -144,4 +144,4 @@ public class LevelDbDataSource implements KeyValueDataSource {
|
||||
logger.error("Failed to find the db file on the close: {} ", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -108,4 +108,4 @@ public class RedisConnectionImpl implements RedisConnection {
|
||||
public KeyValueDataSource createDataSource(String name) {
|
||||
return new RedisDataSource(name, jedisPool);
|
||||
}
|
||||
}
|
||||
}
|
@ -50,4 +50,4 @@ public interface BlockStore {
|
||||
void reset();
|
||||
|
||||
TransactionReceipt getTransactionReceiptByHash(byte[] hash);
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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()))
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -122,4 +122,4 @@ public class CommonConfig {
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
@ -28,4 +28,4 @@ public class DefaultConfig {
|
||||
return new InMemoryBlockStore();
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -31,4 +31,4 @@ public class RemoteConfig {
|
||||
return new InMemoryBlockStore();
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 +
|
||||
'}';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -21,7 +21,7 @@ public class TestProgramInvokeFactory implements ProgramInvokeFactory {
|
||||
|
||||
private final Env env;
|
||||
|
||||
TestProgramInvokeFactory(Env env) {
|
||||
public TestProgramInvokeFactory(Env env) {
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
|
@ -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)) {
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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() + ")";
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -89,4 +89,5 @@ public enum P2pMessageCodes {
|
||||
public byte asByte() {
|
||||
return (byte) (cmd);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -33,8 +33,9 @@ public class PeerServer {
|
||||
|
||||
@Autowired
|
||||
public ChannelManager channelManager;
|
||||
|
||||
@Autowired
|
||||
|
||||
// TODO: this was removed ???
|
||||
@Autowired
|
||||
public EthereumChannelInitializer ethereumChannelInitializer;
|
||||
|
||||
@Autowired
|
||||
|
@ -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()));
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 block’s coinbase address
|
||||
*/
|
||||
COINBASE(0x41, 0),
|
||||
COINBASE(0x41, 0, 1, BaseTier),
|
||||
/**
|
||||
* (x042) Get the block’s timestamp
|
||||
*/
|
||||
TIMESTAMP(0x42, 0),
|
||||
TIMESTAMP(0x42, 0, 1, BaseTier),
|
||||
/**
|
||||
* (0x43) Get the block’s number
|
||||
*/
|
||||
NUMBER(0x43, 0),
|
||||
NUMBER(0x43, 0, 1, BaseTier),
|
||||
/**
|
||||
* (0x44) Get the block’s difficulty
|
||||
*/
|
||||
DIFFICULTY(0x44, 0),
|
||||
DIFFICULTY(0x44, 0, 1, BaseTier),
|
||||
/**
|
||||
* (0x45) Get the block’s 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;
|
||||
}
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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">
|
||||
|
Loading…
x
Reference in New Issue
Block a user