use of org.ethereum.vm.program.invoke.ProgramInvoke in project rskj by rsksmart.
the class TransactionExecutor method create.
private void create() {
RskAddress newContractAddress = tx.getContractAddress();
if (isEmpty(tx.getData())) {
mEndGas = toBI(tx.getGasLimit()).subtract(BigInteger.valueOf(basicTxCost));
cacheTrack.createAccount(newContractAddress);
} else {
ProgramInvoke programInvoke = programInvokeFactory.createProgramInvoke(tx, txindex, executionBlock, cacheTrack, blockStore);
this.vm = new VM(vmConfig, precompiledContracts);
BlockchainConfig configForBlock = config.getBlockchainConfig().getConfigForBlock(executionBlock.getNumber());
this.program = new Program(vmConfig, precompiledContracts, configForBlock, tx.getData(), programInvoke, tx);
// reset storage if the contract with the same address already exists
// TCK test case only - normally this is near-impossible situation in the real network
ContractDetails contractDetails = program.getStorage().getContractDetails(newContractAddress);
for (DataWord key : contractDetails.getStorageKeys()) {
program.storageSave(key, DataWord.ZERO);
}
}
Coin endowment = tx.getValue();
cacheTrack.transfer(tx.getSender(), newContractAddress, endowment);
}
use of org.ethereum.vm.program.invoke.ProgramInvoke in project rskj by rsksmart.
the class TransactionExecutor method call.
private void call() {
if (!readyToExecute) {
return;
}
logger.trace("Call transaction {} {}", toBI(tx.getNonce()), tx.getHash());
RskAddress targetAddress = tx.getReceiveAddress();
// DataWord(targetAddress)) can fail with exception:
// java.lang.RuntimeException: Data word can't exceed 32 bytes:
// if targetAddress size is greater than 32 bytes.
// But init() will detect this earlier
precompiledContract = precompiledContracts.getContractForAddress(new DataWord(targetAddress.getBytes()));
if (precompiledContract != null) {
precompiledContract.init(tx, executionBlock, track, blockStore, receiptStore, result.getLogInfoList());
long requiredGas = precompiledContract.getGasForData(tx.getData());
BigInteger txGasLimit = toBI(tx.getGasLimit());
if (!localCall && txGasLimit.compareTo(BigInteger.valueOf(requiredGas)) < 0) {
// no refund
// no endowment
execError(String.format("Out of Gas calling precompiled contract 0x%s, required: %d, left: %s ", targetAddress.toString(), (requiredGas + basicTxCost), mEndGas));
mEndGas = BigInteger.ZERO;
return;
} else {
long gasUsed = requiredGas + basicTxCost;
mEndGas = txGasLimit.subtract(BigInteger.valueOf(requiredGas + basicTxCost));
// FIXME: save return for vm trace
try {
byte[] out = precompiledContract.execute(tx.getData());
result.setHReturn(out);
} catch (RuntimeException e) {
result.setException(e);
}
result.spendGas(gasUsed);
}
} else {
byte[] code = track.getCode(targetAddress);
if (isEmpty(code)) {
mEndGas = toBI(tx.getGasLimit()).subtract(BigInteger.valueOf(basicTxCost));
result.spendGas(basicTxCost);
} else {
ProgramInvoke programInvoke = programInvokeFactory.createProgramInvoke(tx, txindex, executionBlock, cacheTrack, blockStore);
this.vm = new VM(vmConfig, precompiledContracts);
BlockchainConfig configForBlock = config.getBlockchainConfig().getConfigForBlock(executionBlock.getNumber());
this.program = new Program(vmConfig, precompiledContracts, configForBlock, code, programInvoke, tx);
}
}
if (result.getException() == null) {
Coin endowment = tx.getValue();
cacheTrack.transfer(tx.getSender(), targetAddress, endowment);
}
}
use of org.ethereum.vm.program.invoke.ProgramInvoke in project rskj by rsksmart.
the class Program method createContract.
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
public void createContract(DataWord value, DataWord memStart, DataWord memSize) {
if (getCallDeep() == MAX_DEPTH) {
stackPushZero();
return;
}
RskAddress senderAddress = new RskAddress(getOwnerAddress());
Coin endowment = new Coin(value.getData());
if (isNotCovers(getStorage().getBalance(senderAddress), endowment)) {
stackPushZero();
return;
}
// [1] FETCH THE CODE FROM THE MEMORY
byte[] programCode = memoryChunk(memStart.intValue(), memSize.intValue());
if (isLogEnabled) {
logger.info("creating a new contract inside contract run: [{}]", senderAddress);
}
// actual gas subtract
long gasLimit = getRemainingGas();
spendGas(gasLimit, "internal call");
// [2] CREATE THE CONTRACT ADDRESS
byte[] nonce = getStorage().getNonce(senderAddress).toByteArray();
byte[] newAddressBytes = HashUtil.calcNewAddr(getOwnerAddress().getLast20Bytes(), nonce);
RskAddress newAddress = new RskAddress(newAddressBytes);
if (byTestingSuite()) {
// This keeps track of the contracts created for a test
getResult().addCallCreate(programCode, EMPTY_BYTE_ARRAY, gasLimit, value.getNoLeadZeroesData());
}
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
if (!byTestingSuite()) {
getStorage().increaseNonce(senderAddress);
}
Repository track = getStorage().startTracking();
// In case of hashing collisions, check for any balance before createAccount()
if (track.isExist(newAddress)) {
Coin oldBalance = track.getBalance(newAddress);
track.createAccount(newAddress);
track.addBalance(newAddress, oldBalance);
} else {
track.createAccount(newAddress);
}
// [4] TRANSFER THE BALANCE
track.addBalance(senderAddress, endowment.negate());
Coin newBalance = Coin.ZERO;
if (!byTestingSuite()) {
newBalance = track.addBalance(newAddress, endowment);
}
// [5] COOK THE INVOKE AND EXECUTE
InternalTransaction internalTx = addInternalTx(nonce, getGasLimit(), senderAddress, RskAddress.nullAddress(), endowment, programCode, "create");
ProgramInvoke programInvoke = programInvokeFactory.createProgramInvoke(this, new DataWord(newAddressBytes), getOwnerAddress(), value, gasLimit, newBalance, null, track, this.invoke.getBlockStore(), byTestingSuite());
ProgramResult programResult = ProgramResult.empty();
// reset return buffer right before the call
returnDataBuffer = null;
if (isNotEmpty(programCode)) {
VM vm = new VM(config, precompiledContracts);
Program program = new Program(config, precompiledContracts, blockchainConfig, programCode, programInvoke, internalTx);
vm.play(program);
programResult = program.getResult();
}
if (programResult.getException() != null || programResult.isRevert()) {
if (isLogEnabled) {
logger.debug("contract run halted by Exception: contract: [{}], exception: [{}]", newAddress, programResult.getException());
}
if (internalTx == null) {
throw new NullPointerException();
}
internalTx.reject();
programResult.rejectInternalTransactions();
programResult.rejectLogInfos();
track.rollback();
stackPushZero();
if (programResult.getException() != null) {
return;
} else {
returnDataBuffer = result.getHReturn();
}
} else {
// 4. CREATE THE CONTRACT OUT OF RETURN
byte[] code = programResult.getHReturn();
int codeLength = getLength(code);
long storageCost = (long) codeLength * GasCost.CREATE_DATA;
long afterSpend = programInvoke.getGas() - storageCost - programResult.getGasUsed();
if (afterSpend < 0) {
programResult.setException(ExceptionHelper.notEnoughSpendingGas("No gas to return just created contract", storageCost, this));
} else if (codeLength > Constants.getMaxContractSize()) {
programResult.setException(ExceptionHelper.tooLargeContractSize(Constants.getMaxContractSize(), codeLength));
} else {
programResult.spendGas(storageCost);
track.saveCode(newAddress, code);
}
track.commit();
getResult().addDeleteAccounts(programResult.getDeleteAccounts());
getResult().addLogInfos(programResult.getLogInfoList());
// IN SUCCESS PUSH THE ADDRESS INTO THE STACK
stackPush(new DataWord(newAddressBytes));
}
// 5. REFUND THE REMAIN GAS
long refundGas = gasLimit - programResult.getGasUsed();
if (refundGas > 0) {
refundGas(refundGas, "remain gas from the internal call");
if (isGasLogEnabled) {
gasLogger.info("The remaining gas is refunded, account: [{}], gas: [{}] ", Hex.toHexString(getOwnerAddress().getLast20Bytes()), refundGas);
}
}
}
use of org.ethereum.vm.program.invoke.ProgramInvoke in project rskj by rsksmart.
the class Program method executeCode.
public boolean executeCode(MessageCall msg, RskAddress contextAddress, Coin contextBalance, InternalTransaction internalTx, Repository track, byte[] programCode, RskAddress senderAddress, byte[] data) {
// reset return buffer right before the call
returnDataBuffer = null;
ProgramResult childResult = null;
ProgramInvoke programInvoke = programInvokeFactory.createProgramInvoke(this, new DataWord(contextAddress.getBytes()), msg.getType() == MsgType.DELEGATECALL ? getCallerAddress() : getOwnerAddress(), msg.getType() == MsgType.DELEGATECALL ? getCallValue() : msg.getEndowment(), limitToMaxLong(msg.getGas()), contextBalance, data, track, this.invoke.getBlockStore(), byTestingSuite());
VM vm = new VM(config, precompiledContracts);
Program program = new Program(config, precompiledContracts, blockchainConfig, programCode, programInvoke, internalTx);
vm.play(program);
childResult = program.getResult();
getTrace().merge(program.getTrace());
getResult().merge(childResult);
boolean childCallSuccessful = true;
if (childResult.getException() != null || childResult.isRevert()) {
if (isGasLogEnabled) {
gasLogger.debug("contract run halted by Exception: contract: [{}], exception: [{}]", contextAddress, childResult.getException());
}
internalTx.reject();
childResult.rejectInternalTransactions();
childResult.rejectLogInfos();
track.rollback();
// and we only do that when the call is successful or there's a REVERT operation.
if (childResult.getException() != null) {
return false;
}
childCallSuccessful = false;
} else {
// 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK
track.commit();
}
// 3. APPLY RESULTS: childResult.getHReturn() into out_memory allocated
byte[] buffer = childResult.getHReturn();
int offset = msg.getOutDataOffs().intValue();
int size = msg.getOutDataSize().intValue();
memorySaveLimited(offset, buffer, size);
returnDataBuffer = buffer;
// 5. REFUND THE REMAIN GAS
BigInteger refundGas = msg.getGas().value().subtract(toBI(childResult.getGasUsed()));
if (isPositive(refundGas)) {
// Since the original gas transferred was < Long.MAX_VALUE then the refund
// also fits in a long.
// SUICIDE refunds 24.000 and SSTORE clear refunds 15.000 gas.
// The accumulated refund can not exceed half the gas used
// for the current context (i.e. the initial call)
// Therefore, the regundGas also fits in a long.
refundGas(refundGas.longValue(), "remaining gas from the internal call");
if (isGasLogEnabled) {
gasLogger.info("The remaining gas refunded, account: [{}], gas: [{}] ", senderAddress, refundGas.toString());
}
}
return childCallSuccessful;
}
use of org.ethereum.vm.program.invoke.ProgramInvoke in project rskj by rsksmart.
the class TestRunner method runTestCase.
public List<String> runTestCase(TestCase testCase) {
logger.info("\n***");
logger.info(" Running test case: [" + testCase.getName() + "]");
logger.info("***\n");
List<String> results = new ArrayList<>();
logger.info("--------- PRE ---------");
Repository repository = loadRepository(new RepositoryImpl(config).startTracking(), testCase.getPre());
try {
/* 2. Create ProgramInvoke - Env/Exec */
Env env = testCase.getEnv();
Exec exec = testCase.getExec();
Logs logs = testCase.getLogs();
byte[] address = exec.getAddress();
byte[] origin = exec.getOrigin();
byte[] caller = exec.getCaller();
byte[] balance = ByteUtil.bigIntegerToBytes(repository.getBalance(new RskAddress(exec.getAddress())).asBigInteger());
byte[] gasPrice = exec.getGasPrice();
byte[] gas = exec.getGas();
byte[] callValue = exec.getValue();
byte[] msgData = exec.getData();
byte[] lastHash = env.getPreviousHash();
byte[] coinbase = env.getCurrentCoinbase();
long timestamp = ByteUtil.byteArrayToLong(env.getCurrentTimestamp());
long number = ByteUtil.byteArrayToLong(env.getCurrentNumber());
byte[] difficulty = env.getCurrentDifficulty();
byte[] gaslimit = env.getCurrentGasLimit();
// Origin and caller need to exist in order to be able to execute
if (repository.getAccountState(new RskAddress(origin)) == null)
repository.createAccount(new RskAddress(origin));
if (repository.getAccountState(new RskAddress(caller)) == null)
repository.createAccount(new RskAddress(caller));
ProgramInvoke programInvoke = new ProgramInvokeImpl(address, origin, caller, balance, gasPrice, gas, callValue, msgData, lastHash, coinbase, timestamp, number, 0, difficulty, gaslimit, repository, new BlockStoreDummy(), true);
/* 3. Create Program - exec.code */
/* 4. run VM */
VM vm = new VM(vmConfig, precompiledContracts);
Program program = new Program(vmConfig, precompiledContracts, mock(BlockchainConfig.class), exec.getCode(), programInvoke, null);
boolean vmDidThrowAnEception = false;
Exception e = null;
ThreadMXBean thread;
Boolean oldMode;
long startTime = 0;
thread = ManagementFactory.getThreadMXBean();
if (thread.isThreadCpuTimeSupported()) {
oldMode = thread.isThreadCpuTimeEnabled();
thread.setThreadCpuTimeEnabled(true);
// in nanoseconds.
startTime = thread.getCurrentThreadCpuTime();
}
try {
vm.steps(program, Long.MAX_VALUE);
;
} catch (RuntimeException ex) {
vmDidThrowAnEception = true;
e = ex;
}
if (startTime != 0) {
long endTime = thread.getCurrentThreadCpuTime();
// de nano a micro.
long deltaTime = (endTime - startTime) / 1000;
logger.info("Time elapsed [uS]: " + Long.toString(deltaTime));
}
try {
saveProgramTraceFile(config, testCase.getName(), program.getTrace());
} catch (IOException ioe) {
vmDidThrowAnEception = true;
e = ioe;
}
if (testCase.getPost().size() == 0) {
if (vmDidThrowAnEception != true) {
String output = "VM was expected to throw an exception";
logger.info(output);
results.add(output);
} else
logger.info("VM did throw an exception: " + e.toString());
} else {
if (vmDidThrowAnEception) {
String output = "VM threw an unexpected exception: " + e.toString();
logger.info(output, e);
results.add(output);
return results;
}
this.trace = program.getTrace();
logger.info("--------- POST --------");
/* 5. Assert Post values */
for (RskAddress addr : testCase.getPost().keySet()) {
AccountState accountState = testCase.getPost().get(addr);
long expectedNonce = accountState.getNonceLong();
Coin expectedBalance = accountState.getBalance();
byte[] expectedCode = accountState.getCode();
boolean accountExist = (null != repository.getAccountState(addr));
if (!accountExist) {
String output = String.format("The expected account does not exist. key: [ %s ]", addr);
logger.info(output);
results.add(output);
continue;
}
long actualNonce = repository.getNonce(addr).longValue();
Coin actualBalance = repository.getBalance(addr);
byte[] actualCode = repository.getCode(addr);
if (actualCode == null)
actualCode = "".getBytes();
if (expectedNonce != actualNonce) {
String output = String.format("The nonce result is different. key: [ %s ], expectedNonce: [ %d ] is actualNonce: [ %d ] ", addr, expectedNonce, actualNonce);
logger.info(output);
results.add(output);
}
if (!expectedBalance.equals(actualBalance)) {
String output = String.format("The balance result is different. key: [ %s ], expectedBalance: [ %s ] is actualBalance: [ %s ] ", addr, expectedBalance.toString(), actualBalance.toString());
logger.info(output);
results.add(output);
}
if (!Arrays.equals(expectedCode, actualCode)) {
String output = String.format("The code result is different. account: [ %s ], expectedCode: [ %s ] is actualCode: [ %s ] ", addr, Hex.toHexString(expectedCode), Hex.toHexString(actualCode));
logger.info(output);
results.add(output);
}
// assert storage
Map<DataWord, DataWord> storage = accountState.getStorage();
for (DataWord storageKey : storage.keySet()) {
byte[] expectedStValue = storage.get(storageKey).getData();
ContractDetails contractDetails = program.getStorage().getContractDetails(accountState.getAddress());
if (contractDetails == null) {
String output = String.format("Storage raw doesn't exist: key [ %s ], expectedValue: [ %s ]", Hex.toHexString(storageKey.getData()), Hex.toHexString(expectedStValue));
logger.info(output);
results.add(output);
continue;
}
Map<DataWord, DataWord> testStorage = contractDetails.getStorage();
DataWord actualValue = testStorage.get(new DataWord(storageKey.getData()));
if (actualValue == null || !Arrays.equals(expectedStValue, actualValue.getData())) {
String output = String.format("Storage value different: key [ %s ], expectedValue: [ %s ], actualValue: [ %s ]", Hex.toHexString(storageKey.getData()), Hex.toHexString(expectedStValue), actualValue == null ? "" : Hex.toHexString(actualValue.getNoLeadZeroesData()));
logger.info(output);
results.add(output);
}
}
/* asset logs */
List<LogInfo> logResult = program.getResult().getLogInfoList();
Iterator<LogInfo> postLogs = logs.getIterator();
int i = 0;
while (postLogs.hasNext()) {
LogInfo expectedLogInfo = postLogs.next();
LogInfo foundLogInfo = null;
if (logResult.size() > i) {
foundLogInfo = logResult.get(i);
}
if (foundLogInfo == null) {
String output = String.format("Expected log [ %s ]", expectedLogInfo.toString());
logger.info(output);
results.add(output);
} else {
if (!Arrays.equals(expectedLogInfo.getAddress(), foundLogInfo.getAddress())) {
String output = String.format("Expected address [ %s ], found [ %s ]", Hex.toHexString(expectedLogInfo.getAddress()), Hex.toHexString(foundLogInfo.getAddress()));
logger.info(output);
results.add(output);
}
if (!Arrays.equals(expectedLogInfo.getData(), foundLogInfo.getData())) {
String output = String.format("Expected data [ %s ], found [ %s ]", Hex.toHexString(expectedLogInfo.getData()), Hex.toHexString(foundLogInfo.getData()));
logger.info(output);
results.add(output);
}
if (!expectedLogInfo.getBloom().equals(foundLogInfo.getBloom())) {
String output = String.format("Expected bloom [ %s ], found [ %s ]", Hex.toHexString(expectedLogInfo.getBloom().getData()), Hex.toHexString(foundLogInfo.getBloom().getData()));
logger.info(output);
results.add(output);
}
if (expectedLogInfo.getTopics().size() != foundLogInfo.getTopics().size()) {
String output = String.format("Expected number of topics [ %d ], found [ %d ]", expectedLogInfo.getTopics().size(), foundLogInfo.getTopics().size());
logger.info(output);
results.add(output);
} else {
int j = 0;
for (DataWord topic : expectedLogInfo.getTopics()) {
byte[] foundTopic = foundLogInfo.getTopics().get(j).getData();
if (!Arrays.equals(topic.getData(), foundTopic)) {
String output = String.format("Expected topic [ %s ], found [ %s ]", Hex.toHexString(topic.getData()), Hex.toHexString(foundTopic));
logger.info(output);
results.add(output);
}
++j;
}
}
}
++i;
}
}
// TODO: assert that you have no extra accounts in the repository
// TODO: -> basically the deleted by suicide should be deleted
// TODO: -> and no unexpected created
List<org.ethereum.vm.CallCreate> resultCallCreates = program.getResult().getCallCreateList();
// assert call creates
for (int i = 0; i < testCase.getCallCreateList().size(); ++i) {
org.ethereum.vm.CallCreate resultCallCreate = null;
if (resultCallCreates != null && resultCallCreates.size() > i) {
resultCallCreate = resultCallCreates.get(i);
}
CallCreate expectedCallCreate = testCase.getCallCreateList().get(i);
if (resultCallCreate == null && expectedCallCreate != null) {
String output = String.format("Missing call/create invoke: to: [ %s ], data: [ %s ], gas: [ %s ], value: [ %s ]", Hex.toHexString(expectedCallCreate.getDestination()), Hex.toHexString(expectedCallCreate.getData()), Long.toHexString(expectedCallCreate.getGasLimit()), Hex.toHexString(expectedCallCreate.getValue()));
logger.info(output);
results.add(output);
continue;
}
boolean assertDestination = Arrays.equals(expectedCallCreate.getDestination(), resultCallCreate.getDestination());
if (!assertDestination) {
String output = String.format("Call/Create destination is different. Expected: [ %s ], result: [ %s ]", Hex.toHexString(expectedCallCreate.getDestination()), Hex.toHexString(resultCallCreate.getDestination()));
logger.info(output);
results.add(output);
}
boolean assertData = Arrays.equals(expectedCallCreate.getData(), resultCallCreate.getData());
if (!assertData) {
String output = String.format("Call/Create data is different. Expected: [ %s ], result: [ %s ]", Hex.toHexString(expectedCallCreate.getData()), Hex.toHexString(resultCallCreate.getData()));
logger.info(output);
results.add(output);
}
boolean assertGasLimit = expectedCallCreate.getGasLimit() == resultCallCreate.getGasLimit();
if (!assertGasLimit) {
String output = String.format("Call/Create gasLimit is different. Expected: [ %s ], result: [ %s ]", Long.toHexString(expectedCallCreate.getGasLimit()), Long.toHexString(resultCallCreate.getGasLimit()));
logger.info(output);
results.add(output);
}
boolean assertValue = Arrays.equals(expectedCallCreate.getValue(), resultCallCreate.getValue());
if (!assertValue) {
String output = String.format("Call/Create value is different. Expected: [ %s ], result: [ %s ]", Hex.toHexString(expectedCallCreate.getValue()), Hex.toHexString(resultCallCreate.getValue()));
logger.info(output);
results.add(output);
}
}
// assert out
byte[] expectedHReturn = testCase.getOut();
byte[] actualHReturn = EMPTY_BYTE_ARRAY;
if (program.getResult().getHReturn() != null) {
actualHReturn = program.getResult().getHReturn();
}
if (!Arrays.equals(expectedHReturn, actualHReturn)) {
String output = String.format("HReturn is different. Expected hReturn: [ %s ], actual hReturn: [ %s ]", Hex.toHexString(expectedHReturn), Hex.toHexString(actualHReturn));
logger.info(output);
results.add(output);
}
// assert gas
BigInteger expectedGas = new BigInteger(1, testCase.getGas());
BigInteger actualGas = new BigInteger(1, gas).subtract(BigInteger.valueOf(program.getResult().getGasUsed()));
if (validateGasUsed)
if (!expectedGas.equals(actualGas)) {
String output = String.format("Gas remaining is different. Expected gas remaining: [ %s ], actual gas remaining: [ %s ]", expectedGas.toString(), actualGas.toString());
logger.info(output);
results.add(output);
}
/*
* end of if(testCase.getPost().size() == 0)
*/
}
return results;
} finally {
// repository.close();
}
}
Aggregations