Search in sources :

Example 1 with VM

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;
    }
}
Also used : DataWord(org.ethereum.vm.DataWord) AccountState(org.ethereum.core.AccountState) ErrorException(io.nuls.contract.vm.exception.ErrorException) Result(io.nuls.contract.vm.Result) ClassCode(io.nuls.contract.vm.code.ClassCode) VM(io.nuls.contract.vm.VM) MethodCode(io.nuls.contract.vm.code.MethodCode) ObjectRef(io.nuls.contract.vm.ObjectRef) ErrorException(io.nuls.contract.vm.exception.ErrorException)

Aggregations

ObjectRef (io.nuls.contract.vm.ObjectRef)1 Result (io.nuls.contract.vm.Result)1 VM (io.nuls.contract.vm.VM)1 ClassCode (io.nuls.contract.vm.code.ClassCode)1 MethodCode (io.nuls.contract.vm.code.MethodCode)1 ErrorException (io.nuls.contract.vm.exception.ErrorException)1 AccountState (org.ethereum.core.AccountState)1 DataWord (org.ethereum.vm.DataWord)1