cp/src/main/java/org/bdware/sc/ContractProcess.java
2023-03-27 20:30:09 +08:00

1455 lines
59 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.doip.audit.EndpointConfig;
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.handler.DOOPRequestHandler;
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 DOOPRequestHandler doopRequestHandler;
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.hasDoipModule()) {
// 只有一台机器去更新Router中的repoInfo就可以了
updateRepoInfo(contract.getCreateParam());
invokeOnStartingDoipServer(cn, contract.getCreateParam(), jo);
}
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() throws Exception {
// 正式启动
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());
}
}
}
DOOPBeforeExecHandler doopBeforeExecHandler = null;
DOOPAfterExecHandler doopAfterExecHandler = null;
doopRequestHandler = null;
for (FunctionNode fun : cn.getFunctions()) {
if (fun.isConfidential()) {
fun.appendBeforeInvokeHandler(new ConfidentialHandler(fun));
}
ArgSchemaHandler argSchemaHandler = createHandlerIfExist(fun, ArgSchemaHandler.class);
if (argSchemaHandler != null) {
fun.appendBeforeInvokeHandler(argSchemaHandler);
}
if (fun.isExport()) {
//if(fun.annotations...)
AccessHandler accessHandler = createHandlerIfExist(fun, 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));
}
if (fun.isDoipOperation()) {
if (doopRequestHandler == null) {
doopRequestHandler = new DOOPRequestHandler();
}
fun.appendBeforeInvokeHandler(new DOOPBeforeExecHandler(fun.getDoipOperationInfo().operation));
fun.appendAfterInvokeHandler(new DOOPAfterExecHandler(fun.getDoipOperationInfo().operation));
doopRequestHandler.addDoipOperation(fun);
}
}
}
<T extends AnnotationHook> T createHandlerIfExist(FunctionNode function, Class<T> clz) {
YJSAnnotation annotation = clz.getAnnotation(YJSAnnotation.class);
if (annotation == null) return null;
try {
AnnotationNode node = function.getAnnotation(annotation.name());
if (node == null) return null;
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) {
updateRepoInfo(contract.getCreateParam());
invokeOnStartingDoipServer(cn, contract.getCreateParam(), jo);
}
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 updateRepoInfo(JsonElement arg) throws Exception {
// 只有0号节点需要初始化IRP连接去updateRepoInfo
if (JavaScriptEntry.shardingID == 0) {
// DOOP relevant logic
DoipClusterServer server = DoipClusterServer.getDOOPServerInstance();
if (server == null) {
JsonObject createParams = arg.getAsJsonObject();
if (createParams.has("router")) {
JsonElement routerInfo = createParams.get("router");
if (!routerInfo.isJsonObject())
throw new Exception("Provide wrong router info in create params to DoipModule");
else {
EndpointConfig endpointConfig = JsonUtil.GSON.fromJson(routerInfo.getAsJsonObject(), EndpointConfig.class);
DoipClusterServer.createDOOPServerInstance(endpointConfig);
}
} else {
throw new Exception("DoipModule should provide router info in create params");
}
server = DoipClusterServer.getDOOPServerInstance();
}
// 只有一台机器去更新Router中的repoInfo就可以了
server.updateRepoInfo(contract, cn);
}
}
public void invokeOnStartingDoipServer(ContractNode cn, JsonElement arg, JsonObject returnValue) {
ContractRequest onStartingDoipServer = new ContractRequest();
onStartingDoipServer.setAction("onServerStart");
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("onServerStart");
try {
JsonElement onStartingDoipServerRes = invoke(onStartingDoipServer, funNode).result;
returnValue.add("doipModuleStartResult", onStartingDoipServerRes);
int startPort = ContractProcess.instance.server.getPort() + 1;
if (arg.isJsonObject() && arg.getAsJsonObject().has("doipStartPort")) {
startPort = arg.getAsJsonObject().get("doipStartPort").getAsInt();
}
LOGGER.info("Fetch the onStartingDoipServerRes from router successfully, the result is " + onStartingDoipServerRes);
int doipListenPort = DoipClusterServer.startDoipServer(startPort);
returnValue.addProperty("doipListenPort", doipListenPort);
returnValue.addProperty("doipStartPort", startPort);
} catch (Exception e) {
LOGGER.error("DoipLocalSingleton cannot starts properly, plz check the onServerStart 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());
}
}
}