Merged poc-9-merge-rlpx from main repo.

This commit is contained in:
Adrian Tiberius 2015-05-14 19:47:06 +02:00
parent 22f70082c4
commit ea2fc0239f
41 changed files with 606 additions and 169 deletions

View File

@ -45,6 +45,7 @@ public class SystemProperties {
private final static int DEFAULT_PEER_LISTEN_PORT = 30303;
private final static String DEFAULT_KEY_VALUE_DATA_SOURCE = "leveldb";
private final static boolean DEFAULT_REDIS_ENABLED = true;
private static final String DEFAULT_BLOCKS_LOADER = "";
/* Testing */
@ -288,6 +289,11 @@ public class SystemProperties {
return Boolean.parseBoolean(prop.getProperty("GitHubTests.VMTest.loadLocal", String.valueOf(DEFAULT_VMTEST_LOAD_LOCAL)));
}
public String blocksLoader() {
return prop.getProperty("blocks.loader", DEFAULT_BLOCKS_LOADER);
}
public static void main(String args[]) {
SystemProperties systemProperties = new SystemProperties();
systemProperties.print();

View File

@ -563,19 +563,19 @@ public class BlockchainImpl implements Blockchain {
public void storeBlock(Block block, List<TransactionReceipt> receipts) {
/* Debug check to see if the state is still as expected */
if (logger.isWarnEnabled()) {
String blockStateRootHash = Hex.toHexString(block.getStateRoot());
String worldStateRootHash = Hex.toHexString(repository.getRoot());
if (!blockStateRootHash.equals(worldStateRootHash)) {
String blockStateRootHash = Hex.toHexString(block.getStateRoot());
String worldStateRootHash = Hex.toHexString(repository.getRoot());
if (!blockStateRootHash.equals(worldStateRootHash)) {
stateLogger.error("BLOCK: STATE CONFLICT! block: {} worldstate {} mismatch", block.getNumber(), worldStateRootHash);
adminInfo.lostConsensus();
stateLogger.error("BLOCK: STATE CONFLICT! block: {} worldstate {} mismatch", block.getNumber(), worldStateRootHash);
adminInfo.lostConsensus();
// in case of rollback hard move the root
System.out.println("CONFLICT: BLOCK #" + block.getNumber() );
System.exit(1);
// in case of rollback hard move the root
// Block parentBlock = blockStore.getBlockByHash(block.getParentHash());
// repository.syncToRoot(parentBlock.getStateRoot());
// todo: after the rollback happens other block should be requested
}
// todo: after the rollback happens other block should be requested
}
blockStore.saveBlock(block, receipts);

View File

@ -1,5 +1,6 @@
package org.ethereum.core;
//import jdk.jfr.events.ThrowablesEvent;
import org.ethereum.db.BlockStore;
import org.ethereum.facade.Repository;
import org.ethereum.listener.EthereumListener;
@ -181,13 +182,18 @@ public class TransactionExecutor {
private void create() {
byte[] newContractAddress = tx.getContractAddress();
if (!(tx.getData().length == 0)){
if (tx.getData() != null && !(tx.getData().length == 0)){
ProgramInvoke programInvoke =
programInvokeFactory.createProgramInvoke(tx, currentBlock, cacheTrack, blockStore);
this.vm = new VM();
this.program = new Program(tx.getData(), programInvoke);
} else {
m_endGas = toBI(tx.getGasLimit()).longValue() - basicTxCost;
cacheTrack.createAccount(tx.getContractAddress());
}
BigInteger endowment = toBI(tx.getValue());

View File

@ -368,4 +368,4 @@ public class Wallet {
public void setHigh(long high) {
this.high = high;
}
}
}

View File

@ -29,7 +29,7 @@ public class RedisConnectionImpl implements RedisConnection {
if (!SystemProperties.CONFIG.isRedisEnabled()) return;
String redisCloudUrl = System.getenv(REDISCLOUD_URL);
if (hasLength(redisCloudUrl)) {
if (!hasLength(redisCloudUrl)) {
logger.info("Cannot connect to Redis. 'REDISCLOUD_URL' environment variable is not defined.");
return;
}

View File

@ -17,37 +17,27 @@ public interface BlockStore {
public byte[] getBlockHashByNumber(long blockNumber);
//@Transactional(readOnly = true)
Block getBlockByNumber(long blockNumber);
//@Transactional(readOnly = true)
Block getBlockByHash(byte[] hash);
//@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
List<byte[]> getListOfHashesStartFrom(byte[] hash, int qty);
//@Transactional
void deleteBlocksSince(long number);
//@Transactional
void saveBlock(Block block, List<TransactionReceipt> receipts);
//@Transactional(readOnly = true)
BigInteger getTotalDifficultySince(long number);
//@Transactional(readOnly = true)
BigInteger getTotalDifficulty();
//@Transactional(readOnly = true)
Block getBestBlock();
//@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
List<Block> getAllBlocks();
//@Transactional
void reset();
TransactionReceipt getTransactionReceiptByHash(byte[] hash);
}
}

View File

@ -0,0 +1,194 @@
package org.ethereum.db;
import org.ethereum.core.Block;
import org.ethereum.core.TransactionReceipt;
import org.ethereum.util.ByteUtil;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
/**
* @author Roman Mandeleil
* @since 12.11.2014
*/
public class BlockStoreImpl implements BlockStore {
private SessionFactory sessionFactory;
public BlockStoreImpl(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
@Transactional(readOnly = true)
public byte[] getBlockHashByNumber(long blockNumber) {
Block block = getBlockByNumber(blockNumber);
if (block != null) return block.getHash();
return ByteUtil.EMPTY_BYTE_ARRAY;
}
@Override
@Transactional(readOnly = true)
public Block getBlockByNumber(long blockNumber) {
List result = sessionFactory.getCurrentSession().
createQuery("from BlockVO where number = :number").
setParameter("number", blockNumber).list();
if (result.size() == 0) return null;
BlockVO vo = (BlockVO) result.get(0);
return new Block(vo.rlp);
}
@Override
@Transactional(readOnly = true)
public Block getBlockByHash(byte[] hash) {
List result = sessionFactory.getCurrentSession().
createQuery("from BlockVO where hash = :hash").
setParameter("hash", hash).list();
if (result.size() == 0) return null;
BlockVO vo = (BlockVO) result.get(0);
return new Block(vo.rlp);
}
@Override
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public List<byte[]> getListOfHashesStartFrom(byte[] hash, int qty) {
List<byte[]> hashes = new ArrayList<>();
// find block number of that block hash
Block block = getBlockByHash(hash);
if (block == null) return hashes;
List<byte[]> result = sessionFactory.getCurrentSession().
createQuery("select hash from BlockVO where number <= :number and number >= :limit order by number desc").
setParameter("number", block.getNumber()).
setParameter("limit", block.getNumber() - qty).
setMaxResults(qty).list();
for (byte[] h : result)
hashes.add(h);
return hashes;
}
@Override
@Transactional
public void deleteBlocksSince(long number) {
sessionFactory.getCurrentSession().
createQuery("delete from BlockVO where number > :number").
setParameter("number", number).
executeUpdate();
}
@Override
@Transactional
public void saveBlock(Block block, List<TransactionReceipt> receipts) {
BlockVO blockVO = new BlockVO(block.getNumber(), block.getHash(),
block.getEncoded(), block.getCumulativeDifficulty());
for (TransactionReceipt receipt : receipts) {
byte[] hash = receipt.getTransaction().getHash();
byte[] rlp = receipt.getEncoded();
TransactionReceiptVO transactionReceiptVO = new TransactionReceiptVO(hash, rlp);
sessionFactory.getCurrentSession().persist(transactionReceiptVO);
}
sessionFactory.getCurrentSession().persist(blockVO);
}
@Override
@Transactional(readOnly = true)
public BigInteger getTotalDifficultySince(long number) {
return (BigInteger) sessionFactory.getCurrentSession().
createQuery("select sum(cumulativeDifficulty) from BlockVO where number > :number").
setParameter("number", number).
uniqueResult();
}
@Override
@Transactional(readOnly = true)
public BigInteger getTotalDifficulty() {
return (BigInteger) sessionFactory.getCurrentSession().
createQuery("select sum(cumulativeDifficulty) from BlockVO").uniqueResult();
}
@Override
@Transactional(readOnly = true)
public Block getBestBlock() {
Long bestNumber = (Long)
sessionFactory.getCurrentSession().createQuery("select max(number) from BlockVO").uniqueResult();
List result = sessionFactory.getCurrentSession().
createQuery("from BlockVO where number = :number").setParameter("number", bestNumber).list();
if (result.isEmpty()) return null;
BlockVO vo = (BlockVO) result.get(0);
return new Block(vo.rlp);
}
@Override
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public List<Block> getAllBlocks() {
List<BlockVO> result = sessionFactory.getCurrentSession().
createQuery("from BlockVO").list();
ArrayList<Block> blocks = new ArrayList<>();
for (BlockVO blockVO : result) {
blocks.add(new Block(blockVO.getRlp()));
}
return blocks;
}
@Override
@Transactional
public void reset() {
sessionFactory.getCurrentSession().
createQuery("delete from BlockVO").executeUpdate();
}
@Override
@Transactional(readOnly = true)
public TransactionReceipt getTransactionReceiptByHash(byte[] hash) {
List result = sessionFactory.getCurrentSession().
createQuery("from TransactionReceiptVO where hash = :hash").
setParameter("hash", hash).list();
if (result.size() == 0) return null;
TransactionReceiptVO vo = (TransactionReceiptVO) result.get(0);
return new TransactionReceipt(vo.rlp);
}
}

View File

@ -301,4 +301,4 @@ public class RepositoryDummy extends RepositoryImpl {
cacheAccounts.put(wrap(addr), account);
cacheDetails.put(wrap(addr), details);
}
}
}

View File

@ -486,4 +486,4 @@ public class RepositoryImpl implements Repository {
public byte[] getRoot() {
return worldState.getRootHash();
}
}
}

View File

@ -287,4 +287,4 @@ public class RepositoryTrack implements Repository {
throw new UnsupportedOperationException();
}
}
}

View File

@ -302,4 +302,4 @@ public class RepositoryVMTestDummy extends RepositoryImpl{
cacheAccounts.put(wrap(addr), account);
cacheDetails.put(wrap(addr), details);
}
}
}

View File

@ -4,16 +4,14 @@ import org.ethereum.core.Transaction;
import org.ethereum.core.Wallet;
import org.ethereum.listener.EthereumListener;
import org.ethereum.manager.AdminInfo;
import org.ethereum.manager.BlockLoader;
import org.ethereum.net.client.PeerClient;
import org.ethereum.net.peerdiscovery.PeerInfo;
import org.ethereum.net.rlpx.FrameCodec;
import org.ethereum.net.server.ChannelManager;
import org.springframework.context.ApplicationContext;
import java.math.BigInteger;
import java.net.InetAddress;
import java.util.Set;
import java.util.concurrent.Future;
@ -139,5 +137,5 @@ public interface Ethereum {
public Set<Transaction> getPendingTransactions();
public BlockLoader getBlockLoader();
}

View File

@ -33,4 +33,4 @@ public class EthereumFactory {
return context.getBean(Ethereum.class);
}
}
}

View File

@ -4,10 +4,10 @@ import org.ethereum.core.Transaction;
import org.ethereum.core.Wallet;
import org.ethereum.listener.EthereumListener;
import org.ethereum.manager.AdminInfo;
import org.ethereum.manager.BlockLoader;
import org.ethereum.manager.WorldManager;
import org.ethereum.net.client.PeerClient;
import org.ethereum.net.peerdiscovery.PeerInfo;
import org.ethereum.net.rlpx.FrameCodec;
import org.ethereum.net.server.ChannelManager;
import org.ethereum.net.server.PeerServer;
import org.ethereum.net.submit.TransactionExecutor;
@ -55,6 +55,9 @@ public class EthereumImpl implements Ethereum {
@Autowired
ApplicationContext ctx;
@Autowired
BlockLoader blockLoader;
public EthereumImpl() {
System.out.println();
logger.info("EthereumImpl constructor");
@ -253,5 +256,8 @@ public class EthereumImpl implements Ethereum {
return getBlockchain().getPendingTransactions();
}
@Override
public BlockLoader getBlockLoader(){
return blockLoader;
}
}

View File

@ -134,4 +134,4 @@ public class JSONReader {
return fileNames;
}
}
}

View File

@ -92,4 +92,4 @@ public class StateTestRunner {
logger.info("\n\n");
return results;
}
}
}

View File

@ -94,4 +94,4 @@ public class CompositeEthereumListener implements EthereumListener {
listeners.add(listener);
}
}
}

View File

@ -56,4 +56,4 @@ public class AdminInfo {
public List<Long> getBlockExecTime(){
return blockExecTime;
}
}
}

View File

@ -0,0 +1,55 @@
package org.ethereum.manager;
import org.ethereum.core.Block;
import org.ethereum.facade.Blockchain;
import org.ethereum.net.BlockQueue;
import org.spongycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
import static org.ethereum.config.SystemProperties.CONFIG;
@Component
public class BlockLoader {
@Autowired
private Blockchain blockchain;
public void loadBlocks(){
String fileSrc = CONFIG.blocksLoader();
try {
File blocksFile = new File(fileSrc);
System.out.println("Loading blocks: " + fileSrc);
List<String> blocksList = Files.readAllLines(blocksFile.toPath(), StandardCharsets.UTF_8);
for (String blockRLP : blocksList){
byte[] blockRLPBytes = Hex.decode( blockRLP );
Block block = new Block(blockRLPBytes);
if (block.getNumber() > blockchain.getBestBlock().getNumber()){
System.out.println("Importing block #" + block.getNumber());
blockchain.tryToConnect(block);
} else
System.out.println("Skipping block #" + block.getNumber());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -212,4 +212,4 @@ public class WorldManager {
repository.close();
blockchain.close();
}
}
}

View File

@ -281,4 +281,4 @@ public class BlockQueue {
}
}
}

View File

@ -132,4 +132,4 @@ public class MessageQueue {
timer.cancel();
timer.purge();
}
}
}

View File

@ -54,6 +54,7 @@ public class PeerClient {
worldManager.getListener().trace("Connecting to: " + host + ":" + port);
EthereumChannelInitializer ethereumChannelInitializer = (EthereumChannelInitializer)ctx.getBean("ethereumChannelInitializer", EthereumChannelInitializer.class);
ethereumChannelInitializer.setRemoteId(remoteId);
try {
Bootstrap b = new Bootstrap();

View File

@ -139,4 +139,4 @@ public class DiscoveryChannel {
public StatusMessage getStatusHandshake() {
return ethHandler.getHandshakeStatusMessage();
}
}
}

View File

@ -85,4 +85,4 @@ public class WorkerThread implements Runnable {
public String toString() {
return "Worker for: " + this.peerInfo.toString();
}
}
}

View File

@ -157,4 +157,4 @@ public class Channel {
public void setInetSocketAddress(InetSocketAddress inetSocketAddress) {
this.inetSocketAddress = inetSocketAddress;
}
}
}

View File

@ -141,4 +141,4 @@ public class ChannelManager {
public List<Channel> getChannels() {
return channels;
}
}
}

View File

@ -79,4 +79,8 @@ public class EthereumChannelInitializer extends ChannelInitializer<NioSocketChan
ch.config().setOption(ChannelOption.SO_BACKLOG, 1024);
}
}
public void setRemoteId(String remoteId) {
this.remoteId = remoteId;
}
}

View File

@ -84,4 +84,4 @@ public class PeerServer {
}
}
}
}

View File

@ -19,4 +19,4 @@ public class AdvancedDeviceUtils {
//PropertyConfigurator.configure(configFile);
}
}
}
}

View File

@ -405,7 +405,11 @@ public class ByteUtil {
}
public static boolean isNullOrZeroArray(byte[] array){
return (array == null) || (array.length == 0) || (array.length == 1 && array[0] == 0);
return (array == null) || (array.length == 0);
}
public static boolean isSingleZero(byte[] array){
return (array.length == 1 && array[0] == 0);
}

View File

@ -7,6 +7,7 @@ import java.util.*;
import static java.util.Arrays.copyOfRange;
import static org.ethereum.util.ByteUtil.byteArrayToInt;
import static org.ethereum.util.ByteUtil.isNullOrZeroArray;
import static org.ethereum.util.ByteUtil.isSingleZero;
import static org.spongycastle.util.Arrays.concatenate;
import static org.spongycastle.util.BigIntegers.asUnsignedByteArray;
@ -748,6 +749,8 @@ public class RLP {
if (isNullOrZeroArray(srcData))
return new byte[]{(byte) OFFSET_SHORT_ITEM};
else if(isSingleZero(srcData))
return new byte[]{00};
else if (srcData.length == 1 && (srcData[0] & 0xFF) < 0x80) {
return srcData;
} else if (srcData.length < SIZE_THRESHOLD) {

View File

@ -134,4 +134,4 @@ public class Utils {
return sb.append(" ").append(firstHash).append("...").append(lastHash).toString();
}
}
}

View File

@ -53,6 +53,8 @@ public class DataWord implements Comparable<DataWord> {
public DataWord(byte[] data) {
if (data == null)
this.data = ByteUtil.EMPTY_BYTE_ARRAY;
else if (data.length == 32)
this.data = data;
else if (data.length <= 32)
System.arraycopy(data, 0, this.data, 32 - data.length, data.length);
else
@ -84,10 +86,14 @@ public class DataWord implements Comparable<DataWord> {
* @throws ArithmeticException - if this will not fit in an int.
*/
public int intValue() {
BigDecimal tmpValue = new BigDecimal(this.value());
if (this.bytesOccupied() > 4)
return Integer.MAX_VALUE;
return tmpValue.intValueExact();
int intVal = 0;
for (int i = 0; i < data.length; i++)
{
intVal = (intVal << 8) + (data[i] & 0xff);
}
return intVal;
}
/**
@ -99,10 +105,17 @@ public class DataWord implements Comparable<DataWord> {
* @throws ArithmeticException - if this will not fit in a long.
*/
public long longValue() {
BigDecimal tmpValue = new BigDecimal(this.value());
return tmpValue.longValueExact();
long longVal = 0;
for (int i = 0; i < data.length; i++)
{
longVal = (longVal << 8) + (data[i] & 0xff);
}
return longVal;
}
public BigInteger sValue() {
return new BigInteger(data);
}

View File

@ -0,0 +1,209 @@
package org.ethereum.vm;
import org.ethereum.util.ByteUtil;
import java.util.LinkedList;
import static java.lang.String.format;
public class MemoryBuffer {
int CHUNK_SIZE = 1024;
LinkedList<byte[]> chunks = new LinkedList<byte[]>();
int memorySoftSize = 0;
public MemoryBuffer() {
}
public void memorySave(int address, byte[] data) {
ensureAvailable(address, data.length);
int chunkIndex = address / CHUNK_SIZE;
int chunkOffset = address % CHUNK_SIZE;
int toCapture = data.length;
int start = 0;
while(toCapture > 0){
int captured = captureMax(chunkIndex, chunkOffset, toCapture, data, start);
// capture next chunk
++chunkIndex;
chunkOffset = 0;
// mark remind
toCapture -= captured;
start += captured;
}
memorySoftSize = Math.max(memorySoftSize, (int) Math.ceil((double) (address + data.length) / 32) * 32);
}
public void memorySave(int address, int allocSize, byte[] value){
memoryExpand(address, allocSize);
memorySave(address, value);
}
public DataWord memoryLoad(int address){
ensureAvailable(address, 32);
byte[] retData = new byte[32];
int chunkIndex = address / CHUNK_SIZE;
int chunkOffset = address % CHUNK_SIZE;
int toGrab = retData.length;
int start = 0;
while(toGrab > 0){
int copied = grabMax(chunkIndex, chunkOffset, toGrab, retData, start);
// read next chunk from the start
++chunkIndex;
chunkOffset = 0;
// mark remind
toGrab -= copied;
start += copied;
}
memorySoftSize = Math.max(memorySoftSize, (int) Math.ceil((double) (address + retData.length) / 32) * 32);
return new DataWord(retData);
}
public void memoryExpand(int offset, int size){
ensureAvailable(offset, size);
memorySoftSize = Math.max(memorySoftSize,offset + size);
}
public byte[] memoryChunk(int offset, int size) {
byte[] data = new byte[size];
ensureAvailable(offset, size);
int chunkIndex = offset / CHUNK_SIZE;
int chunkOffset = offset % CHUNK_SIZE;
int toGrab = data.length;
int start = 0;
while (toGrab > 0) {
int copied = grabMax(chunkIndex, chunkOffset, toGrab, data, start);
// read next chunk from the start
++chunkIndex;
chunkOffset = 0;
// mark remind
toGrab -= copied;
start += copied;
}
memorySoftSize = Math.max(memorySoftSize, (int) Math.ceil((double) (offset + data.length) / 32) * 32);
return data;
}
public String memoryToString() {
StringBuilder memoryData = new StringBuilder();
StringBuilder firstLine = new StringBuilder();
StringBuilder secondLine = new StringBuilder();
for (int i = 0; i < memorySoftSize; ++i){
byte value = getByte(i);
// Check if value is ASCII
String character = ((byte) 0x20 <= value && value <= (byte) 0x7e) ? new String(new byte[]{value}) : "?";
firstLine.append(character).append("");
secondLine.append(ByteUtil.oneByteToHexString(value)).append(" ");
if ((i + 1) % 8 == 0) {
String tmp = format("%4s", Integer.toString(i - 7, 16)).replace(" ", "0");
memoryData.append("").append(tmp).append(" ");
memoryData.append(firstLine).append(" ");
memoryData.append(secondLine);
if (i + 1 < memorySoftSize) memoryData.append("\n");
firstLine.setLength(0);
secondLine.setLength(0);
}
}
return memoryData.toString();
}
public int getSize(){
return memorySoftSize;
}
/*****************************/
/*****************************/
/*****************************/
// just access expecting all data valid
byte getByte(int address){
int chunkIndex = address / CHUNK_SIZE;
int chunkOffset = address % CHUNK_SIZE;
byte[] chunk = chunks.get(chunkIndex);
return chunk[chunkOffset];
}
void ensureAvailable(int address, int offset){
int memHardSize = getMemoryHardSize();
int endNewMem = Math.max(memHardSize, address + offset);
// there is enough mem allocated
if (endNewMem <= memHardSize) return;
int toAllocate = endNewMem - memHardSize ;
int chunks = (toAllocate % (CHUNK_SIZE) == 0) ?
toAllocate / (CHUNK_SIZE) :
(toAllocate / (CHUNK_SIZE)) + 1;
addChunks(chunks);
}
int getMemoryHardSize(){
return chunks.size() * CHUNK_SIZE;
}
int getMemorySoftSize() {
return memorySoftSize;
}
int captureMax(int chunkIndex, int chunkOffset, int size, byte[] src, int srcPos){
byte[] chunk = chunks.get(chunkIndex);
int toCapture = Math.min(size, chunk.length - chunkOffset);
System.arraycopy(src, srcPos, chunk, chunkOffset, toCapture);
return toCapture;
}
int grabMax(int chunkIndex, int chunkOffset, int size, byte[] dest, int destPos) {
byte[] chunk = chunks.get(chunkIndex);
int toGrab = Math.min(size, chunk.length - chunkOffset);
System.arraycopy(chunk, chunkOffset, dest, destPos, toGrab);
return toGrab;
}
void addChunks(int num){
for (int i = 0; i < num; ++i)
addChunk();
}
void addChunk(){
chunks.add(new byte[CHUNK_SIZE]);
}
}

View File

@ -3,7 +3,6 @@ package org.ethereum.vm;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.ContractDetails;
import org.ethereum.facade.Repository;
import org.ethereum.util.BIUtil;
import org.ethereum.util.ByteUtil;
import org.ethereum.vm.MessageCall.MsgType;
import org.ethereum.vm.PrecompiledContracts.PrecompiledContract;
@ -54,7 +53,7 @@ public class Program {
private ProgramListener listener;
Stack<DataWord> stack = new Stack<>();
ByteBuffer memory = null;
MemoryBuffer memory = new MemoryBuffer();
DataWord programAddress;
ProgramResult result = new ProgramResult();
@ -164,8 +163,8 @@ public class Program {
stopped = true;
}
public void setHReturn(ByteBuffer buff) {
result.setHReturn(buff.array());
public void setHReturn(byte[] buff) {
result.setHReturn(buff);
}
public void step() {
@ -209,15 +208,15 @@ public class Program {
}
public int getMemSize() {
return memory != null ? memory.limit() : 0;
return memory.getSize();
}
public void memorySave(DataWord addrB, DataWord value) {
memorySave(addrB.intValue(), value.getData());
memory.memorySave(addrB.intValue(), value.getData());
}
public void memorySave(int addr, byte[] value) {
memorySave(addr, value.length, value);
memory.memorySave(addr, value);
}
public void memoryExpand(DataWord outDataOffs, DataWord outDataSize) {
@ -225,10 +224,7 @@ public class Program {
if (outDataSize.isZero())
return ;
int maxAddress = outDataOffs.intValue() + outDataSize.intValue();
if (getMemSize() < maxAddress) {
memorySave(maxAddress, new byte[]{0});
}
memory.memoryExpand(outDataOffs.intValue(), outDataSize.intValue());
}
/**
@ -239,47 +235,20 @@ public class Program {
* @param value the data to write to memory
*/
public void memorySave(int addr, int allocSize, byte[] value) {
allocateMemory(addr, allocSize);
System.arraycopy(value, 0, memory.array(), addr, value.length);
memory.memorySave(addr, allocSize, value);
}
public DataWord memoryLoad(DataWord addr) {
return memoryLoad(addr.intValue());
return memory.memoryLoad(addr.intValue());
}
public DataWord memoryLoad(int address) {
allocateMemory(address, DataWord.ZERO.getData().length);
DataWord newMem = new DataWord();
System.arraycopy(memory.array(), address, newMem.getData(), 0, newMem.getData().length);
return newMem;
return memory.memoryLoad(address);
}
public ByteBuffer memoryChunk(DataWord offsetData, DataWord sizeData) {
return memoryChunk(offsetData.intValue(), sizeData.intValue());
}
/**
* Returns a piece of memory from a given offset and specified size
* If the offset + size exceed the current memory-size,
* the remainder will be filled with empty bytes.
*
* @param offset byte address in memory
* @param size the amount of bytes to return
* @return ByteBuffer containing the chunk of memory data
*/
public ByteBuffer memoryChunk(int offset, int size) {
allocateMemory(offset, size);
byte[] chunk;
if (memory != null && size != 0)
chunk = Arrays.copyOfRange(memory.array(), offset, offset + size);
else
chunk = new byte[size];
return ByteBuffer.wrap(chunk);
public byte[] memoryChunk(int offset, int size) {
return memory.memoryChunk(offset, size);
}
/**
@ -290,14 +259,7 @@ public class Program {
* @param size the number of bytes to allocate
*/
public void allocateMemory(int offset, int size) {
int memSize = memory != null ? memory.limit() : 0;
double newMemSize = Math.max(memSize, size != 0 ?
Math.ceil((double) (offset + size) / 32) * 32 : 0);
ByteBuffer tmpMem = ByteBuffer.allocate((int) newMemSize);
if (memory != null)
tmpMem.put(memory.array(), 0, memory.limit());
memory = tmpMem;
memory.memoryExpand(offset, size);
}
@ -332,7 +294,7 @@ public class Program {
}
// [1] FETCH THE CODE FROM THE MEMORY
byte[] programCode = memoryChunk(memStart, memSize).array();
byte[] programCode = memoryChunk(memStart.intValue(), memSize.intValue());
if (logger.isInfoEnabled())
logger.info("creating a new contract inside contract run: [{}]", Hex.toHexString(senderAddress));
@ -453,7 +415,7 @@ public class Program {
return;
}
byte[] data = memoryChunk(msg.getInDataOffs(), msg.getInDataSize()).array();
byte[] data = memoryChunk(msg.getInDataOffs().intValue(), msg.getInDataSize().intValue());
// FETCH THE SAVED STORAGE
byte[] codeAddress = msg.getCodeAddress().getLast20Bytes();
@ -550,7 +512,7 @@ public class Program {
refundGas.toString());
}
} else {
this.refundGas(msg.getGas().longValue(), "remaining gas from the internal call");
this.refundGas(msg.getGas().longValue(), "remaining gas from the internal call");
}
}
@ -704,28 +666,7 @@ public class Program {
}
public String memoryToString() {
StringBuilder memoryData = new StringBuilder();
StringBuilder firstLine = new StringBuilder();
StringBuilder secondLine = new StringBuilder();
for (int i = 0; memory != null && i < memory.limit(); ++i) {
byte value = memory.get(i);
// Check if value is ASCII
String character = ((byte) 0x20 <= value && value <= (byte) 0x7e) ? new String(new byte[]{value}) : "?";
firstLine.append(character).append("");
secondLine.append(ByteUtil.oneByteToHexString(value)).append(" ");
if ((i + 1) % 8 == 0) {
String tmp = format("%4s", Integer.toString(i - 7, 16)).replace(" ", "0");
memoryData.append("").append(tmp).append(" ");
memoryData.append(firstLine).append(" ");
memoryData.append(secondLine);
if (i + 1 < memory.limit()) memoryData.append("\n");
firstLine.setLength(0);
secondLine.setLength(0);
}
}
return memoryData.toString();
return memory.memoryToString();
}
public void fullTrace() {
@ -737,6 +678,7 @@ public class Program {
stackData.append(" ").append(stack.get(i));
if (i < stack.size() - 1) stackData.append("\n");
}
if (stackData.length() > 0) stackData.insert(0, "\n");
ContractDetails contractDetails = this.result.getRepository().
@ -754,20 +696,26 @@ public class Program {
StringBuilder memoryData = new StringBuilder();
StringBuilder oneLine = new StringBuilder();
for (int i = 0; memory != null && i < memory.limit(); ++i) {
if (memory.getSize() > 32)
memoryData.append("... Memory Folded.... ")
.append("(")
.append(memory.getSize())
.append(") bytes");
else
for (int i = 0; i < memory.getSize(); ++i) {
byte value = memory.get(i);
oneLine.append(ByteUtil.oneByteToHexString(value)).append(" ");
byte value = memory.getByte(i);
oneLine.append(ByteUtil.oneByteToHexString(value)).append(" ");
if ((i + 1) % 16 == 0) {
String tmp = format("[%4s]-[%4s]", Integer.toString(i - 15, 16),
Integer.toString(i, 16)).replace(" ", "0");
memoryData.append("").append(tmp).append(" ");
memoryData.append(oneLine);
if (i < memory.limit()) memoryData.append("\n");
oneLine.setLength(0);
if ((i + 1) % 16 == 0) {
String tmp = format("[%4s]-[%4s]", Integer.toString(i - 15, 16),
Integer.toString(i, 16)).replace(" ", "0");
memoryData.append("").append(tmp).append(" ");
memoryData.append(oneLine);
if (i < memory.getSize()) memoryData.append("\n");
oneLine.setLength(0);
}
}
}
if (memoryData.length() > 0) memoryData.insert(0, "\n");
StringBuilder opsString = new StringBuilder();
@ -834,7 +782,7 @@ public class Program {
ContractDetails contractDetails = this.result.getRepository().
getContractDetails(this.programAddress.getLast20Bytes());
op.saveStorageMap(contractDetails.getStorage());
op.saveMemory(memory);
op.saveMemory(memory.memoryChunk(0, memory.getSize()));
op.saveStack(stack);
programTrace.addOp(op);
@ -842,7 +790,7 @@ public class Program {
public void saveProgramTraceToFile(String fileName) {
if (!CONFIG.vmTrace() || hasLength(CONFIG.vmTraceDir())) return;
if (!CONFIG.vmTrace() || !hasLength(CONFIG.vmTraceDir())) return;
String dir = CONFIG.databaseDir() + "/" + CONFIG.vmTraceDir() + "/";
@ -949,7 +897,8 @@ public class Program {
return;
}
byte[] data = this.memoryChunk(msg.getInDataOffs(), msg.getInDataSize()).array();
byte[] data = this.memoryChunk(msg.getInDataOffs().intValue(),
msg.getInDataSize().intValue());
transfer(track, senderAddress, codeAddress, msg.getEndowment().value());
@ -1062,15 +1011,15 @@ public class Program {
/**
* used mostly for testing reasons
*/
public ByteBuffer getMemory() {
return memory;
public byte[] getMemory() {
return memory.memoryChunk(0, memory.getSize());
}
/**
* used mostly for testing reasons
*/
public void initMem(ByteBuffer memory) {
this.memory = memory;
public void initMem(byte[] data) {
this.memory.memorySave(0, data);
}

View File

@ -189,4 +189,4 @@ public class ProgramInvokeFactoryImpl implements ProgramInvokeFactory {
public void setBlockchain(Blockchain blockchain) {
this.blockchain = blockchain;
}
}
}

View File

@ -1,21 +1,20 @@
package org.ethereum.vm;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.ContractDetails;
import org.ethereum.util.ByteUtil;
import org.ethereum.vm.MessageCall.MsgType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import static org.ethereum.config.SystemProperties.CONFIG;
import static org.ethereum.crypto.HashUtil.sha3;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
import static org.ethereum.vm.OpCode.*;
/**
@ -183,7 +182,7 @@ public class VM {
gasCost += GasCost.NEW_ACCT_CALL;
//TODO #POC9 Make sure this is converted to BigInteger (256num support)
if (stack.get(stack.size() - 3).intValue() > 0 )
if (!stack.get(stack.size() - 3).isZero() )
gasCost += GasCost.VT_CALL;
callGas = callGasWord.longValue();
@ -261,7 +260,7 @@ public class VM {
* Stop and Arithmetic Operations
*/
case STOP: {
program.setHReturn(ByteBuffer.allocate(0));
program.setHReturn(EMPTY_BYTE_ARRAY);
program.stop();
}
break;
@ -576,9 +575,9 @@ public class VM {
case SHA3: {
DataWord memOffsetData = program.stackPop();
DataWord lengthData = program.stackPop();
ByteBuffer buffer = program.memoryChunk(memOffsetData, lengthData);
byte[] buffer = program.memoryChunk(memOffsetData.intValue(), lengthData.intValue());
byte[] encoded = HashUtil.sha3(buffer.array());
byte[] encoded = sha3(buffer);
DataWord word = new DataWord(encoded);
if (logger.isInfoEnabled())
@ -702,7 +701,7 @@ public class VM {
case CODECOPY:
case EXTCODECOPY: {
byte[] fullCode = ByteUtil.EMPTY_BYTE_ARRAY;
byte[] fullCode = EMPTY_BYTE_ARRAY;
if (op == OpCode.CODECOPY)
fullCode = program.getCode();
@ -856,10 +855,10 @@ public class VM {
topics.add(topic);
}
ByteBuffer data = program.memoryChunk(memStart, memOffset);
byte[] data = program.memoryChunk(memStart.intValue(), memOffset.intValue());
LogInfo logInfo =
new LogInfo(address.getLast20Bytes(), topics, data.array());
new LogInfo(address.getLast20Bytes(), topics, data);
if (logger.isInfoEnabled())
hint = logInfo.toString();
@ -1056,7 +1055,7 @@ public class VM {
DataWord codeAddress = program.stackPop();
DataWord value = program.stackPop();
if( value.intValue() > 0)
if( !value.isZero())
gas = new DataWord(gas.intValue() + GasCost.STIPEND_CALL);
DataWord inDataOffs = program.stackPop();
@ -1100,11 +1099,11 @@ public class VM {
DataWord offset = program.stackPop();
DataWord size = program.stackPop();
ByteBuffer hReturn = program.memoryChunk(offset, size);
byte[] hReturn = program.memoryChunk(offset.intValue(), size.intValue());
program.setHReturn(hReturn);
if (logger.isInfoEnabled())
hint = "data: " + Hex.toHexString(hReturn.array())
hint = "data: " + Hex.toHexString(hReturn)
+ " offset: " + offset.value()
+ " size: " + size.value();

View File

@ -66,9 +66,8 @@ public class Op {
}
}
public void saveMemory(ByteBuffer memory) {
if (memory != null)
this.memory = Arrays.copyOf(memory.array(), memory.array().length);
public void saveMemory(byte[] memory) {
this.memory = memory;
}
public void saveStack(Stack<DataWord> stack) {

View File

@ -68,4 +68,4 @@ public class EtherSaleWallet {
}
return data;
}
}
}

View File

@ -112,7 +112,8 @@
<bean id="listener" class="org.ethereum.listener.CompositeEthereumListener">
</bean>
<bean id="blockLoader" class="org.ethereum.manager.BlockLoader">
</bean>
<bean id="blockStore" class="org.ethereum.db.InMemoryBlockStore">
</bean>