Fix memory allocation and add unit tests

This commit is contained in:
nicksavers 2014-09-09 01:35:39 +02:00
parent c51e56831a
commit 561d1dd296
22 changed files with 556 additions and 203 deletions

View File

@ -1,6 +1,7 @@
package org.ethereum.core;
import org.ethereum.db.Repository;
import org.ethereum.db.RepositoryImpl;
import org.ethereum.facade.Blockchain;
import org.ethereum.listener.EthereumListener;
import org.ethereum.manager.WorldManager;
import org.ethereum.net.BlockQueue;
@ -50,7 +51,7 @@ import static org.ethereum.core.Denomination.SZABO;
* Created on: 20/05/2014 10:44
*
*/
public class Blockchain implements org.ethereum.facade.Blockchain{
public class BlockchainImpl implements Blockchain {
private static final Logger logger = LoggerFactory.getLogger("blockchain");
private static final Logger stateLogger = LoggerFactory.getLogger("state");
@ -58,7 +59,7 @@ public class Blockchain implements org.ethereum.facade.Blockchain{
// to avoid using minGasPrice=0 from Genesis for the wallet
private static final long INITIAL_MIN_GAS_PRICE = 10 * SZABO.longValue();
private Repository repository;
private RepositoryImpl repository;
private Block lastBlock;
// keep the index of the chain for
@ -67,7 +68,7 @@ public class Blockchain implements org.ethereum.facade.Blockchain{
private final BlockQueue blockQueue = new BlockQueue();
public Blockchain(Repository repository) {
public BlockchainImpl(RepositoryImpl repository) {
this.repository = repository;
}
@ -324,7 +325,7 @@ public class Blockchain implements org.ethereum.facade.Blockchain{
if (isContractCreation || code != null) {
// START TRACKING FOR REVERT CHANGES OPTION
Repository trackRepository = repository.getTrack();
RepositoryImpl trackRepository = repository.getTrack();
trackRepository.startTracking();
try {
@ -385,7 +386,7 @@ public class Blockchain implements org.ethereum.facade.Blockchain{
* @param contractAddress
*/
private void applyProgramResult(ProgramResult result, BigInteger gasDebit,
Repository repository, byte[] senderAddress,
RepositoryImpl repository, byte[] senderAddress,
byte[] contractAddress, byte[] coinbase, boolean initResults) {
if (result.getException() != null

View File

@ -3,9 +3,10 @@ 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.BlockchainImpl;
import org.ethereum.core.Genesis;
import org.ethereum.crypto.HashUtil;
import org.ethereum.facade.Repository;
import org.ethereum.json.EtherObjectMapper;
import org.ethereum.json.JSONHelper;
import org.ethereum.listener.EthereumListener;
@ -18,6 +19,7 @@ import org.iq80.leveldb.DBIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
@ -51,7 +53,7 @@ import static org.ethereum.config.SystemProperties.CONFIG;
* @author: Roman Mandeleil
* Created on: 23/06/2014 23:01
*/
public class Repository implements org.ethereum.facade.Repository{
public class RepositoryImpl implements Repository {
private Logger logger = LoggerFactory.getLogger("repository");
@ -72,7 +74,7 @@ public class Repository implements org.ethereum.facade.Repository{
*
* @See loadBlockchain() to update the stateRoot
*/
public Repository() {
public RepositoryImpl() {
chainDB = new DatabaseImpl("blockchain");
detailsDB = new DatabaseImpl("details");
contractDetailsDB = new TrackDatabase(detailsDB);
@ -81,15 +83,15 @@ public class Repository implements org.ethereum.facade.Repository{
accountStateDB = new TrackTrie(worldState);
}
private Repository(TrackTrie accountStateDB, TrackDatabase contractDetailsDB) {
private RepositoryImpl(TrackTrie accountStateDB, TrackDatabase contractDetailsDB) {
this.accountStateDB = accountStateDB;
this.contractDetailsDB = contractDetailsDB;
}
public Repository getTrack() {
public RepositoryImpl getTrack() {
TrackTrie trackState = new TrackTrie(accountStateDB);
TrackDatabase trackDetails = new TrackDatabase(contractDetailsDB);
return new Repository (trackState, trackDetails);
return new RepositoryImpl (trackState, trackDetails);
}
public void startTracking() {
@ -121,8 +123,8 @@ public class Repository implements org.ethereum.facade.Repository{
this.worldState.sync();
}
public Blockchain loadBlockchain() {
Blockchain blockchain = WorldManager.getInstance().getBlockchain();
public BlockchainImpl loadBlockchain() {
BlockchainImpl blockchain = WorldManager.getInstance().getBlockchain();
DBIterator iterator = chainDB.iterator();
try {
if (!iterator.hasNext()) {

View File

@ -7,7 +7,7 @@ import java.util.concurrent.Future;
import org.ethereum.core.Transaction;
import org.ethereum.core.Wallet;
import org.ethereum.db.Repository;
import org.ethereum.db.RepositoryImpl;
import org.ethereum.listener.EthereumListener;
import org.ethereum.manager.WorldManager;
import org.ethereum.net.client.ClientPeer;
@ -195,7 +195,7 @@ public class EthereumImpl implements Ethereum {
@Override
public Repository getRepository(){
public RepositoryImpl getRepository(){
return WorldManager.getInstance().getRepository();
}

View File

@ -3,8 +3,6 @@ package org.ethereum.facade;
import org.ethereum.core.AccountState;
import org.ethereum.db.ContractDetails;
import java.math.BigInteger;
/**
* www.ethereumJ.com
*
@ -16,5 +14,4 @@ public interface Repository {
public AccountState getAccountState(byte[] addr);
public ContractDetails getContractDetails(byte[] addr);
}

View File

@ -4,7 +4,7 @@ import org.ethereum.core.AccountState;
import org.ethereum.core.Block;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.db.ContractDetails;
import org.ethereum.db.Repository;
import org.ethereum.db.RepositoryImpl;
import org.ethereum.util.ByteUtil;
import org.ethereum.vm.DataWord;
import org.spongycastle.util.encoders.Hex;
@ -67,7 +67,7 @@ public class JSONHelper {
public static void dumpBlock(ObjectNode blockNode, Block block,
long gasUsed, byte[] state, List<ByteArrayWrapper> keys,
Repository repository) {
RepositoryImpl repository) {
blockNode.put("coinbase", Hex.toHexString(block.getCoinbase()));
blockNode.put("difficulty", new BigInteger(1, block.calcDifficulty()).toString());

View File

@ -2,7 +2,7 @@ package org.ethereum.jsontestsuite;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.db.ContractDetails;
import org.ethereum.db.Repository;
import org.ethereum.db.RepositoryImpl;
import org.ethereum.util.ByteUtil;
import org.ethereum.vm.*;
import org.slf4j.Logger;
@ -46,7 +46,7 @@ public class TestRunner {
List<String> results = new ArrayList<>();
Repository repository = new Repository();
RepositoryImpl repository = new RepositoryImpl();
/* 1. Store pre-exist accounts - Pre */
for (ByteArrayWrapper key : testCase.getPre().keySet()){

View File

@ -2,17 +2,14 @@ package org.ethereum.manager;
import static org.ethereum.config.SystemProperties.CONFIG;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import org.ethereum.core.AccountState;
import org.ethereum.core.Blockchain;
import org.ethereum.core.BlockchainImpl;
import org.ethereum.core.Wallet;
import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.Repository;
import org.ethereum.db.RepositoryImpl;
import org.ethereum.listener.EthereumListener;
import org.ethereum.net.client.ClientPeer;
import org.ethereum.net.client.PeerData;
@ -28,8 +25,8 @@ import org.ethereum.net.peerdiscovery.PeerDiscovery;
*/
public class WorldManager {
private Blockchain blockchain;
private Repository repository;
private BlockchainImpl blockchain;
private RepositoryImpl repository;
private Wallet wallet;
private PeerDiscovery peerDiscovery;
@ -48,8 +45,8 @@ public class WorldManager {
}
private WorldManager() {
this.repository = new Repository();
this.blockchain = new Blockchain(repository);
this.repository = new RepositoryImpl();
this.blockchain = new BlockchainImpl(repository);
// Initialize PeerData
List<PeerData> peerDataList = parsePeerDiscoveryIpList(CONFIG.peerDiscoveryIPList());
@ -60,17 +57,15 @@ public class WorldManager {
}
// used for testing
public void reset(){
this.repository = new Repository();
this.blockchain = new Blockchain(repository);
public void reset() {
this.repository = new RepositoryImpl();
this.blockchain = new BlockchainImpl(repository);
}
public void init(){
this.wallet = new Wallet();
public void init() {
this.wallet = new Wallet();
byte[] cowAddr = HashUtil.sha3("cow".getBytes());
ECKey key = ECKey.fromPrivate(cowAddr);
wallet.importKey(cowAddr);
// AccountState state = wallet.getAccountState(key.getAddress());
@ -79,20 +74,19 @@ public class WorldManager {
String secret = CONFIG.coinbaseSecret();
byte[] cbAddr = HashUtil.sha3(secret.getBytes());
wallet.importKey(cbAddr);
}
public static WorldManager getInstance() {
return WorldManagerHolder.instance;
}
public void addListener(EthereumListener listener){
public void addListener(EthereumListener listener) {
this.listener = listener;
}
public void addPeers(final Set<PeerData> newPeers) {
synchronized (peers){
synchronized (peers) {
for (final PeerData peer : newPeers) {
if (peerDiscovery.isStarted() && !peers.contains(peer)) {
peerDiscovery.addNewPeerData(peer);
@ -149,11 +143,11 @@ public class WorldManager {
this.wallet = wallet;
}
public Repository getRepository() {
public RepositoryImpl getRepository() {
return repository;
}
public Blockchain getBlockchain() {
public BlockchainImpl getBlockchain() {
return blockchain;
}

View File

@ -6,6 +6,7 @@ import org.spongycastle.util.Arrays;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
/**
@ -41,15 +42,12 @@ public class DataWord implements Comparable<DataWord> {
}
public DataWord(byte[] data) {
if (data == null) {
this.data = new byte[] {};
return;
}
if (data.length > 32)
throw new RuntimeException("Data word can't exit 32 bytes: " + data);
System.arraycopy(data, 0, this.data, 32 - data.length, data.length);
if (data == null)
this.data = ByteUtil.EMPTY_BYTE_ARRAY;
else if (data.length <= 32)
System.arraycopy(data, 0, this.data, 32 - data.length, data.length);
else
throw new RuntimeException("Data word can't exit 32 bytes: " + data);
}
public byte[] getData() {
@ -67,12 +65,32 @@ public class DataWord implements Comparable<DataWord> {
return new BigInteger(1, data);
}
/**
* Converts this DataWord to an int, checking for lost information.
* If this DataWord is out of the possible range for an int result
* then an ArithmeticException is thrown.
*
* @return this DataWord converted to an int.
* @throws ArithmeticException - if this will not fit in an int.
*/
public int intValue() {
return new BigInteger(1, data).intValue();
// FIXME: Disabled for POC5
// BigDecimal tmpValue = new BigDecimal(this.value());
// return tmpValue.intValueExact();
return this.value().intValue();
}
/**
* Converts this DataWord to a long, checking for lost information.
* If this DataWord is out of the possible range for a long result
* then an ArithmeticException is thrown.
*
* @return this DataWord converted to a long.
* @throws ArithmeticException - if this will not fit in a long.
*/
public long longValue() {
return new BigInteger(1, data).longValue();
BigDecimal tmpValue = new BigDecimal(this.value());
return tmpValue.longValueExact();
}
public BigInteger sValue() {
@ -219,7 +237,7 @@ public class DataWord implements Comparable<DataWord> {
// TODO: improve with no BigInteger
public void exp(DataWord word) {
BigInteger result = value().pow(word.value().intValue());
BigInteger result = value().pow(word.intValue());
byte[] bytes = result.toByteArray();
ByteBuffer data = ByteBuffer.allocate(32);
@ -300,4 +318,4 @@ public class DataWord implements Comparable<DataWord> {
data, 0, data.length,
o.getData(), 0, o.getData().length);
}
}
}

View File

@ -2,7 +2,7 @@ package org.ethereum.vm;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.ContractDetails;
import org.ethereum.db.Repository;
import org.ethereum.db.RepositoryImpl;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.Utils;
import org.slf4j.Logger;
@ -43,16 +43,17 @@ public class Program {
ProgramInvoke invokeData;
public Program(byte[] ops, ProgramInvoke invokeData) {
this.invokeHash = invokeData.hashCode();
result.setRepository(invokeData.getRepository());
if (ops == null) ops = new byte[0]; //throw new RuntimeException("program can not run with ops: null");
this.invokeData = invokeData;
if (ops == null) ops = ByteUtil.EMPTY_BYTE_ARRAY;
this.ops = ops;
this.programAddress = invokeData.getOwnerAddress();
}
if (invokeData != null) {
this.invokeData = invokeData;
this.programAddress = invokeData.getOwnerAddress();
this.invokeHash = invokeData.hashCode();
this.result.setRepository(invokeData.getRepository());
}
}
public byte getCurrentOp() {
if(ops.length == 0)
@ -93,7 +94,7 @@ public class Program {
public void setPC(DataWord pc) {
this.pc = pc.value().intValue();
this.pc = pc.intValue();
if (this.pc == ops.length) {
stop();
@ -162,71 +163,79 @@ public class Program {
}
public void memorySave(DataWord addrB, DataWord value) {
memorySave(addrB.getData(), value.getData());
memorySave(addrB.intValue(), value.getData());
}
public void memorySave(byte[] addr, byte[] value) {
int address = new BigInteger(1, addr).intValue();
allocateMemory(address, value);
System.arraycopy(value, 0, memory.array(), address, value.length);
public void memorySave(int addr, byte[] value) {
memorySave(addr, value.length, value);
}
/**
* Allocates a piece of memory and stores value at given offset address
*
* @param addr is the offset address
* @param allocSize size of memory needed to write
* @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);
}
public DataWord memoryLoad(DataWord addr) {
return memoryLoad(addr.intValue());
}
public DataWord memoryLoad(int address) {
int address = new BigInteger(1, addr.getData()).intValue();
allocateMemory(address, DataWord.ZERO.getData());
allocateMemory(address, DataWord.ZERO.getData().length);
byte[] data = new byte[32];
System.arraycopy(memory.array(), address, data , 0 ,32);
DataWord newMem = new DataWord();
System.arraycopy(memory.array(), address, newMem.getData(), 0, newMem.getData().length);
return new DataWord(data);
return newMem;
}
public ByteBuffer memoryChunk(DataWord offsetData, DataWord sizeData) {
return memoryChunk(offsetData.intValue(), sizeData.intValue());
}
int offset = offsetData.intValue();
int size = sizeData.intValue();
byte[] chunk = new byte[size];
allocateMemory(offset, new byte[size]);
/**
* 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) {
if (memory != null) {
if (memory.limit() < offset + size) size = memory.limit() - offset;
System.arraycopy(memory.array(), offset, chunk, 0, size);
}
allocateMemory(offset, size);
byte[] chunk;
if (memory != null)
chunk = Arrays.copyOfRange(memory.array(), offset, offset+size);
else
chunk = new byte[size];
return ByteBuffer.wrap(chunk);
}
private void allocateMemory(int address, byte[] value) {
/**
* Allocates extra memory in the program for
* a specified size, calculated from a given offset
*
* @param offset the memory address offset
* @param size the number of bytes to allocate
*/
protected void allocateMemory(int offset, int size) {
int memSize = 0;
if (memory != null) memSize = memory.limit();
// check if you need to allocate
if (memSize < (address + value.length)) {
long overlap = memSize - address;
int sizeToAllocate = 0;
if (memSize > address) {
sizeToAllocate = memSize + value.length;
} else {
sizeToAllocate = memSize + (address - memSize) + value.length;
}
if (overlap > 0) sizeToAllocate -= overlap;
// complete to 32
sizeToAllocate = (sizeToAllocate % 32)==0 ? sizeToAllocate :
sizeToAllocate + (32 - sizeToAllocate % 32);
sizeToAllocate = (sizeToAllocate == 0)? 32: sizeToAllocate;
ByteBuffer tmpMem = ByteBuffer.allocate(sizeToAllocate);
if (memory != null)
System.arraycopy(memory.array(), 0, tmpMem.array(), 0, memory.limit());
memory = tmpMem;
}
int memSize = memory != null ? memory.limit(): 0;
double newMemSize = Math.max(memSize, Math.ceil((double)(offset + size) / 32) * 32);
ByteBuffer tmpMem = ByteBuffer.allocate((int)newMemSize);
if (memory != null)
tmpMem.put(memory.array(), 0, memory.limit());
memory = tmpMem;
}
public void suicide(DataWord obtainer) {
@ -282,13 +291,13 @@ public class Program {
result.getRepository().addBalance(senderAddress, endowment.negate());
result.getRepository().addBalance(newAddress, endowment);
Repository trackRepository = result.getRepository().getTrack();
trackRepository.startTracking();
RepositoryImpl trackRepositoryImpl = result.getRepository().getTrack();
trackRepositoryImpl.startTracking();
// [5] COOK THE INVOKE AND EXECUTE
ProgramInvoke programInvoke =
ProgramInvokeFactory.createProgramInvoke(this, new DataWord(newAddress), DataWord.ZERO,
new DataWord(gas), BigInteger.ZERO, null, trackRepository, this.invokeData.getCallDeep() + 1);
new DataWord(gas), BigInteger.ZERO, null, trackRepositoryImpl, this.invokeData.getCallDeep() + 1);
VM vm = new VM();
Program program = new Program(programCode.array(), programInvoke);
@ -300,18 +309,18 @@ public class Program {
result.getException() instanceof Program.OutOfGasException) {
logger.info("contract run halted by OutOfGas: new contract init ={}" , Hex.toHexString(newAddress));
trackRepository.rollback();
trackRepositoryImpl.rollback();
stackPushZero();
return;
}
// 4. CREATE THE CONTRACT OUT OF RETURN
byte[] code = result.getHReturn().array();
trackRepository.saveCode(newAddress, code);
trackRepositoryImpl.saveCode(newAddress, code);
// IN SUCCESS PUSH THE ADDRESS INTO THE STACK
stackPush(new DataWord(newAddress));
trackRepository.commit();
trackRepositoryImpl.commit();
// 5. REFUND THE REMAIN GAS
long refundGas = gas - result.getGasUsed();
@ -388,15 +397,15 @@ public class Program {
// actual gas subtract
this.spendGas(gas.intValue(), "internal call");
Repository trackRepository = result.getRepository().getTrack();
trackRepository.startTracking();
trackRepository.addBalance(toAddress, endowmentValue.value());
RepositoryImpl trackRepositoryImpl = result.getRepository().getTrack();
trackRepositoryImpl.startTracking();
trackRepositoryImpl.addBalance(toAddress, endowmentValue.value());
ProgramInvoke programInvoke =
ProgramInvokeFactory.createProgramInvoke(this, toAddressDW,
endowmentValue, gas, result.getRepository().getBalance(toAddress),
data.array(),
trackRepository, this.invokeData.getCallDeep() + 1);
trackRepositoryImpl, this.invokeData.getCallDeep() + 1);
ProgramResult result = null;
@ -413,7 +422,7 @@ public class Program {
result.getException() instanceof Program.OutOfGasException) {
logger.info("contract run halted by OutOfGas: contract={}" , Hex.toHexString(toAddress));
trackRepository.rollback();
trackRepositoryImpl.rollback();
stackPushZero();
return;
}
@ -421,24 +430,20 @@ public class Program {
// 3. APPLY RESULTS: result.getHReturn() into out_memory allocated
if (result != null) {
ByteBuffer buffer = result.getHReturn();
if (buffer != null) {
int retSize = buffer.array().length;
int allocSize = outDataSize.intValue();
int allocSize = outDataSize.intValue();
if (buffer != null && allocSize > 0) {
int retSize = buffer.limit();
int offset = outDataOffs.intValue();
if (retSize > allocSize) {
byte[] outArray = Arrays.copyOf(buffer.array(), allocSize);
this.memorySave(outArray, buffer.array());
} else if (retSize == 0 || retSize == allocSize){
this.memorySave(outDataOffs.getData(), buffer.array());
this.memorySave(offset, buffer.array());
} else {
byte[] outArray = new byte[allocSize];
System.arraycopy(buffer.array(), 0, outArray, 0, retSize);
this.memorySave(outDataOffs.getData(), outArray);
this.memorySave(offset, allocSize, buffer.array());
}
}
}
// 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK
trackRepository.commit();
trackRepositoryImpl.commit();
stackPushOne();
// 5. REFUND THE REMAIN GAS
@ -702,4 +707,4 @@ public class Program {
@SuppressWarnings("serial")
public class OutOfGasException extends RuntimeException {
}
}
}

View File

@ -1,6 +1,6 @@
package org.ethereum.vm;
import org.ethereum.db.Repository;
import org.ethereum.db.RepositoryImpl;
/**
* www.ethereumJ.com
@ -27,7 +27,7 @@ public interface ProgramInvoke {
public DataWord getDifficulty();
public DataWord getGaslimit();
public Repository getRepository();
public RepositoryImpl getRepository();
public boolean byTransaction();
boolean byTestingSuite();

View File

@ -2,7 +2,7 @@ package org.ethereum.vm;
import org.ethereum.core.Block;
import org.ethereum.core.Transaction;
import org.ethereum.db.Repository;
import org.ethereum.db.RepositoryImpl;
import org.ethereum.manager.WorldManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -21,7 +21,7 @@ public class ProgramInvokeFactory {
private static Logger logger = LoggerFactory.getLogger("VM");
// Invocation by the wire tx
public static ProgramInvoke createProgramInvoke(Transaction tx, Block block, Repository repository) {
public static ProgramInvoke createProgramInvoke(Transaction tx, Block block, RepositoryImpl repository) {
// https://ethereum.etherpad.mozilla.org/26
Block lastBlock = WorldManager.getInstance().getBlockchain().getLastBlock();
@ -122,7 +122,7 @@ public class ProgramInvokeFactory {
public static ProgramInvoke createProgramInvoke(Program program, DataWord toAddress,
DataWord inValue, DataWord inGas,
BigInteger balanceInt, byte[] dataIn,
Repository repository, int callDeep) {
RepositoryImpl repository, int callDeep) {
DataWord address = toAddress;
DataWord origin = program.getOriginAddress();

View File

@ -1,6 +1,6 @@
package org.ethereum.vm;
import org.ethereum.db.Repository;
import org.ethereum.db.RepositoryImpl;
import java.math.BigInteger;
import java.util.Arrays;
@ -26,7 +26,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
Map<DataWord, DataWord> storage;
private Repository repository;
private RepositoryImpl repository;
private boolean byTransaction = true;
private boolean byTestingSuite = false;
private int callDeep = 0;
@ -34,7 +34,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
public ProgramInvokeImpl(DataWord address, DataWord origin, DataWord caller, DataWord balance,
DataWord gasPrice, DataWord gas, DataWord callValue, byte[] msgData,
DataWord lastHash, DataWord coinbase, DataWord timestamp, DataWord number, DataWord difficulty,
DataWord gaslimit, Repository repository, int callDeep) {
DataWord gaslimit, RepositoryImpl repository, int callDeep) {
// Transaction env
this.address = address;
@ -63,7 +63,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
byte[] gasPrice, byte[] gas, byte[] callValue, byte[] msgData,
byte[] lastHash, byte[] coinbase, long timestamp, long number, byte[] difficulty,
long gaslimit,
Repository repository, boolean byTestingSuite) {
RepositoryImpl repository, boolean byTestingSuite) {
this(address, origin, caller, balance, gasPrice, gas, callValue, msgData, lastHash, coinbase,
timestamp, number, difficulty, gaslimit, repository);
this.byTestingSuite = byTestingSuite;
@ -74,7 +74,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
byte[] gasPrice, byte[] gas, byte[] callValue, byte[] msgData,
byte[] lastHash, byte[] coinbase, long timestamp, long number, byte[] difficulty,
long gaslimit,
Repository repository) {
RepositoryImpl repository) {
// Transaction env
this.address = new DataWord(address);
@ -215,7 +215,7 @@ public class ProgramInvokeImpl implements ProgramInvoke {
/* Storage */
public Map<DataWord, DataWord> getStorage() { return storage; }
public Repository getRepository() {
public RepositoryImpl getRepository() {
return repository;
}

View File

@ -2,7 +2,7 @@ package org.ethereum.vm;
import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.Repository;
import org.ethereum.db.RepositoryImpl;
import org.spongycastle.util.encoders.Hex;
/**
@ -15,7 +15,7 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
private byte[] msgData;
private Repository repository = null;
private RepositoryImpl repository = null;
private String ownerAddress = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
public ProgramInvokeMockImpl(byte[] msgDataRaw) {
@ -24,7 +24,7 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
}
public ProgramInvokeMockImpl() {
this.repository = new Repository();
this.repository = new RepositoryImpl();
this.repository.createAccount(Hex.decode(ownerAddress));
}
@ -179,11 +179,11 @@ public class ProgramInvokeMockImpl implements ProgramInvoke {
}
@Override
public Repository getRepository() {
public RepositoryImpl getRepository() {
return this.repository;
}
public void setRepository(Repository repository) {
public void setRepository(RepositoryImpl repository) {
this.repository = repository;
}

View File

@ -1,6 +1,6 @@
package org.ethereum.vm;
import org.ethereum.db.Repository;
import org.ethereum.db.RepositoryImpl;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@ -18,7 +18,7 @@ public class ProgramResult {
private RuntimeException exception;
private List<DataWord> deleteAccounts;
private Repository repository = null;
private RepositoryImpl repository = null;
/*
* for testing runs ,
@ -55,11 +55,11 @@ public class ProgramResult {
this.exception = exception;
}
public Repository getRepository() {
public RepositoryImpl getRepository() {
return repository;
}
public void setRepository(Repository repository) {
public void setRepository(RepositoryImpl repository) {
this.repository = repository;
}

View File

@ -144,8 +144,8 @@ public class VM {
}
callGas = callGasWord.longValue();
// Casting to long (causing overflow) as workaround for PoC5 - should be removed for PoC6
long x = stack.get(stack.size()-6).value().add(stack.get(stack.size()-7).value()).longValue();
long y = stack.get(stack.size()-4).value().add(stack.get(stack.size()-5).value()).longValue();
long x = stack.get(stack.size()-4).value().add(stack.get(stack.size()-5).value()).longValue(); // in offset+size
long y = stack.get(stack.size()-6).value().add(stack.get(stack.size()-7).value()).longValue(); // out offset+size
newMemSize = BigInteger.valueOf(Math.max(x, y));
break;
case CREATE:
@ -157,6 +157,7 @@ public class VM {
}
program.spendGas(gasCost, op.name());
// Avoid overflows
if(newMemSize.compareTo(MAX_GAS) == 1) {
throw program.new OutOfGasException();
}
@ -422,7 +423,7 @@ public class VM {
DataWord word2 = program.stackPop();
DataWord result = null;
if (word1.value().compareTo(_32_) == -1) {
byte tmp = word2.getData()[word1.value().intValue()];
byte tmp = word2.getData()[word1.intValue()];
word2.and(DataWord.ZERO);
word2.getData()[31] = tmp;
result = word2;
@ -535,7 +536,7 @@ public class VM {
if (logger.isInfoEnabled())
hint = "data: " + Hex.toHexString(msgData);
program.memorySave(memOffsetData.getData(), msgData);
program.memorySave(memOffsetData.intValue(), msgData);
program.step();
} break;
case CODESIZE:{
@ -552,8 +553,8 @@ public class VM {
DataWord codeOffsetData = program.stackPop();
DataWord lengthData = program.stackPop();
int length = lengthData.value().intValue();
int codeOffset = codeOffsetData.value().intValue();
int length = lengthData.intValue();
int codeOffset = codeOffsetData.intValue();
if (program.ops.length < length + codeOffset) {
program.stop();
@ -566,7 +567,7 @@ public class VM {
if (logger.isInfoEnabled())
hint = "code: " + Hex.toHexString(code);
program.memorySave(memOffsetData.getData(), code);
program.memorySave(memOffsetData.intValue(), code);
program.step();
} break;
case GASPRICE:{
@ -678,7 +679,7 @@ public class VM {
DataWord addr = program.stackPop();
DataWord value = program.stackPop();
byte[] byteVal = {value.getData()[31]};
program.memorySave(addr.getData(), byteVal);
program.memorySave(addr.intValue(), byteVal);
program.step();
} break;
case SLOAD:{
@ -938,4 +939,4 @@ public class VM {
@SuppressWarnings("serial")
private class IllegalOperationException extends RuntimeException {}
}
}

View File

@ -2,7 +2,7 @@ package org.ethereum.core;
import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.Repository;
import org.ethereum.db.RepositoryImpl;
import org.ethereum.manager.WorldManager;
import org.junit.*;
import org.junit.runners.MethodSorters;
@ -37,7 +37,7 @@ public class WalletTest {
@Test // Testing account for simple balance set
public void accountTest_1(){
Repository repository = WorldManager.getInstance().getRepository();
RepositoryImpl repository = WorldManager.getInstance().getRepository();
ECKey cowKey = ECKey.fromPrivate(HashUtil.sha3("cow".getBytes()));
repository.createAccount(cowKey.getAddress());
@ -55,7 +55,7 @@ public class WalletTest {
@Test // test account balance with pending "unblocked" transaction
public void accountTest_2(){
Repository repository = WorldManager.getInstance().getRepository();
RepositoryImpl repository = WorldManager.getInstance().getRepository();
ECKey cowKey = ECKey.fromPrivate(HashUtil.sha3("cow".getBytes()));
repository.createAccount(cowKey.getAddress());

View File

@ -23,7 +23,7 @@ public class RepositoryTest {
public void test1() {
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
Repository repository = new Repository();
RepositoryImpl repository = new RepositoryImpl();
try {
AccountState createdState = repository.createAccount(Hex.decode(addr));
@ -38,7 +38,7 @@ public class RepositoryTest {
public void test2() {
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
Repository repository = new Repository();
RepositoryImpl repository = new RepositoryImpl();
try {
BigInteger nonce0 = repository.getNonce(Hex.decode(addr));
@ -61,7 +61,7 @@ public class RepositoryTest {
public void test3() {
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
Repository repository = new Repository();
RepositoryImpl repository = new RepositoryImpl();
try {
BigInteger nonce0 = repository.getNonce(Hex.decode(addr));
@ -85,7 +85,7 @@ public class RepositoryTest {
public void test4() {
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
Repository repository = new Repository();
RepositoryImpl repository = new RepositoryImpl();
try {
BigInteger balance0 = repository.getBalance(Hex.decode(addr));
@ -107,7 +107,7 @@ public class RepositoryTest {
public void test5() {
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
Repository repository = new Repository();
RepositoryImpl repository = new RepositoryImpl();
try {
BigInteger balance0 = repository.getBalance(Hex.decode(addr));
@ -133,7 +133,7 @@ public class RepositoryTest {
public void test6() {
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
Repository repository = new Repository();
RepositoryImpl repository = new RepositoryImpl();
byte[] code;
try {
@ -151,7 +151,7 @@ public class RepositoryTest {
String codeString = "7f60c860005461012c602054000000000000000000000000000000000000000000600060206000f200";
String codeHash = "8f0d7fc8cc6fdd688fa58ae9256310069f5659ed2a8a3af994d80350fbf1e798";
Repository repository = new Repository();
RepositoryImpl repository = new RepositoryImpl();
try {
byte[] code0 = repository.getCode(Hex.decode(addr));
@ -174,7 +174,7 @@ public class RepositoryTest {
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
String codeHash = "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470";
Repository repository = new Repository();
RepositoryImpl repository = new RepositoryImpl();
try {
byte[] code0 = repository.getCode(Hex.decode(addr));
@ -197,7 +197,7 @@ public class RepositoryTest {
byte[] keyBytes = Hex.decode("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
DataWord key = new DataWord(keyBytes);
Repository repository = new Repository();
RepositoryImpl repository = new RepositoryImpl();
try {
DataWord value = repository.getStorageValue(Hex.decode(addr), key);
@ -211,7 +211,7 @@ public class RepositoryTest {
public void test10() {
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
Repository repository = new Repository();
RepositoryImpl repository = new RepositoryImpl();
try {
repository.createAccount(Hex.decode(addr));
@ -233,7 +233,7 @@ public class RepositoryTest {
String addr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
String expectedStorageHash = "a737c40a4aa895fb9eb464536c376ee7c2c08eb733c8fd2353fcc62dc734f075";
Repository repository = new Repository();
RepositoryImpl repository = new RepositoryImpl();
try {
repository.createAccount(Hex.decode(addr));
@ -283,10 +283,10 @@ public class RepositoryTest {
String expectedStorageHash = "365ed874ad42c2b4af335212465291e03dcd1f0c5b600f40f048ed238ad61fd3";
long expectedBalance = 333;
Repository origRepository = new Repository();
RepositoryImpl origRepository = new RepositoryImpl();
try {
Repository repository = origRepository.getTrack();
RepositoryImpl repository = origRepository.getTrack();
repository.startTracking();
repository.createAccount(Hex.decode(addr));
@ -310,8 +310,8 @@ public class RepositoryTest {
long expectedBalance_1 = 55500;
long expectedBalance_2 = 0;
Repository origRepository = new Repository();
Repository repository = origRepository.getTrack();
RepositoryImpl origRepository = new RepositoryImpl();
RepositoryImpl repository = origRepository.getTrack();
repository.startTracking();
repository.createAccount(Hex.decode(addr));
@ -336,8 +336,8 @@ public class RepositoryTest {
long expectedBalance = 55500;
Repository origRepository = new Repository();
Repository repository = origRepository.getTrack();
RepositoryImpl origRepository = new RepositoryImpl();
RepositoryImpl repository = origRepository.getTrack();
try {
repository.createAccount(Hex.decode(addr_1));

View File

@ -15,7 +15,7 @@ import static org.junit.Assert.assertEquals;
public class UtilsTest {
@Test
public void getValueShortString1() {
public void testGetValueShortString1() {
String expected = "123·(10^24)";
String result = Utils.getValueShortString(new BigInteger("123456789123445654363653463"));
@ -24,7 +24,7 @@ public class UtilsTest {
}
@Test
public void getValueShortString2() {
public void testGetValueShortString2() {
String expected = "123·(10^3)";
String result = Utils.getValueShortString(new BigInteger("123456"));
@ -33,7 +33,7 @@ public class UtilsTest {
}
@Test
public void getValueShortString3() {
public void testGetValueShortString3() {
String expected = "1·(10^3)";
String result = Utils.getValueShortString(new BigInteger("1234"));
@ -42,7 +42,7 @@ public class UtilsTest {
}
@Test
public void getValueShortString4() {
public void testGetValueShortString4() {
String expected = "123·(10^0)";
String result = Utils.getValueShortString(new BigInteger("123"));
@ -51,7 +51,7 @@ public class UtilsTest {
}
@Test
public void getValueShortString5() {
public void testGetValueShortString5() {
byte[] decimal = Hex.decode("3913517ebd3c0c65000000");
String expected = "69·(10^24)";
@ -59,4 +59,4 @@ public class UtilsTest {
assertEquals(expected, result);
}
}
}

View File

@ -0,0 +1,335 @@
package org.ethereum.vm;
import static org.junit.Assert.*;
import java.nio.ByteBuffer;
import org.ethereum.util.ByteUtil;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
public class ProgramMemoryTest {
ProgramInvokeMockImpl pi = null;
Program program;
ByteBuffer memory;
@Before
public void createProgram() {
program = new Program(ByteUtil.EMPTY_BYTE_ARRAY, pi);
}
@Test
public void testGetMemSize() {
ByteBuffer memory = ByteBuffer.allocate(64);
program.memory = memory;
assertEquals(64, program.getMemSize());
}
@Test
@Ignore
public void testMemorySave() {
fail("Not yet implemented");
}
@Test
@Ignore
public void testMemoryLoad() {
fail("Not yet implemented");
}
@Test
@Ignore
public void testMemoryChunk() {
fail("Not yet implemented");
}
@Test
public void testAllocateMemory1() {
memory = ByteBuffer.allocate(64);
int offset = 32;
int size = 32;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(64, program.getMemSize());
}
@Test
public void testAllocateMemory2() {
// memory.limit() > offset, == size
// memory.limit() < offset + size
memory = ByteBuffer.allocate(64);
int offset = 32;
int size = 64;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(96, program.getMemSize());
}
@Test
public void testAllocateMemory3() {
// memory.limit() > offset, > size
memory = ByteBuffer.allocate(64);
int offset = 0;
int size = 32;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(64, program.getMemSize());
}
@Test
public void testAllocateMemory4() {
memory = ByteBuffer.allocate(64);;
int offset = 0;
int size = 64;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(64, program.getMemSize());
}
@Test
public void testAllocateMemory5() {
memory = ByteBuffer.allocate(64);
int offset = 0;
int size = 0;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(64, program.getMemSize());
}
@Test
public void testAllocateMemory6() {
// memory.limit() == offset, > size
memory = ByteBuffer.allocate(64);
int offset = 64;
int size = 32;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(96, program.getMemSize());
}
@Test
public void testAllocateMemory7() {
// memory.limit() == offset - size
memory = ByteBuffer.allocate(64);
int offset = 96;
int size = 32;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(128, program.getMemSize());
}
@Test
public void testAllocateMemory8() {
memory = ByteBuffer.allocate(64);
int offset = 0;
int size = 96;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(96, program.getMemSize());
}
@Test
public void testAllocateMemory9() {
// memory.limit() < offset, > size
// memory.limit() < offset - size
memory = ByteBuffer.allocate(64);
int offset = 96;
int size = 0;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(96, program.getMemSize());
}
/************************************************/
@Test
public void testAllocateMemory10() {
// memory = null, offset > size
int offset = 32;
int size = 0;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(32, program.getMemSize());
}
@Test
public void testAllocateMemory11() {
// memory = null, offset < size
int offset = 0;
int size = 32;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(32, program.getMemSize());
}
@Test
public void testAllocateMemory12() {
// memory.limit() < offset, < size
memory = ByteBuffer.allocate(32);
int offset = 64;
int size = 96;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(160, program.getMemSize());
}
@Test
public void testAllocateMemory13() {
// memory.limit() > offset, < size
memory = ByteBuffer.allocate(64);
int offset = 32;
int size = 128;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(160, program.getMemSize());
}
@Test
public void testAllocateMemory14() {
// memory.limit() < offset, == size
memory = ByteBuffer.allocate(64);
int offset = 96;
int size = 64;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(160, program.getMemSize());
}
@Test
public void testAllocateMemory15() {
// memory.limit() == offset, < size
memory = ByteBuffer.allocate(64);
int offset = 64;
int size = 96;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(160, program.getMemSize());
}
@Test
public void testAllocateMemory16() {
// memory.limit() == offset, == size
// memory.limit() > offset - size
memory = ByteBuffer.allocate(64);
int offset = 64;
int size = 64;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(128, program.getMemSize());
}
@Test
public void testAllocateMemory17() {
// memory.limit() > offset + size
memory = ByteBuffer.allocate(96);
int offset = 32;
int size = 32;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(96, program.getMemSize());
}
@Test
public void testAllocateMemoryUnrounded1() {
// memory unrounded
memory = ByteBuffer.allocate(16);
int offset = 64;
int size = 32;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(96, program.getMemSize());
}
@Test
public void testAllocateMemoryUnrounded2() {
// offset unrounded
memory = ByteBuffer.allocate(32);
int offset = 16;
int size = 32;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(64, program.getMemSize());
}
@Test
public void testAllocateMemoryUnrounded3() {
// size unrounded
memory = ByteBuffer.allocate(32);
int offset = 64;
int size = 16;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(96, program.getMemSize());
}
@Test
public void testAllocateMemoryUnrounded4() {
// memory + offset unrounded
memory = ByteBuffer.allocate(16);
int offset = 16;
int size = 32;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(64 , program.getMemSize());
}
@Test
public void testAllocateMemoryUnrounded5() {
// memory + size unrounded
memory = ByteBuffer.allocate(16);
int offset = 32;
int size = 16;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(64, program.getMemSize());
}
@Test
public void testAllocateMemoryUnrounded6() {
// offset + size unrounded
memory = ByteBuffer.allocate(32);
int offset = 16;
int size = 16;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(32, program.getMemSize());
}
@Test
public void testAllocateMemoryUnrounded7() {
// memory + offset + size unrounded
memory = ByteBuffer.allocate(16);
int offset = 16;
int size = 16;
program.memory = memory;
program.allocateMemory(offset, size);
assertEquals(32,program.getMemSize());
}
}

View File

@ -2,7 +2,7 @@ package org.ethereum.vm;
import org.ethereum.core.AccountState;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.Repository;
import org.ethereum.db.RepositoryImpl;
import org.junit.FixMethodOrder;
import org.junit.Ignore;
import org.junit.Test;
@ -59,7 +59,7 @@ public class VMComplexTest {
ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl();
pi.setOwnerAddress("77045e71a7a2c50903d88e564cd72fab11e82051");
Repository repository = pi.getRepository();
RepositoryImpl repository = pi.getRepository();
repository.createAccount(callerAddrB);
repository.addBalance(callerAddrB, new BigInteger("100000000000000000000"));
@ -134,7 +134,7 @@ public class VMComplexTest {
ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl();
pi.setOwnerAddress(contractB_addr);
Repository repository = pi.getRepository();
RepositoryImpl repository = pi.getRepository();
byte[] contractB_addr_bytes = Hex.decode(contractB_addr);
byte[] codeB = Hex.decode(code_b);
@ -234,7 +234,7 @@ public class VMComplexTest {
ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl();
pi.setOwnerAddress(contractB_addr);
Repository repository = pi.getRepository();
RepositoryImpl repository = pi.getRepository();
repository.createAccount(contractA_addr_bytes);
repository.saveCode(contractA_addr_bytes, codeA);
@ -315,7 +315,7 @@ public class VMComplexTest {
ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl();
pi.setOwnerAddress(contractA_addr);
Repository repository = pi.getRepository();
RepositoryImpl repository = pi.getRepository();
byte[] caller_addr_bytes = Hex.decode(callerAddr);

View File

@ -1,6 +1,6 @@
package org.ethereum.vm;
import org.ethereum.db.Repository;
import org.ethereum.db.RepositoryImpl;
import org.ethereum.vm.Program.OutOfGasException;
import org.junit.FixMethodOrder;
import org.junit.Test;
@ -1540,7 +1540,7 @@ public class VMTest {
vm.step(program);
vm.step(program);
Repository repository = program.result.getRepository();
RepositoryImpl repository = program.result.getRepository();
DataWord key = new DataWord(Hex.decode(s_expected_key));
DataWord val = repository.getStorageValue(invoke.getOwnerAddress().getNoLeadZeroesData(), key);

View File

@ -2,7 +2,7 @@ package org.ethereum.gui;
import org.ethereum.core.Block;
import org.ethereum.core.Transaction;
import org.ethereum.db.Repository;
import org.ethereum.db.RepositoryImpl;
import org.ethereum.manager.WorldManager;
import org.ethereum.vm.*;
import org.spongycastle.util.encoders.Hex;
@ -49,7 +49,7 @@ public class ProgramPlayDialog extends JPanel implements ActionListener,
outputList = new ArrayList<String>();
VM vm = new VM();
Repository tractRepository = WorldManager.getInstance().getRepository().getTrack();
RepositoryImpl tractRepository = WorldManager.getInstance().getRepository().getTrack();
Program program = new Program(code ,
ProgramInvokeFactory.createProgramInvoke(tx, lastBlock, tractRepository));