Use Repository as abstraction layer for Blockchain
This commit is contained in:
parent
d8063c5ff3
commit
2b42ebaac2
|
@ -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() {
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
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.Map;
|
||||
|
||||
|
@ -53,20 +54,38 @@ 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<>();
|
||||
|
||||
public Blockchain() {
|
||||
this.chainDb = new DatabaseImpl("blockchain");
|
||||
private Map<String, Transaction> pendingTransactions = Collections
|
||||
.synchronizedMap(new HashMap<String, Transaction>());
|
||||
|
||||
private BlockQueue blockQueue = new BlockQueue();
|
||||
|
||||
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() {
|
||||
|
@ -77,12 +96,19 @@ 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 add(Block block) {
|
||||
|
@ -92,13 +118,13 @@ public class Blockchain {
|
|||
|
||||
// if it is the first block to add
|
||||
// make sure the parent is genesis
|
||||
if (index.isEmpty()
|
||||
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;
|
||||
|
@ -121,15 +147,6 @@ public class Blockchain {
|
|||
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 processBlock(Block block) {
|
||||
|
@ -138,7 +155,7 @@ public class Blockchain {
|
|||
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.storeBlock(block);
|
||||
|
@ -147,7 +164,7 @@ public class Blockchain {
|
|||
}
|
||||
}
|
||||
|
||||
private void storeBlock(Block block) {
|
||||
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());
|
||||
|
@ -155,12 +172,13 @@ public class Blockchain {
|
|||
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.chainDb.put(ByteUtil.longToBytes(block.getNumber()), block.getEncoded());
|
||||
this.index.put(block.getNumber(), block.getHash());
|
||||
this.repository.saveBlock(block);
|
||||
this.blockCache.put(block.getNumber(), block.getHash());
|
||||
this.setLastBlock(block);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
|
@ -168,46 +186,233 @@ public class Blockchain {
|
|||
logger.info("*** Block chain size: [ {} ]", this.getSize());
|
||||
}
|
||||
|
||||
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 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;
|
||||
|
||||
}
|
||||
|
||||
public byte[] getLatestBlockHash() {
|
||||
if (index.isEmpty())
|
||||
return Genesis.getInstance().getHash();
|
||||
else
|
||||
return getLastBlock().getHash();
|
||||
// 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);
|
||||
}
|
||||
|
||||
public void load() {
|
||||
DBIterator iterator = chainDb.iterator();
|
||||
try {
|
||||
if (!iterator.hasNext()) {
|
||||
logger.info("DB is empty - adding Genesis");
|
||||
this.lastBlock = Genesis.getInstance();
|
||||
this.storeBlock(lastBlock);
|
||||
logger.debug("Block #{} -> {}", Genesis.NUMBER, lastBlock.toFlatString());
|
||||
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 {
|
||||
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);
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (this.chainDb != null)
|
||||
chainDb.close();
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,8 @@ 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;
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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,10 +60,18 @@ public class Repository {
|
|||
// TODO: Listeners listeners
|
||||
// TODO: cash impl
|
||||
|
||||
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() {
|
||||
chainDB = new DatabaseImpl("blockchain");
|
||||
detailsDB = new DatabaseImpl("details");
|
||||
contractDetailsDB = new TrackDatabase(detailsDB);
|
||||
stateDB = new DatabaseImpl("state");
|
||||
|
@ -67,7 +79,6 @@ public class Repository {
|
|||
accountStateDB = new TrackTrie(worldState);
|
||||
}
|
||||
|
||||
|
||||
private Repository(TrackTrie accountStateDB, TrackDatabase contractDetailsDB) {
|
||||
this.accountStateDB = accountStateDB;
|
||||
this.contractDetailsDB = contractDetailsDB;
|
||||
|
@ -80,23 +91,68 @@ 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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -126,8 +182,6 @@ public class Repository {
|
|||
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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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[]) {
|
||||
|
|
|
@ -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,11 +52,9 @@ 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 {
|
||||
|
@ -94,247 +85,10 @@ 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) {
|
||||
|
||||
if(block.getNumber() == 1157) {
|
||||
logger.debug("Block 1157");
|
||||
}
|
||||
|
||||
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
|
||||
* 2) the dialog send the transaction to a net
|
||||
|
@ -362,6 +116,14 @@ public class WorldManager {
|
|||
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;
|
||||
}
|
||||
|
@ -370,10 +132,14 @@ 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;
|
||||
}
|
||||
|
@ -394,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) {
|
||||
|
@ -420,12 +185,7 @@ public class WorldManager {
|
|||
peerDiscovery.stop();
|
||||
}
|
||||
|
||||
public BlockQueue getBlockQueue() {
|
||||
return blockQueue;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
blockchain.close();
|
||||
repository.close();
|
||||
}
|
||||
|
||||
|
|
|
@ -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,7 +40,7 @@ public class BlockQueue {
|
|||
|
||||
Block block = blockQueue.poll();
|
||||
|
||||
WorldManager.getInstance().getBlockChain().add(block);
|
||||
WorldManager.getInstance().getBlockchain().add(block);
|
||||
}
|
||||
|
||||
public void addBlocks(List<Block> blockList) {
|
||||
|
@ -64,7 +64,7 @@ public class BlockQueue {
|
|||
public Block getLast() {
|
||||
|
||||
if (blockQueue.isEmpty())
|
||||
return WorldManager.getInstance().getBlockChain().getLastBlock();
|
||||
return WorldManager.getInstance().getBlockchain().getLastBlock();
|
||||
|
||||
return lastBlock;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -88,7 +88,7 @@ public class GetChainMessage extends Message {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Class getAnswerMessage() {
|
||||
public Class<BlocksMessage> getAnswerMessage() {
|
||||
return BlocksMessage.class;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ public class GetPeersMessage extends Message {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Class getAnswerMessage() {
|
||||
public Class<PeersMessage> getAnswerMessage() {
|
||||
return PeersMessage.class;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<>();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -99,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
|
||||
|
|
|
@ -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().add(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().add(genesis);
|
||||
WorldManager.getInstance().getBlockchain().add(genesis);
|
||||
|
||||
// Test with block
|
||||
Block block1 = new Block(Hex.decode(block_1));
|
||||
|
|
Loading…
Reference in New Issue