use of io.nuls.contract.vm.VM in project nuls by nuls-io.
the class ProgramExecutorImpl method execute.
private ProgramResult execute(ProgramInvoke programInvoke) {
if (programInvoke.getPrice() < 1) {
return revert("gas price must be greater than zero");
}
if (programInvoke.getGasLimit() < 1) {
return revert("gas must be greater than zero");
}
long maxGas;
if (programInvoke.isViewMethod()) {
maxGas = VMContext.getCustomMaxViewGasLimit();
} else {
maxGas = VM.MAX_GAS;
}
if (programInvoke.getGasLimit() > maxGas) {
return revert("gas must be less than " + maxGas);
}
if (programInvoke.getValue().compareTo(BigInteger.ZERO) < 0) {
return revert("value can't be less than zero");
}
blockNumber = programInvoke.getNumber();
logTime("start");
try {
Map<String, ClassCode> classCodes;
if (programInvoke.isCreate()) {
if (programInvoke.getData() == null) {
return revert("contract code can't be null");
}
classCodes = ClassCodeLoader.loadJarCache(programInvoke.getData());
logTime("load new code");
ProgramChecker.check(classCodes);
logTime("check code");
AccountState accountState = repository.getAccountState(programInvoke.getContractAddress());
if (accountState != null) {
return revert(String.format("contract[%s] already exists", programInvoke.getAddress()));
}
accountState = repository.createAccount(programInvoke.getContractAddress(), programInvoke.getSender());
logTime("new account state");
repository.saveCode(programInvoke.getContractAddress(), programInvoke.getData());
logTime("save code");
} else {
if ("<init>".equals(programInvoke.getMethodName())) {
return revert("can't invoke <init> method");
}
AccountState accountState = repository.getAccountState(programInvoke.getContractAddress());
if (accountState == null) {
return revert(String.format("contract[%s] does not exist", programInvoke.getAddress()));
}
logTime("load account state");
if (accountState.getNonce().compareTo(BigInteger.ZERO) <= 0) {
return revert(String.format("contract[%s] has stopped", programInvoke.getAddress()));
}
byte[] codes = repository.getCode(programInvoke.getContractAddress());
classCodes = ClassCodeLoader.loadJarCache(codes);
logTime("load code");
}
VM vm = VMFactory.createVM();
logTime("load vm");
vm.heap.loadClassCodes(classCodes);
vm.methodArea.loadClassCodes(classCodes);
logTime("load classes");
ClassCode contractClassCode = getContractClassCode(classCodes);
String methodDesc = ProgramDescriptors.parseDesc(programInvoke.getMethodDesc());
MethodCode methodCode = vm.methodArea.loadMethod(contractClassCode.name, programInvoke.getMethodName(), methodDesc);
if (methodCode == null) {
return revert(String.format("can't find method %s%s", programInvoke.getMethodName(), programInvoke.getMethodDesc() == null ? "" : programInvoke.getMethodDesc()));
}
if (!methodCode.isPublic) {
return revert("can only invoke public method");
}
if (!methodCode.hasPayableAnnotation() && programInvoke.getValue().compareTo(BigInteger.ZERO) > 0) {
return revert("not a payable method");
}
if (methodCode.argsVariableType.size() != programInvoke.getArgs().length) {
return revert(String.format("require %s parameters in method [%s%s]", methodCode.argsVariableType.size(), methodCode.name, methodCode.normalDesc));
}
logTime("load method");
ObjectRef objectRef;
if (programInvoke.isCreate()) {
objectRef = vm.heap.newContract(programInvoke.getContractAddress(), contractClassCode, repository);
} else {
objectRef = vm.heap.loadContract(programInvoke.getContractAddress(), contractClassCode, repository);
}
logTime("load contract ref");
if (programInvoke.getValue().compareTo(BigInteger.ZERO) > 0) {
getAccount(programInvoke.getContractAddress()).addBalance(programInvoke.getValue());
}
vm.setProgramExecutor(this);
vm.setRepository(repository);
vm.setGas(programInvoke.getGasLimit());
vm.addGasUsed(programInvoke.getData() == null ? 0 : programInvoke.getData().length);
logTime("load end");
vm.run(objectRef, methodCode, vmContext, programInvoke);
logTime("run");
ProgramResult programResult = new ProgramResult();
programResult.setGasUsed(vm.getGasUsed());
Result vmResult = vm.getResult();
Object resultValue = vmResult.getValue();
if (vmResult.isError() || vmResult.isException()) {
if (resultValue != null && resultValue instanceof ObjectRef) {
vm.setResult(new Result());
String error = vm.heap.runToString((ObjectRef) resultValue);
String stackTrace = vm.heap.stackTrace((ObjectRef) resultValue);
programResult.error(error);
programResult.setStackTrace(stackTrace);
} else {
programResult.error(null);
}
logTime("contract exception");
this.revert = true;
programResult.setGasUsed(vm.getGasUsed());
return programResult;
}
programResult.setTransfers(vm.getTransfers());
programResult.setEvents(vm.getEvents());
programResult.setBalance(getAccount(programInvoke.getContractAddress()).getBalance());
if (resultValue != null) {
if (resultValue instanceof ObjectRef) {
String result = vm.heap.runToString((ObjectRef) resultValue);
programResult.setResult(result);
} else {
programResult.setResult(resultValue.toString());
}
}
if (methodCode.isPublic && methodCode.hasViewAnnotation()) {
this.revert = true;
programResult.view();
programResult.setGasUsed(vm.getGasUsed());
return programResult;
}
logTime("contract return");
Map<DataWord, DataWord> contractState = vm.heap.contractState();
logTime("contract state");
for (Map.Entry<DataWord, DataWord> entry : contractState.entrySet()) {
DataWord key = entry.getKey();
DataWord value = entry.getValue();
repository.addStorageRow(programInvoke.getContractAddress(), key, value);
}
logTime("add contract state");
repository.increaseNonce(programInvoke.getContractAddress());
programResult.setNonce(repository.getNonce(programInvoke.getContractAddress()));
programResult.setGasUsed(vm.getGasUsed());
return programResult;
} catch (ErrorException e) {
this.revert = true;
// log.error("", e);
ProgramResult programResult = new ProgramResult();
programResult.setGasUsed(e.getGasUsed());
// programResult.setStackTrace(e.getStackTraceMessage());
logTime("error");
return programResult.error(e.getMessage());
} catch (Exception e) {
log.error("", e);
ProgramResult programResult = revert(e.getMessage());
// programResult.setStackTrace(ExceptionUtils.getStackTrace(e));
return programResult;
}
}
Aggregations