CALL: first draft
+ recursive contract tested + tracking changes for trie introduced + genesis updated to protocol 20
This commit is contained in:
parent
f917bf8e97
commit
8cad126c0c
|
@ -1,6 +1,6 @@
|
|||
package org.ethereum.core;
|
||||
|
||||
import org.ethereum.db.Database;
|
||||
import org.ethereum.db.DatabaseImpl;
|
||||
import org.ethereum.manager.WorldManager;
|
||||
import org.ethereum.net.message.StaticMessages;
|
||||
import org.ethereum.net.submit.WalletTransaction;
|
||||
|
@ -10,7 +10,6 @@ import org.slf4j.LoggerFactory;
|
|||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
||||
import static org.ethereum.core.Denomination.*;
|
||||
|
@ -31,7 +30,7 @@ public class Blockchain {
|
|||
// to avoid using minGasPrice=0 from Genesis for the wallet
|
||||
private static long INITIAL_MIN_GAS_PRICE = 10 * SZABO.longValue();
|
||||
|
||||
private Database db;
|
||||
private DatabaseImpl db;
|
||||
private Wallet wallet;
|
||||
|
||||
private long gasPrice = 1000;
|
||||
|
|
|
@ -48,8 +48,6 @@ public class ContractDetails {
|
|||
RLPItem rlpItem = (RLPItem)values.get(i);
|
||||
storageValues.add(new DataWord(rlpItem.getRLPData()));
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
public ContractDetails(Map<DataWord, DataWord> storage) {
|
||||
|
@ -97,7 +95,9 @@ public class ContractDetails {
|
|||
|
||||
Map<DataWord, DataWord> storage = new HashMap<>();
|
||||
|
||||
for (int i = 0; i < storageKeys.size(); ++i){
|
||||
for (int i = 0;
|
||||
storageKeys != null &&
|
||||
i < storageKeys.size(); ++i){
|
||||
storage.put(storageKeys.get(i), storageValues.get(i));
|
||||
}
|
||||
|
||||
|
|
|
@ -26,22 +26,24 @@ import org.spongycastle.util.encoders.Hex;
|
|||
* See Yellow Paper: http://www.gavwood.com/Paper.pdf (Appendix I. Genesis Block)
|
||||
*/
|
||||
public class Genesis extends Block {
|
||||
|
||||
|
||||
private String[] premine = new String[] {
|
||||
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
|
||||
"e4157b34ea9615cfbde6b4fda419828124b70c78", // # (CH)
|
||||
"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // # (V)
|
||||
"6c386a4b26f73c802f34673f7248bb118f97424a", // # (HH)
|
||||
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826", // # (R)
|
||||
"2ef47100e0787b915105fd5e3f4ff6752079d5cb", // # (M)
|
||||
"e6716f9544a56c530d868e4bfbacb172315bdead", // # (J)
|
||||
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // # (A)
|
||||
};
|
||||
|
||||
Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
// The proof-of-concept series include a development premine, making the state root hash
|
||||
// The proof-of-concept series include a development premine, making the state root hash
|
||||
// some value stateRoot. The latest documentation should be consulted for the value of the state root.
|
||||
private AccountState acct = new AccountState(BigInteger.ZERO, BigInteger.valueOf(2).pow(200));
|
||||
private String[] premine = new String[] {
|
||||
"2ef47100e0787b915105fd5e3f4ff6752079d5cb", // # (M)
|
||||
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // # (A)
|
||||
"e6716f9544a56c530d868e4bfbacb172315bdead", // # (J)
|
||||
"8a40bfaa73256b60764c1bf40675a99083efb075", // # (G)
|
||||
"e4157b34ea9615cfbde6b4fda419828124b70c78", // # (CH)
|
||||
"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // # (V)
|
||||
"6c386a4b26f73c802f34673f7248bb118f97424a", // # (HH)
|
||||
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826" }; // # (R)
|
||||
|
||||
|
||||
|
||||
private static byte[] zeroHash256 = new byte[32];
|
||||
private static byte[] zeroHash160 = new byte[20];
|
||||
private static byte[] sha3EmptyList = HashUtil.sha3(RLP.encodeList());
|
||||
|
|
|
@ -156,7 +156,7 @@ public class Transaction {
|
|||
}
|
||||
|
||||
// TODO: performance improve multiply without BigInteger
|
||||
public BigInteger getTotalGasDebit(){
|
||||
public BigInteger getTotalGasValueDebit(){
|
||||
return new BigInteger(1, gasLimit).multiply(new BigInteger(1,gasPrice));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,95 +1,15 @@
|
|||
package org.ethereum.db;
|
||||
|
||||
import static org.iq80.leveldb.impl.Iq80DBFactory.factory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.ethereum.config.SystemProperties;
|
||||
import org.iq80.leveldb.DB;
|
||||
import org.iq80.leveldb.DBIterator;
|
||||
import org.iq80.leveldb.Options;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Generic interface for Ethereum database
|
||||
*
|
||||
* LevelDB key/value pair DB implementation will be used.
|
||||
* Choice must be made between:
|
||||
* Pure Java: https://github.com/dain/leveldb
|
||||
* JNI binding: https://github.com/fusesource/leveldbjni
|
||||
* www.ethereumJ.com
|
||||
*
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 11/06/2014 15:38
|
||||
*/
|
||||
public class Database {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(Database.class);
|
||||
private DB db;
|
||||
private String name;
|
||||
|
||||
|
||||
public Database(String name) {
|
||||
// Initialize Database
|
||||
this.name = name;
|
||||
Options options = new Options();
|
||||
options.createIfMissing(true);
|
||||
try {
|
||||
logger.debug("Opening database");
|
||||
if(SystemProperties.CONFIG.databaseReset()) {
|
||||
logger.debug("Destroying '" + name + "' DB on startup ENABLED");
|
||||
destroyDB(name);
|
||||
}
|
||||
logger.debug("Initializing new or existing DB: '" + name + "'");
|
||||
options.createIfMissing(true);
|
||||
db = factory.open(new File(name), options);
|
||||
// logger.debug("Showing database stats");
|
||||
// String stats = DATABASE.getProperty("leveldb.stats");
|
||||
// logger.debug(stats);
|
||||
} catch (IOException ioe) {
|
||||
logger.error(ioe.getMessage(), ioe);
|
||||
throw new RuntimeException("Can't initialize database");
|
||||
}
|
||||
}
|
||||
|
||||
public void destroyDB(String name) {
|
||||
logger.debug("Destroying existing DB");
|
||||
Options options = new Options();
|
||||
try {
|
||||
factory.destroy(new File(name), options);
|
||||
} catch (IOException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Insert object(value) (key = sha3(value)) */
|
||||
public void put(byte[] key, byte[] value) {
|
||||
db.put(key, value);
|
||||
}
|
||||
|
||||
/** Get object (key) -> value */
|
||||
public byte[] get(byte[] key) {
|
||||
return db.get(key);
|
||||
}
|
||||
|
||||
/** Delete object (key) from db **/
|
||||
public void delete(byte[] key) {
|
||||
delete(key);
|
||||
}
|
||||
|
||||
public DBIterator iterator() {
|
||||
return db.iterator();
|
||||
}
|
||||
|
||||
public DB getDb() {
|
||||
return this.db;
|
||||
}
|
||||
public interface Database {
|
||||
|
||||
public void close(){
|
||||
try {
|
||||
logger.info("Release DB: {}", name);
|
||||
db.close();
|
||||
} catch (IOException e) {
|
||||
logger.error("failed to find the db file on the close: {} ", name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public void put(byte[] key, byte[] value);
|
||||
public byte[] get(byte[] key);
|
||||
public void delete(byte[] key);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
package org.ethereum.db;
|
||||
|
||||
import static org.iq80.leveldb.impl.Iq80DBFactory.factory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.ethereum.config.SystemProperties;
|
||||
import org.iq80.leveldb.DB;
|
||||
import org.iq80.leveldb.DBIterator;
|
||||
import org.iq80.leveldb.Options;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Generic interface for Ethereum database
|
||||
*
|
||||
* LevelDB key/value pair DB implementation will be used.
|
||||
* Choice must be made between:
|
||||
* Pure Java: https://github.com/dain/leveldb
|
||||
* JNI binding: https://github.com/fusesource/leveldbjni
|
||||
*/
|
||||
public class DatabaseImpl implements Database{
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(DatabaseImpl.class);
|
||||
private DB db;
|
||||
private String name;
|
||||
|
||||
|
||||
public DatabaseImpl(String name) {
|
||||
// Initialize Database
|
||||
this.name = name;
|
||||
Options options = new Options();
|
||||
options.createIfMissing(true);
|
||||
try {
|
||||
logger.debug("Opening database");
|
||||
if(SystemProperties.CONFIG.databaseReset()) {
|
||||
logger.debug("Destroying '" + name + "' DB on startup ENABLED");
|
||||
destroyDB(name);
|
||||
}
|
||||
logger.debug("Initializing new or existing DB: '" + name + "'");
|
||||
options.createIfMissing(true);
|
||||
db = factory.open(new File(name), options);
|
||||
// logger.debug("Showing database stats");
|
||||
// String stats = DATABASE.getProperty("leveldb.stats");
|
||||
// logger.debug(stats);
|
||||
} catch (IOException ioe) {
|
||||
logger.error(ioe.getMessage(), ioe);
|
||||
throw new RuntimeException("Can't initialize database");
|
||||
}
|
||||
}
|
||||
|
||||
public void destroyDB(String name) {
|
||||
logger.debug("Destroying existing DB");
|
||||
Options options = new Options();
|
||||
try {
|
||||
factory.destroy(new File(name), options);
|
||||
} catch (IOException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Insert object(value) (key = sha3(value)) */
|
||||
public void put(byte[] key, byte[] value) {
|
||||
db.put(key, value);
|
||||
}
|
||||
|
||||
/** Get object (key) -> value */
|
||||
public byte[] get(byte[] key) {
|
||||
return db.get(key);
|
||||
}
|
||||
|
||||
/** Delete object (key) from db **/
|
||||
public void delete(byte[] key) {
|
||||
delete(key);
|
||||
}
|
||||
|
||||
public DBIterator iterator() {
|
||||
return db.iterator();
|
||||
}
|
||||
|
||||
public DB getDb() {
|
||||
return this.db;
|
||||
}
|
||||
|
||||
public void close(){
|
||||
try {
|
||||
logger.info("Release DB: {}", name);
|
||||
db.close();
|
||||
} catch (IOException e) {
|
||||
logger.error("failed to find the db file on the close: {} ", name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -10,7 +10,7 @@ import java.util.Map;
|
|||
* Created on: 11/06/2014 14:09
|
||||
*/
|
||||
|
||||
public class TrackDatabase {
|
||||
public class TrackDatabase implements Database{
|
||||
|
||||
private Database db;
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@ package org.ethereum.gui;
|
|||
import org.ethereum.core.Block;
|
||||
import org.ethereum.core.ContractDetails;
|
||||
import org.ethereum.core.Transaction;
|
||||
import org.ethereum.db.TrackDatabase;
|
||||
import org.ethereum.manager.WorldManager;
|
||||
import org.ethereum.trie.TrackTrie;
|
||||
import org.ethereum.vm.Program;
|
||||
import org.ethereum.vm.ProgramInvokeFactory;
|
||||
import org.ethereum.vm.ProgramInvokeImpl;
|
||||
|
@ -40,8 +43,17 @@ public class ProgramPlayDialog extends JPanel implements ActionListener,
|
|||
outputList = new ArrayList<String>();
|
||||
VM vm = new VM();
|
||||
|
||||
TrackDatabase trackDetailDB = new TrackDatabase( WorldManager.instance.detaildDB );
|
||||
TrackDatabase trackChainDb = new TrackDatabase( WorldManager.instance.chainDB);
|
||||
TrackTrie trackStateDB = new TrackTrie(WorldManager.instance.worldState );
|
||||
|
||||
Program program = new Program(code ,
|
||||
ProgramInvokeFactory.createProgramInvoke(tx, lastBlock, contractDetails));
|
||||
ProgramInvokeFactory.createProgramInvoke(tx, lastBlock, contractDetails,
|
||||
trackDetailDB, trackChainDb, trackStateDB));
|
||||
|
||||
trackDetailDB.rollbackTrack();
|
||||
trackChainDb.rollbackTrack();
|
||||
trackStateDB.rollbackTrack();
|
||||
|
||||
program.addListener(this);
|
||||
program.fullTrace();
|
||||
|
@ -53,7 +65,6 @@ public class ProgramPlayDialog extends JPanel implements ActionListener,
|
|||
stepSlider = new JSlider(JSlider.HORIZONTAL,
|
||||
0, outputList.size() - 1, 0);
|
||||
|
||||
|
||||
stepSlider.addChangeListener(this);
|
||||
|
||||
//Turn on labels at major tick marks.
|
||||
|
|
|
@ -5,7 +5,9 @@ import org.ethereum.core.Block;
|
|||
import org.ethereum.core.ContractDetails;
|
||||
import org.ethereum.core.Transaction;
|
||||
import org.ethereum.crypto.HashUtil;
|
||||
import org.ethereum.db.Database;
|
||||
import org.ethereum.db.DatabaseImpl;
|
||||
import org.ethereum.db.TrackDatabase;
|
||||
import org.ethereum.trie.TrackTrie;
|
||||
import org.ethereum.trie.Trie;
|
||||
import org.ethereum.vm.*;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -37,9 +39,9 @@ public class WorldManager {
|
|||
private Map<String, Transaction> pendingTransactions =
|
||||
Collections.synchronizedMap(new HashMap<String, Transaction>());
|
||||
|
||||
public Database chainDB = new Database("blockchain");
|
||||
public Database stateDB = new Database("state");
|
||||
public Database detaildDB = new Database("details");
|
||||
public DatabaseImpl chainDB = new DatabaseImpl("blockchain");
|
||||
public DatabaseImpl stateDB = new DatabaseImpl("state");
|
||||
public DatabaseImpl detaildDB = new DatabaseImpl("details");
|
||||
|
||||
public Trie worldState = new Trie(stateDB.getDb());
|
||||
|
||||
|
@ -48,6 +50,8 @@ public class WorldManager {
|
|||
// TODO: refactor the wallet transactions to the world manager
|
||||
MainData.instance.getBlockchain().addWalletTransaction(tx);
|
||||
|
||||
// TODO: what is going on with simple wallet transfer
|
||||
|
||||
// 1. VALIDATE THE NONCE
|
||||
byte[] senderAddress = tx.getSender();
|
||||
byte[] stateData = worldState.get(senderAddress);
|
||||
|
@ -66,18 +70,26 @@ public class WorldManager {
|
|||
return;
|
||||
}
|
||||
|
||||
// 2. THE SIMPLE BALANCE CHANGE SHOULD HAPPEN ANYWAY
|
||||
AccountState receiverState = null;
|
||||
// 2.1 PERFORM THE GAS VALUE TX
|
||||
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
|
||||
|
||||
// Check if the receive is a new contract
|
||||
// first of all debit the gas from the issuer
|
||||
AccountState receiverState = null;
|
||||
BigInteger gasDebit = tx.getTotalGasValueDebit();
|
||||
byte[] contractAddress;
|
||||
|
||||
// Contract creation or existing Contract call
|
||||
if (tx.isContractCreation()) {
|
||||
byte[] contractAddress = tx.getContractAddress();
|
||||
|
||||
// credit the receiver
|
||||
contractAddress = tx.getContractAddress();
|
||||
receiverState = new AccountState();
|
||||
worldState.update(contractAddress, receiverState.getEncoded());
|
||||
stateLogger.info("New contract created address={}",
|
||||
Hex.toHexString(contractAddress));
|
||||
} else {
|
||||
// receiver was not set by creation of contract
|
||||
|
||||
contractAddress = tx.getReceiveAddress();
|
||||
byte[] accountData = this.worldState.get(tx.getReceiveAddress());
|
||||
if (accountData.length == 0){
|
||||
receiverState = new AccountState();
|
||||
|
@ -87,98 +99,134 @@ public class WorldManager {
|
|||
} else {
|
||||
receiverState = new AccountState(accountData);
|
||||
if (stateLogger.isInfoEnabled())
|
||||
stateLogger.info("Account updated address={}",
|
||||
stateLogger.info("Account found address={}",
|
||||
Hex.toHexString(tx.getReceiveAddress()));
|
||||
}
|
||||
}
|
||||
if(tx.getValue() != null) {
|
||||
receiverState.addToBalance(new BigInteger(1, tx.getValue()));
|
||||
senderState.addToBalance(new BigInteger(1, tx.getValue()).negate());
|
||||
}
|
||||
|
||||
// 2.2 UPDATE THE NONCE
|
||||
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
|
||||
if (senderState.getBalance().compareTo(BigInteger.ZERO) == 1) {
|
||||
senderState.incrementNonce();
|
||||
worldState.update(tx.getSender(), senderState.getEncoded());
|
||||
worldState.update(tx.getReceiveAddress(), receiverState.getEncoded());
|
||||
}
|
||||
|
||||
// 3. FIND OUT WHAT IS THE TRANSACTION TYPE
|
||||
if (tx.isContractCreation()) {
|
||||
|
||||
byte[] initCode = tx.getData();
|
||||
|
||||
Block lastBlock =
|
||||
MainData.instance.getBlockchain().getLastBlock();
|
||||
|
||||
ProgramInvoke programInvoke =
|
||||
ProgramInvokeFactory.createProgramInvoke(tx, lastBlock, null);
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("running the init for contract: addres={}" ,
|
||||
Hex.toHexString(tx.getContractAddress()));
|
||||
|
||||
// first of all debit the gas from the issuer
|
||||
BigInteger gasDebit = tx.getTotalGasDebit();
|
||||
senderState.addToBalance(gasDebit.negate());
|
||||
if (senderState.getBalance().signum() == -1){
|
||||
// todo: the sender can't afford this contract do Out-Of-Gas
|
||||
|
||||
}
|
||||
|
||||
if(stateLogger.isInfoEnabled())
|
||||
stateLogger.info("Before contract execution the sender address debit with gas total cost, \n sender={} \n contract={} \n gas_debit= {}",
|
||||
Hex.toHexString( tx.getSender() ), Hex.toHexString(tx.getContractAddress()), gasDebit);
|
||||
stateLogger.info("Before contract execution the sender address debit with gas total cost, " +
|
||||
"\n sender={} \n gas_debit= {}",
|
||||
Hex.toHexString( tx.getSender() ), gasDebit);
|
||||
|
||||
}
|
||||
|
||||
// actual gas value debit from the sender
|
||||
// the purchase gas will be available for the
|
||||
// contract in the execution state, and
|
||||
// can be validate using GAS op
|
||||
if (gasDebit.signum() == 1){
|
||||
|
||||
if (senderState.getBalance().subtract(gasDebit).signum() == -1){
|
||||
logger.info("No gas to start the execution: sender={}" , Hex.toHexString(tx.getSender()));
|
||||
return;
|
||||
}
|
||||
senderState.addToBalance(gasDebit.negate());
|
||||
worldState.update(senderAddress, senderState.getEncoded());
|
||||
}
|
||||
|
||||
VM vm = new VM();
|
||||
Program program = new Program(initCode, programInvoke);
|
||||
vm.play(program);
|
||||
ProgramResult result = program.getResult();
|
||||
applyProgramResult(result, gasDebit, senderState, receiverState, senderAddress, tx.getContractAddress());
|
||||
// 3. START TRACKING FOR REVERT CHANGES OPTION !!!
|
||||
TrackDatabase trackDetailDB = new TrackDatabase( WorldManager.instance.detaildDB );
|
||||
TrackDatabase trackChainDb = new TrackDatabase( WorldManager.instance.chainDB);
|
||||
TrackTrie trackStateDB = new TrackTrie(WorldManager.instance.worldState );
|
||||
|
||||
} else {
|
||||
trackDetailDB.startTrack();
|
||||
trackChainDb.startTrack();
|
||||
trackStateDB.startTrack();
|
||||
|
||||
if (receiverState.getCodeHash() != HashUtil.EMPTY_DATA_HASH){
|
||||
try {
|
||||
|
||||
byte[] programCode = chainDB.get(receiverState.getCodeHash());
|
||||
if (programCode != null && programCode.length != 0){
|
||||
// 4. THE SIMPLE VALUE/BALANCE CHANGE
|
||||
if(tx.getValue() != null) {
|
||||
|
||||
Block lastBlock =
|
||||
MainData.instance.getBlockchain().getLastBlock();
|
||||
if (senderState.getBalance().subtract(new BigInteger(1, tx.getValue())).signum() >= 0){
|
||||
receiverState.addToBalance(new BigInteger(1, tx.getValue()));
|
||||
senderState.addToBalance(new BigInteger(1, tx.getValue()).negate());
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("calling for existing contract: addres={}" , Hex.toHexString(tx.getReceiveAddress()));
|
||||
trackStateDB.update(senderAddress, senderState.getEncoded());
|
||||
trackStateDB.update(contractAddress, receiverState.getEncoded());
|
||||
|
||||
// first of all debit the gas from the issuer
|
||||
BigInteger gasDebit = tx.getTotalGasDebit();
|
||||
senderState.addToBalance(gasDebit.negate());
|
||||
if (senderState.getBalance().signum() == -1){
|
||||
// todo: the sender can't afford this contract do Out-Of-Gas
|
||||
}
|
||||
|
||||
if(stateLogger.isInfoEnabled())
|
||||
stateLogger.info("Before contract execution the sender address debit with gas total cost, \n sender={} \n contract={} \n gas_debit= {}",
|
||||
Hex.toHexString( tx.getSender() ), Hex.toHexString(tx.getReceiveAddress()), gasDebit);
|
||||
worldState.update(senderAddress, senderState.getEncoded());
|
||||
|
||||
// FETCH THE SAVED STORAGE
|
||||
ContractDetails details = null;
|
||||
byte[] detailsRLPData = detaildDB.get(tx.getReceiveAddress());
|
||||
if (detailsRLPData.length > 0)
|
||||
details = new ContractDetails(detailsRLPData);
|
||||
|
||||
ProgramInvoke programInvoke =
|
||||
ProgramInvokeFactory.createProgramInvoke(tx, lastBlock, details);
|
||||
|
||||
VM vm = new VM();
|
||||
Program program = new Program(programCode, programInvoke);
|
||||
vm.play(program);
|
||||
|
||||
ProgramResult result = program.getResult();
|
||||
applyProgramResult(result, gasDebit, senderState, receiverState, senderAddress, tx.getReceiveAddress());
|
||||
if (stateLogger.isInfoEnabled())
|
||||
stateLogger.info("Update value balance \n " +
|
||||
"sender={}, receiver={}, value={}",
|
||||
Hex.toHexString(senderAddress),
|
||||
Hex.toHexString(contractAddress),
|
||||
new BigInteger( tx.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
// 3. FIND OUT WHAT IS THE TRANSACTION TYPE
|
||||
if (tx.isContractCreation()) {
|
||||
|
||||
byte[] initCode = tx.getData();
|
||||
|
||||
Block lastBlock =
|
||||
MainData.instance.getBlockchain().getLastBlock();
|
||||
|
||||
ProgramInvoke programInvoke =
|
||||
ProgramInvokeFactory.createProgramInvoke(tx, lastBlock, null, trackDetailDB, trackChainDb, trackStateDB);
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("running the init for contract: addres={}" ,
|
||||
Hex.toHexString(tx.getContractAddress()));
|
||||
|
||||
|
||||
VM vm = new VM();
|
||||
Program program = new Program(initCode, programInvoke);
|
||||
vm.play(program);
|
||||
ProgramResult result = program.getResult();
|
||||
applyProgramResult(result, gasDebit, senderState, receiverState, senderAddress, tx.getContractAddress());
|
||||
|
||||
} else {
|
||||
|
||||
if (receiverState.getCodeHash() != HashUtil.EMPTY_DATA_HASH){
|
||||
|
||||
byte[] programCode = chainDB.get(receiverState.getCodeHash());
|
||||
if (programCode != null && programCode.length != 0){
|
||||
|
||||
Block lastBlock =
|
||||
MainData.instance.getBlockchain().getLastBlock();
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("calling for existing contract: addres={}" , Hex.toHexString(tx.getReceiveAddress()));
|
||||
|
||||
|
||||
// FETCH THE SAVED STORAGE
|
||||
ContractDetails details = null;
|
||||
byte[] detailsRLPData = detaildDB.get(tx.getReceiveAddress());
|
||||
if (detailsRLPData.length > 0)
|
||||
details = new ContractDetails(detailsRLPData);
|
||||
|
||||
ProgramInvoke programInvoke =
|
||||
ProgramInvokeFactory.createProgramInvoke(tx, lastBlock, details, trackDetailDB, trackChainDb, trackStateDB);
|
||||
|
||||
VM vm = new VM();
|
||||
Program program = new Program(programCode, programInvoke);
|
||||
vm.play(program);
|
||||
|
||||
ProgramResult result = program.getResult();
|
||||
applyProgramResult(result, gasDebit, senderState, receiverState, senderAddress, tx.getReceiveAddress());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
trackDetailDB.rollbackTrack();
|
||||
trackChainDb.rollbackTrack();
|
||||
trackStateDB.rollbackTrack();
|
||||
return;
|
||||
}
|
||||
|
||||
trackDetailDB.commitTrack();
|
||||
trackChainDb.commitTrack();
|
||||
trackStateDB.commitTrack();
|
||||
|
||||
pendingTransactions.put(Hex.toHexString(tx.getHash()), tx);
|
||||
}
|
||||
|
||||
|
@ -201,9 +249,9 @@ public class WorldManager {
|
|||
|
||||
if (result.getException() != null &&
|
||||
result.getException() instanceof Program.OutOfGasException){
|
||||
logger.info("contract run halted by OutOfGas: contract={}", Hex.toHexString(contractAddress));
|
||||
|
||||
// todo: find out what exactly should be reverted in that case
|
||||
return;
|
||||
throw result.getException();
|
||||
}
|
||||
|
||||
// Save the code created by init
|
||||
|
@ -239,12 +287,6 @@ public class WorldManager {
|
|||
Hex.toHexString(bodyCode));
|
||||
}
|
||||
|
||||
// Save the storage changes.
|
||||
Map<DataWord, DataWord> storage = result.getStorage();
|
||||
if (storage != null){
|
||||
ContractDetails contractDetails = new ContractDetails(storage);
|
||||
detaildDB.put(contractAddress , contractDetails.getEncoded());
|
||||
}
|
||||
}
|
||||
|
||||
public void applyTransactionList(List<Transaction> txList) {
|
||||
|
|
|
@ -16,7 +16,7 @@ public class StaticMessages {
|
|||
public static final byte[] GET_TRANSACTIONS = Hex.decode("2240089100000002C116");
|
||||
|
||||
public static final byte[] DISCONNECT_08 = Hex.decode("2240089100000003C20108");
|
||||
public static final byte[] GENESIS_HASH = Hex.decode("77ef4fdaf389dca53236bcf7f72698e154eab2828f86fbc4fc6cd9225d285c89");
|
||||
public static final byte[] GENESIS_HASH = Hex.decode("56fff6ab5ef6f1ef8dafb7b4571b89a9ae1ab870e54197c59ea10ba6f2c7eb60");
|
||||
public static final byte[] MAGIC_PACKET = Hex.decode("22400891");
|
||||
|
||||
static {
|
||||
|
@ -26,7 +26,7 @@ public class StaticMessages {
|
|||
public static HelloMessage generateHelloMessage(){
|
||||
byte[] peerIdBytes = HashUtil.randomPeerId();
|
||||
|
||||
return new HelloMessage((byte) 0x13, (byte) 0x00,
|
||||
return new HelloMessage((byte) 0x14, (byte) 0x00,
|
||||
"EthereumJ [v0.5.1] by RomanJ", (byte) 0b00000111,
|
||||
(short) 30303, peerIdBytes);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
package org.ethereum.trie;
|
||||
|
||||
import org.ethereum.db.ByteArrayWrapper;
|
||||
import org.ethereum.db.Database;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
*
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 11/06/2014 19:47
|
||||
*/
|
||||
|
||||
public class TrackTrie implements TrieFacade {
|
||||
|
||||
|
||||
private TrieFacade trie;
|
||||
|
||||
private boolean trackingChanges;
|
||||
private Map<ByteArrayWrapper, byte[]> changes;
|
||||
private Map<ByteArrayWrapper, byte[]> deletes;
|
||||
|
||||
public TrackTrie(TrieFacade trie) {
|
||||
this.trie = trie ;
|
||||
}
|
||||
|
||||
|
||||
public void startTrack(){
|
||||
changes = new HashMap<>();
|
||||
deletes = new HashMap<>();
|
||||
trackingChanges = true;
|
||||
}
|
||||
|
||||
public void commitTrack(){
|
||||
for(ByteArrayWrapper key : changes.keySet()){
|
||||
trie.update(key.getData(), changes.get(key));
|
||||
}
|
||||
changes = null;
|
||||
trackingChanges = false;
|
||||
}
|
||||
|
||||
public void rollbackTrack(){
|
||||
changes = new HashMap<>();
|
||||
deletes = new HashMap<>();
|
||||
changes = null;
|
||||
trackingChanges = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void update(byte[] key, byte[] value) {
|
||||
if (trackingChanges){
|
||||
changes.put( new ByteArrayWrapper(key) , value);
|
||||
} else {
|
||||
trie.update(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] get(byte[] key) {
|
||||
if(trackingChanges){
|
||||
ByteArrayWrapper wKey = new ByteArrayWrapper(key);
|
||||
if (deletes.get(wKey) != null) return null;
|
||||
if (changes.get(wKey) != null) return changes.get(wKey);
|
||||
return trie.get(key);
|
||||
}
|
||||
return trie.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(byte[] key) {
|
||||
if (trackingChanges){
|
||||
ByteArrayWrapper wKey = new ByteArrayWrapper(key);
|
||||
deletes.put(wKey, null);
|
||||
} else {
|
||||
trie.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7,8 +7,10 @@ import static org.ethereum.util.CompactEncoder.*;
|
|||
import java.util.Arrays;
|
||||
|
||||
import org.ethereum.crypto.HashUtil;
|
||||
import org.ethereum.db.Database;
|
||||
import org.ethereum.util.Value;
|
||||
import org.iq80.leveldb.DB;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
/**
|
||||
* The modified Merkle Patricia tree (trie) provides a persistent data structure
|
||||
|
@ -31,7 +33,7 @@ import org.iq80.leveldb.DB;
|
|||
* @author: Nick Savers
|
||||
* Created on: 20/05/2014 10:44
|
||||
*/
|
||||
public class Trie {
|
||||
public class Trie implements TrieFacade{
|
||||
|
||||
private static byte PAIR_SIZE = 2;
|
||||
private static byte LIST_SIZE = 17;
|
||||
|
@ -124,6 +126,16 @@ public class Trie {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete a key/value pair from the trie
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
public void delete(byte[] key) {
|
||||
delete(Hex.encode(key));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete a key/value pair from the trie
|
||||
*
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package org.ethereum.trie;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
*
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 11/06/2014 19:44
|
||||
*/
|
||||
|
||||
public interface TrieFacade {
|
||||
|
||||
public void update(byte[] key, byte[] value);
|
||||
public byte[] get(byte[] key);
|
||||
public void delete(byte[] key);
|
||||
|
||||
|
||||
}
|
|
@ -154,4 +154,23 @@ public class ByteUtil {
|
|||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
public static byte[] stripLeadingZeroes(byte[] data){
|
||||
|
||||
if (data == null) return null;
|
||||
|
||||
int firstNonZero = 0;
|
||||
for (int i = 0; i < data.length; ++i)
|
||||
if (data[i] != 0){
|
||||
firstNonZero = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (firstNonZero == 0) return data;
|
||||
|
||||
byte[] result = new byte[data.length - firstNonZero];
|
||||
System.arraycopy(data, firstNonZero, result, 0, data.length - firstNonZero);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package org.ethereum.vm;
|
||||
|
||||
import org.ethereum.util.ByteUtil;
|
||||
import org.spongycastle.util.Arrays;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
|
@ -55,6 +56,10 @@ public class DataWord {
|
|||
return data;
|
||||
}
|
||||
|
||||
public byte[] getNoLeadZeroesData() {
|
||||
return ByteUtil.stripLeadingZeroes(data);
|
||||
}
|
||||
|
||||
public BigInteger value(){
|
||||
return new BigInteger(1, data);
|
||||
}
|
||||
|
@ -83,9 +88,7 @@ public class DataWord {
|
|||
// when the number is explicit defined
|
||||
// as negative
|
||||
public boolean isNegative(){
|
||||
|
||||
int result = data[0] & 0x80;
|
||||
|
||||
return result == 0x80;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package org.ethereum.vm;
|
||||
|
||||
import org.ethereum.core.AccountState;
|
||||
import org.ethereum.core.ContractDetails;
|
||||
import org.ethereum.db.TrackDatabase;
|
||||
import org.ethereum.trie.TrackTrie;
|
||||
import org.ethereum.util.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -33,19 +36,20 @@ public class Program {
|
|||
|
||||
ProgramInvoke invokeData;
|
||||
|
||||
Map<byte[], DataWord> addressChange;
|
||||
|
||||
|
||||
public Program(byte[] ops, ProgramInvoke invokeData) {
|
||||
|
||||
spendGas(GasCost.TRANSACTION);
|
||||
spendGas(GasCost.TXDATA * invokeData.getDataSize().intValue());
|
||||
result.setStateDb(invokeData.getStateDb());
|
||||
result.setChainDb(invokeData.getChainDb());
|
||||
result.setDetailDB(invokeData.getDetaildDB());
|
||||
|
||||
if (ops == null) throw new RuntimeException("program can not run with ops: null");
|
||||
|
||||
this.invokeData = invokeData;
|
||||
this.ops = ops;
|
||||
|
||||
spendGas(GasCost.TRANSACTION);
|
||||
spendGas(GasCost.TXDATA * invokeData.getDataSize().intValue());
|
||||
|
||||
if (invokeData.getStorage() != null){
|
||||
storage = invokeData.getStorage();
|
||||
}
|
||||
|
@ -64,6 +68,12 @@ public class Program {
|
|||
stack.push(stackWord);
|
||||
}
|
||||
|
||||
public void stackPushZero(){
|
||||
DataWord stackWord = new DataWord(0);
|
||||
stack.push(stackWord);
|
||||
}
|
||||
|
||||
|
||||
public void stackPush(DataWord stackWord){
|
||||
stack.push(stackWord);
|
||||
}
|
||||
|
@ -127,7 +137,7 @@ public class Program {
|
|||
throw new RuntimeException("attempted pull action for empty stack");
|
||||
}
|
||||
return stack.pop();
|
||||
};
|
||||
}
|
||||
|
||||
public int getMemSize(){
|
||||
|
||||
|
@ -163,12 +173,14 @@ public class Program {
|
|||
|
||||
int offset = offsetData.value().intValue();
|
||||
int size = sizeData.value().intValue();
|
||||
allocateMemory(offset, new byte[sizeData.intValue()]);
|
||||
|
||||
byte[] chunk = new byte[size];
|
||||
|
||||
if (memory.limit() < offset + size) size = memory.limit() - offset;
|
||||
|
||||
System.arraycopy(memory.array(), offset, chunk, 0, size);
|
||||
if (memory != null){
|
||||
if (memory.limit() < offset + size) size = memory.limit() - offset;
|
||||
System.arraycopy(memory.array(), offset, chunk, 0, size);
|
||||
}
|
||||
|
||||
return ByteBuffer.wrap(chunk);
|
||||
}
|
||||
|
@ -203,13 +215,135 @@ public class Program {
|
|||
}
|
||||
}
|
||||
|
||||
public void sendToAddress(byte[] addr, DataWord bChange ){
|
||||
|
||||
DataWord currentBChange = addressChange.get(addr);
|
||||
if (currentBChange == null){
|
||||
addressChange.put(addr, bChange);
|
||||
} else {
|
||||
currentBChange.add(bChange);
|
||||
/**
|
||||
* That method implement internal calls
|
||||
* and code invocations
|
||||
*
|
||||
* @param gas
|
||||
* @param toAddressDW
|
||||
* @param endowmentValue
|
||||
* @param inDataOffs
|
||||
* @param inDataSize
|
||||
* @param outDataOffs
|
||||
* @param outDataSize
|
||||
*/
|
||||
public void callToAddress(DataWord gas, DataWord toAddressDW, DataWord endowmentValue,
|
||||
DataWord inDataOffs, DataWord inDataSize,DataWord outDataOffs, DataWord outDataSize){
|
||||
|
||||
ByteBuffer data = memoryChunk(inDataOffs, inDataSize);
|
||||
|
||||
// FETCH THE SAVED STORAGE
|
||||
ContractDetails details = null;
|
||||
byte[] toAddress = toAddressDW.getNoLeadZeroesData();
|
||||
|
||||
byte[] detailsRLPData = invokeData.getDetaildDB().get(toAddress);
|
||||
if (detailsRLPData != null && detailsRLPData.length > 0)
|
||||
details = new ContractDetails(detailsRLPData);
|
||||
|
||||
AccountState receiverState;
|
||||
byte[] accountData = result.getStateDb().get(toAddress);
|
||||
if (accountData == null){
|
||||
|
||||
logger.info("no saved address in db to call: address={}" ,Hex.toHexString(toAddress));
|
||||
return;
|
||||
} else{
|
||||
|
||||
receiverState = new AccountState(accountData);
|
||||
}
|
||||
|
||||
// todo: endowment rollbacked move it from here
|
||||
receiverState.addToBalance(endowmentValue.value());
|
||||
result.getStateDb().update(toAddress, receiverState.getEncoded());
|
||||
// todo: endowment rollbacked move it from here
|
||||
|
||||
byte[] programCode = result.getChainDb().get(receiverState.getCodeHash());
|
||||
if (programCode != null && programCode.length != 0){
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("calling for existing contract: address={}" ,
|
||||
Hex.toHexString(toAddress));
|
||||
|
||||
byte[] senderAddress = this.getOwnerAddress().getNoLeadZeroesData();
|
||||
byte[] senderStateB = this.result.getStateDb().get(senderAddress);
|
||||
if (senderStateB == null){
|
||||
logger.info("This should not happen in any case, this inside contract run is is evidence for contract to exist: \n" +
|
||||
"address={}", Hex.toHexString(senderAddress));
|
||||
return;
|
||||
}
|
||||
|
||||
AccountState senderState = new AccountState(senderStateB);
|
||||
|
||||
// 2.1 PERFORM THE GAS VALUE TX
|
||||
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
|
||||
if (this.getGas().longValue() - gas.longValue() < 0 ){
|
||||
logger.info("No gas for the internal call, \n" +
|
||||
"fromAddress={}, toAddress={}",
|
||||
Hex.toHexString(senderAddress), Hex.toHexString(toAddress));
|
||||
|
||||
this.stackPushZero();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 2.2 UPDATE THE NONCE
|
||||
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
|
||||
senderState.incrementNonce();
|
||||
|
||||
TrackTrie stateDB = new TrackTrie( result.getStateDb() );
|
||||
TrackDatabase chainDB = new TrackDatabase( result.getChainDb() );
|
||||
TrackDatabase detailDB = new TrackDatabase( result.getDetailDB() );
|
||||
|
||||
detailDB.startTrack();
|
||||
chainDB.startTrack();
|
||||
stateDB.startTrack();
|
||||
|
||||
// todo: update the balance/value simple transfer
|
||||
|
||||
Map<DataWord, DataWord> storage = null;
|
||||
if (details != null)
|
||||
storage = details.getStorage();
|
||||
|
||||
ProgramInvoke programInvoke =
|
||||
ProgramInvokeFactory.createProgramInvoke(this, toAddressDW, storage, endowmentValue, gas,receiverState.getBalance(),
|
||||
data.array(),
|
||||
detailDB, chainDB, stateDB);
|
||||
|
||||
VM vm = new VM();
|
||||
Program program = new Program(programCode, programInvoke);
|
||||
vm.play(program);
|
||||
ProgramResult result = program.getResult();
|
||||
|
||||
if (result.getException() != null &&
|
||||
result.getException() instanceof Program.OutOfGasException){
|
||||
logger.info("contract run halted by OutOfGas: contract={}" , Hex.toHexString(toAddress));
|
||||
|
||||
detailDB.rollbackTrack();
|
||||
chainDB.rollbackTrack();
|
||||
stateDB.rollbackTrack();
|
||||
stackPushZero();
|
||||
return;
|
||||
}
|
||||
|
||||
// todo: apply results: result.gethReturn()
|
||||
// todo: refund for remain gas
|
||||
|
||||
detailDB.commitTrack();
|
||||
chainDB.commitTrack();
|
||||
stateDB.commitTrack();
|
||||
stackPush(new DataWord(1));
|
||||
|
||||
// the gas spent in any internal outcome
|
||||
spendGas(result.getGasUsed());
|
||||
logger.info("The usage of the gas in external call updated", result.getGasUsed());
|
||||
|
||||
// update the storage , it could
|
||||
// change by the call
|
||||
byte[] contractDetailBytes =
|
||||
result.getDetailDB().get(getOwnerAddress().getNoLeadZeroesData());
|
||||
if (contractDetailBytes != null){
|
||||
this.storage = new ContractDetails(contractDetailBytes).getStorage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,6 +365,11 @@ public class Program {
|
|||
DataWord keyWord = new DataWord(key);
|
||||
DataWord valWord = new DataWord(val);
|
||||
storage.put(keyWord, valWord);
|
||||
|
||||
if (storage != null){
|
||||
ContractDetails contractDetails = new ContractDetails(storage);
|
||||
result.getDetailDB().put(getOwnerAddress().getNoLeadZeroesData() , contractDetails.getEncoded());
|
||||
}
|
||||
}
|
||||
|
||||
public DataWord getOwnerAddress(){
|
||||
|
@ -411,7 +550,7 @@ public class Program {
|
|||
if (listener != null){
|
||||
listener.output(globalOutput.toString());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(ProgramListener listener){
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package org.ethereum.vm;
|
||||
|
||||
import org.ethereum.db.TrackDatabase;
|
||||
import org.ethereum.trie.TrackTrie;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -31,4 +34,9 @@ public interface ProgramInvoke {
|
|||
|
||||
public Map<DataWord, DataWord> getStorage();
|
||||
|
||||
public TrackDatabase getDetaildDB();
|
||||
public TrackDatabase getChainDb();
|
||||
public TrackTrie getStateDb();
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
package org.ethereum.vm;
|
||||
|
||||
import org.abego.treelayout.internal.util.Contract;
|
||||
import org.ethereum.core.AccountState;
|
||||
import org.ethereum.core.Block;
|
||||
import org.ethereum.core.ContractDetails;
|
||||
import org.ethereum.core.Transaction;
|
||||
import org.ethereum.db.TrackDatabase;
|
||||
import org.ethereum.manager.WorldManager;
|
||||
import org.ethereum.util.ByteUtil;
|
||||
import org.ethereum.trie.TrackTrie;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -19,9 +23,11 @@ import java.util.Map;
|
|||
|
||||
public class ProgramInvokeFactory {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger("VM");
|
||||
|
||||
// Invocation by the wire tx
|
||||
public static ProgramInvoke createProgramInvoke(Transaction tx, Block lastBlock, ContractDetails details){
|
||||
public static ProgramInvoke createProgramInvoke(Transaction tx, Block lastBlock, ContractDetails details,
|
||||
TrackDatabase detaildDB, TrackDatabase chainDb, TrackTrie stateDB){
|
||||
|
||||
// https://ethereum.etherpad.mozilla.org/26
|
||||
|
||||
|
@ -38,7 +44,7 @@ public class ProgramInvokeFactory {
|
|||
byte[] caller = tx.getSender();
|
||||
|
||||
/*** BALANCE op ***/
|
||||
byte[] addressStateData = WorldManager.instance.worldState.get(address);
|
||||
byte[] addressStateData = stateDB.get(address);
|
||||
|
||||
byte[] balance = null;
|
||||
if (addressStateData.length == 0)
|
||||
|
@ -86,10 +92,117 @@ public class ProgramInvokeFactory {
|
|||
if (details != null)
|
||||
storage = details.getStorage();
|
||||
|
||||
detaildDB.startTrack();
|
||||
chainDb.startTrack();
|
||||
stateDB.startTrack();
|
||||
|
||||
if (logger.isInfoEnabled()){
|
||||
logger.info("Program invocation: \n" +
|
||||
"address={}\n" +
|
||||
"origin={}\n" +
|
||||
"caller={}\n" +
|
||||
"balance={}\n" +
|
||||
"gasPrice={}\n" +
|
||||
"gas={}\n" +
|
||||
"callValue={}\n" +
|
||||
"data={}\n" +
|
||||
"lastHash={}\n" +
|
||||
"coinbase={}\n" +
|
||||
"timestamp={}\n" +
|
||||
"blockNumber={}\n" +
|
||||
"difficulty={}\n" +
|
||||
"gaslimit={}\n"
|
||||
,
|
||||
Hex.toHexString(address),
|
||||
Hex.toHexString(origin),
|
||||
Hex.toHexString(caller),
|
||||
Hex.toHexString(balance),
|
||||
Hex.toHexString(gasPrice),
|
||||
Hex.toHexString(gas),
|
||||
Hex.toHexString(callValue),
|
||||
Hex.toHexString(data),
|
||||
Hex.toHexString(lastHash),
|
||||
Hex.toHexString(coinbase),
|
||||
timestamp,
|
||||
number,
|
||||
Hex.toHexString(difficulty),
|
||||
gaslimit);
|
||||
}
|
||||
|
||||
ProgramInvoke programInvoke =
|
||||
new ProgramInvokeImpl(address, origin, caller, balance, gasPrice, gas, callValue, data,
|
||||
lastHash, coinbase, timestamp, number, difficulty, gaslimit, storage);
|
||||
lastHash, coinbase, timestamp, number, difficulty, gaslimit, storage,
|
||||
detaildDB, chainDb, stateDB);
|
||||
|
||||
return programInvoke;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This invocation created for contract call contract
|
||||
*/
|
||||
public static ProgramInvoke createProgramInvoke(Program program, DataWord toAddress,
|
||||
Map<DataWord, DataWord> storageIn,
|
||||
DataWord inValue, DataWord inGas,
|
||||
BigInteger balanceInt, byte[] dataIn,
|
||||
TrackDatabase detailDB, TrackDatabase chainDB, TrackTrie stateDB){
|
||||
|
||||
|
||||
DataWord address = toAddress;
|
||||
DataWord origin = program.getOriginAddress();
|
||||
DataWord caller = program.getOwnerAddress();
|
||||
|
||||
DataWord balance = new DataWord(balanceInt.toByteArray());
|
||||
DataWord gasPrice = program.getGasPrice();
|
||||
DataWord gas = inGas;
|
||||
DataWord callValue = inValue;
|
||||
|
||||
byte[] data = dataIn;
|
||||
DataWord lastHash = program.getPrevHash();
|
||||
DataWord coinbase = program.getCoinbase();
|
||||
DataWord timestamp = program.getTimestamp();
|
||||
DataWord number = program.getNumber();
|
||||
DataWord difficulty = program.getDifficulty();
|
||||
DataWord gasLimit = program.getGaslimit();
|
||||
|
||||
Map<DataWord, DataWord> storage = storageIn;
|
||||
|
||||
if (logger.isInfoEnabled()){
|
||||
logger.info("Program invocation: \n" +
|
||||
"address={}\n" +
|
||||
"origin={}\n" +
|
||||
"caller={}\n" +
|
||||
"balance={}\n" +
|
||||
"gasPrice={}\n" +
|
||||
"gas={}\n" +
|
||||
"callValue={}\n" +
|
||||
"data={}\n" +
|
||||
"lastHash={}\n" +
|
||||
"coinbase={}\n" +
|
||||
"timestamp={}\n" +
|
||||
"blockNumber={}\n" +
|
||||
"difficulty={}\n" +
|
||||
"gaslimit={}\n"
|
||||
,
|
||||
Hex.toHexString(address.getData()),
|
||||
Hex.toHexString(origin.getData()),
|
||||
Hex.toHexString(caller.getData()),
|
||||
Hex.toHexString(balance.getData()),
|
||||
Hex.toHexString(gasPrice.getData()),
|
||||
Hex.toHexString(gas.getData()),
|
||||
Hex.toHexString(callValue.getData()),
|
||||
Hex.toHexString(data),
|
||||
Hex.toHexString(lastHash.getData()),
|
||||
Hex.toHexString(coinbase.getData()),
|
||||
Hex.toHexString(timestamp.getData()),
|
||||
Hex.toHexString(number.getData()),
|
||||
Hex.toHexString(difficulty.getData()),
|
||||
Hex.toHexString(gasLimit.getData()));
|
||||
}
|
||||
|
||||
return new ProgramInvokeImpl(address, origin, caller, balance, gasPrice, gas, callValue,
|
||||
data, lastHash, coinbase, timestamp, number, difficulty, gasLimit,
|
||||
storage, detailDB, chainDB, stateDB);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.ethereum.vm;
|
||||
|
||||
import org.ethereum.db.TrackDatabase;
|
||||
import org.ethereum.trie.TrackTrie;
|
||||
import org.ethereum.util.ByteUtil;
|
||||
|
||||
import java.util.Map;
|
||||
|
@ -10,7 +12,7 @@ import java.util.Map;
|
|||
* Created on: 03/06/2014 15:00
|
||||
*/
|
||||
|
||||
public class ProgramInvokeImpl implements ProgramInvoke {
|
||||
public class ProgramInvokeImpl implements ProgramInvoke {
|
||||
|
||||
/*** TRANSACTION env ***/
|
||||
DataWord address;
|
||||
|
@ -33,10 +35,48 @@ public class ProgramInvokeImpl implements ProgramInvoke {
|
|||
|
||||
Map<DataWord, DataWord> storage;
|
||||
|
||||
TrackDatabase detaildDB;
|
||||
TrackDatabase chainDb;
|
||||
TrackTrie stateDb;
|
||||
|
||||
public ProgramInvokeImpl(DataWord address, DataWord origin, DataWord caller, DataWord balance,
|
||||
DataWord gasPrice, DataWord gas, DataWord callValue, byte[] msgData,
|
||||
DataWord lastHash, DataWord coinbase, DataWord timestamp, DataWord number, DataWord difficulty,
|
||||
DataWord gaslimit, Map<DataWord, DataWord> storage,
|
||||
TrackDatabase detaildDB, TrackDatabase chainDb, TrackTrie stateDB) {
|
||||
|
||||
// Transaction env
|
||||
this.address = address;
|
||||
this.origin = origin;
|
||||
this.caller = caller;
|
||||
this.balance = balance;
|
||||
this.gasPrice = gasPrice;
|
||||
this.gas = gas;
|
||||
this.callValue = callValue;
|
||||
this.msgData = msgData;
|
||||
|
||||
// last Block env
|
||||
this.prevHash = lastHash;
|
||||
this.coinbase = coinbase;
|
||||
this.timestamp = timestamp;
|
||||
this.number = number;
|
||||
this.difficulty = difficulty;
|
||||
this.gaslimit = gaslimit;
|
||||
|
||||
this.storage = storage;
|
||||
|
||||
this.detaildDB = detaildDB;
|
||||
this.chainDb = chainDb;
|
||||
this.stateDb = stateDB;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public ProgramInvokeImpl(byte[] address, byte[] origin, byte[] caller, byte[] balance,
|
||||
byte[] gasPrice, byte[] gas, byte[] callValue, byte[] msgData,
|
||||
byte[] lastHash, byte[] coinbase, long timestamp, long number, byte[] difficulty,
|
||||
long gaslimit, Map<DataWord, DataWord> storage) {
|
||||
long gaslimit, Map<DataWord, DataWord> storage,
|
||||
TrackDatabase detaildDB, TrackDatabase chainDb, TrackTrie stateDB) {
|
||||
|
||||
// Transaction env
|
||||
this.address = new DataWord(address);
|
||||
|
@ -57,6 +97,10 @@ public class ProgramInvokeImpl implements ProgramInvoke {
|
|||
this.gaslimit = new DataWord(gaslimit);
|
||||
|
||||
this.storage = storage;
|
||||
|
||||
this.detaildDB = detaildDB;
|
||||
this.chainDb = chainDb;
|
||||
this.stateDb = stateDB;
|
||||
}
|
||||
|
||||
/* ADDRESS op */
|
||||
|
@ -177,4 +221,17 @@ public class ProgramInvokeImpl implements ProgramInvoke {
|
|||
|
||||
/* Storage */
|
||||
public Map<DataWord, DataWord> getStorage(){ return storage; }
|
||||
|
||||
|
||||
public TrackDatabase getDetaildDB() {
|
||||
return detaildDB;
|
||||
}
|
||||
|
||||
public TrackDatabase getChainDb() {
|
||||
return chainDb;
|
||||
}
|
||||
|
||||
public TrackTrie getStateDb() {
|
||||
return stateDb;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package org.ethereum.vm;
|
||||
|
||||
import org.ethereum.db.TrackDatabase;
|
||||
import org.ethereum.trie.TrackTrie;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -15,8 +17,10 @@ public class ProgramResult {
|
|||
private int gasUsed = 0;
|
||||
private ByteBuffer hReturn = null;
|
||||
private RuntimeException exception;
|
||||
private Map<DataWord, DataWord> storage;
|
||||
|
||||
TrackDatabase detailDB;
|
||||
TrackDatabase chainDb;
|
||||
TrackTrie stateDb;
|
||||
|
||||
public void spendGas(int gas){
|
||||
gasUsed += gas;
|
||||
|
@ -44,11 +48,27 @@ public class ProgramResult {
|
|||
this.exception = exception;
|
||||
}
|
||||
|
||||
public Map<DataWord, DataWord> getStorage() {
|
||||
return storage;
|
||||
public TrackDatabase getDetailDB() {
|
||||
return detailDB;
|
||||
}
|
||||
|
||||
public void setStorage(Map<DataWord, DataWord> storage) {
|
||||
this.storage = storage;
|
||||
public void setDetailDB(TrackDatabase detailDB) {
|
||||
this.detailDB = detailDB;
|
||||
}
|
||||
|
||||
public TrackDatabase getChainDb() {
|
||||
return chainDb;
|
||||
}
|
||||
|
||||
public void setChainDb(TrackDatabase chainDb) {
|
||||
this.chainDb = chainDb;
|
||||
}
|
||||
|
||||
public TrackTrie getStateDb() {
|
||||
return stateDb;
|
||||
}
|
||||
|
||||
public void setStateDb(TrackTrie stateDb) {
|
||||
this.stateDb = stateDb;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -518,14 +518,18 @@ public class VM {
|
|||
program.step();
|
||||
} break;
|
||||
case CALL:{
|
||||
|
||||
DataWord gas = program.stackPop();
|
||||
DataWord toAddress = program.stackPop();
|
||||
DataWord value = program.stackPop();
|
||||
|
||||
program.sendToAddress(toAddress.data, value);
|
||||
DataWord inDataOffs = program.stackPop();
|
||||
DataWord inDataSize = program.stackPop();
|
||||
|
||||
// todo: find out if we should or not execute
|
||||
// todo: the contract for real
|
||||
DataWord outDataOffs = program.stackPop();
|
||||
DataWord outDataSize = program.stackPop();
|
||||
|
||||
program.callToAddress(gas, toAddress, value, inDataOffs, inDataSize,outDataOffs, outDataSize);
|
||||
|
||||
program.step();
|
||||
} break;
|
||||
|
@ -566,8 +570,6 @@ public class VM {
|
|||
this.step(program);
|
||||
} catch (RuntimeException e) {
|
||||
program.setRuntimeFailure(e);
|
||||
} finally{
|
||||
program.getResult().setStorage(program.storage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import static org.junit.Assert.*;
|
|||
public class BlockTest {
|
||||
|
||||
// https://ethereum.etherpad.mozilla.org/12
|
||||
private String CPP_PoC5_GENESIS_HEX_RLP_ENCODED = "f8abf8a7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a023b503734ff34ddb7bd5e478f1645680ec778ab3f90007cb1c854653693e5adc80834000008080830f4240808080a004994f67dc55b09e814ab7ffc8df3686b4afb2bb53e60eae97ef043fe03fb829c0c0";
|
||||
private String CPP_PoC5_GENESIS_HEX_RLP_ENCODED = "f8abf8a7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a011cc4aaa3b2f97cd6c858fcc0903b9b34b071e1798c91645f0e05e267028cb4a80834000008080830f4240808080a004994f67dc55b09e814ab7ffc8df3686b4afb2bb53e60eae97ef043fe03fb829c0c0";
|
||||
private String CPP_PoC5_GENESIS_HEX_HASH = Hex.toHexString(StaticMessages.GENESIS_HASH);
|
||||
|
||||
String block_1 = "f9072df8d3a077ef4fdaf389dca53236bcf7f72698e154eab2828f86fbc4fc6c"
|
||||
|
@ -168,7 +168,7 @@ public class BlockTest {
|
|||
assertEquals(new BigInteger(1, Genesis.DIFFICULTY), difficulty);
|
||||
|
||||
Block block1 = new Block(Hex.decode(block_1));
|
||||
diffBytes = block1.calcDifficulty();
|
||||
diffBytes = block1.calcDifficulty();
|
||||
difficulty = new BigInteger(1, diffBytes);
|
||||
System.out.println("Block#1 difficulty = " + difficulty.toString());
|
||||
assertEquals(new BigInteger(""), difficulty);
|
||||
|
|
|
@ -17,7 +17,7 @@ public class TrackDatabaseTest {
|
|||
@Test
|
||||
public void test1(){
|
||||
|
||||
Database db1 = new Database("temp");
|
||||
DatabaseImpl db1 = new DatabaseImpl("temp");
|
||||
TrackDatabase trackDatabase1 = new TrackDatabase(db1);
|
||||
|
||||
trackDatabase1.put(Hex.decode("abcdef"), Hex.decode("abcdef"));
|
||||
|
|
|
@ -2,6 +2,8 @@ package org.ethereum.vm;
|
|||
|
||||
import org.ethereum.crypto.ECKey;
|
||||
import org.ethereum.crypto.HashUtil;
|
||||
import org.ethereum.db.TrackDatabase;
|
||||
import org.ethereum.trie.TrackTrie;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import java.util.Map;
|
||||
|
@ -165,4 +167,19 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
|
|||
public Map<DataWord, DataWord> getStorage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackDatabase getDetaildDB() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackDatabase getChainDb() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackTrie getStateDb() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue