Merge pull request #74 from nicksavers/repofactor
Performance and better separation of concerns
This commit is contained in:
commit
dfd2e26c6e
|
@ -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 contract—this 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);
|
||||
|
|
|
@ -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,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,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() {
|
||||
|
@ -78,49 +96,45 @@ 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()) {
|
||||
|
@ -128,88 +142,277 @@ public class Blockchain {
|
|||
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.chainDb.put(ByteUtil.longToBytes(block.getNumber()), block.getEncoded());
|
||||
this.index.put(block.getNumber(), block.getHash());
|
||||
|
||||
this.applyBlock(block);
|
||||
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 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
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getLatestBlockHash() {
|
||||
if (index.isEmpty())
|
||||
return Genesis.getInstance().getHash();
|
||||
else
|
||||
return getLastBlock().getHash();
|
||||
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());
|
||||
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 {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,11 +35,12 @@ public class HashUtil {
|
|||
}
|
||||
|
||||
public static byte[] sha3(byte[] input) {
|
||||
return SHA3Helper.sha3(input);
|
||||
}
|
||||
|
||||
public static String sha3String(String input) {
|
||||
return SHA3Helper.sha3String(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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,20 +7,22 @@ import org.spongycastle.util.encoders.Hex;
|
|||
|
||||
public class SHA3Helper {
|
||||
|
||||
private static int DEFAULT_SIZE = 256;
|
||||
|
||||
public static String sha3String(String message) {
|
||||
return sha3String(message, new SHA3Digest(256), true);
|
||||
return sha3String(message, new SHA3Digest(DEFAULT_SIZE), true);
|
||||
}
|
||||
|
||||
public static String sha3String(byte[] message) {
|
||||
return sha3String(message, new SHA3Digest(256), true);
|
||||
return sha3String(message, new SHA3Digest(DEFAULT_SIZE), true);
|
||||
}
|
||||
|
||||
public static byte[] sha3(String message) {
|
||||
return sha3(Hex.decode(message), new SHA3Digest(256), true);
|
||||
return sha3(Hex.decode(message), new SHA3Digest(DEFAULT_SIZE), true);
|
||||
}
|
||||
|
||||
public static byte[] sha3(byte[] message) {
|
||||
return sha3(message, new SHA3Digest(256), true);
|
||||
return sha3(message, new SHA3Digest(DEFAULT_SIZE), true);
|
||||
}
|
||||
|
||||
protected static String sha3String(String message, Size bitSize) {
|
||||
|
@ -91,4 +93,5 @@ public class SHA3Helper {
|
|||
return this.bits;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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,243 +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) {
|
||||
|
||||
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
|
||||
|
@ -358,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;
|
||||
}
|
||||
|
@ -366,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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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().applyBlock(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;
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -19,11 +19,11 @@ 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_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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue