diff --git a/source/contract/contract-maven-plugin/pom.xml b/source/contract/contract-maven-plugin/pom.xml
index 6cca1389..9596695e 100644
--- a/source/contract/contract-maven-plugin/pom.xml
+++ b/source/contract/contract-maven-plugin/pom.xml
@@ -35,10 +35,6 @@
${project.version}
-
- com.github.javaparser
- javaparser-core
-
org.apache.maven
@@ -58,6 +54,12 @@
2.6
+
+ org.ow2.asm
+ asm
+ 5.0.4
+
+
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/AbstractContract.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/AbstractContract.java
new file mode 100644
index 00000000..389dd33a
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/AbstractContract.java
@@ -0,0 +1,98 @@
+package com.jd.blockchain.contract.maven;
+
+import com.jd.blockchain.contract.maven.rule.BlackList;
+import com.jd.blockchain.contract.maven.rule.WhiteList;
+
+import java.util.List;
+
+public abstract class AbstractContract {
+
+ protected String className;
+
+ public String getClassName() {
+ return className;
+ }
+
+ public String getDotClassName() {
+ return className.replaceAll("/", ".");
+ }
+
+ protected String format(final String inputFormat) {
+ String formatResult;
+
+ String outputFormat = inputFormat;
+ if (inputFormat.endsWith(";")) {
+ outputFormat = inputFormat.substring(0, inputFormat.length() - 1);
+ }
+ if (outputFormat.startsWith("[L") && outputFormat.length() > 2) {
+ // 说明是数组,但不显示
+ formatResult = outputFormat.substring(2);
+ } else if (outputFormat.startsWith("[") && outputFormat.length() > 1) {
+ // 说明是数组
+ formatResult = outputFormat.substring(1);
+ } else if (outputFormat.startsWith("L") && outputFormat.length() > 1) {
+ // 说明是非基础类型
+ formatResult = outputFormat.substring(1);
+ } else {
+ formatResult = outputFormat;
+ }
+
+ return formatResult;
+ }
+
+ public static BlackList initBlack(List blackList) {
+ BlackList contractBlack = new BlackList();
+ if (blackList != null && !blackList.isEmpty()) {
+ for (String black : blackList) {
+ // 首先判断该black是package还是
+ String packageName = isPackageAndReturn(black);
+ if (packageName != null) {
+ // 说明是包
+ contractBlack.addBlackPackage(packageName);
+ } else {
+ String[] classAndMethod = black.split("-");
+ if (classAndMethod.length == 1) {
+ // 说明只有ClassName
+ contractBlack.addBlack(classAndMethod[0], BlackList.COMMON_METHOD);
+ } else {
+ contractBlack.addBlack(classAndMethod[0], classAndMethod[1]);
+ }
+ }
+ }
+ }
+
+ return contractBlack;
+ }
+
+ public static WhiteList initWhite(List whiteList) {
+ WhiteList contractWhite = new WhiteList();
+
+ if (whiteList != null && !whiteList.isEmpty()) {
+ for (String white : whiteList) {
+ String packageName = isPackageAndReturn(white);
+ if (packageName != null) {
+ // 说明是包
+ contractWhite.addWhite(packageName);
+ } else {
+ contractWhite.addWhite(white);
+ }
+ }
+ }
+
+ return contractWhite;
+ }
+
+ /**
+ * 获取配置的packageName
+ *
+ * @param config
+ * @return
+ * 假设为包,则返回其包名,否则返回NULL
+ */
+ public static String isPackageAndReturn(String config) {
+ if (config.endsWith("*")) {
+ return config.substring(0, config.length() - 2);
+ }
+ return null;
+ }
+}
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractCheckMojo.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractCheckMojo.java
deleted file mode 100644
index 81e5f272..00000000
--- a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractCheckMojo.java
+++ /dev/null
@@ -1,216 +0,0 @@
-//package com.jd.blockchain.contract.maven;
-//
-//import org.apache.commons.io.FileUtils;
-//import org.apache.maven.model.Model;
-//import org.apache.maven.model.Plugin;
-//import org.apache.maven.model.PluginExecution;
-//import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
-//import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
-//import org.apache.maven.plugin.AbstractMojo;
-//import org.apache.maven.plugin.MojoFailureException;
-//import org.apache.maven.plugins.annotations.Mojo;
-//import org.apache.maven.plugins.annotations.Parameter;
-//import org.apache.maven.project.MavenProject;
-//import org.apache.maven.shared.invoker.*;
-//import org.codehaus.plexus.util.xml.Xpp3Dom;
-//import org.slf4j.Logger;
-//import org.slf4j.LoggerFactory;
-//
-//import java.io.ByteArrayOutputStream;
-//import java.io.File;
-//import java.io.FileInputStream;
-//import java.io.IOException;
-//import java.util.ArrayList;
-//import java.util.Collections;
-//import java.util.List;
-//
-//
-//@Mojo(name = "contractCheck")
-//public class ContractCheckMojo extends AbstractMojo {
-//
-// Logger LOG = LoggerFactory.getLogger(ContractCheckMojo.class);
-//
-// public static final String CONTRACT_VERIFY = "contractVerify";
-//
-// private static final String CONTRACT_MAVEN_PLUGIN = "contract-maven-plugin";
-//
-// private static final String MAVEN_ASSEMBLY_PLUGIN = "maven-assembly-plugin";
-//
-// private static final String JDCHAIN_PACKAGE = "com.jd.blockchain";
-//
-// private static final String APACHE_MAVEN_PLUGINS = "org.apache.maven.plugins";
-//
-// private static final String GOALS_VERIFY = "package";
-//
-// private static final String GOALS_PACKAGE = "package";
-//
-// private static final String OUT_POM_XML = "ContractPom.xml";
-//
-// @Parameter(defaultValue = "${project}", required = true, readonly = true)
-// private MavenProject project;
-//
-// /**
-// * jar's name;
-// */
-// @Parameter
-// private String finalName;
-//
-// /**
-// * mainClass;
-// */
-// @Parameter
-// private String mainClass;
-// /**
-// * ledgerVersion;
-// */
-// @Parameter
-// private String ledgerVersion;
-//
-// /**
-// * mvnHome;
-// */
-// @Parameter
-// private String mvnHome;
-//
-// /**
-// * first compile the class, then parse it;
-// */
-// @Override
-// public void execute() throws MojoFailureException {
-// compileFiles();
-// }
-//
-// private void compileFiles() throws MojoFailureException {
-// try (FileInputStream fis = new FileInputStream(project.getFile())) {
-//
-// MavenXpp3Reader reader = new MavenXpp3Reader();
-// Model model = reader.read(fis);
-//
-// //delete this plugin(contractCheck) from destination pom.xml;then add the proper plugins;
-// Plugin plugin = model.getBuild().getPluginsAsMap()
-// .get(JDCHAIN_PACKAGE + ":" + CONTRACT_MAVEN_PLUGIN);
-// if(plugin == null){
-// plugin = model.getBuild().getPluginsAsMap()
-// .get(APACHE_MAVEN_PLUGINS + ":" + CONTRACT_MAVEN_PLUGIN);
-// }
-//
-// if(plugin == null) {
-// return;
-// }
-//
-// model.getBuild().removePlugin(plugin);
-//
-// List plugins = new ArrayList<>();
-// plugins.add(createAssembly());
-// plugins.add(createContractVerify());
-//
-// model.getBuild().setPlugins(plugins);
-//
-// handle(model);
-//
-// } catch (Exception e) {
-// LOG.error(e.getMessage());
-// throw new MojoFailureException(e.getMessage());
-// }
-// }
-//
-// private void invokeCompile(File file) {
-// InvocationRequest request = new DefaultInvocationRequest();
-//
-// Invoker invoker = new DefaultInvoker();
-// try {
-// request.setPomFile(file);
-//
-// request.setGoals(Collections.singletonList(GOALS_VERIFY));
-// invoker.setMavenHome(new File(mvnHome));
-// invoker.execute(request);
-// } catch (MavenInvocationException e) {
-// LOG.error(e.getMessage());
-// throw new IllegalStateException(e);
-// }
-// }
-//
-// private Plugin createContractVerify() {
-// Plugin plugin = new Plugin();
-// plugin.setGroupId(JDCHAIN_PACKAGE);
-// plugin.setArtifactId(CONTRACT_MAVEN_PLUGIN);
-// plugin.setVersion(ledgerVersion);
-//
-// Xpp3Dom finalNameNode = new Xpp3Dom("finalName");
-// finalNameNode.setValue(finalName);
-// Xpp3Dom configuration = new Xpp3Dom("configuration");
-// configuration.addChild(finalNameNode);
-//
-// plugin.setConfiguration(configuration);
-// plugin.setExecutions(pluginExecution("make-assembly", GOALS_VERIFY, CONTRACT_VERIFY));
-//
-// return plugin;
-// }
-//
-// private Plugin createAssembly() {
-// Plugin plugin = new Plugin();
-// plugin.setArtifactId(MAVEN_ASSEMBLY_PLUGIN);
-//
-// Xpp3Dom configuration = new Xpp3Dom("configuration");
-//
-// Xpp3Dom mainClassNode = new Xpp3Dom("mainClass");
-// mainClassNode.setValue(mainClass);
-//
-// Xpp3Dom manifest = new Xpp3Dom("manifest");
-// manifest.addChild(mainClassNode);
-//
-// Xpp3Dom archive = new Xpp3Dom("archive");
-// archive.addChild(manifest);
-//
-// Xpp3Dom finalNameNode = new Xpp3Dom("finalName");
-// finalNameNode.setValue(finalName);
-//
-// Xpp3Dom appendAssemblyId = new Xpp3Dom("appendAssemblyId");
-// appendAssemblyId.setValue("false");
-//
-// Xpp3Dom descriptorRef = new Xpp3Dom("descriptorRef");
-// descriptorRef.setValue("jar-with-dependencies");
-// Xpp3Dom descriptorRefs = new Xpp3Dom("descriptorRefs");
-// descriptorRefs.addChild(descriptorRef);
-//
-// configuration.addChild(finalNameNode);
-// configuration.addChild(appendAssemblyId);
-// configuration.addChild(archive);
-// configuration.addChild(descriptorRefs);
-//
-// plugin.setConfiguration(configuration);
-// plugin.setExecutions(pluginExecution("make-assembly", GOALS_PACKAGE, "single"));
-//
-// return plugin;
-// }
-//
-// private List pluginExecution(String id, String phase, String goal) {
-// PluginExecution pluginExecution = new PluginExecution();
-// pluginExecution.setId(id);
-// pluginExecution.setPhase(phase);
-// List goals = new ArrayList<>();
-// goals.add(goal);
-// pluginExecution.setGoals(goals);
-// List pluginExecutions = new ArrayList<>();
-// pluginExecutions.add(pluginExecution);
-//
-// return pluginExecutions;
-// }
-//
-// private void handle(Model model) throws IOException {
-//
-// MavenXpp3Writer mavenXpp3Writer = new MavenXpp3Writer();
-//
-// ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-//
-// mavenXpp3Writer.write(outputStream, model);
-//
-// byte[] buffer = outputStream.toByteArray();
-//
-// File outPom = new File(project.getBasedir().getPath(), OUT_POM_XML);
-//
-// FileUtils.writeByteArrayToFile(outPom, buffer);
-//
-// invokeCompile(outPom);
-// }
-//}
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractClass.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractClass.java
new file mode 100644
index 00000000..b9d3de7b
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractClass.java
@@ -0,0 +1,66 @@
+package com.jd.blockchain.contract.maven;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class ContractClass extends AbstractContract {
+
+ // 若出现同名的方法则进行合并(将两个方法中涉及到的内容合并在一起)
+ private Map methods = new ConcurrentHashMap<>();
+
+ public ContractClass(String className) {
+ if (className.contains(".")) {
+ this.className = className.replaceAll("\\.", "/");
+ } else {
+ this.className = className;
+ }
+ }
+
+ /**
+ * 返回构造方法
+ *
+ * @return
+ */
+ public ContractMethod constructor() {
+ return methods.get(ContractConstant.METHOD_INIT);
+ }
+
+ /**
+ * 返回该类的所有变量
+ *
+ * @return
+ */
+ public List fields() {
+
+ List fields = new ArrayList<>();
+
+ // 构造方法
+ ContractMethod initMethod = constructor();
+ if (initMethod != null) {
+ fields.addAll(initMethod.getClassFieldList(className));
+ }
+ // CLINIT方法
+ ContractMethod clInitMethod = methods.get(ContractConstant.METHOD_CLINIT);
+ if (clInitMethod != null) {
+ fields.addAll(clInitMethod.getClassFieldList(className));
+ }
+ return fields;
+ }
+
+ public synchronized ContractMethod method(String methodName) {
+ if (methods.containsKey(methodName)) {
+ return methods.get(methodName);
+ }
+ ContractMethod method = new ContractMethod(this.className, methodName);
+
+ methods.put(methodName, method);
+
+ return method;
+ }
+
+ public Map getMethods() {
+ return methods;
+ }
+}
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractCompileMojo.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractCompileMojo.java
index 61a3dfff..3ddf15a0 100644
--- a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractCompileMojo.java
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractCompileMojo.java
@@ -1,20 +1,94 @@
package com.jd.blockchain.contract.maven;
+import com.jd.blockchain.contract.ContractJarUtils;
+import com.jd.blockchain.contract.maven.rule.BlackList;
+import com.jd.blockchain.contract.maven.rule.WhiteList;
+import com.jd.blockchain.contract.maven.rule.DependencyExclude;
+import com.jd.blockchain.contract.maven.verify.ResolveEngine;
+import com.jd.blockchain.contract.maven.verify.VerifyEngine;
+import org.apache.commons.io.FileUtils;
+import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.assembly.mojos.SingleAssemblyMojo;
import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.project.MavenProject;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Set;
@Mojo(name = "compile")
public class ContractCompileMojo extends SingleAssemblyMojo {
public static final String JAR_DEPENDENCE = "jar-with-dependencies";
+ public static final String SCOPE_PROVIDED = "provided";
+
+ public static final String SCOPE_COMPILE = "compile";
+
+ private DependencyExclude dependencyExclude = new DependencyExclude();
+
+ private static BlackList black;
+
+ private static WhiteList white;
+
+ static {
+ init();
+ }
+
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
+ // 首先对MainClass进行校验,要求必须有MainClass
+ String mainClass = mainClassVerify();
+
+ // 将JDChain本身代码之外的代码移除(不打包进整个Jar)
+ handleArtifactExclude(super.getProject().getDependencyArtifacts());
+
+ // 此参数用于设置将所有第三方依赖的Jar包打散为.class,与主代码打包在一起,生成一个jar包
+ super.setDescriptorRefs(new String[]{JAR_DEPENDENCE});
+
+ // 执行打包命令
+ super.execute();
+
+ // 将本次打包好的文件重新命名,以便于后续重新打包需要
+ // 把文件改名,然后重新再生成一个文件
+ File dstFile;
+ try {
+ dstFile = rename(getProject(), getFinalName());
+ } catch (IOException e) {
+ getLog().error(e);
+ throw new MojoFailureException(e.getMessage());
+ }
+
+ // 首先校验该类的Jar包中是否包含不符合规范的命名,以及该类的代码中的部分解析
+ File finalJarFile = verify(dstFile, mainClass);
+
+ // 将所有的依赖的jar包全部打包进一个包中,以便于进行ASM检查
+ handleArtifactCompile(super.getProject().getDependencyArtifacts());
+
+ // 然后再打包一次,本次打包完成后,其中的代码包含所有的class(JDK自身的除外)
+ super.execute();
+
+ // 对代码中的一些规则进行校验,主要是校验其是否包含一些不允许使用的类、包、方法等
+ verify(mainClass);
+
+ // 删除中间的一些文件
+ try {
+ FileUtils.forceDelete(dstFile);
+ } catch (IOException e) {
+ throw new MojoFailureException(e.getMessage());
+ }
+
+ // 若执行到此处没有异常则表明打包成功,打印打包成功消息
+ getLog().info(String.format("JDChain's Contract compile success, path = %s !", finalJarFile.getPath()));
+ }
+
+ private String mainClassVerify() throws MojoFailureException {
// 要求必须有MainClass
+ String mainClass;
try {
- String mainClass = super.getJarArchiveConfiguration().getManifest().getMainClass();
+ mainClass = super.getJarArchiveConfiguration().getManifest().getMainClass();
// 校验MainClass,要求MainClass必须不能为空
if (mainClass == null || mainClass.length() == 0) {
throw new MojoFailureException("MainClass is NULL !!!");
@@ -23,16 +97,76 @@ public class ContractCompileMojo extends SingleAssemblyMojo {
} catch (Exception e) {
throw new MojoFailureException("MainClass is null: " + e.getMessage(), e );
}
+ return mainClass;
+ }
- // 此参数用于设置将所有第三方依赖的Jar包打散为.class,与主代码打包在一起,生成一个jar包
- super.setDescriptorRefs(new String[]{JAR_DEPENDENCE});
+ private void handleArtifactExclude(Set artifacts) {
+ for (Artifact artifact : artifacts) {
+ String groupId = artifact.getGroupId(), artifactId = artifact.getArtifactId();
+ if (dependencyExclude.isExclude(groupId, artifactId)) {
+ getLog().info(String.format("GroupId[%s] ArtifactId[%s] belongs to DependencyExclude !!!", groupId, artifactId));
+ // 属于排除的名单之中
+ artifact.setScope(SCOPE_PROVIDED);
+ }
+ }
+ }
- // 执行打包命令
- super.execute();
+ private void handleArtifactCompile(Set artifacts) {
+ for (Artifact artifact : artifacts) {
+ if (artifact.getScope().equals(SCOPE_PROVIDED)) {
+ // 将所有的provided设置为compile,以便于后续编译
+ artifact.setScope(SCOPE_COMPILE);
+ }
+ }
+ }
+
+ private File rename(MavenProject project, String finalName) throws IOException {
+ String srcJarPath = jarPath(project, finalName);
+ String dstJarPath = project.getBuild().getDirectory() +
+ File.separator + finalName + ".jar";
+ File dstFile = new File(dstJarPath);
+ FileUtils.copyFile(new File(srcJarPath), dstFile);
+ FileUtils.forceDelete(new File(srcJarPath));
+ return dstFile;
+ }
+
+ private String jarPath(MavenProject project, String finalName) {
+ return project.getBuild().getDirectory() +
+ File.separator + finalName + "-" + JAR_DEPENDENCE + ".jar";
+ }
+
+ private void verify(String mainClass) throws MojoFailureException {
+ try {
- ContractResolveEngine engine = new ContractResolveEngine(getLog(), getProject(), getFinalName());
+ File jarFile = new File(jarPath(getProject(), getFinalName()));
- // 打包并进行校验
- engine.compileAndVerify();
+ VerifyEngine verifyEngine = new VerifyEngine(getLog(), jarFile, mainClass, black, white);
+
+ verifyEngine.verify();
+
+ // 校验完成后将该jar包删除
+ FileUtils.forceDelete(jarFile);
+
+ } catch (Exception e) {
+ getLog().error(e);
+ throw new MojoFailureException(e.getMessage());
+ }
+ }
+
+ private File verify(File jarFile, String mainClass) throws MojoFailureException {
+
+ ResolveEngine resolveEngine = new ResolveEngine(getLog(), jarFile, mainClass);
+
+ return resolveEngine.verify();
+
+ }
+
+ private static void init() {
+ try {
+ black = AbstractContract.initBlack(ContractJarUtils.loadBlackConf());
+ white = AbstractContract.initWhite(ContractJarUtils.loadWhiteConf());
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
}
}
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractConstant.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractConstant.java
new file mode 100644
index 00000000..a94fb624
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractConstant.java
@@ -0,0 +1,9 @@
+package com.jd.blockchain.contract.maven;
+
+public class ContractConstant {
+
+ public static final String METHOD_INIT = "";
+
+ public static final String METHOD_CLINIT = "";
+
+}
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractDeployExeUtil.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractDeployExeUtil.java
deleted file mode 100644
index bd6a1b23..00000000
--- a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractDeployExeUtil.java
+++ /dev/null
@@ -1,176 +0,0 @@
-//package com.jd.blockchain.contract.maven;
-//
-//import com.jd.blockchain.binaryproto.DataContractRegistry;
-//import com.jd.blockchain.crypto.HashDigest;
-//import com.jd.blockchain.crypto.PrivKey;
-//import com.jd.blockchain.crypto.PubKey;
-//import com.jd.blockchain.ledger.*;
-//import com.jd.blockchain.sdk.BlockchainService;
-//import com.jd.blockchain.sdk.client.GatewayServiceFactory;
-//import com.jd.blockchain.tools.keygen.KeyGenCommand;
-//import com.jd.blockchain.utils.Bytes;
-//import com.jd.blockchain.utils.codec.Base58Utils;
-//import com.jd.blockchain.utils.net.NetworkAddress;
-//
-//import java.io.File;
-//import java.io.FileInputStream;
-//import java.io.IOException;
-//import java.io.InputStream;
-//
-///**
-// * @Author zhaogw
-// * @Date 2018/11/2 10:18
-// */
-//public enum ContractDeployExeUtil {
-// instance;
-// private BlockchainService bcsrv;
-// private Bytes contractAddress;
-//
-// public BlockchainKeypair getKeyPair(String pubPath, String prvPath, String rawPassword){
-// PubKey pub = null;
-// PrivKey prv = null;
-// try {
-// prv = KeyGenCommand.readPrivKey(prvPath, KeyGenCommand.encodePassword(rawPassword));
-// pub = KeyGenCommand.readPubKey(pubPath);
-//
-// } catch (Exception e) {
-// e.printStackTrace();
-// }
-//
-// return new BlockchainKeypair(pub, prv);
-// }
-//
-// public PubKey getPubKey(String pubPath){
-// PubKey pub = null;
-// try {
-// if(pubPath == null){
-// BlockchainKeypair contractKeyPair = BlockchainKeyGenerator.getInstance().generate();
-// pub = contractKeyPair.getPubKey();
-// }else {
-// pub = KeyGenCommand.readPubKey(pubPath);
-// }
-//
-// } catch (Exception e) {
-// e.printStackTrace();
-// }
-//
-// return pub;
-// }
-// public byte[] getChainCode(String path){
-// byte[] chainCode = null;
-// File file = null;
-// InputStream input = null;
-// try {
-// file = new File(path);
-// input = new FileInputStream(file);
-// chainCode = new byte[input.available()];
-// input.read(chainCode);
-//
-// } catch (IOException e) {
-// e.printStackTrace();
-// } finally {
-// try {
-// if(input!=null){
-// input.close();
-// }
-// } catch (IOException e) {
-// e.printStackTrace();
-// }
-// }
-// return chainCode;
-// }
-//
-// private void register(){
-// DataContractRegistry.register(TransactionContent.class);
-// DataContractRegistry.register(TransactionContentBody.class);
-// DataContractRegistry.register(TransactionRequest.class);
-// DataContractRegistry.register(NodeRequest.class);
-// DataContractRegistry.register(EndpointRequest.class);
-// DataContractRegistry.register(TransactionResponse.class);
-// DataContractRegistry.register(DataAccountKVSetOperation.class);
-// DataContractRegistry.register(DataAccountKVSetOperation.KVWriteEntry.class);
-// DataContractRegistry.register(Operation.class);
-// DataContractRegistry.register(ContractCodeDeployOperation.class);
-// DataContractRegistry.register(ContractEventSendOperation.class);
-// DataContractRegistry.register(DataAccountRegisterOperation.class);
-// DataContractRegistry.register(UserRegisterOperation.class);
-// }
-//
-// public BlockchainService initBcsrv(String host, int port) {
-// if(bcsrv!=null){
-// return bcsrv;
-// }
-// NetworkAddress addr = new NetworkAddress(host, port);
-// GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(addr);
-// bcsrv = gwsrvFact.getBlockchainService();
-// return bcsrv;
-// }
-//
-// public boolean deploy(HashDigest ledgerHash, BlockchainIdentity contractIdentity, BlockchainKeypair ownerKey, byte[] chainCode){
-// register();
-//
-// TransactionTemplate txTpl = bcsrv.newTransaction(ledgerHash);
-// txTpl.contracts().deploy(contractIdentity, chainCode);
-// PreparedTransaction ptx = txTpl.prepare();
-// ptx.sign(ownerKey);
-// // 提交并等待共识返回;
-// TransactionResponse txResp = ptx.commit();
-//
-// // 验证结果;
-// contractAddress = contractIdentity.getAddress();
-// this.setContractAddress(contractAddress);
-// System.out.println("contract's address="+contractAddress);
-// return txResp.isSuccess();
-// }
-// public boolean deploy(String host, int port, HashDigest ledgerHash, BlockchainKeypair ownerKey, byte[] chainCode){
-// register();
-//
-// BlockchainIdentity contractIdentity = BlockchainKeyGenerator.getInstance().generate().getIdentity();
-// initBcsrv(host,port);
-// return deploy(ledgerHash, contractIdentity, ownerKey, chainCode);
-// }
-//
-// // 根据用户指定的公钥生成合约地址
-// public boolean deploy(String host, int port, String ledger,String ownerPubPath, String ownerPrvPath,
-// String ownerPassword, String chainCodePath,String pubPath){
-// PubKey pubKey = getPubKey(pubPath);
-// BlockchainIdentity contractIdentity = new BlockchainIdentityData(pubKey);
-// byte[] chainCode = getChainCode(chainCodePath);
-//
-// BlockchainKeypair ownerKey = getKeyPair(ownerPubPath, ownerPrvPath, ownerPassword);
-// HashDigest ledgerHash = new HashDigest(Base58Utils.decode(ledger));
-// initBcsrv(host,port);
-// return deploy(ledgerHash, contractIdentity, ownerKey, chainCode);
-// }
-//
-//
-//// 暂不支持从插件执行合约;此外,由于合约参数调用的格式发生变化,故此方法被废弃;by: huanghaiquan at 2019-04-30;
-//
-//// public boolean exeContract(String ledger,String ownerPubPath, String ownerPrvPath,
-//// String ownerPassword,String event,String contractArgs){
-//// BlockchainKeypair ownerKey = getKeyPair(ownerPubPath, ownerPrvPath, ownerPassword);
-//// HashDigest ledgerHash = new HashDigest(Base58Utils.decode(ledger));
-////
-//// // 定义交易,传输最简单的数字、字符串、提取合约中的地址;
-//// TransactionTemplate txTpl = bcsrv.newTransaction(ledgerHash);
-//// txTpl.contractEvents().send(getContractAddress(),event,contractArgs.getBytes());
-////
-//// // 签名;
-//// PreparedTransaction ptx = txTpl.prepare();
-//// ptx.sign(ownerKey);
-////
-//// // 提交并等待共识返回;
-//// TransactionResponse txResp = ptx.commit();
-////
-//// // 验证结果;
-//// return txResp.isSuccess();
-//// }
-//
-// public Bytes getContractAddress() {
-// return contractAddress;
-// }
-//
-// public void setContractAddress(Bytes contractAddress) {
-// this.contractAddress = contractAddress;
-// }
-//}
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractDeployMojo.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractDeployMojo.java
deleted file mode 100644
index 89a82a36..00000000
--- a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractDeployMojo.java
+++ /dev/null
@@ -1,120 +0,0 @@
-//package com.jd.blockchain.contract.maven;
-//
-//import com.jd.blockchain.crypto.HashDigest;
-//import com.jd.blockchain.crypto.PrivKey;
-//import com.jd.blockchain.crypto.PubKey;
-//import com.jd.blockchain.ledger.BlockchainKeypair;
-//import com.jd.blockchain.tools.keygen.KeyGenCommand;
-//import com.jd.blockchain.utils.StringUtils;
-//import com.jd.blockchain.utils.codec.Base58Utils;
-//import com.jd.blockchain.utils.io.FileUtils;
-//import org.apache.maven.plugin.AbstractMojo;
-//import org.apache.maven.plugin.MojoFailureException;
-//import org.apache.maven.plugins.annotations.Mojo;
-//import org.apache.maven.plugins.annotations.Parameter;
-//import org.slf4j.Logger;
-//import org.slf4j.LoggerFactory;
-//
-//import java.io.File;
-//import java.io.FileInputStream;
-//import java.io.IOException;
-//import java.io.InputStream;
-//import java.util.Properties;
-//
-///**
-// * for contract remote deploy;
-// * @goal contractDeploy
-// * @phase process-sources
-// * @Author zhaogw
-// * @Date 2018/10/18 10:12
-// */
-//
-//@Mojo(name = "deploy")
-//public class ContractDeployMojo extends AbstractMojo {
-// Logger logger = LoggerFactory.getLogger(ContractDeployMojo.class);
-//
-// @Parameter
-// private File config;
-//
-// @Override
-// public void execute()throws MojoFailureException {
-//
-// Properties prop = new Properties();
-// InputStream input = null;
-//
-// try {
-// input = new FileInputStream(config);
-// prop.load(input);
-//
-// } catch (IOException ex) {
-// logger.error(ex.getMessage());
-// throw new MojoFailureException("io error");
-// } finally {
-// if (input != null) {
-// try {
-// input.close();
-// } catch (IOException e) {
-// logger.error(e.getMessage());
-// }
-// }
-// }
-// int port;
-// try {
-// port = Integer.parseInt(prop.getProperty("port"));
-// }catch (NumberFormatException e){
-// logger.error(e.getMessage());
-// throw new MojoFailureException("invalid port");
-// }
-// String host = prop.getProperty("host");
-// String ledger = prop.getProperty("ledger");
-// String pubKey = prop.getProperty("pubKey");
-// String prvKey = prop.getProperty("prvKey");
-// String password = prop.getProperty("password");
-// String contractPath = prop.getProperty("contractPath");
-//
-//
-// if(StringUtils.isEmpty(host)){
-// logger.info("host不能为空");
-// return;
-// }
-//
-// if(StringUtils.isEmpty(ledger)){
-// logger.info("ledger不能为空.");
-// return;
-// }
-// if(StringUtils.isEmpty(pubKey)){
-// logger.info("pubKey不能为空.");
-// return;
-// }
-// if(StringUtils.isEmpty(prvKey)){
-// logger.info("prvKey不能为空.");
-// return;
-// }
-// if(StringUtils.isEmpty(contractPath)){
-// logger.info("contractPath不能为空.");
-// return;
-// }
-//
-// File contract = new File(contractPath);
-// if (!contract.isFile()){
-// logger.info("文件"+contractPath+"不存在");
-// return;
-// }
-// byte[] contractBytes = FileUtils.readBytes(contractPath);
-//
-//
-// PrivKey prv = KeyGenCommand.decodePrivKeyWithRawPassword(prvKey, password);
-// PubKey pub = KeyGenCommand.decodePubKey(pubKey);
-// BlockchainKeypair blockchainKeyPair = new BlockchainKeypair(pub, prv);
-// HashDigest ledgerHash = new HashDigest(Base58Utils.decode(ledger));
-//
-// StringBuffer sb = new StringBuffer();
-// sb.append("host:"+ host).append(",port:"+port).append(",ledgerHash:"+ledgerHash.toBase58()).
-// append(",pubKey:"+pubKey).append(",prvKey:"+prv).append(",contractPath:"+contractPath);
-// logger.info(sb.toString());
-// ContractDeployExeUtil.instance.deploy(host,port,ledgerHash, blockchainKeyPair, contractBytes);
-// }
-//
-//}
-//
-//
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractField.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractField.java
new file mode 100644
index 00000000..f4b0f396
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractField.java
@@ -0,0 +1,43 @@
+package com.jd.blockchain.contract.maven;
+
+public class ContractField extends AbstractContract {
+
+ private String fieldName;
+
+ private String fieldType;
+
+ private boolean isStatic;
+
+ public ContractField(String className, String fieldName, String fieldType) {
+ this(className, fieldName, fieldType, false);
+ }
+
+ public ContractField(String className, String fieldName, String fieldType, boolean isStatic) {
+ this.className = format(className);
+ this.fieldName = fieldName;
+ this.fieldType = format(fieldType);
+ this.isStatic = isStatic;
+ }
+
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ public String getFieldType() {
+ return fieldType;
+ }
+
+ public boolean isStatic() {
+ return isStatic;
+ }
+
+ @Override
+ public String toString() {
+ return "ContractField{" +
+ "className='" + className + '\'' +
+ ", fieldName='" + fieldName + '\'' +
+ ", fieldType='" + fieldType + '\'' +
+ ", isStatic=" + isStatic +
+ '}';
+ }
+}
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractMethod.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractMethod.java
new file mode 100644
index 00000000..6dc2e3a7
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractMethod.java
@@ -0,0 +1,81 @@
+package com.jd.blockchain.contract.maven;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class ContractMethod extends AbstractContract {
+
+ private String methodName;
+
+ private String[] paramTypes;
+
+ private String[] returnTypes;
+
+ private List fieldList = new ArrayList<>();
+
+ private List methodList = new ArrayList<>();
+
+ public ContractMethod(String className, String methodName) {
+ this(className, methodName, null, null);
+ }
+
+ public ContractMethod(String className, String methodName, String[] paramTypes, String[] returnTypes) {
+ this.className = format(className);
+ this.methodName = methodName;
+ this.paramTypes = paramTypes;
+ this.returnTypes = returnTypes;
+ }
+
+ public void addMethod(String className, String methodName, String[] paramTypes, String[] returnTypes) {
+ methodList.add(new ContractMethod(className, methodName, paramTypes, returnTypes));
+ }
+
+ public void addField(String className, String fieldName, String fieldType) {
+ this.fieldList.add(new ContractField(className, fieldName, fieldType));
+ }
+
+ public void addStaticField(String className, String fieldName, String fieldType) {
+ this.fieldList.add(new ContractField(className, fieldName, fieldType, true));
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public String[] getParamTypes() {
+ return paramTypes;
+ }
+
+ public List getAllFieldList() {
+ return fieldList;
+ }
+
+ public List getClassFieldList(String cName) {
+ List classFieldList = new ArrayList<>();
+ if (!fieldList.isEmpty()) {
+ for (ContractField field : fieldList) {
+ if (field.getClassName().equals(cName)) {
+ classFieldList.add(field);
+ }
+ }
+ }
+ return classFieldList;
+ }
+
+ public List getMethodList() {
+ return methodList;
+ }
+
+ @Override
+ public String toString() {
+ return "ContractMethod{" +
+ "className='" + className + '\'' +
+ ", methodName='" + methodName + '\'' +
+ ", paramTypes=" + Arrays.toString(paramTypes) +
+ ", returnTypes=" + Arrays.toString(returnTypes) +
+ ", fieldList=" + fieldList +
+ ", methodList=" + methodList +
+ '}';
+ }
+}
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractResolveEngine.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractResolveEngine.java
deleted file mode 100644
index 3fb2404d..00000000
--- a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractResolveEngine.java
+++ /dev/null
@@ -1,459 +0,0 @@
-package com.jd.blockchain.contract.maven;
-
-import com.github.javaparser.JavaParser;
-import com.github.javaparser.ast.CompilationUnit;
-import com.github.javaparser.ast.ImportDeclaration;
-import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
-import com.jd.blockchain.contract.ContractType;
-import org.apache.commons.io.FileUtils;
-import org.apache.maven.plugin.MojoFailureException;
-import org.apache.maven.plugin.logging.Log;
-import org.apache.maven.project.MavenProject;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.nio.charset.StandardCharsets;
-import java.util.*;
-import java.util.jar.Attributes;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-
-import static com.jd.blockchain.contract.ContractJarUtils.*;
-import static com.jd.blockchain.contract.maven.ContractCompileMojo.JAR_DEPENDENCE;
-import static com.jd.blockchain.utils.decompiler.utils.DecompilerUtils.decompileJarFile;
-
-public class ContractResolveEngine {
-
- private static final String JAVA_SUFFIX = ".java";
-
- private static final String PATH_DIRECT =
- "src" + File.separator +
- "main" + File.separator +
- "java" + File.separator;
-
- private static final String CONFIG = "config.properties";
-
- private static final String BLACK_PACKAGE_LIST = "black.package.list";
-
- private static final String BLACK_CLASS_LIST = "black.class.list";
-
- private static final String BLACK_NAME_LIST = "black.name.list";
-
- private static List blackNameList;
-
- private static List blackPackageList;
-
- private static Set blackClassSet;
-
- static {
- try {
- configInit();
- } catch (Exception e) {
- throw new IllegalStateException(e);
- }
- }
-
- private Log LOGGER;
-
- private MavenProject project;
-
- private String finalName;
-
- public ContractResolveEngine(Log LOGGER, MavenProject project, String finalName) {
- this.LOGGER = LOGGER;
- this.project = project;
- this.finalName = finalName;
- }
-
- public void compileAndVerify() throws MojoFailureException {
- try {
- jarCopy();
- verify(compileCustomJar());
- } catch (IOException e) {
- throw new MojoFailureException("IO Error : " + e.getMessage(), e);
- } catch (MojoFailureException ex) {
- throw ex;
- }
- }
-
- private void jarCopy() throws IOException {
- String srcJarPath = project.getBuild().getDirectory() +
- File.separator + finalName + "-" + JAR_DEPENDENCE + ".jar";
- String dstJarPath = project.getBuild().getDirectory() +
- File.separator + finalName + ".jar";
- FileUtils.copyFile(new File(srcJarPath), new File(dstJarPath));
- }
-
- private File compileCustomJar() throws IOException {
- return copyAndManage(project, finalName);
- }
-
- private void verify(File jarFile) throws MojoFailureException {
- try {
- // 首先校验MainClass
- try {
- verifyMainClass(jarFile);
- } catch (Exception e) {
- jarFile.delete();
- LOGGER.error(e.getMessage());
- throw e;
- }
-
- LinkedList totalClassList = loadAllClass(jarFile);
- // 该项目路径
- String projectDir = project.getBasedir().getPath();
- // 代码路径
- String codeBaseDir = projectDir + File.separator + PATH_DIRECT;
-
- if (!totalClassList.isEmpty()) {
-
- boolean isOK = true;
-
- for (String clazz : totalClassList) {
-
- LOGGER.debug(String.format("Verify Class[%s] start......", clazz));
- // 获取其包名
- String packageName = packageName(clazz);
-
- LOGGER.debug(String.format("Class[%s] 's package name = %s", clazz, packageName));
-
- // 包的名字黑名单,不能打包该类进入Jar包中,或者合约不能命名这样的名字
- boolean isNameBlack = false;
- for (ContractPackage blackName : blackNameList) {
- isNameBlack = verifyPackage(packageName, blackName);
- if (isNameBlack) {
- break;
- }
- }
-
- // 假设是黑名单则打印日志
- if (isNameBlack) {
- // 打印信息供检查
- LOGGER.error(String.format("Class[%s]'s Package-Name belong to BlackNameList !!!", clazz));
- isOK = false;
- continue;
- }
-
- // 获取该Class对应的Java文件
- File javaFile = new File(codeBaseDir + clazz + JAVA_SUFFIX);
-
- boolean isNeedDelete = false;
- if (!javaFile.exists()) {
- LOGGER.debug(String.format("Class[%s] -> Java[%s] is not exist, start decompile ...", clazz, jarFile.getPath()));
- // 表明不是项目中的内容,需要通过反编译获取该文件
- String source = null;
- try {
- source = decompileJarFile(jarFile.getPath(), clazz, true, StandardCharsets.UTF_8.name());
- if (source == null || source.length() == 0) {
- throw new IllegalStateException();
- }
- } catch (Exception e) {
- LOGGER.warn(String.format("Decompile Jar[%s]->Class[%s] Fail !!!", jarFile.getPath(), clazz));
- }
- // 将source写入Java文件
- File sourceTempJavaFile = new File(tempPath(codeBaseDir, clazz));
- FileUtils.writeStringToFile(sourceTempJavaFile, source == null ? "" : source);
- javaFile = sourceTempJavaFile;
- isNeedDelete = true;
- } else {
- LOGGER.debug(String.format("Class[%s] -> Java[%s] is exist", clazz, jarFile.getPath()));
- }
-
- LOGGER.info(String.format("Parse Java File [%s] start......", javaFile.getPath()));
- // 解析文件中的内容
- CompilationUnit compilationUnit = JavaParser.parse(javaFile);
-
- MethodVisitor methodVisitor = new MethodVisitor();
-
- compilationUnit.accept(methodVisitor, null);
-
- List imports = methodVisitor.importClasses;
-
- if (!imports.isEmpty()) {
- for (String importClass : imports) {
- LOGGER.debug(String.format("Class[%s] read import -> [%s]", clazz, importClass));
- if (importClass.endsWith("*")) {
- // 导入的是包
- for (ContractPackage blackPackage : blackPackageList) {
- String importPackageName = importClass.substring(0, importClass.length() - 2);
- if (verifyPackage(importPackageName, blackPackage)) {
- // 打印信息供检查
- LOGGER.error(String.format("Class[%s]'s import class [%s] belong to BlackPackageList !!!", clazz, importClass));
- isOK = false;
- break;
- }
- }
- } else {
- // 导入的是具体的类,则判断类黑名单 + 包黑名单
- if (blackClassSet.contains(importClass)) {
- // 包含导入类,该方式无法通过验证
- LOGGER.error(String.format("Class[%s]'s import class [%s] belong to BlackClassList !!!", clazz, importClass));
- isOK = false;
- } else {
- // 判断导入的该类与黑名单导入包的对应关系
- for (ContractPackage blackPackage : blackPackageList) {
- if (verifyClass(importClass, blackPackage)) {
- LOGGER.error(String.format("Class[%s]'s import class [%s] belong to BlackPackageList !!!", clazz, importClass));
- isOK = false;
- break;
- }
- }
- }
- }
- }
- }
- if (isNeedDelete) {
- javaFile.delete();
- }
- LOGGER.debug(String.format("Verify Class[%s] end......", clazz));
- }
- if (!isOK) {
- // 需要将该Jar删除
- jarFile.delete();
- throw new IllegalStateException("There are many Illegal information, please check !!!");
- }
- } else {
- jarFile.delete();
- throw new IllegalStateException("There is none class !!!");
- }
- } catch (Exception e) {
- LOGGER.error(e.getMessage());
- throw new MojoFailureException(e.getMessage(), e);
- }
- }
-
- private void verifyMainClass(File jarFile) throws Exception {
- // 加载main-class,开始校验类型
- LOGGER.debug(String.format("Verify Jar [%s] 's MainClass start...", jarFile.getName()));
- URL jarURL = jarFile.toURI().toURL();
- ClassLoader classLoader = new URLClassLoader(new URL[]{jarURL}, this.getClass().getClassLoader());
- Attributes m = new JarFile(jarFile).getManifest().getMainAttributes();
- String contractMainClass = m.getValue(Attributes.Name.MAIN_CLASS);
- Class mainClass = classLoader.loadClass(contractMainClass);
- ContractType.resolve(mainClass);
- LOGGER.debug(String.format("Verify Jar [%s] 's MainClass end...", jarFile.getName()));
- }
-
- private static List blackNameList(Properties config) {
- return blackList(config, BLACK_NAME_LIST);
- }
-
- private static Set blackClassSet(Properties config) {
- Set blackClassSet = new HashSet<>();
- String attrProp = config.getProperty(BLACK_CLASS_LIST);
- if (attrProp != null && attrProp.length() > 0) {
- String[] attrPropArray = attrProp.split(",");
- for (String attr : attrPropArray) {
- blackClassSet.add(attr.trim());
- }
- }
- return blackClassSet;
- }
-
- private static List blackPackageList(Properties config) {
- return blackList(config, BLACK_PACKAGE_LIST);
- }
-
- private static List blackList(Properties config, String attrName) {
- List list = new ArrayList<>();
- String attrProp = config.getProperty(attrName);
- if (attrProp != null || attrProp.length() > 0) {
- String[] attrPropArray = attrProp.split(",");
- for (String attr : attrPropArray) {
- list.add(new ContractPackage(attr));
- }
- }
- return list;
- }
-
- private boolean verifyPackage(String packageName, ContractPackage contractPackage) {
- boolean verify = false;
- if (packageName.equals(contractPackage.packageName)) {
- // 完全相同
- verify = true;
- } else if (packageName.startsWith(contractPackage.packageName) &&
- contractPackage.isTotal) {
- // 以某个包开头
- verify = true;
- }
- return verify;
- }
-
- private boolean verifyClass(String className, ContractPackage contractPackage) {
- boolean verify = false;
-
- if (contractPackage.isTotal) {
- // 表示该包下面的其他所有包都会受限制,此处需要判断起始
- if (className.startsWith(contractPackage.packageName)) {
- verify = true;
- }
- } else {
- // 表示该包必须完整匹配ClassName所在包
- // 获取ClassName所在包
- String packageName = packageNameByDot(className);
- if (packageName.equals(contractPackage.packageName)) {
- verify = true;
- }
- }
- return verify;
- }
-
- private String packageNameByDot(String className) {
- String[] array = className.split(".");
- if (Character.isLowerCase(array[array.length - 2].charAt(0))) {
- // 如果是小写,表示非内部类
- // 获取完整包名
- return className.substring(0, className.lastIndexOf("."));
- }
- // 表示为内部类,该包拼装组成
- StringBuilder buffer = new StringBuilder();
- for (String s : array) {
- if (buffer.length() > 0) {
- buffer.append(".");
- }
- if (Character.isUpperCase(s.charAt(0))) {
- // 表明已经到具体类
- break;
- }
- buffer.append(s);
- }
-
- if (buffer.length() == 0) {
- throw new IllegalStateException(String.format("Import Class [%s] Illegal !!!", className));
- }
-
- return buffer.toString();
- }
-
- private String packageName(String clazz) {
- int index = clazz.lastIndexOf("/");
- String packageName = clazz.substring(0, index);
- return packageName.replaceAll("/", ".");
- }
-
- private File copyAndManage(MavenProject project, String finalName) throws IOException {
- // 首先将Jar包转换为指定的格式
- String srcJarPath = project.getBuild().getDirectory() +
- File.separator + finalName + ".jar";
-
- String dstJarPath = project.getBuild().getDirectory() +
- File.separator + finalName + "-temp-" + System.currentTimeMillis() + ".jar";
-
- File srcJar = new File(srcJarPath), dstJar = new File(dstJarPath);
-
- LOGGER.debug(String.format("Jar from [%s] to [%s] Copying", srcJarPath, dstJarPath));
- // 首先进行Copy处理
- copy(srcJar, dstJar);
- LOGGER.debug(String.format("Jar from [%s] to [%s] Copied", srcJarPath, dstJarPath));
-
- byte[] txtBytes = contractMF(FileUtils.readFileToByteArray(dstJar)).getBytes(StandardCharsets.UTF_8);
-
- String finalJarPath = project.getBuild().getDirectory() +
- File.separator + finalName + "-jdchain.jar";
-
- File finalJar = new File(finalJarPath);
-
- copy(dstJar, finalJar, contractMFJarEntry(), txtBytes, null);
-
- // 删除临时文件
- FileUtils.forceDelete(dstJar);
-
- return finalJar;
- }
-
- private static void configInit() throws Exception {
- Properties config = loadConfig();
-
- blackNameList = blackNameList(config);
-
- blackPackageList = blackPackageList(config);
-
- blackClassSet = blackClassSet(config);
- }
-
- private static Properties loadConfig() throws Exception {
-
- Properties properties = new Properties();
-
- properties.load(ContractResolveEngine.class.getResourceAsStream(File.separator + CONFIG));
-
- return properties;
- }
-
- private LinkedList loadAllClass(File file) throws Exception {
- JarFile jarFile = new JarFile(file);
- LinkedList allClass = new LinkedList<>();
- Enumeration jarEntries = jarFile.entries();
- while (jarEntries.hasMoreElements()) {
- JarEntry jarEntry = jarEntries.nextElement();
- String entryName = jarEntry.getName();
- if (entryName.endsWith(".class")) {
- // 内部类,不需要处理
- if (!entryName.contains("$")) {
- allClass.addLast(entryName.substring(0, entryName.length() - 6));
- }
- }
- }
- return allClass;
- }
-
- private String tempPath(String codeBaseDir, String clazz) {
- // 获取最后的名称
- String[] classArray = clazz.split("/");
- String tempPath = codeBaseDir + classArray[classArray.length - 1] + "_" +
- System.currentTimeMillis() + "_" + System.nanoTime() + JAVA_SUFFIX;
- return tempPath;
- }
-
- private static class MethodVisitor extends VoidVisitorAdapter {
-
- private List importClasses = new ArrayList<>();
-
- @Override
- public void visit(ImportDeclaration n, Void arg) {
- importClasses.add(parseClass(n.toString()));
- super.visit(n, arg);
- }
-
- private String parseClass(String importInfo) {
- String className = importInfo.substring(7, importInfo.length() - 2);
- if (importInfo.startsWith("import static ")) {
- // 获取静态方法的类信息
- className = importInfo.substring(14, importInfo.lastIndexOf("."));
- }
- if (!className.contains(".")) {
- throw new IllegalStateException(String.format("Import Class [%s] is Illegal !!", className));
- }
- return className;
- }
- }
-
- private static class ContractPackage {
-
- private String packageName;
-
- private boolean isTotal = false;
-
- public ContractPackage() {
- }
-
- public ContractPackage(String totalPackage) {
- if (totalPackage.endsWith("*")) {
- this.packageName = totalPackage.substring(0, totalPackage.length() - 2).trim();
- this.isTotal = true;
- } else {
- this.packageName = totalPackage;
- }
- }
-
- public String getPackageName() {
- return packageName;
- }
-
- public boolean isTotal() {
- return isTotal;
- }
- }
-}
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/asm/ASMClassVisitor.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/asm/ASMClassVisitor.java
new file mode 100644
index 00000000..27bfeee4
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/asm/ASMClassVisitor.java
@@ -0,0 +1,22 @@
+package com.jd.blockchain.contract.maven.asm;
+
+import com.jd.blockchain.contract.maven.ContractClass;
+import com.jd.blockchain.contract.maven.ContractMethod;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class ASMClassVisitor extends ClassVisitor {
+
+ private ContractClass contractClass;
+
+ public ASMClassVisitor(ContractClass contractClass) {
+ super(Opcodes.ASM5);
+ this.contractClass = contractClass;
+ }
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+ MethodVisitor superMV = super.visitMethod(access, name, desc, signature, exceptions);
+ ContractMethod method = this.contractClass.method(name);
+ return new ASMMethodVisitor(superMV, method);
+ }
+}
\ No newline at end of file
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/asm/ASMMethodVisitor.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/asm/ASMMethodVisitor.java
new file mode 100644
index 00000000..03488203
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/asm/ASMMethodVisitor.java
@@ -0,0 +1,108 @@
+package com.jd.blockchain.contract.maven.asm;
+
+import com.jd.blockchain.contract.maven.ContractMethod;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.TypePath;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ASMMethodVisitor extends MethodVisitor {
+
+ private ContractMethod method;
+
+ public ASMMethodVisitor(MethodVisitor mv, ContractMethod method) {
+ super(Opcodes.ASM5, mv);
+ this.method = method;
+ }
+
+ @Override
+ public void visitFieldInsn(int type, String cName, String fName, String fType) {
+ if (type == 178 || type == 179) {
+ this.method.addStaticField(cName, fName, fType);
+ } else {
+ this.method.addField(cName, fName, fType);
+ }
+ super.visitFieldInsn(type, cName, fName, fType);
+ }
+
+ @Override
+ public void visitMethodInsn(int type, String cName, String mName, String params, boolean b) {
+ ParamsAndReturn paramsAndReturn = resolveParamsAndReturn(params);
+ this.method.addMethod(cName, mName, paramsAndReturn.paramTypes, paramsAndReturn.returnTypes);
+ super.visitMethodInsn(type, cName, mName, params, b);
+ }
+
+ private ParamsAndReturn resolveParamsAndReturn(String params) {
+ // 格式:
+ // 1、(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+ // 2、()I
+ // 3、(Ljava/lang/String;)V
+ // 4、()V
+ // 5、([Ljava/lang/Object;)Ljava/util/List; false
+ // 从上面分析可以得出:括号内的是入参,右括号后面的是返回值,其中V表示Void,即空;
+ String[] paramArray = params.split("\\)");
+ String paramTypeChars = "";
+ if (!paramArray[0].equals("(")) {
+ // 表明入参不为空
+ paramTypeChars = paramArray[0].split("\\(")[1];
+ }
+ String returnTypeChars = paramArray[1];
+ return new ParamsAndReturn(paramTypeChars, returnTypeChars);
+ }
+
+ static class ParamsAndReturn {
+
+ String[] paramTypes;
+
+ String[] returnTypes;
+
+ public ParamsAndReturn(String paramsTypeChars, String returnTypeChars) {
+ initParamsType(paramsTypeChars);
+ initReturnType(returnTypeChars);
+ }
+
+ private void initParamsType(String paramsTypeChars) {
+ List paramList = handleTypes(paramsTypeChars);
+ if (!paramList.isEmpty()) {
+ this.paramTypes = new String[paramList.size()];
+ paramList.toArray(this.paramTypes);
+ }
+ }
+
+ private void initReturnType(String returnTypeChar) {
+ // 按照分号分隔
+ List returnList = handleTypes(returnTypeChar);
+ if (!returnList.isEmpty()) {
+ this.returnTypes = new String[returnList.size()];
+ returnList.toArray(this.returnTypes);
+ }
+ }
+
+ private List handleTypes(String typeChars) {
+ String[] types = typeChars.split(";");
+ List typeList = new ArrayList<>();
+ if (types.length > 0) {
+ for (String type : types) {
+ if (type.length() > 0) {
+ if (type.startsWith("[L") && type.length() > 2) {
+ // 说明是数组
+ typeList.add(type.substring(2) + "[]");
+ } else if (type.startsWith("[") && type.length() > 1) {
+ // 说明是数组
+ typeList.add(type.substring(1));
+ } else if (type.startsWith("L") && type.length() > 1) {
+ // 说明是非基础类型
+ typeList.add(type.substring(1));
+ } else {
+ typeList.add(type);
+ }
+ }
+ }
+ }
+ return typeList;
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/rule/BlackList.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/rule/BlackList.java
new file mode 100644
index 00000000..97044df6
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/rule/BlackList.java
@@ -0,0 +1,154 @@
+package com.jd.blockchain.contract.maven.rule;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class BlackList {
+
+ public static final String COMMON_METHOD = "*";
+
+ public static final String INIT_METHOD = "init";
+
+ // 合约黑名单
+ private final Map blackClassMap = new ConcurrentHashMap<>();
+
+ private final List blackPackages = new ArrayList<>();
+
+ public synchronized BlackList addBlack(String className, String methodName) {
+ String trimClassName = className.trim();
+ BlackClass blackClass = blackClassMap.get(trimClassName);
+ if (blackClass != null) {
+ blackClass.addMethod(methodName);
+ } else {
+ blackClass = new BlackClass(trimClassName);
+ blackClass.addMethod(methodName);
+ blackClassMap.put(trimClassName, blackClass);
+ }
+ return this;
+ }
+
+ public synchronized BlackList addBlack(Class> clazz, String methodName) {
+ return addBlack(clazz.getName(), methodName);
+ }
+
+ public synchronized BlackList addBlack(Class> clazz) {
+ return addBlack(clazz.getName(), COMMON_METHOD);
+ }
+
+ public synchronized BlackList addBlackPackage(String packageName) {
+ blackPackages.add(packageName.trim() + "."); // 末尾增加一个点,防止后续判断是拼凑
+ return this;
+ }
+
+ public boolean isBlackClass(String className) {
+ if (isContainsPackage(className)) {
+ return true;
+ }
+ BlackClass blackClass = blackClassMap.get(className);
+ if (blackClass == null) {
+ return false;
+ }
+ return blackClass.isBlack();
+ }
+
+ public boolean isBlack(Class> clazz, String methodName) {
+ // 判断该Class是否属于黑名单
+ if (isCurrentClassBlack(clazz, methodName)) {
+ return true;
+ }
+ // 当前Class不是黑名单的情况下,处理其对应的父类和接口
+ // 获取该Class对应的接口和父类列表
+ Set> superClassAndAllInterfaces = new HashSet<>();
+
+ loadSuperClassAndAllInterfaces(clazz, superClassAndAllInterfaces);
+
+ // 循环判断每个父类和接口
+ for (Class> currClass : superClassAndAllInterfaces) {
+ if (isCurrentClassBlack(currClass, methodName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isCurrentClassBlack(Class> clazz, String methodName) {
+
+ String packageName = clazz.getPackage().getName();
+ for (String bp : blackPackages) {
+ if (packageName.equals(bp) || packageName.startsWith(bp)) {
+ return true;
+ }
+ }
+ // 判断该类本身是否属于黑名单
+ String className = clazz.getName();
+ BlackClass blackClass = blackClassMap.get(className);
+ if (blackClass != null) {
+ // 判断其方法
+ return blackClass.isBlack(methodName);
+ }
+ return false;
+ }
+
+ public boolean isBlackField(Class> clazz) {
+ return isBlack(clazz, INIT_METHOD);
+ }
+
+ private boolean isContainsPackage(String className) {
+ for (String bp : blackPackages) {
+ if (className.equals(bp) || className.startsWith(bp)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void loadSuperClassAndAllInterfaces(Class> currentClass, Set> allClassList) {
+ if (currentClass == null) {
+ return;
+ }
+
+ if (!allClassList.contains(currentClass)) {
+ allClassList.add(currentClass);
+ // 处理其父类
+ Class> superClass = currentClass.getSuperclass();
+ loadSuperClassAndAllInterfaces(superClass, allClassList);
+
+ // 处理其所有接口
+ Class>[] allInterfaces = currentClass.getInterfaces();
+ if (allInterfaces != null && allInterfaces.length > 0) {
+ for (Class> intf : allInterfaces) {
+ loadSuperClassAndAllInterfaces(intf, allClassList);
+ }
+ }
+ }
+ }
+
+ private static class BlackClass {
+
+ String className;
+
+ Set methods = new HashSet<>();
+
+ BlackClass(String className) {
+ this.className = className;
+ }
+
+ void addMethod(String methodName) {
+ methods.add(methodName);
+ }
+
+ boolean isBlack(String methodName) {
+ // 假设method为*则表示所有的方法
+ if (methods.contains(COMMON_METHOD)) {
+ return true;
+ }
+ return methods.contains(methodName);
+ }
+
+ boolean isBlack() {
+ return isBlack(COMMON_METHOD);
+ }
+ }
+}
+
+
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/rule/DependencyExclude.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/rule/DependencyExclude.java
new file mode 100644
index 00000000..6c3dca44
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/rule/DependencyExclude.java
@@ -0,0 +1,93 @@
+package com.jd.blockchain.contract.maven.rule;
+
+import com.jd.blockchain.contract.ContractJarUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class DependencyExclude {
+
+ private static final String COMMON_ARTIFACTID = "*";
+
+ private static final String CONFIG = "provided.conf";
+
+ private static final Map> DEPENDENCYS = new ConcurrentHashMap<>();
+
+ static {
+ try {
+ init();
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private static void init() throws Exception {
+ List readLines = ContractJarUtils.loadConfig(CONFIG);
+ if (!readLines.isEmpty()) {
+ for (String line : readLines) {
+ // groupId/artifactId
+ String[] lines = line.split(",");
+ if (lines.length > 0) {
+ for (String depend : lines) {
+ String[] depends = depend.split("/");
+ if (depends.length == 2) {
+ String groupId = depends[0], artifactId = depends[1];
+ Dependency dependency = new Dependency(groupId, artifactId);
+ addDependency(dependency);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private synchronized static void addDependency(Dependency dependency) {
+ String groupId = dependency.groupId;
+ if (!DEPENDENCYS.containsKey(groupId)) {
+ List dependencies = new ArrayList<>();
+ dependencies.add(dependency);
+ DEPENDENCYS.put(groupId, dependencies);
+ } else {
+ List dependencies = DEPENDENCYS.get(groupId);
+ dependencies.add(dependency);
+ }
+ }
+
+ public boolean isExclude(String groupId, String artifactId) {
+ List dependencies = DEPENDENCYS.get(groupId);
+
+ if (dependencies == null || dependencies.isEmpty()) {
+ return false;
+ }
+
+ for (Dependency dependency : dependencies) {
+ if (dependency.isEqualArtifactId(artifactId)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ static class Dependency {
+
+ String groupId;
+
+ String artifactId;
+
+ public Dependency(String groupId, String artifactId) {
+ this.groupId = groupId;
+ this.artifactId = artifactId;
+ }
+
+ public boolean isEqualArtifactId(String artiId) {
+ if (artifactId.equals(COMMON_ARTIFACTID)) {
+ return true;
+ }
+ return artifactId.equals(artiId);
+ }
+ }
+}
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/rule/WhiteList.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/rule/WhiteList.java
new file mode 100644
index 00000000..eeb1e250
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/rule/WhiteList.java
@@ -0,0 +1,30 @@
+package com.jd.blockchain.contract.maven.rule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class WhiteList {
+
+ // 合约白名单(白名单通常数量较少,主要是JDChain内部包)
+ private final List whiteClasses = new ArrayList<>();
+
+ public void addWhite(String className) {
+ whiteClasses.add(className.trim());
+ }
+
+ public boolean isWhite(Class> clazz) {
+ String className = clazz.getName();
+ return isWhite(className);
+ }
+
+ public boolean isWhite(String className) {
+ for (String white : whiteClasses) {
+ if (white.equals(className) || className.startsWith(white)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/verify/ResolveEngine.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/verify/ResolveEngine.java
new file mode 100644
index 00000000..059f7337
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/verify/ResolveEngine.java
@@ -0,0 +1,133 @@
+package com.jd.blockchain.contract.maven.verify;
+
+import com.jd.blockchain.contract.ContractJarUtils;
+import com.jd.blockchain.contract.ContractType;
+import org.apache.commons.io.FileUtils;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugin.logging.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import static com.jd.blockchain.contract.ContractJarUtils.*;
+
+public class ResolveEngine {
+
+ private Log LOGGER;
+
+ private File jarFile;
+
+ private String mainClass;
+
+ public ResolveEngine(Log LOGGER, File jarFile, String mainClass) {
+ this.LOGGER = LOGGER;
+ this.jarFile = jarFile;
+ this.mainClass = mainClass;
+ }
+
+ public File verify() throws MojoFailureException {
+ try {
+ // 首先校验MainClass
+ ClassLoader classLoader = verifyMainClass(jarFile);
+
+ // 检查jar包中所有的class的命名,要求其包名不能为com.jd.blockchain.*
+ LinkedList totalClasses = loadAllClass(jarFile);
+
+ if (!totalClasses.isEmpty()) {
+
+ for (String clazz : totalClasses) {
+
+ String dotClassName = dotClassName(clazz);
+
+ LOGGER.debug(String.format("Verify Dependency Class[%s] start......", dotClassName));
+ // 获取其包名
+ Class> currentClass = classLoader.loadClass(dotClassName);
+
+ String packageName = currentClass.getPackage().getName();
+
+ if (ContractJarUtils.isJDChainPackage(packageName)) {
+ throw new IllegalStateException(String.format("Class[%s]'s package[%s] cannot start with %s !",
+ dotClassName, packageName, ContractJarUtils.JDCHAIN_PACKAGE));
+ }
+
+ LOGGER.debug(String.format("Verify Class[%s] end......", clazz));
+ }
+ }
+
+ // 处理完成之后,生成finalName-JDChain-Contract.jar
+ return compileCustomJar();
+ } catch (Exception e) {
+ LOGGER.error(e.getMessage());
+ throw new MojoFailureException(e.getMessage());
+ }
+ }
+
+ private File compileCustomJar() throws IOException {
+
+ String fileParentPath = jarFile.getParentFile().getPath();
+
+ String jarFileName = jarFile.getName();
+
+ String fileName = jarFileName.substring(0, jarFileName.lastIndexOf("."));
+
+ // 首先将Jar包转换为指定的格式
+ String dstJarPath = fileParentPath + File.separator +
+ fileName + "-temp-" + System.currentTimeMillis() + ".jar";
+
+ File srcJar = jarFile, dstJar = new File(dstJarPath);
+
+ LOGGER.debug(String.format("Jar from [%s] to [%s] Copying", jarFile.getPath(), dstJarPath));
+ // 首先进行Copy处理
+ copy(srcJar, dstJar);
+
+ LOGGER.debug(String.format("Jar from [%s] to [%s] Copied", jarFile.getPath(), dstJarPath));
+
+ byte[] txtBytes = contractMF(FileUtils.readFileToByteArray(dstJar)).getBytes(StandardCharsets.UTF_8);
+
+ String finalJarPath = fileParentPath + File.separator + fileName + "-JDChain-Contract.jar";
+
+ File finalJar = new File(finalJarPath);
+
+ // 生成最终的Jar文件
+ copy(dstJar, finalJar, contractMFJarEntry(), txtBytes, null);
+
+ // 删除临时文件
+ FileUtils.forceDelete(dstJar);
+
+ return finalJar;
+ }
+
+ private ClassLoader verifyMainClass(File jarFile) throws Exception {
+ // 加载main-class,开始校验类型
+ LOGGER.debug(String.format("Verify Jar [%s] 's MainClass start...", jarFile.getName()));
+ URL jarURL = jarFile.toURI().toURL();
+ ClassLoader classLoader = new URLClassLoader(new URL[]{jarURL});
+ Class> mClass = classLoader.loadClass(mainClass);
+ ContractType.resolve(mClass);
+ LOGGER.debug(String.format("Verify Jar [%s] 's MainClass end...", jarFile.getName()));
+ return classLoader;
+ }
+
+ private LinkedList loadAllClass(File file) throws Exception {
+ JarFile jarFile = new JarFile(file);
+ LinkedList allClass = new LinkedList<>();
+ Enumeration jarEntries = jarFile.entries();
+ while (jarEntries.hasMoreElements()) {
+ JarEntry jarEntry = jarEntries.nextElement();
+ String entryName = jarEntry.getName();
+ if (entryName.endsWith(".class")) {
+ // 内部类,不需要处理
+ if (!entryName.contains("$")) {
+ allClass.addLast(entryName.substring(0, entryName.length() - 6));
+ }
+ }
+ }
+ return allClass;
+ }
+}
diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/verify/VerifyEngine.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/verify/VerifyEngine.java
new file mode 100644
index 00000000..ea3d723c
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/verify/VerifyEngine.java
@@ -0,0 +1,216 @@
+package com.jd.blockchain.contract.maven.verify;
+
+import com.jd.blockchain.contract.ContractJarUtils;
+import com.jd.blockchain.contract.maven.ContractClass;
+import com.jd.blockchain.contract.maven.ContractField;
+import com.jd.blockchain.contract.maven.ContractMethod;
+import com.jd.blockchain.contract.maven.asm.ASMClassVisitor;
+import com.jd.blockchain.contract.maven.rule.BlackList;
+import com.jd.blockchain.contract.maven.rule.WhiteList;
+import org.apache.maven.plugin.logging.Log;
+import org.objectweb.asm.ClassReader;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static com.jd.blockchain.contract.ContractJarUtils.loadAllClasses;
+
+
+public class VerifyEngine {
+
+ private Log LOGGER;
+
+ private File jarFile;
+
+ private String mainClass;
+
+ private BlackList black;
+
+ private WhiteList white;
+
+ // 代表的只是当前方法,不代表该方法中的内部方法
+ private Set haveManagedMethods = new HashSet<>();
+
+ // 代表的是处理的参数
+ private Set haveManagedFields = new HashSet<>();
+
+ public VerifyEngine(Log LOGGER, File jarFile, String mainClass, BlackList black, WhiteList white) {
+ this.LOGGER = LOGGER;
+ this.jarFile = jarFile;
+ this.mainClass = mainClass;
+ this.black = black;
+ this.white = white;
+ }
+
+ public void verify() throws Exception {
+ // 加载所有的jar,然后ASM获取MAP
+ URL jarURL = jarFile.toURI().toURL();
+
+ URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{jarURL});
+
+ // 解析Jar包中所有的Class
+ Map allContractClasses = resolveClasses(jarClasses());
+
+ // 开始处理MainClass
+ verify(urlClassLoader, allContractClasses);
+
+ }
+
+ public void verify(URLClassLoader urlClassLoader, Map allContractClasses) throws Exception {
+ // 获取MainClass
+ String mainClassKey = convertClassKey(mainClass);
+ ContractClass mainContractClass = allContractClasses.get(mainClassKey);
+ if (mainContractClass == null) {
+ LOGGER.error(String.format("Load Main Class = [%s] NULL !!!", mainClass));
+ throw new IllegalStateException("MainClass is NULL !!!");
+ }
+ // 校验该Class中所有方法
+ Map methods = mainContractClass.getMethods();
+ if (!methods.isEmpty()) {
+ for (Map.Entry entry : methods.entrySet()) {
+ ContractMethod method = entry.getValue();
+ verify(urlClassLoader, allContractClasses, method);
+ }
+ }
+ }
+
+ public void verify(URLClassLoader urlClassLoader, Map allContractClasses, ContractMethod method) throws Exception {
+ // 获取方法中涉及到的所有的Class及Method
+ // 首先判断该方法对应的Class是否由urlClassLoader加载
+ // 首先判断该ClassName对应方法是否处理过
+ String managedKey = managedKey(method);
+ if (haveManagedMethods.contains(managedKey)) {
+ return;
+ }
+ // 将该方法设置为已处理
+ haveManagedMethods.add(managedKey);
+ String dotClassName = method.getDotClassName();
+ Class> dotClass = urlClassLoader.loadClass(dotClassName);
+
+ if (dotClass == null) {
+ return;
+ }
+ String dotClassLoader = null;
+ ClassLoader classLoader = dotClass.getClassLoader();
+
+ if (classLoader != null) {
+ dotClassLoader = dotClass.getClassLoader().toString();
+ }
+
+ if (dotClassLoader != null && dotClassLoader.contains("URLClassLoader")) {
+
+ // 说明是URLClassLoader,这个需要先从黑名单和白名单列表中操作
+ // 首先判断是否是黑名单,黑名单优先级最高
+ if (black.isBlack(dotClass, method.getMethodName())) {
+ throw new IllegalStateException(String.format("Class [%s] Method [%s] is Black !!!", dotClassName, method.getMethodName()));
+ } else {
+ // 不是黑名单的情况下,判断是否为白名单
+ if (white.isWhite(dotClass)) {
+ return;
+ }
+ // 如果不属于白名单,则需要判断其子方法
+ List innerMethods = method.getMethodList();
+ if (!innerMethods.isEmpty()) {
+ for (ContractMethod innerMethod : innerMethods) {
+ // 需要重新从AllMap中获取,因为生成时并未处理其关联关系
+ ContractClass innerClass = allContractClasses.get(innerMethod.getClassName());
+ if (innerClass != null) {
+ ContractMethod verifyMethod = innerClass.method(innerMethod.getMethodName());
+ verify(urlClassLoader, allContractClasses, verifyMethod);
+ } else {
+ verify(urlClassLoader, allContractClasses, innerMethod);
+ }
+ }
+ }
+ List innerFields = method.getAllFieldList();
+ if (!innerFields.isEmpty()) {
+ for (ContractField innerField : innerFields) {
+ verify(urlClassLoader, innerField);
+ }
+ }
+ }
+ } else {
+ // 非URLClassLoader加载的类,只需要做判断即可
+ // 1、不再需要获取其方法;
+ // 2、只需要判断黑名单不需要判断白名单
+ if (black.isBlack(dotClass, method.getMethodName())) {
+ throw new IllegalStateException(String.format("Class [%s] Method [%s] is Black !!!", dotClassName, method.getMethodName()));
+ }
+ }
+ }
+
+ public void verify(URLClassLoader urlClassLoader, ContractField field) throws Exception {
+ // 获取方法中涉及到的所有的Class及Method
+ // 首先判断该方法对应的Class是否由urlClassLoader加载
+ // 首先判断该ClassName对应方法是否处理过
+ String managedKey = managedKey(field);
+ if (haveManagedFields.contains(managedKey)) {
+ return;
+ }
+ // 将该字段设置为已读
+ haveManagedFields.add(managedKey);
+
+ Class> dotClass = urlClassLoader.loadClass(field.getDotClassName());
+
+ if (dotClass == null) {
+ return;
+ }
+
+ if (black.isBlackField(dotClass)) {
+ throw new IllegalStateException(String.format("Class [%s] Field [%s] is Black !!!", field.getDotClassName(), field.getFieldName()));
+ }
+ }
+
+ private Map jarClasses() throws Exception {
+ return loadAllClasses(jarFile);
+ }
+
+ private Map resolveClasses(Map allClasses) {
+
+ Map allContractClasses = new ConcurrentHashMap<>();
+
+ for (Map.Entry entry : allClasses.entrySet()) {
+ byte[] classContent = entry.getValue();
+ if (classContent == null || classContent.length == 0) {
+ continue;
+ }
+ String className = entry.getKey().substring(0, entry.getKey().length() - 6);
+
+ String dotClassName = ContractJarUtils.dotClassName(className);
+ if (white.isWhite(dotClassName) || black.isBlackClass(dotClassName)) {
+ continue;
+ }
+
+ LOGGER.info(String.format("Resolve Class [%s] ...", className));
+
+ ContractClass contractClass = new ContractClass(className);
+ ClassReader cr = new ClassReader(classContent);
+ cr.accept(new ASMClassVisitor(contractClass), ClassReader.SKIP_DEBUG);
+ allContractClasses.put(className, contractClass);
+ }
+ return allContractClasses;
+ }
+
+ private String convertClassKey(final String classKey) {
+ String newClassKey = classKey;
+ if (classKey.endsWith(".class")) {
+ newClassKey = classKey.substring(0, classKey.length() - 6);
+ }
+ newClassKey = newClassKey.replaceAll("\\.", "/");
+ return newClassKey;
+ }
+
+ private String managedKey(ContractMethod method) {
+ return method.getDotClassName() + "-" + method.getMethodName();
+ }
+
+ private String managedKey(ContractField field) {
+ return field.getDotClassName() + "--" + field.getFieldName();
+ }
+}
diff --git a/source/contract/contract-maven-plugin/src/main/resources/blacks.conf b/source/contract/contract-maven-plugin/src/main/resources/blacks.conf
new file mode 100644
index 00000000..533c4850
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/resources/blacks.conf
@@ -0,0 +1,11 @@
+java.io.*
+java.nio.*
+java.net.*
+java.sql.*
+java.lang.reflect.*
+java.lang.Class
+java.lang.ClassLoader
+java.util.Random
+java.lang.System-currentTimeMillis
+java.lang.System-nanoTime
+com.jd.blockchain.ledger.BlockchainKeyGenerator-generate
\ No newline at end of file
diff --git a/source/contract/contract-maven-plugin/src/main/resources/config.properties b/source/contract/contract-maven-plugin/src/main/resources/config.properties
deleted file mode 100644
index b54de2fb..00000000
--- a/source/contract/contract-maven-plugin/src/main/resources/config.properties
+++ /dev/null
@@ -1,8 +0,0 @@
-#black.name.list:打包为合约Jar后,每个Class文件不允许使用或包含的名称,默认不允许使用com.jd.blockchain.*及一些内部已经引用的包
-black.name.list=com.jd.blockchain.*, com.alibaba.fastjson.*, org.apache.commons.io.*, org.apache.commons.codec.*, io.netty.*
-
-#black.package.list:打包为合约中的每个Class都不允许使用的包列表,某个包下面的所有包通过.*表示
-black.package.list=java.io.*, java.nio.*, java.net.*, org.apache.commons.io.*
-
-#black.class.list:打包为合约中的每个Class都不允许使用的类列表
-black.class.list=java.util.Random, com.jd.blockchain.ledger.BlockchainKeyGenerator
\ No newline at end of file
diff --git a/source/contract/contract-maven-plugin/src/main/resources/provideds.conf b/source/contract/contract-maven-plugin/src/main/resources/provideds.conf
new file mode 100644
index 00000000..b2df66eb
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/resources/provideds.conf
@@ -0,0 +1,22 @@
+com.jd.blockchain/*
+com.alibaba/fastjson
+org.slf4j/*
+org.apache.logging.log4j/*
+org.aspectj/*
+redis.clients/*
+org.rocksdb/*
+io.grpc/*
+org.apache.commons/*
+org.apache.httpcomponents/*
+org.apache.logging.log4j/*
+org.reflections/reflections
+com.google.guava/guava
+commons-cli/commons-cli
+commons-codec/commons-codec
+commons-httpclient/commons-httpclient
+commons-io/commons-io
+io.netty/*
+org.slf4j/*
+org.springframework.boot/*
+org.springframework.security/*
+org.springframework/*
\ No newline at end of file
diff --git a/source/contract/contract-maven-plugin/src/main/resources/whites.conf b/source/contract/contract-maven-plugin/src/main/resources/whites.conf
new file mode 100644
index 00000000..a495db60
--- /dev/null
+++ b/source/contract/contract-maven-plugin/src/main/resources/whites.conf
@@ -0,0 +1 @@
+com.jd.blockchain.*
\ No newline at end of file
diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/contract/ContractJarUtils.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/contract/ContractJarUtils.java
index 5a183006..d27323f2 100644
--- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/contract/ContractJarUtils.java
+++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/contract/ContractJarUtils.java
@@ -4,21 +4,23 @@ import com.jd.blockchain.crypto.Crypto;
import com.jd.blockchain.crypto.HashDigest;
import com.jd.blockchain.crypto.HashFunction;
import com.jd.blockchain.utils.io.BytesUtils;
-import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
-import java.util.Enumeration;
-import java.util.Random;
+import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
public class ContractJarUtils {
+ public static final String BLACK_CONF = "black.conf";
+
+ public static final String WHITE_CONF = "white.conf";
+
private static final String CONTRACT_MF = "META-INF/CONTRACT.MF";
private static final HashFunction HASH_FUNCTION = Crypto.getHashFunction("SHA256");
@@ -27,6 +29,87 @@ public class ContractJarUtils {
private static final byte[] JDCHAIN_MARK = "JDChain".getBytes(StandardCharsets.UTF_8);
+ public static final String JDCHAIN_PACKAGE = "com.jd.blockchain";
+
+ public static boolean isJDChainPackage(String packageName) {
+ if (packageName.equals(JDCHAIN_PACKAGE)) {
+ return true;
+ }
+ return packageName.startsWith(JDCHAIN_PACKAGE + ".");
+ }
+
+ public static List loadWhiteConf() {
+
+ return resolveConfig(WHITE_CONF);
+ }
+
+ public static List loadBlackConf() {
+ return resolveConfig(BLACK_CONF);
+ }
+
+ public static List resolveConfig(String fileName) {
+ List configs = new ArrayList<>();
+
+ try {
+ List readLines = loadConfig(fileName);
+ if (!readLines.isEmpty()) {
+ for (String readLine : readLines) {
+ String[] lines = readLine.split(",");
+ configs.addAll(Arrays.asList(lines));
+ }
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+
+ return configs;
+ }
+
+ public static List loadConfig(String fileName) throws Exception {
+
+ return IOUtils.readLines(
+ ContractJarUtils.class.getResourceAsStream(File.separator + fileName));
+ }
+
+ public static Map loadAllClasses(final File jar) throws Exception {
+ Map allClasses = new HashMap<>();
+ JarFile jarFile = new JarFile(jar);
+ Enumeration jarEntries = jarFile.entries();
+ while(jarEntries.hasMoreElements()){
+ JarEntry jarEntry = jarEntries.nextElement();
+ String entryName = jarEntry.getName();
+ if (verify(entryName)) {
+ byte[] classContent = readStream(jarFile.getInputStream(jarEntry));
+ if (classContent != null && classContent.length > 0) {
+ allClasses.put(entryName, classContent);
+ }
+ }
+ }
+ jarFile.close();
+
+ return allClasses;
+ }
+
+ private static boolean verify(String entryName) {
+
+ if (entryName.endsWith(".class")
+ && !entryName.startsWith("META-INF")
+ && !entryName.contains("-")
+ && entryName.contains("/")) {
+ return true;
+ }
+ return false;
+ }
+
+ public static String dotClassName(String className) {
+ String dotClassName = className;
+ if (className.endsWith(".class")) {
+ dotClassName = className.substring(0, className.length() - 6);
+ }
+ dotClassName = dotClassName.replaceAll("/", ".");
+ return dotClassName;
+ }
+
public static void verify(byte[] chainCode) {
if (chainCode == null || chainCode.length == 0) {
throw new IllegalStateException("Contract's chaincode is empty !!!");