package org.bdware.sc;

import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.MalformedJsonException;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.Configurator;
import org.bdware.analysis.BasicBlock;
import org.bdware.analysis.CFGraph;
import org.bdware.analysis.FrontCF;
import org.bdware.analysis.dynamic.NaiveDynamicTaintAnalysis;
import org.bdware.analysis.dynamic.TracedFile;
import org.bdware.analysis.example.MultiSourceTaintAnalysis;
import org.bdware.analysis.gas.Evaluates;
import org.bdware.analysis.gas.PPCount;
import org.bdware.analysis.taint.TaintBB;
import org.bdware.analysis.taint.TaintCFG;
import org.bdware.analysis.taint.TaintResult;
import org.bdware.sc.ContractResult.Status;
import org.bdware.sc.bean.*;
import org.bdware.sc.boundry.JavaScriptEntry;
import org.bdware.sc.boundry.Resources;
import org.bdware.sc.boundry.utils.RocksDBUtil;
import org.bdware.sc.boundry.utils.UtilRegistry;
import org.bdware.sc.compiler.YJSCompiler;
import org.bdware.sc.conn.ByteUtil;
import org.bdware.sc.conn.ServiceServer;
import org.bdware.sc.conn.SocketGet;
import org.bdware.sc.engine.DesktopEngine;
import org.bdware.sc.engine.JSONTool;
import org.bdware.sc.engine.hook.*;
import org.bdware.sc.handler.ContractHandler;
import org.bdware.sc.index.TimeSerialIndex;
import org.bdware.sc.node.*;
import org.bdware.sc.server.DoipClusterServer;
import org.bdware.sc.trace.ProgramPointCounter;
import org.bdware.sc.util.FileUtil;
import org.bdware.sc.util.HashUtil;
import org.bdware.sc.util.JsonUtil;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;

import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import java.io.*;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.ZipFile;

public class ContractProcess {
    private static final byte[] ZIP_HEADER_1 = new byte[]{80, 75, 3, 4};
    private static final byte[] ZIP_HEADER_2 = new byte[]{80, 75, 5, 6};
    private static final org.apache.logging.log4j.Logger LOGGER =
            org.apache.logging.log4j.LogManager.getLogger(ContractProcess.class);
    public static ContractProcess instance;

    public final String cmi;
    public final ContractHandler handler;
    private final Set<String> cachedRequests = new HashSet<>();
    public ServiceServer server;
    public DesktopEngine engine;
    String dbPath;
    String dir;
    Contract contract;
    ProjectConfig projectConfig;
    ContractNode cn;
    DumpTask dt;
    Map<String, String> isOpen = new HashMap<>();
    long gasLimit = 0;
    Map<String, String> logDetails = new HashMap<>();
    String memorySet; // from manifest
    HashMap<String, CFGraph> CFGmap = new HashMap<>();
    HashMap<String, Long> ppCountMap = new HashMap<>();
    List<String> function = new ArrayList<>();
    private TimeSerialIndex logIndex;
    private RocksDBUtil edion;
    private String pid;

    public ContractProcess(int port, String cmi) {
        handler = new ContractHandler(this);
        this.server = new ServiceServer(handler, port);
        this.cmi = cmi;
    }

    public static void main(String[] args) {
        int port = 1616;
        String cmi = "";
        InputStream pidInput = System.in;
        for (String arg : args) {
            if (arg.startsWith("-port")) {
                String portStr = arg.substring(6);
                if (portStr.replaceAll("\\d+", "").isEmpty()) {
                    port = Integer.parseInt(portStr);
                }
            } else if (arg.startsWith("-cmi")) {
                cmi = arg.substring(5);
            } else if (arg.startsWith("-debug")) {
                LOGGER.info("log level: debug");
                Configurator.setRootLevel(Level.DEBUG);
            } else if (arg.startsWith("-disablePID")) {
                pidInput = new ByteArrayInputStream("CP PID:-1".getBytes(StandardCharsets.UTF_8));
            }
        }
        Scanner sc = new Scanner(pidInput);
        for (String str; sc.hasNextLine(); ) {
            str = sc.nextLine();
            LOGGER.info("[CP From STDIN] " + str);
            if (str.contains("CP PID:")) {
                int pid = Integer.parseInt(str.replace("CP PID:", ""));
                System.setProperty("io.netty.processId", pid + "");
                LOGGER.info("[CP SET PID DONE] " + str);
                break;
            }
        }
        LOGGER.info("[Create CP]");
        instance = new ContractProcess(port, cmi);
    }

    public static boolean isArchiveFile(File file) {
        if (null == file) {
            return false;
        }

        if (file.isDirectory()) {
            return false;
        }

        boolean isArchive = false;
        try (InputStream input = new FileInputStream(file)) {
            byte[] buffer = new byte[4];
            int length = input.read(buffer, 0, 4);
            if (length == 4) {
                isArchive =
                        (Arrays.equals(ZIP_HEADER_1, buffer))
                                || (Arrays.equals(ZIP_HEADER_2, buffer));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return isArchive;
    }

    public static long toByte(String size) {
        String[] unit = {"B", "KB", "MB", "GB", "TB"};

        long res;
        String[] a = size.split(" ");
        double r = Double.parseDouble(a[0]);

        if (a[1].equals(unit[1])) {
            r = Math.pow(1024, 1);
        } else if (a[1].equals(unit[2])) {
            r = Math.pow(1024, 2);
        } else if (a[1].equals(unit[3])) {
            r = Math.pow(1024, 3);
        } else if (a[1].equals(unit[4])) {
            r = Math.pow(1024, 4);
        }

        res = (long) r;
        return res;
    }

    public static String getContractDir() {
        if (null != instance && null != instance.cn && null != instance.cn.getContractName()) {
            return instance.cn.getContractName();
        }
        return "debug";
    }

    public String getContractName() {
        return cn.getContractName();
    }

    public String staticVerify(Contract c) {
        // ContractResult ret = new ContractResult(Status.Exception, "");
        LOGGER.info("ccccc--cccc" + JsonUtil.toJson(c) + "\n" + c.getPublicKey());
        ContractResult ret = new ContractResult(Status.Error, new JsonPrimitive(""));
        try {
            String script = c.getScriptStr();
            ContractNode cn;
            YJSCompiler compiler = new YJSCompiler();
            if (script.startsWith("/")) {
                ZipFile zf = new ZipFile(script);
                ContractZipBundle czb = compiler.compile(zf);

                cn = czb.mergeContractNode();
            } else {
                cn =
                        compiler.compile(
                                new ByteArrayInputStream(script.getBytes()), "contract_main.yjs");
            }
            DesktopEngine engine = new DesktopEngine(); // engine.loadJar(zf);
            engine.loadContract(c, cn, ret.isInsnLimit);
            Map<String, byte[]> clzs = engine.dumpClass();
            Map<String, MethodNode> methods = new HashMap<>();
            for (byte[] clz : clzs.values()) {
                ClassNode classNode = new ClassNode();
                ClassReader cr = new ClassReader(clz);
                cr.accept(classNode, ClassReader.EXPAND_FRAMES);
                for (MethodNode mn : classNode.methods) {
                    methods.put(mn.name, mn);
                }
            }
            JsonObject result = new JsonObject();
            for (FunctionNode fn : cn.getFunctions()) {
                System.out.println("[ContractManager] verify:" + fn.functionName);

                MethodNode mn = methods.get(fn.functionName);
                if (mn != null) {
                    System.out.println(
                            "[ContractManager] getMethodNode, verify:" + fn.functionName);
                    TaintResult.nLocals = mn.maxLocals;
                    TaintResult.nStack = mn.maxStack;
                    TaintCFG cfg = new TaintCFG(mn);
                    TaintResult.printer.setLabelOrder(cfg.getLabelOrder());
                    try {
                        MultiSourceTaintAnalysis analysis = new MultiSourceTaintAnalysis(cfg);
                        analysis.analysis();
                        TaintBB bb = cfg.getLastBlock();
                        if (bb != null)
                            result.addProperty(fn.functionName, bb.getResultWithTaintBit());
                        System.out.println("[ContractManager] verifyDone:" + fn.functionName);
                    } catch (Exception e) {
                        ByteArrayOutputStream bo = new ByteArrayOutputStream();
                        e.printStackTrace(new PrintStream(bo));
                        result.addProperty(fn.functionName, bo.toString());
                        e.printStackTrace();
                    }
                }
            }
            ret.status = Status.Success;
            ret.result = result;
        } catch (Exception e) {
            ret.status = Status.Exception;
            JsonObject a = new JsonObject();
            a.addProperty("info", e.getMessage());
            ret.result = a;
            e.printStackTrace();
        }
        return JsonUtil.toJson(ret);
    }

    public String getControlFlow(Contract c) {
        try {
            // String parameters = c.getOwner();
            c.setPublicKey("temporypubkey");
            long start = System.currentTimeMillis();
            ContractNode cn;
            DesktopEngine engine = new DesktopEngine(); // engine.loadJar(zf);
            YJSCompiler compiler = new YJSCompiler();
            cn = compiler.compile(new ZipFile(c.getScriptStr())).mergeContractNode();
            engine.loadContract(c, cn, false);
            Map<String, byte[]> clzs = engine.dumpClass();
            Map<String, MethodNode> methods = new HashMap<>();
            for (byte[] clz : clzs.values()) {
                ClassNode classNode = new ClassNode();
                ClassReader cr = new ClassReader(clz);
                cr.accept(classNode, ClassReader.EXPAND_FRAMES);
                for (MethodNode mn : classNode.methods) {
                    methods.put(mn.name, mn);
                }
            }
            Map<String, FrontCF> result = new HashMap<>();
            for (FunctionNode fn : cn.getFunctions()) {
                System.out.println("[ContractManager] getCFG:" + fn.functionName);
                MethodNode mn = methods.get(fn.functionName);
                if (mn != null) {
                    /*
                                        * CFGraph cfg = new CFGraph(mn) {
                                        *
                                        * @Override public BasicBlock getBasicBlock(int id) { return new
                                        * BasicBlock(id); } }; FrontCF frontCF = new FrontCF(graph); String[]
                    data =
                                        * fn.plainText().split("\n"); for (int i = 0; i <
                    graph.getBasicBlockSize();
                                        * i++) { BasicBlock bb = graph.getBasicBlockAt(i); String decompiled =
                    ""; if
                                        * (bb.lineNum - 1 < data.length && bb.lineNum > 0) { decompiled =
                                        * data[bb.lineNum - 1]; } frontCF.addBB(bb, decompiled); Set<BasicBlock>
                    suc =
                                        * graph.getSucBlocks(bb); for (BasicBlock sucBB : suc)
                    frontCF.addEdge(bb,
                                        * sucBB); }
                                        */
                    TaintResult.nLocals = mn.maxLocals;
                    TaintResult.nStack = mn.maxStack;
                    TaintCFG cfg = new TaintCFG(mn);
                    TaintResult.printer.setLabelOrder(cfg.getLabelOrder());
                    MultiSourceTaintAnalysis analysis = new MultiSourceTaintAnalysis(cfg);
                    // ByValueDependencyAnalysis
                    analysis.analysis();
                    // ControlDependencyAnalysis
                    Map<Integer, List<Integer>> map = MultiSourceTaintAnalysis.depAnalysis(cfg);
                    FrontCF frontCF = new FrontCF(cfg);
                    String[] data = fn.plainText().split("\n");
                    for (int i = 0; i < cfg.getBasicBlockSize(); i++) {
                        BasicBlock bb = cfg.getBasicBlockAt(i);
                        String decompiled = "";
                        if (bb.lineNum - 1 < data.length && bb.lineNum > 0) {
                            decompiled = data[bb.lineNum - 1];
                        }
                        List<Integer> ids = map.get(i);
                        frontCF.addBB(bb, decompiled, ids, cfg);
                        Set<BasicBlock> suc = cfg.getSucBlocks(bb);
                        for (BasicBlock sucBB : suc) frontCF.addEdge(bb, sucBB);
                    }
                    // get result
                    // TaintBB lastBlock = cfg.getLastBlock();
                    //                    if (lastBlock != null) {
                    //                        frontCF.ret = lastBlock.getResultWithTaintBit();
                    //                        // System.out.println(frontCF.ret);
                    //                        if (parameters != null && parameters != "") {
                    //                            // System.out.println(parameters);
                    //                            // frontCF.finalRet = "yes";
                    //                            Gson gson = JsonUtil;
                    //                            JsonParser jsonParser = new JsonParser();
                    //                            JsonArray jsonArray =
                    // jsonParser.parse(parameters).getAsJsonArray();
                    //                            List<String> listConstraint = new ArrayList<>();
                    //                            List<String> listResource = new ArrayList<>();
                    //                            for (JsonElement je : jsonArray) {
                    //                                Bean bean = gson.fromJson(je, Bean.class);
                    //                                switch (bean.name) {
                    //                                    case "open":
                    //                                        listConstraint.add("open");
                    //                                        break;
                    //                                    case "byValue":
                    //                                        listConstraint.add("byValue");
                    //                                        break;
                    //                                    case "control":
                    //                                        listConstraint.add("control");
                    //                                        break;
                    //                                    case "close":
                    //                                        listConstraint.add("close");
                    //                                        break;
                    //                                    case "originalData":
                    //                                        listResource.add("originalData");
                    //                                        break;
                    //                                    case "contractCall":
                    //                                        listResource.add("contractCall");
                    //                                        break;
                    //                                }
                    //                            }
                    ////                            String dep =
                    // frontCF.blocks.get(frontCF.blocks.size() - 1).blockDep;
                    ////                            if ((listConstraint.contains("open")
                    ////                                            ||
                    // listConstraint.contains("close"))
                    ////                                    && (frontCF.ret != null || dep != null))
                    ////                                frontCF.finalRet = "不通过";
                    ////                             else frontCF.finalRet = "通过";
                    //                         }
                    //                    }
                    result.put(fn.functionName, frontCF);
                }
            }
            System.out.println("Test:" + JsonUtil.toJson(result));
            long end = System.currentTimeMillis();
            System.out.println(end - start);
            return JsonUtil.toJson(result);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return "{\"status\":\"failed\"}";
    }

    // 启动时写入数据库
    private void logCode() {
        Map<String, byte[]> clzs = engine.dumpClass(); // 合约字节码
        // 将clzs中byte[]进行分Base64编码

        Map<String, String> clzs2 = new HashMap<>();
        for (String k : clzs.keySet()) {
            String v = ByteUtil.encodeBASE64(clzs.get(k));
            clzs2.put(k, v);
        }
        String code = JsonUtil.toJson(clzs2);
        Map<String, String> map1 = new HashMap<>();
        map1.put("contract-bytecode", code); // 合约字节码
        map1.put("contractID", contract.getID());
        map1.put("contractType", contract.getType().name());
        // String str = ContractManager.dbPath + ";" + contractName + ";startContract" +
        // ";" + map1;
        map1.put("operation", "startContract");
        map1.put("timestamp", System.currentTimeMillis() + "");
        writeContractDB(map1);
    }

    // TODO
    public String setDesktopPermission(String isChanged) {
        try {
            System.out.println("permission" + isChanged);
            String[] pmList = isChanged.split(",");
            String yancloud_desktop = "";
            isOpen.put(pmList[0], pmList[1]);
            yancloud_desktop += UtilRegistry.getInitStr(pmList[0], pmList[1].equals("open"));
            engine.getNashornEngine()
                    .getContext()
                    .setAttribute(
                            ScriptEngine.FILENAME, yancloud_desktop, ScriptContext.ENGINE_SCOPE);
            engine.getNashornEngine().eval(yancloud_desktop);
        } catch (ScriptException e) {
            e.printStackTrace();
        }
        return "success";
    }

    public String getMemorySet() {
        if (null == memorySet) {
            return "";
        }
        return this.memorySet;
    }

    public String getLogType(String funName) {
        return logDetails.get(funName);
    }

    // 判断是否满足Oracle和Contact的执行要求
    public String verifyOracleAndContractPermission(Contract contract) {
        // 权限校验 如果是Oracle 启动方式只能是Sole 否则报错
        if (cn.getYjsType() == YjsType.Oracle && contract.getType() != ContractExecType.Sole && contract.getType() != ContractExecType.Sharding) {
            LOGGER.info("Oracle only support Sole ContractType!");
            return JsonUtil.toJson(
                    new ContractResult(
                            Status.Error,
                            new JsonPrimitive("Oracle only support Sole ContractType!")));
        }
        // 权限校验 如果是contract 申请了MySQL等权限 报错
        if (cn.getYjsType() == YjsType.Contract) {
            for (Permission per : cn.getPermission()) {
                if (per == Permission.SQL
                        || per == Permission.Http
                        || per == Permission.RocksDB
                        || per == Permission.MongoDB) {
                    LOGGER.debug("Contract can not have permissions of IO!");
                    return JsonUtil.toJson(
                            new ContractResult(
                                    Status.Error,
                                    new JsonPrimitive("Contract can not have permissions of IO|")));
                }
            }
        }
        return "";
    }

    public String setMembers(List<String> members) {
        JavaScriptEntry.members = members;
        if (members != null)
            return members.size() + "";
        else return "0";
    }

    public String setContractBundle(Contract contract) {
        try {
            //            long start = System.currentTimeMillis();
            //            long start0 = start;
            this.contract = contract;
            JavaScriptEntry.random = new Random();
            JavaScriptEntry.invokeID = 0L;
            JavaScriptEntry.random.setSeed(Integer.parseInt(contract.getID()));
            JavaScriptEntry.numOfCopies = this.contract.getNumOfCopies();
            JavaScriptEntry.shardingID =
                    this.contract.getShardingId(); // 设置javaScriptEntry中的shardingID
            // JavaScriptEntry
            String zipPath = contract.getScriptStr();
            if (isArchiveFile(new File(zipPath))) {
                ZipFile zf = new ZipFile(zipPath);
                ContractZipBundle zipBundle = new YJSCompiler().compile(zf);
                cn = zipBundle.mergeContractNode();
                // check functionNodes
                List<FunctionNode> functionNodes = cn.getFunctions();
                injectHandlers();

                this.contract.setYjsType(cn.getYjsType());
                memorySet = cn.memorySet;
                this.contract.sourcePath = zipBundle.getManifest().sourcePath;

                LOGGER.debug(
                        "check sourcePath\n\tin-contract="
                                + this.contract.sourcePath
                                + "\n\tin-manifest="
                                + zipBundle.getManifest().sourcePath);
                // zhanghongwei

                /*                if (ret.getManifest().getInsnLimit() != 0) {
                    gasLimit=ret.getManifest().getInsnLimit();
                    isInsnLimit = true;
                }*/
                String ver = verifyOracleAndContractPermission(contract);
                if (!ver.isEmpty()) {
                    return ver;
                }

                for (Permission per : cn.getPermission()) {
                    isOpen.put(per.name(), "open");
                }

                handleLog();

                // System.out.println("[ret.getManifest().getInsnLimit()]" +
                // ret.getManifest().getInsnLimit());

                engine = new DesktopEngine(zipBundle.getManifest(), zipPath, contract);
                engine.loadJar(zf);
                engine.registerResource(new Resources(zf, engine.getClassLoad()));
                ContractResult result = engine.loadContract(contract, cn, cn.getInstrumentBranch());
                JsonObject jo = new JsonObject();
                ContractResult onCreate = invokeOnCreate(contract.getCreateParam());
                jo.add("onCreate", JsonUtil.parseObject(onCreate));
                jo.add("loadContract", JsonUtil.parseObject(result));
                jo.addProperty("status", result.status.merge(onCreate.status).toString());
                LOGGER.debug("result: " + jo.toString());

                // doipModule的话，拉起DoipServer服务端口
                if(cn.getYjsType() == YjsType.DoipModule) {
                    // 只有一台机器去更新Router中的repoInfo就可以了
                    if(JavaScriptEntry.shardingID == 0) {
                        DoipClusterServer server = DoipClusterServer.getDOOPServerInstance();
                        server.updateRepoInfo(contract, cn);
                    }
                    invokeOnStartingDoipServer(cn, contract.getCreateParam());
                }

                return jo.toString();
            } else {
                contract.setScript(FileUtil.getFileContent(zipPath));
                return setContract(contract);
            }

        } catch (MalformedJsonException | JsonSyntaxException e) {
            return JsonUtil.toJson(
                    new ContractResult(
                            Status.Error,
                            new JsonPrimitive("parse manifest.json error, not json format!")));
        } catch (Exception e) {
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(bo));
            return JsonUtil.toJson(
                    new ContractResult(Status.Error, new JsonPrimitive(bo.toString())));
        }
    }

    private void injectHandlers() {
        // 正式启动
        if (!this.contract.isDebug()) {
            // this.engine.getResources().loadAsString("/maskConfig.json");
            for (FunctionNode fun : cn.getFunctions()) {

                if (fun.isExport()) {
                    // System.out.println("isExport");
                    fun.appendBeforeInvokeHandler(new MockTemplateHandler());

                }
            }
        }
        for (FunctionNode fun : cn.getFunctions()) {
            if (fun.isConfidential()) {
                fun.appendBeforeInvokeHandler(new ConfidentialHandler(fun));
            }
            ArgSchemaHandler argSchemaHandler = createHandlerIfExist(fun, fun.annotations, ArgSchemaHandler.class);
            if (argSchemaHandler != null) {
                fun.appendBeforeInvokeHandler(argSchemaHandler);
            }

            if (fun.isDoipOperation()) {
                fun.appendBeforeInvokeHandler(DOOPHandler.createDOOPHandler());
            }

            if (fun.isExport()) {
                //if(fun.annotations...)
                AccessHandler accessHandler = createHandlerIfExist(fun,fun.annotations,AccessHandler.class);
                if (accessHandler != null) {
                    fun.appendBeforeInvokeHandler(accessHandler);
                }
                fun.appendAfterInvokeHandler(new ObjToJsonHandler());
                // fun.appendBeforeInvokeHandler(new ReadMeHandler());
                // Mask是用于返回真正结果之后，做一些偏移，以保护数据隐私。
                // if (fun.isMask()) {

                // String maskConfig =
                //        engine.getResources().loadAsString("/maskConfig.json");
                // System.out.println("injectMask"+maskConfig);
                // System.out.println("injectMask"+this.contract.Mask);
                fun.appendAfterInvokeHandler(new MaskHandler());
                // }
            }
            if (fun.isHomomorphicEncrypt()) {
                LOGGER.info("injectHandlers--------------------------------1");
                fun.appendAfterInvokeHandler(new HomomorphicEncryptHandler(fun));
            }
            if (fun.isHomomorphicDecrypt()) {
                fun.appendAfterInvokeHandler(new HomomorphicDecryptHandler(fun));
            }

        }
    }

    <T extends AnnotationHook> T createHandlerIfExist(FunctionNode function, List<AnnotationNode> annotations, Class<T> clz) {
        YJSAnnotation annotation = clz.getAnnotation(YJSAnnotation.class);
        if (annotation == null) return null;
        if (annotations == null) return null;
        for (AnnotationNode node : annotations) {
            if (annotation.name().equals(node.getType())) {
                try {
                    Method m = clz.getDeclaredMethod("fromAnnotationNode", FunctionNode.class, AnnotationNode.class);
                    T result = (T) m.invoke(null, function, node);
                    return result;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    public String changeDumpPeriod(String period) {
        System.out.println("[ContractProcess] period" + period);

        startAutoDump();
        return "success";
    }

    public String getDumpPeriod() {
        return projectConfig.getDumpPeriod();
    }

    public String setContract(Contract contract) {
        try {
            JavaScriptEntry.random = new Random();
            JavaScriptEntry.invokeID = 0L;
            JavaScriptEntry.random.setSeed(Integer.parseInt(contract.getID()));
            JavaScriptEntry.numOfCopies = contract.getNumOfCopies();
            // TODO Optimize, 4 seconds takes to create an Engine.
            engine = new DesktopEngine();
            this.contract = contract;
            this.contract.sourcePath = "script_" + System.currentTimeMillis();
            YJSCompiler compiler = new YJSCompiler();
            cn = compiler.compile(contract.getScript(), null); // 这一步初始化ContractNode
            contract.setYjsType(cn.getYjsType());

            injectHandlers();
            String ver = verifyOracleAndContractPermission(contract);
            if (!ver.equals("")) {
                return ver;
            }
            handleLog();
            LOGGER.info("load script, contract:" + JsonUtil.toJson(contract.getScriptStr()));
            LOGGER.info("load cn:" + JsonUtil.toJson(cn));
            ContractResult ret =
                    engine.loadContract(contract, cn, cn.getInstrumentBranch());
            ContractResult onCreate = invokeOnCreate(contract.getCreateParam());
            JsonObject jo = new JsonObject();
            jo.add("onCreate", JsonUtil.parseObject(onCreate));
            jo.add("loadContract", JsonUtil.parseObject(ret));
            jo.addProperty("status", ret.status.merge(onCreate.status).toString());
            LOGGER.debug("result: " + jo.toString());

            // doipModule的话，拉起DoipServer服务端口
            if(cn.getYjsType() == YjsType.DoipModule) {
                // 只有一台机器去更新Router中的repoInfo就可以了
                if(JavaScriptEntry.shardingID == 0) {
                    DoipClusterServer server = DoipClusterServer.getDOOPServerInstance();
                    server.updateRepoInfo(contract, cn);
                }
                invokeOnStartingDoipServer(cn, contract.getCreateParam());
            }

            return jo.toString();
        } catch (Exception e) {
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(bo));
            return JsonUtil.toJson(
                    new ContractResult(Status.Error, new JsonPrimitive(bo.toString())));
        }
    }

    public void invokeOnStartingDoipServer(ContractNode cn, JsonElement arg) {
        ContractRequest onStartingDoipServer = new ContractRequest();
        onStartingDoipServer.setAction("invokeOnStartingDoipServer");
        if (arg == null) {
            if (engine != null && engine.getManifest() != null && engine.getManifest().createParam != null)
                arg = engine.getManifest().createParam;
            else
                arg = new JsonPrimitive("");
        }
        onStartingDoipServer.setArg(arg);
        LOGGER.debug("invoke onStartingDoipServer, param:" + onStartingDoipServer.getArg().toString());
        onStartingDoipServer.setRequester(contract.getOwner());
        if (contract.getDoipFlag() && null != contract.getDOI() && !contract.getDOI().isEmpty()) {
            onStartingDoipServer.setRequesterDOI(contract.getDOI());
        } else {
            onStartingDoipServer.setRequesterDOI("empty");
        }
        FunctionNode funNode = cn.getFunction("invokeOnStartingDoipServer");

        try {
            JsonElement onStartingDoipServerRes = invoke(onStartingDoipServer, funNode).result;
            LOGGER.info("Fetch the onStartingDoipServerRes from router successfully, the result is " + onStartingDoipServerRes);
            if(onStartingDoipServerRes.isJsonObject()) {
                JsonObject onStartingDoipServerJO = onStartingDoipServerRes.getAsJsonObject();
                if (!onStartingDoipServerJO.has("doipAddr")) {
                    throw new Exception("the doipAddr is improper");
                } else {
                    JsonElement doipAddrJE = onStartingDoipServerJO.get("doipAddr");
                    if(doipAddrJE.isJsonArray()) {
                        JsonArray doipAddrJA = doipAddrJE.getAsJsonArray();
                        for(int i = 0 ; i < doipAddrJA.size() ; i++) {
                            DoipClusterServer.startDoipServer(doipAddrJA.get(i).getAsString());
                        }
                    } else {
                        DoipClusterServer.startDoipServer(doipAddrJE.getAsString());
                    }
                }
            } else {
                throw new Exception("the onStartingDoipServerRes doesn't return the correct json result");
            }

        } catch (Exception e) {
            LOGGER.error("DoipLocalSingleton cannot starts properly, plz check the invokeOnStartingDoipServer function");
            e.printStackTrace();
        }
    }

    public void invokeOnStartingDoipServer2(JsonElement arg) {
        Object[] funcArgs = new Object[3];
        funcArgs[0] = JSONTool.convertJsonElementToMirror(arg);
        funcArgs[1] = contract.getOwner();
        if (contract.getDoipFlag() && null != contract.getDOI() && !contract.getDOI().isEmpty()) {
            funcArgs[2] = contract.getDOI();
        } else {
            funcArgs[2] = "empty";
        }
        Object result = engine.invokeFunction("invokeOnStartingDoipServer", funcArgs);
        Map<String, String> resMap = (Map<String, String>) result;
        try {
            if (!resMap.containsKey("doipAddr")) {
                throw new Exception("the doipAddr is improper");
            } else {
                DoipClusterServer.startDoipServer(resMap.get("doipAddr"));
            }
        } catch (Exception e) {
            LOGGER.error("DoipLocalSingleton cannot starts properly, plz check the invokeOnStartingDoipServer function");
            e.printStackTrace();
        }
    }

    private void handleLog() {
        for (FunctionNode fun : cn.getFunctions()) {
            StringBuilder detail = new StringBuilder();

            for (LogType type : fun.getLogTypes()) {
                switch (type) {
                    case Arg:
                        detail.append("Arg;");
                        break;
                    case Branch:
                        detail.append("Branch;");
                        break;
                    case Result:
                        detail.append("Result;");
                        break;
                    default:
                        break;
                }
            }

            if (fun.getLogToBDContract()) detail.append("bdcontract;");
            if (fun.getLogToNamedLedger()) {
                for (String str : fun.getLedgerNames()) {
                    detail.append("bdledger:").append(str).append(";");
                }
            }
            logDetails.put(fun.functionName, detail.toString());
        }
    }

    public String setDBInfo(String path) {
        dbPath = path;
        /*
         * File confDB = new File(path); if (!confDB.exists()) confDB.mkdirs();
         */
        return "success";
    }

    public long getUsedMemory(String arg) {
        Runtime r = Runtime.getRuntime();
        return r.totalMemory() - r.freeMemory();
    }

    private ContractResult invokeOnCreate(JsonElement arg) {
        long start = System.currentTimeMillis();
        logIndex = new TimeSerialIndex("./ContractDB/" + cn.getContractName() + ".index");
        LOGGER.debug("timeSerialIndex: " + (System.currentTimeMillis() - start));
        start = System.currentTimeMillis();
        edion = RocksDBUtil.loadDB("defaultLog", false);
        LOGGER.debug("create RocksDB: " + (System.currentTimeMillis() - start));
        start = System.currentTimeMillis();
        engine.redirectTracePS(new Logger(new ByteArrayOutputStream(), this));
        // startContract时写入数据库
        if (null != cn.getLogTypes() && cn.getLogTypes().contains(LogType.Code)) {
            logCode();
        }

        JavaScriptEntry.setSM2KeyPair(contract.getPublicKey(), contract.getKey());
        if (null != contract.getDOI() && !contract.getDOI().isEmpty()) {
            JavaScriptEntry.doi = contract.getDOI();
        }
        if (null != contract.getAuthInfoPersistDOI()
                && !contract.getAuthInfoPersistDOI().isEmpty()) {
            JavaScriptEntry.authInfoPersistDOI = contract.getAuthInfoPersistDOI();
        }
        JavaScriptEntry.isDebug = contract.isDebug();
        ContractRequest onCreate = new ContractRequest();
        onCreate.setAction("onCreate");
        if (arg == null) {
            if (engine != null && engine.getManifest() != null && engine.getManifest().createParam != null)
                arg = engine.getManifest().createParam;
            else
                arg = new JsonPrimitive("");
        }
        onCreate.setArg(arg);
        LOGGER.debug("invoke onCreate, param:" + onCreate.getArg().toString());
        onCreate.setRequester(contract.getOwner());
        if (contract.getDoipFlag() && null != contract.getDOI() && !contract.getDOI().isEmpty()) {
            onCreate.setRequesterDOI(contract.getDOI());
        } else {
            onCreate.setRequesterDOI("empty");
        }
        FunctionNode funNode = cn.getFunction("onCreate");
        return invoke(onCreate, funNode);
    }

    public void resetContractName(String name) {
        if (name != null)
            cn.resetContractName(name);
    }

    private ContractResult invokeOnRecover(JsonElement arg) {
        ContractRequest onRecover = new ContractRequest();
        onRecover.setAction("onRecover");
        if (arg == null)
            onRecover.setArg("null");
        else
            onRecover.setArg(arg);
        onRecover.setRequester(contract.getOwner());
        if (contract.getDoipFlag()
                && (contract.getDOI() != null)
                && (contract.getDOI().length() > 0)) {
            onRecover.setRequesterDOI(contract.getDOI());
        } else {
            onRecover.setRequesterDOI("empty");
        }
        FunctionNode funNode = cn.getFunction("onRecover");
        return invoke(onRecover, funNode);
    }

    private ContractResult invoke(ContractRequest onRecover, FunctionNode funNode) {
        if (funNode != null) {
            funNode.setIsExport(true);
            ContractResult result = engine.executeContract(onRecover);
            funNode.setIsExport(false);
            engine.getTracePS().clean();
            return result;
        }
        return new ContractResult(Status.Success, new JsonPrimitive("no funNode found"));
    }

    // public String executeBundle(ContractZipBundle czb, String arg) {
    // ContractRequest ac = null;
    // ContractResult result = null;
    // try {
    // ac = JsonUtil.fromJson(arg, ContractRequest.class);
    // } catch (Exception e) {
    // }
    // if (ac == null) {
    // result = new ContractResult(ContractResult.Status.Error, "Illegal
    // Arguments!");
    // return JsonUtil.toJson(result);
    // }
    //
    // ContractManifest cm = czb.getManifest();
    // switch (cm.getType()) {
    // case Data:
    // case Algorithm:
    // result = engine.executeContract(ac);
    // return JsonUtil.toJson(result);
    // case Application:
    // default:
    // return "todo";
    // }
    // }

    public boolean isSigRequired() {
        return cn.sigRequired;
    }

    public String requestLog(long offset, int size) {
        List<Long> hashes = logIndex.request(offset, size);
        List<Map<String, String>> jo = new ArrayList<>();

        TypeToken<Map<String, String>> token = new TypeToken<Map<String, String>>() {
        };
        for (Long hash : hashes)
            try {
                Map<String, String> obj =
                        JsonUtil.fromJson(edion.get(hash.toString()), token.getType());
                jo.add(obj);
            } catch (JsonSyntaxException e) {
                e.printStackTrace();
            }
        return JsonUtil.toJson(jo);
    }

    public String requestLast(int count) {
        List<Long> hashes = logIndex.requestLast(count);
        List<Map<String, String>> jo = new ArrayList<>();
        TypeToken<Map<String, String>> token = new TypeToken<Map<String, String>>() {
        };
        String log;
        for (Long hash : hashes)
            try {
                log = edion.get(hash.toString());
                if (null == log || 0 == log.length()) {
                    continue;
                }
                Map<String, String> obj = JsonUtil.fromJson(log, token.getType());
                if (obj == null) {
                    System.out.println(
                            "[ContractProcess] requestLast, parseJsonError:" + log + "==");
                    continue;
                }
                obj.put("hash", hash + "");
                jo.add(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
        return JsonUtil.toJson(jo);
    }

    public long logSize() {
        return logIndex.size();
    }

    public String executeFunctionWithoutLimit(String arg) {
        try {
            JsonObject body = JsonUtil.parseString(arg).getAsJsonObject();
            String funcName = body.get("funcName").getAsString();
            JsonArray arr = body.getAsJsonArray("funcArgs");
            Object[] funcArgs = new Object[arr.size()];
            for (int i = 0; i < arr.size(); i++) {
                funcArgs[i] = JSONTool.convertJsonElementToMirror(arr.get(i));
            }
            Object result = engine.invokeFunction(funcName, funcArgs);
            result = JSONTool.convertMirrorToJson(result);
            return JsonUtil.toJson(result);
        } catch (Exception e) {
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(bo));
            return bo.toString();
        }
    }

    public String executeContract(String arg) {
        // TODO
        // eventCenter.pub(new EventMsg("executeContract", arg));
        ContractRequest request;
        ContractResult result;

        try {
            request = JsonUtil.fromJson(arg, ContractRequest.class);
        } catch (Exception ignored) {
            result =
                    new ContractResult(
                            ContractResult.Status.Error, new JsonPrimitive("Illegal Arguments!"));
            return JsonUtil.toJson(result);
        }
        String reqID = request.getRequestID();
        if (cachedRequests.contains(reqID)) {
            LOGGER.info("[Hit Cache]:" + reqID);
            try {
                String cachedResult = edion.get(reqID);
                if (cachedResult != null && !cachedResult.isEmpty()) {
                    return cachedResult;
                }
            } catch (Exception ignored) {
            }
        }
        try {
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            if (request.withDynamicAnalysis) {
                ContractProcess.Logger previous = engine.getTracePS();
                engine.redirectTracePS(new Logger(bo, this));
                result = engine.executeContract(request);
                result.analysis = bo.toString();
                System.out.println(
                        "[ContractProcess] result.analysis = "
                                + result.analysis); // 动态分析bug null pointer
                // branchResult = JsonUtil.toJson(result);
                // branchTrace = result.analysis;
                engine.redirectTracePS(previous);
                dynamicAnalysis(request, result);
            } else if (request.withEvaluatesAnalysis) {
                ContractProcess.Logger previous = engine.getTracePS();

                System.out.println("[size:]" + function.size());
                System.out.println("[action index]:" + function.indexOf(request.getAction()));

                System.out.println("[InsnFeeValue]" + request.getValue());
                System.out.println("[InsnFeeLimit]" + gasLimit);
                int functionIndex = function.indexOf(request.getAction());
                if (ppCountMap == null || ppCountMap.isEmpty()) {
                    System.out.println("没有提前进行预估");
                    evaluatesAnalysis(request.getAction());
                }

                engine.redirectTracePS(
                        new ProgramPointCounter(
                                bo,
                                this,
                                gasLimit,
                                functionIndex,
                                request.getValue(),
                                0L,
                                request.getAction(),
                                ppCountMap));
                result = engine.executeContract(request);

                result.analysis = bo.toString();
                engine.redirectTracePS(previous);

            } else {
                ContractProcess.Logger previous = engine.getTracePS();
                engine.redirectTracePS(new Logger(bo, this));
                result = engine.executeContract(request);
                engine.redirectTracePS(previous);
            }

            String ret = JsonUtil.toJson(result);
            if (reqID != null && reqID.endsWith("_mul")) {
                cachedRequests.add(reqID);
                edion.put(reqID, ret);
            }
            return ret;
        } catch (Exception e) {
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(bo));
            return bo.toString();
        }
    }

    public String evaluatesAnalysis(String getFunction) {
        Map<String, byte[]> clzs = engine.dumpClass();
        Map<String, MethodNode> methods = new HashMap<>();
        for (byte[] clz : clzs.values()) {
            ClassNode classNode = new ClassNode();
            ClassReader cr = new ClassReader(clz);
            cr.accept(classNode, ClassReader.EXPAND_FRAMES);
            for (MethodNode mn : classNode.methods) {
                methods.put(mn.name, mn);
            }
        }
        int flag = 0;
        for (String s : function) {
            MethodNode mn = methods.get(s);
            if (mn != null) {
                CFGraph cfg =
                        new CFGraph(mn) {
                            @Override
                            public BasicBlock getBasicBlock(int id) {
                                return new BasicBlock(id);
                            }
                        };
                // cfg.printSelf();
                CFGmap.put(s, cfg);
                PPCount countFee = new PPCount(cfg, flag);

                BasicBlock bb = cfg.getBasicBlockAt(0);
                countFee.dfs(cfg, bb);
                // System.out.println("[ppmap]:" + PPCount.ppMap);
                // System.out.println("[PPCount.branchCount]"+PPCount.branchCount);
                Evaluates feEvaluates = new Evaluates();
                feEvaluates.getGas(PPCount.branchCount);
                feEvaluates.getInsnGas(PPCount.ppMap);

                PPCount.countFunction(s, Evaluates.map);
                ppCountMap = Evaluates.map;
                System.out.println("+++++++" + PPCount.ppMap);
                flag++;
            }
        }
        for (Map.Entry<String, Long> map : PPCount.functionSumGas.entrySet()) {
            if (map.getKey().contains(getFunction) && map.getKey().contains("true")) {
                System.out.println("[合约方法pub中条件循环为true时：]" + map.getValue());
            } else if (map.getKey().contains(getFunction) && map.getKey().contains("false")) {
                System.out.println("[合约方法pub中条件循环为false时：]" + map.getValue());
            } else if (map.getKey().contains(getFunction)) {
                System.out.println("[合约方法pub中其他语句消耗：]" + map.getValue());
            }
        }
        return PPCount.functionSumGas.toString();
    }

    public void dynamicAnalysis(ContractRequest ac, ContractResult result) {
        Map<String, byte[]> classes = engine.dumpClass();
        Map<String, MethodNode> methods = new HashMap<>();
        for (byte[] clz : classes.values()) {
            ClassNode classNode = new ClassNode();
            ClassReader cr = new ClassReader(clz);
            System.out.print("[cr:]" + cr);
            cr.accept(classNode, ClassReader.EXPAND_FRAMES);
            for (MethodNode mn : classNode.methods) {
                methods.put(mn.name, mn);
            }
        }

        MethodNode mn = methods.get(ac.getAction());
        if (mn != null) {
            TaintResult.nLocals = mn.maxLocals;
            TaintResult.nStack = mn.maxStack;
            TaintCFG cfg = new TaintCFG(mn);
            String trace = result.analysis;
            System.out.println("TraceFile:\n" + trace);
            System.out.println("TraceFile结束=====================================");
            System.out.println("打印cfg图=====================================");
            cfg.printSelf();
            System.out.println("打印cfg图=====================================");
            TracedFile tf = new TracedFile(new ByteArrayInputStream(trace.getBytes()));
            TaintResult.printer.setLabelOrder(cfg.getLabelOrder());
            NaiveDynamicTaintAnalysis analysis = new NaiveDynamicTaintAnalysis(cfg, tf);
            analysis.analysis();

            TaintBB bb = cfg.getLastBlock();
            result.analysis = bb.getResultWithTaintBit();
            System.out.println(
                    "[ContractProcess] dynamically verify: "
                            + ac.getAction()
                            + "-->"
                            + result.analysis);
        }
    }

    public String registerMangerPort(String arg) {
        JavaScriptEntry.get = new SocketGet("127.0.0.1", Integer.parseInt(arg));
        return "success";
    }

    public void subscribe(String functionName) {
        cn.getFunction(functionName).setHandler(true);
        cn.getFunction("_preSub").setHandler(true);
    }

    public void unSubscribe(String functionName) {
        cn.getFunction(functionName).setHandler(false);
    }

    public boolean checkSub() {
        return !JavaScriptEntry.topic_handlers.isEmpty();
    }

    public void beforeSuicide() {
    }

    public String redo(String path) {
        return engine.syncUtil.redo(path);
    }

    public String getMemoryDump(String path) {
        return engine.syncUtil.dumpMemory(path, contract.getStateful());
    }

    public String getJSERandomCur() {
        return JavaScriptEntry.random.toString();
    }

    public String loadMemoryDump(String path) {
        String str = engine.syncUtil.loadMemoryDump(path, contract.getStateful());
        invokeOnRecover(contract.getCreateParam());
        return str;
    }

    // 查看当前合约的权限
    public String showPermission() {
        return JsonUtil.toJson(isOpen);
    }

    // 查看合约进程占用内存大小
    public String getStorage() {
        Runtime run = Runtime.getRuntime();
        long mem = run.totalMemory() - run.freeMemory();
        System.out.println("[ContractProcess] getStorage = " + ByteUtil.byteTo(mem));
        return ByteUtil.byteTo(mem);
    }

    /*
     * 将合约操作计入该合约的本地数据库中
     */
    public void writeContractDB(Map<String, String> data) {
        String path = dbPath;
        if (path == null) {
            //            return "nopath";
            return;
        }
        try {
            String result = JsonUtil.toJson(data);
            long hash = HashUtil.hashStr2Long(result);
            logIndex.index(hash);
            edion.put(String.valueOf(hash), result);
        } catch (Exception e) {
            e.printStackTrace();
            //            return "failed";
        }
        //        return "success";
    }

    @Override
    protected void finalize() {
    }

    public String getDeclaredEvents() {
        return JsonUtil.toJson(cn.events);
    }

    public String getAnnotations() {
        return JsonUtil.toJson(cn.annotations);
    }

    public String getExportedFunctions() {
        List<FunctionDesp> ret = new ArrayList<>();
        for (FunctionNode fn : cn.getFunctions()) {
            if (fn.isExport() && !fn.functionName.equals("onCreate")) {
                function.add(fn.functionName);
                FunctionDesp desp =
                        new FunctionDesp(fn.functionName, fn.annotations, fn.getRouteInfo(), fn.getJoinInfo(), fn.isView());
                ret.add(desp);
            }
        }
        return JsonUtil.toJson(ret);
    }

    public Contract getContract() {
        return contract;
    }

    public String getPID() {
        return this.pid;
    }

    public void setPID(String pid) {
        this.pid = pid;
    }

    public String startAutoDump() {
        String dumpPeriod = projectConfig.getDumpPeriod();
        System.out.println(
                "[ContractProcess] startAutoDump : "
                        + cn.getContractName()
                        + " period = "
                        + dumpPeriod);
        String status = "startAutoDump status 0";
        if (null != dt) {
            if (null == dumpPeriod || dumpPeriod.isEmpty()) {
                dt.cancel();
                status = "startAutoDump status 1";
            } else {
                dt.cancel();
                Timer timer = new Timer();
                dt = new DumpTask();
                timer.schedule(dt, new Date(), Long.parseLong(dumpPeriod));
                status = "startAutoDump status 2";
            }
        } else {
            if (dumpPeriod != null && !dumpPeriod.equals("")) {
                Timer timer = new Timer();
                dt = new DumpTask();
                timer.schedule(dt, new Date(), Long.parseLong(dumpPeriod));
                status = "startAutoDump status 3";
            }
        }

        LOGGER.debug("[ContractProcess] status : " + status);
        return status;
    }

    public String getDir() {
        return this.dir;
    }

    public void setDir(String s) {
        this.dir = s + "/ADSPDir/" + contract.getID() + "/";
        engine.syncUtil.setDir(dir);
    }

    public boolean isDebug() {
        return contract.isDebug();
    }

    public String getCachedTransRecords(String startSeq) {
        int start = Integer.parseInt(startSeq);

        if (engine.syncUtil != null && engine.syncUtil.transRecordUtil != null) {
            return engine.syncUtil.transRecordUtil.getCachedTransRecords(start);
        }

        return "";
    }

    public void clearSyncFiles(String arg) {
        if (engine.syncUtil == null) {
            LOGGER.debug("syncUtil is null, can not clear all sync files!");
            return;
        }
        engine.syncUtil.clearAllFiles();
    }

    public String getStateful() {
        return contract.getStateful() + "";
    }

    public void startSync() {
        // engine.syncUtil.setContractID(contract.getID());
        engine.syncUtil.setStartFlag(true);
    }

    public void setCRFile(String fileName) {
        engine.syncUtil.setCRFile(fileName);
    }

    public void stopSync() {
        engine.syncUtil.setStartFlag(false);
    }

    public String changeDebugFlag(Boolean b) {
        contract.setDebug(b);
        JavaScriptEntry.isDebug = b;
        return "success";
    }

    public String parseYpkPermissions(String ypkPath) {
        YJSCompiler compiler = new YJSCompiler();
        try {
            ContractZipBundle bundle = compiler.compile(new ZipFile(ypkPath));
            ContractNode contractNode = bundle.mergeContractNode();
            return JsonUtil.toJson(contractNode.getPermission());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "[]";
    }

    public ProjectConfig getProjectConfig() {
        return projectConfig;
    }

    public void setProjectConfig(String args) {
        projectConfig = JsonUtil.fromJson(args, ProjectConfig.class);
        String period = projectConfig.getDumpPeriod();

        if (period != null && period.length() > 0) {
            changeDumpPeriod(period);
        }
        //        System.out.println("ContractProcessMask");
        //        JsonObject argsJS = JsonParser.parseString(args).getAsJsonObject();
        //        String contractID = "";
        //        String operation = "";
        //        JsonElement mask = JsonParser.parseString("");
        //        if (argsJS.has("contractID") && argsJS.has("operation") && argsJS.has("maskInfo"))
        // {
        //            contractID = argsJS.get("contractID").getAsString();
        //            System.out.println(contractID);
        //            operation = argsJS.get("operation").getAsString();
        //            System.out.println(operation);
        //            mask = argsJS.get("maskInfo");
        //            System.out.println("mask" + mask);
        //            this.contract.setMask(operation, mask);
        //        }
    }

    public String getDependentContracts() {
        return JsonUtil.toJson(cn.getDependentContracts());
    }

    public static class Logger extends PrintStream {

        ContractProcess cp;
        OutputStream out;

        public Logger(OutputStream out, ContractProcess cp) {
            super(out);
            this.cp = cp;
            this.out = out;
        }

        public Logger(OutputStream out) {
            super(out);
            cp = null;
            this.out = out;
        }

        public Logger(PrintStream err) {
            super(err);
            this.out = null;
        }

        public ContractProcess getCp() {
            return cp;
        }

        public void writeToDB(Map<String, String> data) {
            if (cp != null) {
                cp.writeContractDB(data);
            }
        }
        // TODO we do not need outputTrace?
        // public void outputTrace(String operation) {
        // cp.writeContractDB(operation, this.toString());
        // }

        public String getOutputStr() {
            return out.toString();
        }

        public void clean() {
            if (out instanceof ByteArrayOutputStream) {
                out = new ByteArrayOutputStream();
            }
        }
    }

    public static class Bean {
        public String name;
        public String value;
    }

    private class DumpTask extends TimerTask {
        @Override
        public void run() {
            System.out.println(
                    "[ContractProcess  DumpTask] auto dump period : "
                            + projectConfig.getDumpPeriod());
            File file1 = new File(dir);
            File file2 = new File(file1.getParent());
            String dir2 = file2.getParent() + "/memory/";
            System.out.println("[ContractProcess] auto dump dir " + dir2);
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd.HH_mm_ss"); // 设置日期格式
            File f = new File(dir2 + cn.getContractName(), df.format(new Date()));
            System.out.println("[ContractProcess] dump file " + f.getAbsolutePath());

            File parent = f.getParentFile();
            if (!parent.exists()) {
                parent.mkdirs();
            }
            getMemoryDump(f.getAbsolutePath());
        }
    }
}
