Merge pull request #74 from nicksavers/repofactor

Performance and better separation of concerns
This commit is contained in:
romanman 2014-08-02 19:21:48 +03:00
commit dfd2e26c6e
38 changed files with 729 additions and 671 deletions

View File

@ -1,6 +1,7 @@
package org.ethereum.core;
import org.ethereum.crypto.HashUtil;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
import static org.ethereum.crypto.HashUtil.EMPTY_DATA_HASH;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPList;
@ -28,7 +29,7 @@ public class AccountState {
* I define a convenient equivalence TRIE (σ[a] s ) σ[a] s .
* It shall be understood that σ[a] s is not a physical member
* of the account and does not contribute to its later serialisation */
private byte[] stateRoot = new byte[0];
private byte[] stateRoot = EMPTY_BYTE_ARRAY;
/* The hash of the EVM code of this contractthis is the code
* that gets executed should this address receive a message call;
@ -36,7 +37,7 @@ public class AccountState {
* after construction. All such code fragments are contained in
* the state database under their corresponding hashes for later
* retrieval */
private byte[] codeHash = HashUtil.sha3(new byte[0]);
private byte[] codeHash = EMPTY_DATA_HASH;
public AccountState() {
this(BigInteger.ZERO, BigInteger.ZERO);

View File

@ -110,7 +110,7 @@ public class Block {
}
public Block getParent() {
return WorldManager.getInstance().getBlockChain().getByNumber(this.getNumber() - 1);
return WorldManager.getInstance().getBlockchain().getByNumber(this.getNumber() - 1);
}
public byte[] getParentHash() {

View File

@ -1,19 +1,19 @@
package org.ethereum.core;
import org.ethereum.db.DatabaseImpl;
import org.ethereum.db.Repository;
import org.ethereum.listener.EthereumListener;
import org.ethereum.manager.WorldManager;
import org.ethereum.net.BlockQueue;
import org.ethereum.util.AdvancedDeviceUtils;
import org.ethereum.util.ByteUtil;
import org.iq80.leveldb.DBIterator;
import org.ethereum.vm.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.ethereum.config.SystemProperties.CONFIG;
@ -54,21 +54,39 @@ import static org.ethereum.core.Denomination.SZABO;
public class Blockchain {
private static Logger logger = LoggerFactory.getLogger("blockchain");
private static Logger stateLogger = LoggerFactory.getLogger("state");
// to avoid using minGasPrice=0 from Genesis for the wallet
private static long INITIAL_MIN_GAS_PRICE = 10 * SZABO.longValue();
private DatabaseImpl chainDb;
private Repository repository;
private Block lastBlock;
// keep the index of the chain for
// convenient usage, <block_number, block_hash>
private Map<Long, byte[]> index = new HashMap<>();
private Map<Long, byte[]> blockCache = new HashMap<>();
private Map<String, Transaction> pendingTransactions = Collections
.synchronizedMap(new HashMap<String, Transaction>());
private BlockQueue blockQueue = new BlockQueue();
public Blockchain() {
this.chainDb = new DatabaseImpl("blockchain");
public Blockchain(Repository repository) {
this.repository = repository;
}
public BlockQueue getBlockQueue() {
return blockQueue;
}
public Map<Long, byte[]> getBlockCache() {
return this.blockCache;
}
public long getGasPrice() {
// In case of the genesis block we don't want to rely on the min gas price
return lastBlock.isGenesis() ? lastBlock.getMinGasPrice() : INITIAL_MIN_GAS_PRICE;
}
public Block getLastBlock() {
return lastBlock;
@ -78,138 +96,323 @@ public class Blockchain {
this.lastBlock = block;
}
public byte[] getLatestBlockHash() {
if (blockCache.isEmpty())
return Genesis.getInstance().getHash();
else
return getLastBlock().getHash();
}
public int getSize() {
return index.size();
return blockCache.size();
}
public Block getByNumber(long blockNr) {
return new Block(chainDb.get(ByteUtil.longToBytes(blockNr)));
return repository.getBlock(blockNr);
}
public void applyBlock(Block block) {
public void add(Block block) {
if (block == null)
return;
// if it is the first block to add
// check that the parent is the genesis
if (index.isEmpty()
// make sure the parent is genesis
if (blockCache.isEmpty()
&& !Arrays.equals(Genesis.getInstance().getHash(),
block.getParentHash())) {
return;
}
// if there is some blocks already keep chain continuity
if (!index.isEmpty()) {
if (!blockCache.isEmpty()) {
String hashLast = Hex.toHexString(getLastBlock().getHash());
String blockParentHash = Hex.toHexString(block.getParentHash());
if (!hashLast.equals(blockParentHash)) return;
}
this.addBlock(block);
if (block.getNumber() >= CONFIG.traceStartBlock() && CONFIG.traceStartBlock() != -1) {
AdvancedDeviceUtils.adjustDetailedTracing(block.getNumber());
}
/* Debug check to see if the state is still as expected */
if(logger.isWarnEnabled()) {
String blockStateRootHash = Hex.toHexString(block.getStateRoot());
String worldStateRootHash = Hex.toHexString(WorldManager.getInstance().getRepository().getWorldState().getRootHash());
if(!blockStateRootHash.equals(worldStateRootHash)){
logger.warn("WARNING: STATE CONFLICT! block: {} worldstate {} mismatch", block.getNumber(), worldStateRootHash);
// Last fail on WARNING: STATE CONFLICT! block: 1157 worldstate b1d9a978451ef04c1639011d9516473d51c608dbd25906c89be791707008d2de mismatch
// System.exit(-1);
}
}
this.processBlock(block);
// Remove all wallet transactions as they already approved by the net
for (Transaction tx : block.getTransactionsList()) {
if (logger.isDebugEnabled())
logger.debug("pending cleanup: tx.hash: [{}]", Hex.toHexString( tx.getHash()));
WorldManager.getInstance().removeWalletTransaction(tx);
}
logger.info("*** Block chain size: [ {} ]", this.getSize());
EthereumListener listener = WorldManager.getInstance().getListener();
if (listener != null)
listener.trace(String.format("Block chain size: [ %d ]", this.getSize()));
/*
if (lastBlock.getNumber() >= 30) {
System.out.println("** checkpoint **");
this.close();
WorldManager.getInstance().getRepository().close();
System.exit(1);
}
*/
}
public void addBlock(Block block) {
public void processBlock(Block block) {
if(block.isValid()) {
if (!block.isGenesis()) {
for (Transaction tx : block.getTransactionsList())
// TODO: refactor the wallet pending transactions to the world manager
WorldManager.getInstance().addWalletTransaction(tx);
WorldManager.getInstance().applyBlock(block);
this.applyBlock(block);
WorldManager.getInstance().getWallet().processBlock(block);
}
this.chainDb.put(ByteUtil.longToBytes(block.getNumber()), block.getEncoded());
this.index.put(block.getNumber(), block.getHash());
WorldManager.getInstance().getWallet().processBlock(block);
this.setLastBlock(block);
if (logger.isDebugEnabled())
logger.debug("block added {}", block.toFlatString());
this.storeBlock(block);
} else {
logger.warn("Invalid block with nr: {}", block.getNumber());
}
}
public long getGasPrice() {
// In case of the genesis block we don't want to rely on the min gas price
return lastBlock.isGenesis() ? lastBlock.getMinGasPrice() : INITIAL_MIN_GAS_PRICE;
}
public byte[] getLatestBlockHash() {
if (index.isEmpty())
return Genesis.getInstance().getHash();
else
return getLastBlock().getHash();
public void storeBlock(Block block) {
/* Debug check to see if the state is still as expected */
if(logger.isWarnEnabled()) {
String blockStateRootHash = Hex.toHexString(block.getStateRoot());
String worldStateRootHash = Hex.toHexString(WorldManager.getInstance().getRepository().getWorldState().getRootHash());
if(!blockStateRootHash.equals(worldStateRootHash)){
logger.error("ERROR: STATE CONFLICT! block: {} worldstate {} mismatch", block.getNumber(), worldStateRootHash);
// Last conflict on block 1157 -> worldstate b1d9a978451ef04c1639011d9516473d51c608dbd25906c89be791707008d2de
repository.close();
System.exit(-1); // Don't add block
}
}
this.repository.saveBlock(block);
this.blockCache.put(block.getNumber(), block.getHash());
this.setLastBlock(block);
if (logger.isDebugEnabled())
logger.debug("block added {}", block.toFlatString());
logger.info("*** Block chain size: [ {} ]", this.getSize());
}
public void load() {
DBIterator iterator = chainDb.iterator();
try {
if (!iterator.hasNext()) {
logger.info("DB is empty - adding Genesis");
this.lastBlock = Genesis.getInstance();
this.addBlock(lastBlock);
logger.debug("Block #{} -> {}", Genesis.NUMBER, lastBlock.toFlatString());
} else {
logger.debug("Displaying blocks stored in DB sorted on blocknumber");
for (iterator.seekToFirst(); iterator.hasNext();) {
this.lastBlock = new Block(iterator.next().getValue());
this.index.put(lastBlock.getNumber(), lastBlock.getHash());
logger.debug("Block #{} -> {}", lastBlock.getNumber(), lastBlock.toFlatString());
}
}
} finally {
// Make sure you close the iterator to avoid resource leaks.
try {
iterator.close();
} catch (IOException e) {
logger.error(e.getMessage(), e);
public void applyBlock(Block block) {
int i = 0;
for (Transaction tx : block.getTransactionsList()) {
stateLogger.debug("apply block: [ {} ] tx: [ {} ] ", block.getNumber(), i);
applyTransaction(block, tx, block.getCoinbase());
repository.dumpState(block.getNumber(), i, tx.getHash());
++i;
}
// miner reward
if (repository.getAccountState(block.getCoinbase()) == null)
repository.createAccount(block.getCoinbase());
repository.addBalance(block.getCoinbase(), Block.BLOCK_REWARD);
for (Block uncle : block.getUncleList()) {
repository.addBalance(uncle.getCoinbase(), Block.UNCLE_REWARD);
}
repository.dumpState(block.getNumber(), 0,
null);
}
public void applyTransaction(Block block, Transaction tx, byte[] coinbase) {
byte[] senderAddress = tx.getSender();
AccountState senderAccount = repository.getAccountState(senderAddress);
if (senderAccount == null) {
if (stateLogger.isWarnEnabled())
stateLogger.warn("No such address: {}",
Hex.toHexString(senderAddress));
return;
}
// 1. VALIDATE THE NONCE
BigInteger nonce = senderAccount.getNonce();
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);
return;
}
// 3. FIND OUT THE TRANSACTION TYPE
byte[] receiverAddress, code = null;
boolean isContractCreation = tx.isContractCreation();
if (isContractCreation) {
receiverAddress = tx.getContractAddress();
repository.createAccount(receiverAddress);
if(stateLogger.isDebugEnabled())
stateLogger.debug("new contract created address={}",
Hex.toHexString(receiverAddress));
code = tx.getData(); // init code
if (stateLogger.isDebugEnabled())
stateLogger.debug("running the init for contract: address={}",
Hex.toHexString(receiverAddress));
} else {
receiverAddress = tx.getReceiveAddress();
AccountState receiverState = repository.getAccountState(receiverAddress);
if (receiverState == null) {
repository.createAccount(receiverAddress);
if (stateLogger.isDebugEnabled())
stateLogger.debug("new receiver account created address={}",
Hex.toHexString(receiverAddress));
} else {
code = repository.getCode(receiverAddress);
if (code != null) {
if (stateLogger.isDebugEnabled())
stateLogger.debug("calling for existing contract: address={}",
Hex.toHexString(receiverAddress));
}
}
}
// 2.1 UPDATE THE NONCE
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
repository.increaseNonce(senderAddress);
// 2.2 PERFORM THE GAS VALUE TX
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
BigInteger gasDebit = tx.getTotalGasValueDebit();
// 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
if (gasDebit.signum() == 1) {
BigInteger balance = senderAccount.getBalance();
if (balance.compareTo(gasDebit) == -1) {
logger.debug("No gas to start the execution: sender={}",
Hex.toHexString(senderAddress));
return;
}
repository.addBalance(senderAddress, gasDebit.negate());
// The coinbase get the gas cost
if (coinbase != null)
repository.addBalance(coinbase, gasDebit);
if (stateLogger.isDebugEnabled())
stateLogger.debug(
"Before contract execution debit the sender address with gas total cost, "
+ "\n sender={} \n gas_debit= {}",
Hex.toHexString(senderAddress), gasDebit);
}
// 3. START TRACKING FOR REVERT CHANGES OPTION !!!
Repository trackRepository = repository.getTrack();
trackRepository.startTracking();
try {
// 4. THE SIMPLE VALUE/BALANCE CHANGE
if (tx.getValue() != null) {
BigInteger senderBalance = senderAccount.getBalance();
if (senderBalance.compareTo(new BigInteger(1, tx.getValue())) >= 0) {
repository.addBalance(receiverAddress,
new BigInteger(1, tx.getValue()));
repository.addBalance(senderAddress,
new BigInteger(1, tx.getValue()).negate());
if (stateLogger.isDebugEnabled())
stateLogger.debug("Update value balance \n "
+ "sender={}, receiver={}, value={}",
Hex.toHexString(senderAddress),
Hex.toHexString(receiverAddress),
new BigInteger(tx.getValue()));
}
}
// 5. CREATE OR EXECUTE PROGRAM
if (isContractCreation || code != null) {
Block currBlock = (block == null) ? this.getLastBlock() : block;
ProgramInvoke programInvoke = ProgramInvokeFactory
.createProgramInvoke(tx, currBlock, trackRepository);
VM vm = new VM();
Program program = new Program(code, programInvoke);
if (CONFIG.playVM())
vm.play(program);
ProgramResult result = program.getResult();
applyProgramResult(result, gasDebit, trackRepository,
senderAddress, receiverAddress, coinbase, isContractCreation);
} else {
// refund everything except fee (500 + 5*txdata)
BigInteger gasPrice = new BigInteger(1, tx.getGasPrice());
long dataFee = tx.getData() == null ? 0: tx.getData().length * GasCost.TXDATA;
long minTxFee = GasCost.TRANSACTION + dataFee;
BigInteger refund = gasDebit.subtract(BigInteger.valueOf(
minTxFee).multiply(gasPrice));
if (refund.signum() > 0) {
// gas refund
repository.addBalance(senderAddress, refund);
repository.addBalance(coinbase, refund.negate());
}
}
} catch (RuntimeException e) {
trackRepository.rollback();
return;
}
trackRepository.commit();
pendingTransactions.put(Hex.toHexString(tx.getHash()), tx);
}
public void close() {
if (this.chainDb != null)
chainDb.close();
}
/**
* After any contract code finish the run the certain result should take
* place, according the given circumstances
*
* @param result
* @param gasDebit
* @param senderAddress
* @param contractAddress
*/
private void applyProgramResult(ProgramResult result, BigInteger gasDebit,
Repository repository, byte[] senderAddress,
byte[] contractAddress, byte[] coinbase, boolean initResults) {
if (result.getException() != null
&& result.getException() instanceof Program.OutOfGasException) {
stateLogger.debug("contract run halted by OutOfGas: contract={}",
Hex.toHexString(contractAddress));
throw result.getException();
}
BigInteger gasPrice = BigInteger.valueOf(this.getGasPrice());
BigInteger refund = gasDebit.subtract(BigInteger.valueOf(
result.getGasUsed()).multiply(gasPrice));
if (refund.signum() > 0) {
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());
}
if (initResults) {
// Save the code created by init
byte[] bodyCode = null;
if (result.getHReturn() != null) {
bodyCode = result.getHReturn().array();
}
if (bodyCode != null) {
if (stateLogger.isDebugEnabled())
stateLogger
.debug("saving code of the contract to the db:\n contract={} code={}",
Hex.toHexString(contractAddress),
Hex.toHexString(bodyCode));
repository.saveCode(contractAddress, bodyCode);
}
}
// delete the marked to die accounts
if (result.getDeleteAccounts() == null) return;
for (DataWord address : result.getDeleteAccounts()){
repository.delete(address.getNoLeadZeroesData());
}
}
}

View File

@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* The Wallet handles the management of accounts with addresses and private keys.
@ -33,12 +34,12 @@ public class Wallet {
// private HashMap<Address, BigInteger> rows = new HashMap<>();
// <address, info> table for a wallet
private HashMap<String, Account> rows = new HashMap<String, Account>();
private Map<String, Account> rows = new HashMap<>();
private long high;
private List<WalletListener> listeners = new ArrayList<WalletListener>();
private List<WalletListener> listeners = new ArrayList<>();
private HashMap<BigInteger, Transaction> transactionMap = new HashMap<BigInteger, Transaction>();
private Map<BigInteger, Transaction> transactionMap = new HashMap<>();
public void addNewAccount() {
Account account = new Account();

View File

@ -6,14 +6,19 @@ import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.RLP;
import org.ethereum.util.Utils;
import org.spongycastle.util.encoders.Hex;
import org.ethereum.util.LRUMap;
public class HashUtil {
public static byte[] EMPTY_DATA_HASH = HashUtil.sha3(new byte[0]);
private static final int MAX_ENTRIES = 1000; // Should contain most commonly hashed values
private static LRUMap<ByteArrayWrapper, byte[]> sha3Cache = new LRUMap<>(0, MAX_ENTRIES);
public static final byte[] EMPTY_DATA_HASH = HashUtil.sha3(new byte[0]);
private static final MessageDigest sha256digest;
@ -30,13 +35,14 @@ public class HashUtil {
}
public static byte[] sha3(byte[] input) {
return SHA3Helper.sha3(input);
ByteArrayWrapper inputByteArray = new ByteArrayWrapper(input);
if(sha3Cache.keySet().contains(inputByteArray))
return sha3Cache.get(inputByteArray);
byte[] result = SHA3Helper.sha3(input);
sha3Cache.put(inputByteArray, result);
return result;
}
public static String sha3String(String input) {
return SHA3Helper.sha3String(input);
}
/**
* Calculates RIGTMOST160(SHA3(input)). This is used in address calculations.
*/

View File

@ -7,72 +7,74 @@ import org.spongycastle.util.encoders.Hex;
public class SHA3Helper {
public static String sha3String(String message) {
return sha3String(message, new SHA3Digest(256), true);
}
private static int DEFAULT_SIZE = 256;
public static String sha3String(byte[] message) {
return sha3String(message, new SHA3Digest(256), true);
}
public static String sha3String(String message) {
return sha3String(message, new SHA3Digest(DEFAULT_SIZE), true);
}
public static byte[] sha3(String message) {
return sha3(Hex.decode(message), new SHA3Digest(256), true);
}
public static byte[] sha3(byte[] message) {
return sha3(message, new SHA3Digest(256), true);
}
protected static String sha3String(String message, Size bitSize) {
SHA3Digest digest = new SHA3Digest(bitSize.bits);
return sha3String(message, digest, true);
}
protected static String sha3String(byte[] message, Size bitSize) {
SHA3Digest digest = new SHA3Digest(bitSize.bits);
return sha3String(message, digest, true);
}
protected static String sha3String(String message, Size bitSize, boolean bouncyencoder) {
SHA3Digest digest = new SHA3Digest(bitSize.bits);
return sha3String(message, digest, bouncyencoder);
}
protected static String sha3string(byte[] message, Size bitSize, boolean bouncyencoder) {
SHA3Digest digest = new SHA3Digest(bitSize.bits);
return sha3String(message, digest, bouncyencoder);
}
private static String sha3String(String message, SHA3Digest digest, boolean bouncyencoder) {
if (message != null) {
return sha3String(Hex.decode(message), digest, bouncyencoder);
}
throw new NullPointerException("Can't hash a NULL value");
}
public static String sha3String(byte[] message) {
return sha3String(message, new SHA3Digest(DEFAULT_SIZE), true);
}
private static String sha3String(byte[] message, SHA3Digest digest, boolean bouncyencoder) {
byte[] hash = doSha3(message, digest, bouncyencoder);
if (bouncyencoder) {
return Hex.toHexString(hash);
} else {
BigInteger bigInt = new BigInteger(1, hash);
return bigInt.toString(16);
}
}
private static byte[] sha3(byte[] message, SHA3Digest digest, boolean bouncyencoder) {
return doSha3(message, digest, bouncyencoder);
}
private static byte[] doSha3(byte[] message, SHA3Digest digest, boolean bouncyencoder) {
byte[] hash = new byte[digest.getDigestSize()];
public static byte[] sha3(String message) {
return sha3(Hex.decode(message), new SHA3Digest(DEFAULT_SIZE), true);
}
if (message.length != 0) {
digest.update(message, 0, message.length);
}
digest.doFinal(hash, 0);
return hash;
}
public static byte[] sha3(byte[] message) {
return sha3(message, new SHA3Digest(DEFAULT_SIZE), true);
}
protected static String sha3String(String message, Size bitSize) {
SHA3Digest digest = new SHA3Digest(bitSize.bits);
return sha3String(message, digest, true);
}
protected static String sha3String(byte[] message, Size bitSize) {
SHA3Digest digest = new SHA3Digest(bitSize.bits);
return sha3String(message, digest, true);
}
protected static String sha3String(String message, Size bitSize, boolean bouncyencoder) {
SHA3Digest digest = new SHA3Digest(bitSize.bits);
return sha3String(message, digest, bouncyencoder);
}
protected static String sha3string(byte[] message, Size bitSize, boolean bouncyencoder) {
SHA3Digest digest = new SHA3Digest(bitSize.bits);
return sha3String(message, digest, bouncyencoder);
}
private static String sha3String(String message, SHA3Digest digest, boolean bouncyencoder) {
if (message != null) {
return sha3String(Hex.decode(message), digest, bouncyencoder);
}
throw new NullPointerException("Can't hash a NULL value");
}
private static String sha3String(byte[] message, SHA3Digest digest, boolean bouncyencoder) {
byte[] hash = doSha3(message, digest, bouncyencoder);
if (bouncyencoder) {
return Hex.toHexString(hash);
} else {
BigInteger bigInt = new BigInteger(1, hash);
return bigInt.toString(16);
}
}
private static byte[] sha3(byte[] message, SHA3Digest digest, boolean bouncyencoder) {
return doSha3(message, digest, bouncyencoder);
}
private static byte[] doSha3(byte[] message, SHA3Digest digest, boolean bouncyencoder) {
byte[] hash = new byte[digest.getDigestSize()];
if (message.length != 0) {
digest.update(message, 0, message.length);
}
digest.doFinal(hash, 0);
return hash;
}
public enum Size {
@ -91,4 +93,5 @@ public class SHA3Helper {
return this.bits;
}
}
}

View File

@ -23,15 +23,15 @@ public class ContractDetails {
private byte[] rlpEncoded;
private List<DataWord> storageKeys = new ArrayList<DataWord>();
private List<DataWord> storageValues = new ArrayList<DataWord>();
private List<DataWord> storageKeys = new ArrayList<>();
private List<DataWord> storageValues = new ArrayList<>();
private byte[] code;
private Trie storageTrie = new Trie(null);
public ContractDetails() {
}
public ContractDetails() {
}
public ContractDetails(byte[] rlpCode) {
decode(rlpCode);
@ -165,7 +165,7 @@ public class ContractDetails {
}
public Map<DataWord, DataWord> getStorage() {
Map<DataWord, DataWord> storage = new HashMap<DataWord, DataWord>();
Map<DataWord, DataWord> storage = new HashMap<>();
for (int i = 0; storageKeys != null && i < storageKeys.size(); ++i) {
storage.put(storageKeys.get(i), storageValues.get(i));
}

View File

@ -9,6 +9,7 @@ import java.util.Collections;
import java.util.List;
import org.ethereum.config.SystemProperties;
import org.iq80.leveldb.CompressionType;
import org.iq80.leveldb.DB;
import org.iq80.leveldb.DBIterator;
import org.iq80.leveldb.Options;
@ -34,6 +35,7 @@ public class DatabaseImpl implements Database {
this.name = name;
Options options = new Options();
options.createIfMissing(true);
options.compressionType(CompressionType.NONE);
try {
logger.debug("Opening database");
File dbLocation = new File(System.getProperty("user.dir") + "/" +
@ -45,7 +47,6 @@ public class DatabaseImpl implements Database {
}
logger.debug("Initializing new or existing DB: '" + name + "'");
options.createIfMissing(true);
db = factory.open(fileLocation, options);
// logger.debug("Showing database stats");
// String stats = DATABASE.getProperty("leveldb.stats");

View File

@ -2,6 +2,9 @@ package org.ethereum.db;
import org.codehaus.plexus.util.FileUtils;
import org.ethereum.core.AccountState;
import org.ethereum.core.Block;
import org.ethereum.core.Blockchain;
import org.ethereum.core.Genesis;
import org.ethereum.crypto.HashUtil;
import org.ethereum.json.JSONHelper;
import org.ethereum.manager.WorldManager;
@ -9,6 +12,7 @@ import org.ethereum.trie.TrackTrie;
import org.ethereum.trie.Trie;
import org.ethereum.util.ByteUtil;
import org.ethereum.vm.DataWord;
import org.iq80.leveldb.DBIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.BigIntegers;
@ -56,18 +60,25 @@ public class Repository {
// TODO: Listeners listeners
// TODO: cash impl
private DatabaseImpl detailsDB = null;
private DatabaseImpl stateDB = null;
private DatabaseImpl chainDB = null;
private DatabaseImpl detailsDB = null;
private DatabaseImpl stateDB = null;
/**
* Create a new Repository DAO
* assuming empty db and thus no stateRoot
*
* @See loadBlockchain() to update the stateRoot
*/
public Repository() {
detailsDB = new DatabaseImpl("details");
contractDetailsDB = new TrackDatabase(detailsDB);
stateDB = new DatabaseImpl("state");
worldState = new Trie(stateDB.getDb());
accountStateDB = new TrackTrie(worldState);
chainDB = new DatabaseImpl("blockchain");
detailsDB = new DatabaseImpl("details");
contractDetailsDB = new TrackDatabase(detailsDB);
stateDB = new DatabaseImpl("state");
worldState = new Trie(stateDB.getDb());
accountStateDB = new TrackTrie(worldState);
}
private Repository(TrackTrie accountStateDB, TrackDatabase contractDetailsDB) {
this.accountStateDB = accountStateDB;
this.contractDetailsDB = contractDetailsDB;
@ -80,27 +91,72 @@ public class Repository {
}
public void startTracking() {
logger.info("start tracking");
logger.debug("start tracking");
accountStateDB.startTrack();
contractDetailsDB.startTrack();
}
public void commit() {
logger.info("commit changes");
logger.debug("commit changes");
accountStateDB.commitTrack();
contractDetailsDB.commitTrack();
}
public void rollback() {
logger.info("rollback changes");
logger.debug("rollback changes");
accountStateDB.rollbackTrack();
contractDetailsDB.rollbackTrack();
}
public Block getBlock(long blockNr) {
return new Block(chainDB.get(ByteUtil.longToBytes(blockNr)));
}
public void saveBlock(Block block) {
this.chainDB.put(ByteUtil.longToBytes(block.getNumber()), block.getEncoded());
}
public Blockchain loadBlockchain() {
Blockchain blockchain = new Blockchain(this);
DBIterator iterator = chainDB.iterator();
try {
if (!iterator.hasNext()) {
logger.info("DB is empty - adding Genesis");
blockchain.storeBlock(Genesis.getInstance());
logger.debug("Block #{} -> {}", Genesis.NUMBER, blockchain.getLastBlock().toFlatString());
} else {
logger.debug("Displaying blocks stored in DB sorted on blocknumber");
for (iterator.seekToFirst(); iterator.hasNext();) {
Block block = new Block(iterator.next().getValue());
blockchain.getBlockCache().put(block.getNumber(), block.getHash());
blockchain.setLastBlock(block);
logger.debug("Block #{} -> {}", block.getNumber(), block.toFlatString());
}
logger.info(
"*** Loaded up to block [ {} ] with stateRoot [ {} ]",
blockchain.getLastBlock().getNumber(), Hex
.toHexString(blockchain.getLastBlock()
.getStateRoot()));
}
} finally {
// Make sure you close the iterator to avoid resource leaks.
try {
iterator.close();
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
// Update world state to latest loaded block from db
this.worldState.setRoot(blockchain.getLastBlock().getStateRoot());
return blockchain;
}
public AccountState createAccount(byte[] addr) {
this.validateAddress(addr);
// 1. Save AccountState
AccountState state = new AccountState();
accountStateDB.update(addr, state.getEncoded());
@ -109,8 +165,8 @@ public class Repository {
ContractDetails details = new ContractDetails();
contractDetailsDB.put(addr, details.getEncoded());
if (logger.isInfoEnabled())
logger.info("New account created: [ {} ]", Hex.toHexString(addr));
if (logger.isDebugEnabled())
logger.debug("New account created: [ {} ]", Hex.toHexString(addr));
return state;
}
@ -122,12 +178,10 @@ public class Repository {
public AccountState getAccountState(byte[] addr) {
this.validateAddress(addr);
byte[] accountStateRLP = accountStateDB.get(addr);
if (accountStateRLP.length == 0) {
if (logger.isInfoEnabled())
logger.info("No account: [ {} ]", Hex.toHexString(addr));
return null;
}
AccountState state = new AccountState(accountStateRLP);
@ -138,18 +192,17 @@ public class Repository {
this.validateAddress(addr);
if (logger.isInfoEnabled())
logger.info("Account: [ {} ]", Hex.toHexString(addr));
if (logger.isDebugEnabled())
logger.debug("Get contract details for: [ {} ]", Hex.toHexString(addr));
byte[] accountDetailsRLP = contractDetailsDB.get(addr);
if (accountDetailsRLP == null) {
return null;
}
if (logger.isInfoEnabled())
logger.info("Contract details RLP: [ {} ]", Hex.toHexString(accountDetailsRLP));
if (logger.isDebugEnabled())
logger.debug("Contract details RLP: [ {} ]", Hex.toHexString(accountDetailsRLP));
ContractDetails details = new ContractDetails(accountDetailsRLP);
return details;
@ -167,8 +220,8 @@ public class Repository {
BigInteger newBalance = state.addToBalance(value);
if (logger.isInfoEnabled())
logger.info("Changing balance: account: [ {} ] new balance: [ {} ] delta: [ {} ]",
if (logger.isDebugEnabled())
logger.debug("Changing balance: account: [ {} ] new balance: [ {} ] delta: [ {} ]",
Hex.toHexString(addr), newBalance.toString(), value);
accountStateDB.update(addr, state.getEncoded());
@ -197,8 +250,8 @@ public class Repository {
if (state == null) return BigInteger.ZERO;
state.incrementNonce();
if (logger.isInfoEnabled())
logger.info("Incerement nonce: account: [ {} ] new nonce: [ {} ]",
if (logger.isDebugEnabled())
logger.debug("Incerement nonce: account: [ {} ] new nonce: [ {} ]",
Hex.toHexString(addr), state.getNonce().longValue());
accountStateDB.update(addr, state.getEncoded());
@ -219,8 +272,8 @@ public class Repository {
byte[] storageHash = details.getStorageHash();
state.setStateRoot(storageHash);
if (logger.isInfoEnabled())
logger.info("Storage key/value saved: account: [ {} ]\n key: [ {} ] value: [ {} ]\n new storageHash: [ {} ]",
if (logger.isDebugEnabled())
logger.debug("Storage key/value saved: account: [ {} ]\n key: [ {} ] value: [ {} ]\n new storageHash: [ {} ]",
Hex.toHexString(addr),
Hex.toHexString(key.getNoLeadZeroesData()),
Hex.toHexString(value.getNoLeadZeroesData()),
@ -272,8 +325,8 @@ public class Repository {
byte[] codeHash = HashUtil.sha3(code);
state.setCodeHash(codeHash);
if (logger.isInfoEnabled())
logger.info("Program code saved:\n account: [ {} ]\n codeHash: [ {} ] \n code: [ {} ]",
if (logger.isDebugEnabled())
logger.debug("Program code saved:\n account: [ {} ]\n codeHash: [ {} ] \n code: [ {} ]",
Hex.toHexString(addr),
Hex.toHexString(codeHash),
Hex.toHexString(code));
@ -302,7 +355,7 @@ public class Repository {
return stateDB.dumpKeys();
}
public void dumpState(long blockNumber, int txNumber, String txHash) {
public void dumpState(long blockNumber, int txNumber, byte[] txHash) {
if (!CONFIG.dumpFull()) return;
@ -318,7 +371,7 @@ public class Repository {
String fileName = "";
if (txHash != null)
fileName = String.format("%d_%d_%s.dmp",
blockNumber, txNumber, txHash.substring(0, 8));
blockNumber, txNumber, Hex.toHexString(txHash).substring(0, 8));
else
fileName = String.format("%d_c.dmp", blockNumber);
@ -381,6 +434,8 @@ public class Repository {
worldState.sync();
}
if (this.chainDB != null)
chainDB.close();
if (this.stateDB != null)
stateDB.close();
if (this.detailsDB != null)

View File

@ -85,13 +85,13 @@ public class EthereumImpl implements Ethereum {
@Override
public Block getBlockByIndex(long index){
Block block = WorldManager.getInstance().getBlockChain().getByNumber(index);
Block block = WorldManager.getInstance().getBlockchain().getByNumber(index);
return block;
}
@Override
public long getBlockChainSize(){
return WorldManager.getInstance().getBlockChain().getSize();
return WorldManager.getInstance().getBlockchain().getSize();
}
@Override

View File

@ -74,9 +74,9 @@ public class BlockChainTable extends JFrame {
@Override
public void actionPerformed(ActionEvent e) {
if (WorldManager.getInstance().getBlockChain().getSize() - 1 < lastFindIndex) return;
if (WorldManager.getInstance().getBlockchain().getSize() - 1 < lastFindIndex) return;
Block block = WorldManager.getInstance().getBlockChain().getByNumber(lastFindIndex);
Block block = WorldManager.getInstance().getBlockchain().getByNumber(lastFindIndex);
StringSelection stsel = new StringSelection(block.toString());
Clipboard system = Toolkit.getDefaultToolkit().getSystemClipboard();
system.setContents(stsel,stsel);
@ -96,10 +96,10 @@ public class BlockChainTable extends JFrame {
return;
}
for (int i = lastFindIndex + 1; i < WorldManager.getInstance().getBlockChain().getSize(); ++i) {
for (int i = lastFindIndex + 1; i < WorldManager.getInstance().getBlockchain().getSize(); ++i) {
if (WorldManager.getInstance().getBlockChain().getSize() - 1 < i) return;
Block block = WorldManager.getInstance().getBlockChain().getByNumber(i);
if (WorldManager.getInstance().getBlockchain().getSize() - 1 < i) return;
Block block = WorldManager.getInstance().getBlockchain().getByNumber(i);
boolean found = block.toString().toLowerCase().contains(toFind.toLowerCase());
if (found) {
// TODO: now we find the first occur

View File

@ -16,7 +16,7 @@ public class BlockTableModel extends AbstractTableModel {
public int getRowCount() {
fireTableDataChanged();
int rowCount = WorldManager.getInstance().getBlockChain().getSize();
int rowCount = WorldManager.getInstance().getBlockchain().getSize();
return rowCount;
}
@ -31,7 +31,7 @@ public class BlockTableModel extends AbstractTableModel {
// byte[] hash = MainData.instance.getAllBlocks().get(rowIndex).getHash();
// return Hex.toHexString(hash);
Block block = WorldManager.getInstance().getBlockChain().getByNumber(rowIndex);
Block block = WorldManager.getInstance().getBlockchain().getByNumber(rowIndex);
if (block == null) return "";
return block.toString();

View File

@ -315,7 +315,7 @@ class ContractCallDialog extends JDialog implements MessageAwareDialog {
Transaction tx = createTransaction();
if (tx == null) return;
ProgramPlayDialog.createAndShowGUI(programCode, tx, WorldManager.getInstance().getBlockChain().getLastBlock());
ProgramPlayDialog.createAndShowGUI(programCode, tx, WorldManager.getInstance().getBlockchain().getLastBlock());
}
protected JRootPane createRootPane() {

View File

@ -107,7 +107,7 @@ class ContractSubmitDialog extends JDialog implements MessageAwareDialog {
contractAddrInput.setText(Hex.toHexString(tx.getContractAddress()));
ProgramPlayDialog.createAndShowGUI(tx.getData(), tx,
WorldManager.getInstance().getBlockChain().getLastBlock());
WorldManager.getInstance().getBlockchain().getLastBlock());
}}
);
@ -301,7 +301,7 @@ class ContractSubmitDialog extends JDialog implements MessageAwareDialog {
Account account = ((AccountWrapper)creatorAddressCombo.getSelectedItem()).getAccount();
BigInteger currentBalance = account.getBalance();
BigInteger gasPrice = BigInteger.valueOf(WorldManager.getInstance().getBlockChain().getGasPrice());
BigInteger gasPrice = BigInteger.valueOf(WorldManager.getInstance().getBlockchain().getGasPrice());
BigInteger gasInput = new BigInteger( this.gasInput.getText());
boolean canAfford = currentBalance.compareTo(gasPrice.multiply(gasInput)) >= 0;

View File

@ -118,7 +118,7 @@ class PayOutDialog extends JDialog implements MessageAwareDialog {
byte[] senderPrivKey = account.getEcKey().getPrivKeyBytes();
byte[] nonce = accountState.getNonce() == BigInteger.ZERO ? null : accountState.getNonce().toByteArray();
byte[] gasPrice = BigInteger.valueOf( WorldManager.getInstance().getBlockChain().getGasPrice()).toByteArray();
byte[] gasPrice = BigInteger.valueOf( WorldManager.getInstance().getBlockchain().getGasPrice()).toByteArray();
Transaction tx = new Transaction(nonce, gasPrice, BigIntegers
.asUnsignedByteArray(fee), address, BigIntegers
@ -193,7 +193,7 @@ class PayOutDialog extends JDialog implements MessageAwareDialog {
// check if the tx is affordable
BigInteger ammountValue = new BigInteger(amountText);
BigInteger feeValue = new BigInteger(feeText);
BigInteger gasPrice = BigInteger.valueOf(WorldManager.getInstance().getBlockChain().getGasPrice());
BigInteger gasPrice = BigInteger.valueOf(WorldManager.getInstance().getBlockchain().getGasPrice());
BigInteger currentBalance = accountState.getBalance();
boolean canAfford = gasPrice.multiply(feeValue).add(ammountValue).compareTo(currentBalance) != 1;

View File

@ -6,6 +6,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
@ -225,7 +226,7 @@ public class ToolBar extends JFrame {
cp.add(chainToggle);
cp.add(walletToggle);
WorldManager.getInstance();
WorldManager.getInstance().loadBlockchain();
}
public static void main(String args[]) {

View File

@ -8,7 +8,6 @@ import java.net.UnknownHostException;
import java.util.*;
import org.ethereum.core.AccountState;
import org.ethereum.core.Block;
import org.ethereum.core.Blockchain;
import org.ethereum.core.Transaction;
import org.ethereum.core.Wallet;
@ -16,12 +15,10 @@ import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.Repository;
import org.ethereum.listener.EthereumListener;
import org.ethereum.net.BlockQueue;
import org.ethereum.net.client.ClientPeer;
import org.ethereum.net.client.PeerData;
import org.ethereum.net.peerdiscovery.PeerDiscovery;
import org.ethereum.net.submit.WalletTransaction;
import org.ethereum.vm.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
@ -37,7 +34,6 @@ import org.spongycastle.util.encoders.Hex;
public class WorldManager {
private Logger logger = LoggerFactory.getLogger("main");
private Logger stateLogger = LoggerFactory.getLogger("state");
private Blockchain blockchain;
private Repository repository;
@ -47,9 +43,6 @@ public class WorldManager {
private List<PeerData> peers = Collections.synchronizedList(new ArrayList<PeerData>());
private ClientPeer activePeer;
private Map<String, Transaction> pendingTransactions = Collections
.synchronizedMap(new HashMap<String, Transaction>());
// This map of transaction designed
// to approve the tx by external trusted peer
private Map<String, WalletTransaction> walletTransactions =
@ -59,12 +52,10 @@ public class WorldManager {
private static WorldManager instance;
private BlockQueue blockQueue = new BlockQueue();
public WorldManager() {
this.blockchain = new Blockchain();
private WorldManager() {
this.repository = new Repository();
this.blockchain = new Blockchain(repository);
// Initialize PeerData
try {
InetAddress ip = InetAddress.getByName(CONFIG.peerDiscoveryIP());
@ -94,242 +85,9 @@ public class WorldManager {
public static WorldManager getInstance() {
if(instance == null) {
instance = new WorldManager();
instance.blockchain.load();
}
return instance;
}
public void applyTransaction(Block block, Transaction tx, byte[] coinbase) {
byte[] senderAddress = tx.getSender();
AccountState senderAccount = repository.getAccountState(senderAddress);
if (senderAccount == null) {
if (stateLogger.isWarnEnabled())
stateLogger.warn("No such address: {}",
Hex.toHexString(senderAddress));
return;
}
// 1. VALIDATE THE NONCE
BigInteger nonce = senderAccount.getNonce();
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);
return;
}
// 3. FIND OUT THE TRANSACTION TYPE
byte[] receiverAddress, code = null;
boolean isContractCreation = tx.isContractCreation();
if (isContractCreation) {
receiverAddress = tx.getContractAddress();
repository.createAccount(receiverAddress);
stateLogger.info("New contract created address={}",
Hex.toHexString(receiverAddress));
code = tx.getData(); // init code
if (logger.isInfoEnabled())
logger.info("running the init for contract: address={}",
Hex.toHexString(receiverAddress));
} else {
receiverAddress = tx.getReceiveAddress();
AccountState receiverState = repository.getAccountState(receiverAddress);
if (receiverState == null) {
repository.createAccount(receiverAddress);
if (stateLogger.isInfoEnabled())
stateLogger.info("New receiver account created address={}",
Hex.toHexString(receiverAddress));
} else {
code = repository.getCode(receiverAddress);
if (code != null) {
if (logger.isInfoEnabled())
logger.info("calling for existing contract: address={}",
Hex.toHexString(receiverAddress));
}
}
}
// 2.1 UPDATE THE NONCE
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
repository.increaseNonce(senderAddress);
// 2.2 PERFORM THE GAS VALUE TX
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
BigInteger gasDebit = tx.getTotalGasValueDebit();
// 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
if (gasDebit.signum() == 1) {
BigInteger balance = senderAccount.getBalance();
if (balance.compareTo(gasDebit) == -1) {
logger.info("No gas to start the execution: sender={}",
Hex.toHexString(senderAddress));
return;
}
repository.addBalance(senderAddress, gasDebit.negate());
// The coinbase get the gas cost
if (coinbase != null)
repository.addBalance(coinbase, gasDebit);
if (stateLogger.isInfoEnabled())
stateLogger.info(
"Before contract execution debit the sender address with gas total cost, "
+ "\n sender={} \n gas_debit= {}",
Hex.toHexString(senderAddress), gasDebit);
}
// 3. START TRACKING FOR REVERT CHANGES OPTION !!!
Repository trackRepository = repository.getTrack();
trackRepository.startTracking();
try {
// 4. THE SIMPLE VALUE/BALANCE CHANGE
if (tx.getValue() != null) {
BigInteger senderBalance = senderAccount.getBalance();
if (senderBalance.compareTo(new BigInteger(1, tx.getValue())) >= 0) {
repository.addBalance(receiverAddress,
new BigInteger(1, tx.getValue()));
repository.addBalance(senderAddress,
new BigInteger(1, tx.getValue()).negate());
if (stateLogger.isInfoEnabled())
stateLogger.info("Update value balance \n "
+ "sender={}, receiver={}, value={}",
Hex.toHexString(senderAddress),
Hex.toHexString(receiverAddress),
new BigInteger(tx.getValue()));
}
}
// 5. CREATE OR EXECUTE PROGRAM
if (isContractCreation || code != null) {
Block currBlock = (block == null) ? blockchain.getLastBlock() : block;
ProgramInvoke programInvoke = ProgramInvokeFactory
.createProgramInvoke(tx, currBlock, trackRepository);
VM vm = new VM();
Program program = new Program(code, programInvoke);
if (CONFIG.playVM())
vm.play(program);
ProgramResult result = program.getResult();
applyProgramResult(result, gasDebit, trackRepository,
senderAddress, receiverAddress, coinbase, isContractCreation);
} else {
// refund everything except fee (500 + 5*txdata)
BigInteger gasPrice = new BigInteger(1, tx.getGasPrice());
long dataFee = tx.getData() == null ? 0: tx.getData().length * GasCost.TXDATA;
long minTxFee = GasCost.TRANSACTION + dataFee;
BigInteger refund = gasDebit.subtract(BigInteger.valueOf(
minTxFee).multiply(gasPrice));
if (refund.signum() > 0) {
// gas refund
repository.addBalance(senderAddress, refund);
repository.addBalance(coinbase, refund.negate());
}
}
} catch (RuntimeException e) {
trackRepository.rollback();
return;
}
trackRepository.commit();
pendingTransactions.put(Hex.toHexString(tx.getHash()), tx);
}
/**
* After any contract code finish the run the certain result should take
* place, according the given circumstances
*
* @param result
* @param gasDebit
* @param senderAddress
* @param contractAddress
*/
private void applyProgramResult(ProgramResult result, BigInteger gasDebit,
Repository repository, byte[] senderAddress,
byte[] contractAddress, byte[] coinbase, boolean initResults) {
if (result.getException() != null
&& result.getException() instanceof Program.OutOfGasException) {
logger.info("contract run halted by OutOfGas: contract={}",
Hex.toHexString(contractAddress));
throw result.getException();
}
BigInteger gasPrice = BigInteger.valueOf(blockchain.getGasPrice());
BigInteger refund = gasDebit.subtract(BigInteger.valueOf(
result.getGasUsed()).multiply(gasPrice));
if (refund.signum() > 0) {
if (stateLogger.isInfoEnabled())
stateLogger
.info("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());
}
if (initResults) {
// Save the code created by init
byte[] bodyCode = null;
if (result.getHReturn() != null) {
bodyCode = result.getHReturn().array();
}
if (bodyCode != null) {
repository.saveCode(contractAddress, bodyCode);
if (stateLogger.isInfoEnabled())
stateLogger
.info("saving code of the contract to the db:\n contract={} code={}",
Hex.toHexString(contractAddress),
Hex.toHexString(bodyCode));
}
}
// delete the marked to die accounts
if (result.getDeleteAccounts() == null) return;
for (DataWord address : result.getDeleteAccounts()){
repository.delete(address.getNoLeadZeroesData());
}
}
public void applyBlock(Block block) {
int i = 0;
for (Transaction tx : block.getTransactionsList()) {
logger.info("apply block: [ {} ] tx: [ {} ] ", block.getNumber(), i);
applyTransaction(block, tx, block.getCoinbase());
repository.dumpState(block.getNumber(), i,
Hex.toHexString(tx.getHash()));
++i;
}
// miner reward
if (repository.getAccountState(block.getCoinbase()) == null)
repository.createAccount(block.getCoinbase());
repository.addBalance(block.getCoinbase(), Block.BLOCK_REWARD);
for (Block uncle : block.getUncleList()) {
repository.addBalance(uncle.getCoinbase(), Block.UNCLE_REWARD);
}
repository.dumpState(block.getNumber(), 0,
null);
}
/***********************************************************************
* 1) the dialog put a pending transaction on the list
@ -357,6 +115,14 @@ public class WorldManager {
logger.info("pending transaction removed with hash: {} ", hash);
walletTransactions.remove(hash);
}
public void setRepository(Repository repository) {
this.repository = repository;
}
public void setBlockchain(Blockchain blockchain) {
this.blockchain = blockchain;
}
public void setWallet(Wallet wallet) {
this.wallet = wallet;
@ -366,9 +132,13 @@ public class WorldManager {
return repository;
}
public Blockchain getBlockChain() {
public Blockchain getBlockchain() {
return blockchain;
}
public void loadBlockchain() {
this.blockchain = repository.loadBlockchain();
}
public Wallet getWallet() {
return wallet;
@ -390,7 +160,6 @@ public class WorldManager {
this.listener = listener;
}
public void addPeers(List<PeerData> newPeers) {
for (PeerData peer : newPeers) {
if (this.peers.indexOf(peer) == -1) {
@ -416,12 +185,7 @@ public class WorldManager {
peerDiscovery.stop();
}
public BlockQueue getBlockQueue() {
return blockQueue;
}
public void close() {
blockchain.close();
repository.close();
}

View File

@ -20,10 +20,10 @@ public class BlockQueue {
private static Logger logger = LoggerFactory.getLogger("blockchain");
ConcurrentLinkedQueue<Block> blockQueue = new ConcurrentLinkedQueue<Block>();
Block lastBlock;
private Queue<Block> blockQueue = new ConcurrentLinkedQueue<>();
private Block lastBlock;
Timer timer = new Timer();
private Timer timer = new Timer();
public BlockQueue() {
@ -40,10 +40,10 @@ public class BlockQueue {
Block block = blockQueue.poll();
WorldManager.getInstance().getBlockChain().applyBlock(block);
WorldManager.getInstance().getBlockchain().add(block);
}
public void addBlocks(List<Block> blockList){
public void addBlocks(List<Block> blockList) {
Block lastReceivedBlock = blockList.get(blockList.size() - 1);
if (lastReceivedBlock.getNumber() != getLast().getNumber() + 1) return;
@ -61,15 +61,15 @@ public class BlockQueue {
logger.trace("Blocks waiting to be proceed in the queue: [ {} ]", blockQueue.size());
}
public Block getLast(){
public Block getLast() {
if (blockQueue.isEmpty())
return WorldManager.getInstance().getBlockChain().getLastBlock();
return WorldManager.getInstance().getBlockchain().getLastBlock();
return lastBlock;
}
private class BlockByIndexComparator implements Comparator<Block>{
private class BlockByIndexComparator implements Comparator<Block> {
@Override
public int compare(Block o1, Block o2) {
@ -83,7 +83,7 @@ public class BlockQueue {
}
}
public int size(){
public int size() {
return blockQueue.size();
}

View File

@ -3,7 +3,6 @@ package org.ethereum.net.client;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.CorruptedFrameException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -183,7 +183,8 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
List<Transaction> txList = transactionsMessage.getTransactions();
for(Transaction tx : txList)
WorldManager.getInstance().applyTransaction(null, tx, null);
WorldManager.getInstance().getBlockchain()
.applyTransaction(null, tx, null);
logger.info(transactionsMessage.toString());
if (peerListener != null) peerListener.console(transactionsMessage.toString());
@ -235,7 +236,7 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
}
if (blockList.isEmpty()) return;
WorldManager.getInstance().getBlockQueue().addBlocks(blockList);
WorldManager.getInstance().getBlockchain().getBlockQueue().addBlocks(blockList);
if (peerListener != null) peerListener.console(blocksMessage.toString());
}
@ -318,10 +319,10 @@ public class EthereumProtocolHandler extends ChannelInboundHandlerAdapter {
private void sendGetChain() {
if (WorldManager.getInstance().getBlockQueue().size() >
if (WorldManager.getInstance().getBlockchain().getBlockQueue().size() >
SystemProperties.CONFIG.maxBlocksQueued()) return;
Block lastBlock = WorldManager.getInstance().getBlockQueue().getLast();
Block lastBlock = WorldManager.getInstance().getBlockchain().getBlockQueue().getLast();
byte[] hash = lastBlock.getHash();
GetChainMessage chainMessage =

View File

@ -88,7 +88,7 @@ public class GetChainMessage extends Message {
}
@Override
public Class getAnswerMessage() {
public Class<BlocksMessage> getAnswerMessage() {
return BlocksMessage.class;
}

View File

@ -27,7 +27,7 @@ public class GetPeersMessage extends Message {
}
@Override
public Class getAnswerMessage() {
public Class<PeersMessage> getAnswerMessage() {
return PeersMessage.class;
}
}

View File

@ -1,15 +1,7 @@
package org.ethereum.net.message;
import org.ethereum.util.RLP;
import org.ethereum.util.RLPItem;
import org.ethereum.util.RLPList;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import static org.ethereum.net.Command.HELLO;
/**
* www.ethereumJ.com
* @author: Roman Mandeleil
@ -35,7 +27,7 @@ public class PingMessage extends Message {
}
@Override
public Class getAnswerMessage() {
public Class<PongMessage> getAnswerMessage() {
return PongMessage.class;
}
}

View File

@ -16,13 +16,12 @@ import org.iq80.leveldb.DB;
*/
public class Cache {
private Map<ByteArrayWrapper, Node> nodes;
private Map<ByteArrayWrapper, Node> nodes = new HashMap<>();
private DB db;
private boolean isDirty;
public Cache(DB db) {
this.db = db;
nodes = new HashMap<ByteArrayWrapper, Node>();
}
/**
@ -88,7 +87,7 @@ public class Cache {
// If the nodes grows beyond the 200 entries we simple empty it
// FIXME come up with something better
if (this.nodes.size() > 200) {
this.nodes = new HashMap<ByteArrayWrapper, Node>();
this.nodes = new HashMap<>();
}
}

View File

@ -35,7 +35,7 @@ import org.spongycastle.util.encoders.Hex;
* @author: Nick Savers
* Created on: 20/05/2014 10:44
*/
public class Trie implements TrieFacade{
public class Trie implements TrieFacade {
private Logger logger = LoggerFactory.getLogger("trie");
@ -72,6 +72,10 @@ public class Trie implements TrieFacade{
return root;
}
public void setRoot(Object root) {
this.root = root;
}
public void setCache(Cache cache) {
this.cache = cache;
}

View File

@ -1,10 +1,7 @@
package org.ethereum.util;
import com.maxmind.geoip.Location;
import org.apache.log4j.PropertyConfigurator;
import org.ethereum.db.IpGeoDB;
import java.net.InetAddress;
import java.net.URL;
import static org.ethereum.config.SystemProperties.CONFIG;

View File

@ -10,6 +10,8 @@ import org.spongycastle.util.encoders.Hex;
public class ByteUtil {
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
/**
* Creates a copy of bytes and appends b to the end of it
*/

View File

@ -0,0 +1,24 @@
package org.ethereum.util;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Simple LRU map used for reusing lookup values.
*/
public class LRUMap<K,V> extends LinkedHashMap<K,V> {
private static final long serialVersionUID = 1L;
protected final int maxEntries;
public LRUMap(int initialEntries, int maxEntries) {
super(initialEntries, 0.8f, true);
this.maxEntries = maxEntries;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return size() > maxEntries;
}
}

View File

@ -18,12 +18,12 @@ import java.nio.ByteBuffer;
*/
public class DataWord implements Comparable<DataWord> {
public static final DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack
public static final DataWord ZERO = new DataWord(new byte[32]); // don't push it in to the stack
public static final DataWord ZERO_EMPTY_ARRAY = new DataWord(new byte[0]); // don't push it in to the stack
byte[] data = new byte[32];
private byte[] data = new byte[32];
public DataWord() {
this.data = new byte[32];
}
public DataWord(int num) {

View File

@ -3,6 +3,7 @@ package org.ethereum.vm;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.ContractDetails;
import org.ethereum.db.Repository;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -25,7 +26,7 @@ public class Program {
private int invokeHash;
private ProgramListener listener;
Stack<DataWord> stack = new Stack<DataWord>();
Stack<DataWord> stack = new Stack<>();
ByteBuffer memory = null;
DataWord programAddress;
@ -147,7 +148,7 @@ public class Program {
}
public void memorySave(DataWord addrB, DataWord value) {
memorySave(addrB.data, value.data);
memorySave(addrB.getData(), value.getData());
}
public void memorySave(byte[] addr, byte[] value) {
@ -160,7 +161,7 @@ public class Program {
public DataWord memoryLoad(DataWord addr) {
int address = new BigInteger(1, addr.getData()).intValue();
allocateMemory(address, DataWord.ZERO.data);
allocateMemory(address, DataWord.ZERO.getData());
byte[] data = new byte[32];
System.arraycopy(memory.array(), address, data , 0 ,32);
@ -465,12 +466,12 @@ public class Program {
}
public DataWord getOwnerAddress() {
if (invokeData == null) return new DataWord( new byte[0]);
if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
return this.programAddress.clone();
}
public DataWord getBalance(DataWord address) {
if (invokeData == null) return new DataWord( new byte[0]);
if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
BigInteger balance = result.getRepository().getBalance(address.getLast20Bytes());
DataWord balanceData = new DataWord(balance.toByteArray());
@ -479,43 +480,43 @@ public class Program {
}
public DataWord getOriginAddress() {
if (invokeData == null) return new DataWord( new byte[0]);
if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
return invokeData.getOriginAddress().clone();
}
public DataWord getCallerAddress() {
if (invokeData == null) return new DataWord( new byte[0]);
if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
return invokeData.getCallerAddress().clone();
}
public DataWord getGasPrice() {
if (invokeData == null) return new DataWord( new byte[0]);
if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
return invokeData.getMinGasPrice().clone();
}
public DataWord getGas() {
if (invokeData == null) return new DataWord( new byte[0]);
if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
long afterSpend = invokeData.getGas().longValue() - result.getGasUsed();
return new DataWord(afterSpend);
}
public DataWord getCallValue() {
if (invokeData == null) return new DataWord( new byte[0]);
if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
return invokeData.getCallValue().clone();
}
public DataWord getDataSize() {
if (invokeData == null) return new DataWord( new byte[0]);
if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
return invokeData.getDataSize().clone();
}
public DataWord getDataValue(DataWord index) {
if (invokeData == null) return new DataWord( new byte[0]);
if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
return invokeData.getDataValue(index);
}
public byte[] getDataCopy(DataWord offset, DataWord length) {
if (invokeData == null) return new byte[0];
if (invokeData == null) return ByteUtil.EMPTY_BYTE_ARRAY;
return invokeData.getDataCopy(offset, length);
}

View File

@ -24,7 +24,7 @@ public class ProgramInvokeFactory {
public static ProgramInvoke createProgramInvoke(Transaction tx, Block block, Repository repository) {
// https://ethereum.etherpad.mozilla.org/26
Block lastBlock = WorldManager.getInstance().getBlockChain().getLastBlock();
Block lastBlock = WorldManager.getInstance().getBlockchain().getLastBlock();
/*** ADDRESS op ***/
// YP: Get address of currently executing account.

View File

@ -449,7 +449,7 @@ public class VM {
if (logger.isInfoEnabled())
hint = "data: " + Hex.toHexString(msgData);
program.memorySave(memOffsetData.data, msgData);
program.memorySave(memOffsetData.getData(), msgData);
program.step();
} break;
case CODESIZE:{
@ -759,7 +759,7 @@ public class VM {
if (memoryUsage > 0)
program.spendGas(GasCost.MEMORY * memoryUsage, OpCode.code(op).name() + " (memory usage)");
program.fullTrace();
// program.fullTrace();
} catch (RuntimeException e) {
program.stop();
if(e instanceof OutOfGasException)

View File

@ -23,9 +23,9 @@ log4j.logger.io.netty = ERROR
log4j.logger.wire = ERROR
log4j.logger.VM = DEBUG
log4j.logger.main = INFO
log4j.logger.state = ERROR
log4j.logger.state = DEBUG
log4j.logger.repository = DEBUG
log4j.logger.blockchain = DEBUG
log4j.logger.blockchain = INFO
log4j.logger.ui = ERROR
log4j.logger.gas = ERROR

View File

@ -24,8 +24,8 @@ log4j.logger.wire = ERROR
log4j.logger.VM = ERROR
log4j.logger.main = ERROR
log4j.logger.trie = ERROR
log4j.logger.state = ERROR
log4j.logger.repository = ERROR
log4j.logger.state = INFO
log4j.logger.repository = INFO
log4j.logger.blockchain = INFO
log4j.logger.txs = ERROR
log4j.logger.ui = ERROR

View File

@ -9,8 +9,12 @@ server.acceptConnections = false
#peer.discovery.port = 30303
# Peer Server One: peer discovery
peer.discovery.ip = 54.204.10.41
# Asimov
peer.discovery.ip = 185.43.109.23
peer.discovery.port = 30303
# RomanJ
#peer.discovery.ip = 54.211.14.10
#peer.discovery.port = 30303
@ -30,7 +34,7 @@ peer.discovery.port = 30303
#peer.active.port = 30303
# PoC-5 testnet
peer.active.ip = 54.204.10.41
peer.active.ip = 185.43.109.23
peer.active.port = 30303
#peer.active.ip = 54.72.69.180
@ -80,7 +84,7 @@ samples.dir = samples
# the existing database will be
# destroyed and all the data will be
# downloaded from peers again
database.reset = true
database.reset = false
# place to save physical storage files
database.dir = database
@ -95,7 +99,7 @@ coinbase.secret = "monkey"
# in JSON form to [dump.dir]
# if [dump.full] = true
# posible values [true/false]
dump.full = true
dump.full = false
dump.dir = dmp
# clean the dump dir each start
@ -104,14 +108,14 @@ dump.clean.on.restart = true
# make changes to tracing options
# starting from certain block
# -1 don't make any tracing changes
trace.startblock = 1150
trace.startblock = 1155
# invoke vm program on
# message received,
# if the vm is not invoked
# the balance transfer
# occurs anyway [true/false]
play.vm = false
play.vm = true
# maximum blocks to ask,
# when downloading the chain

View File

@ -115,7 +115,7 @@ public class BlockTest {
assertEquals(new BigInteger(1, Genesis.DIFFICULTY), difficulty);
// Storing genesis because the parent needs to be in the DB for calculation.
WorldManager.getInstance().getBlockChain().addBlock(genesis);
WorldManager.getInstance().getBlockchain().add(genesis);
Block block1 = new Block(Hex.decode(block_1));
BigInteger calcDifficulty = new BigInteger(1, block1.calcDifficulty());
@ -137,7 +137,7 @@ public class BlockTest {
assertEquals(Genesis.GAS_LIMIT, gasLimit);
// Storing genesis because the parent needs to be in the DB for calculation.
WorldManager.getInstance().getBlockChain().addBlock(genesis);
WorldManager.getInstance().getBlockchain().add(genesis);
// Test with block
Block block1 = new Block(Hex.decode(block_1));

View File

@ -44,11 +44,11 @@ public class DataWordTest {
DataWord x = new DataWord(two);
x.add(new DataWord(two));
System.out.println(Hex.toHexString(x.data));
System.out.println(Hex.toHexString(x.getData()));
DataWord y = new DataWord(two);
y.add2(new DataWord(two));
System.out.println(Hex.toHexString(y.data));
System.out.println(Hex.toHexString(y.getData()));
}
@Test
@ -60,13 +60,13 @@ public class DataWordTest {
DataWord x = new DataWord(three);
x.add(new DataWord(three));
assertEquals(32, x.data.length);
System.out.println(Hex.toHexString(x.data));
assertEquals(32, x.getData().length);
System.out.println(Hex.toHexString(x.getData()));
// FAIL
// DataWord y = new DataWord(three);
// y.add2(new DataWord(three));
// System.out.println(Hex.toHexString(y.data));
// System.out.println(Hex.toHexString(y.getData()));
}
@Test
@ -85,7 +85,7 @@ public class DataWordTest {
DataWord x = new DataWord(one);// System.out.println(x.value());
DataWord y = new DataWord(two);// System.out.println(y.value());
y.mod(x);
assertEquals(32, y.data.length);
assertEquals(expected, Hex.toHexString(y.data));
assertEquals(32, y.getData().length);
assertEquals(expected, Hex.toHexString(y.getData()));
}
}

File diff suppressed because it is too large Load Diff