Browse Source

增加合约验证

tags/1.1.0
shaozhuguang 5 years ago
parent
commit
d1bbc6ec22
16 changed files with 640 additions and 302 deletions
  1. +6
    -5
      source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServer.java
  2. +128
    -128
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/CheckImportsMojo.java
  3. +56
    -65
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractCheckMojo.java
  4. +321
    -50
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractVerifyMojo.java
  5. +8
    -1
      source/contract/contract-maven-plugin/src/main/resources/config.properties
  6. +0
    -28
      source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/CheckImportsMojoTest.java
  7. +50
    -0
      source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractTestBase.java
  8. +1
    -2
      source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractVerifyMojoTest.java
  9. +47
    -0
      source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractVerifyTest.java
  10. BIN
      source/contract/contract-maven-plugin/src/test/resources/complex.jar
  11. +1
    -16
      source/gateway/pom.xml
  12. +1
    -3
      source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayQueryServiceHandler.java
  13. +17
    -0
      source/utils/utils-common/pom.xml
  14. +1
    -1
      source/utils/utils-common/src/main/java/com/jd/blockchain/utils/decompiler/loads/BytesTypeLoader.java
  15. +2
    -2
      source/utils/utils-common/src/main/java/com/jd/blockchain/utils/decompiler/utils/DecompilerUtils.java
  16. +1
    -1
      source/utils/utils-common/src/main/java/com/jd/blockchain/utils/jar/ContractJarUtils.java

+ 6
- 5
source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServer.java View File

@@ -123,11 +123,11 @@ public class BftsmartNodeServer extends DefaultRecoverable implements NodeServer
return;
}

protected void initConfig(int id, String systemConfig, String hostsConfig) {
this.tomConfig = new TOMConfiguration(id, systemConfig, hostsConfig);
}
// protected void initConfig(int id, String systemConfig, String hostsConfig) {
//
// this.tomConfig = new TOMConfiguration(id, systemConfig, hostsConfig);
//
// }

protected void initConfig(int id, Properties systemsConfig, HostsConfig hostConfig) {
this.tomConfig = new TOMConfiguration(id, systemsConfig, hostConfig);
@@ -309,6 +309,7 @@ public class BftsmartNodeServer extends DefaultRecoverable implements NodeServer

try {
LOGGER.debug("Start replica...[ID=" + getId() + "]");
// 调整绑定Host
this.replica = new ServiceReplica(tomConfig, this, this);
this.topology = new BftsmartTopology(replica.getReplicaContext().getCurrentView());
status = Status.RUNNING;


+ 128
- 128
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/CheckImportsMojo.java View File

@@ -1,128 +1,128 @@
package com.jd.blockchain;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.PackageDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.jd.blockchain.contract.ContractType;
import com.jd.blockchain.utils.IllegalDataException;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Properties;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
/**
* first step, we want to parse the source code by javaParse. But it's repeated and difficult to parse the source.
* This is a try of "from Initail to Abandoned".
* Since we are good at the class, why not?
* Now we change a way of thinking, first we pre-compile the source code, then parse the *.jar.
*
* by zhaogw
* date 2019-06-05 16:17
*/
@Mojo(name = "checkImports")
public class CheckImportsMojo extends AbstractMojo {
Logger logger = LoggerFactory.getLogger(CheckImportsMojo.class);
@Parameter(defaultValue = "${project}", required = true, readonly = true)
private MavenProject project;
/**
* jar's name;
*/
@Parameter
private String finalName;
@Override
public void execute() throws MojoFailureException {
List<Path> sources;
try {
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("config.properties");
Properties properties = new Properties();
properties.load(inputStream);
String[] packageBlackList = properties.getProperty("blacklist").split(",");
Path baseDirPath = project.getBasedir().toPath();
sources = Files.find(baseDirPath, Integer.MAX_VALUE, (file, attrs) -> (file.toString().endsWith(".java"))).collect(Collectors.toList());
for (Path path : sources) {
CompilationUnit compilationUnit = JavaParser.parse(path);
compilationUnit.accept(new MethodVisitor(), null);
NodeList<ImportDeclaration> imports = compilationUnit.getImports();
for (ImportDeclaration imp : imports) {
String importName = imp.getName().asString();
for (String item : packageBlackList) {
if (importName.startsWith(item)) {
throw new MojoFailureException("在源码中不允许包含此引入包:" + importName);
}
}
}
//now we parse the jar;
String jarPath = project.getBuild().getDirectory()+ File.separator+finalName+".jar";
File jarFile = new File(jarPath);
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);
try {
Class mainClass = classLoader.loadClass(contractMainClass);
ContractType.resolve(mainClass);
} catch (ClassNotFoundException e) {
throw new IllegalDataException(e.getMessage());
}
}
} catch (IOException exception) {
logger.error(exception.getMessage());
throw new MojoFailureException("IO ERROR");
} catch (NullPointerException e) {
logger.error(e.getMessage());
}
}
private class MethodVisitor extends VoidVisitorAdapter<Void> {
@Override
public void visit(MethodDeclaration n, Void arg) {
/* here you can access the attributes of the method.
this method will be called for all methods in this
CompilationUnit, including inner class methods */
logger.info("method:"+n.getName());
super.visit(n, arg);
}
@Override
public void visit(ClassOrInterfaceDeclaration n, Void arg) {
logger.info("class:"+n.getName()+" extends:"+n.getExtendedTypes()+" implements:"+n.getImplementedTypes());
super.visit(n, arg);
}
@Override
public void visit(PackageDeclaration n, Void arg) {
logger.info("package:"+n.getName());
super.visit(n, arg);
}
}
}
//package com.jd.blockchain;
//
//import com.github.javaparser.JavaParser;
//import com.github.javaparser.ast.CompilationUnit;
//import com.github.javaparser.ast.ImportDeclaration;
//import com.github.javaparser.ast.NodeList;
//import com.github.javaparser.ast.PackageDeclaration;
//import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
//import com.github.javaparser.ast.body.MethodDeclaration;
//import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
//import com.jd.blockchain.contract.ContractType;
//import com.jd.blockchain.utils.IllegalDataException;
//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.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//
//import java.io.File;
//import java.io.IOException;
//import java.io.InputStream;
//import java.net.URL;
//import java.net.URLClassLoader;
//import java.nio.file.Files;
//import java.nio.file.Path;
//import java.util.List;
//import java.util.Properties;
//import java.util.jar.Attributes;
//import java.util.jar.JarFile;
//import java.util.stream.Collectors;
//
///**
// * first step, we want to parse the source code by javaParse. But it's repeated and difficult to parse the source.
// * This is a try of "from Initail to Abandoned".
// * Since we are good at the class, why not?
// * Now we change a way of thinking, first we pre-compile the source code, then parse the *.jar.
// *
// * by zhaogw
// * date 2019-06-05 16:17
// */
//@Mojo(name = "checkImports")
//public class CheckImportsMojo extends AbstractMojo {
//
// Logger logger = LoggerFactory.getLogger(CheckImportsMojo.class);
//
// @Parameter(defaultValue = "${project}", required = true, readonly = true)
// private MavenProject project;
//
// /**
// * jar's name;
// */
// @Parameter
// private String finalName;
//
// @Override
// public void execute() throws MojoFailureException {
// List<Path> sources;
// try {
// InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("config.properties");
// Properties properties = new Properties();
// properties.load(inputStream);
// String[] packageBlackList = properties.getProperty("blacklist").split(",");
// Path baseDirPath = project.getBasedir().toPath();
// sources = Files.find(baseDirPath, Integer.MAX_VALUE, (file, attrs) -> (file.toString().endsWith(".java"))).collect(Collectors.toList());
// for (Path path : sources) {
// CompilationUnit compilationUnit = JavaParser.parse(path);
//
// compilationUnit.accept(new MethodVisitor(), null);
//
// NodeList<ImportDeclaration> imports = compilationUnit.getImports();
// for (ImportDeclaration imp : imports) {
// String importName = imp.getName().asString();
// for (String item : packageBlackList) {
// if (importName.startsWith(item)) {
// throw new MojoFailureException("在源码中不允许包含此引入包:" + importName);
// }
// }
// }
//
// //now we parse the jar;
// String jarPath = project.getBuild().getDirectory()+ File.separator+finalName+".jar";
// File jarFile = new File(jarPath);
// 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);
// try {
// Class mainClass = classLoader.loadClass(contractMainClass);
// ContractType.resolve(mainClass);
// } catch (ClassNotFoundException e) {
// throw new IllegalDataException(e.getMessage());
// }
// }
// } catch (IOException exception) {
// logger.error(exception.getMessage());
// throw new MojoFailureException("IO ERROR");
// } catch (NullPointerException e) {
// logger.error(e.getMessage());
// }
// }
//
// private class MethodVisitor extends VoidVisitorAdapter<Void> {
// @Override
// public void visit(MethodDeclaration n, Void arg) {
// /* here you can access the attributes of the method.
// this method will be called for all methods in this
// CompilationUnit, including inner class methods */
// logger.info("method:"+n.getName());
// super.visit(n, arg);
// }
//
// @Override
// public void visit(ClassOrInterfaceDeclaration n, Void arg) {
// logger.info("class:"+n.getName()+" extends:"+n.getExtendedTypes()+" implements:"+n.getImplementedTypes());
//
// super.visit(n, arg);
// }
//
// @Override
// public void visit(PackageDeclaration n, Void arg) {
// logger.info("package:"+n.getName());
// super.visit(n, arg);
// }
//
// }
//}

+ 56
- 65
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractCheckMojo.java View File

@@ -1,6 +1,7 @@
package com.jd.blockchain;

import com.jd.blockchain.utils.ConsoleUtils;
import com.jd.blockchain.ledger.BlockchainKeyGenerator;
import org.apache.commons.io.FileUtils;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
@@ -12,7 +13,6 @@ 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.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@@ -22,9 +22,25 @@ import java.util.Collections;
import java.util.List;


@Mojo(name = "contractCheck")
@Mojo(name = "Contract.Check")
public class ContractCheckMojo extends AbstractMojo {
Logger logger = LoggerFactory.getLogger(ContractCheckMojo.class);
Logger LOG = LoggerFactory.getLogger(ContractCheckMojo.class);

public static final String CONTRACT_VERIFY = "Contract.Verify";

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 = "verify";

private static final String GOALS_PACKAGE = "package";

private static final String OUT_POM_XML = "OutPom.xml";

@Parameter(defaultValue = "${project}", required = true, readonly = true)
private MavenProject project;
@@ -58,27 +74,21 @@ public class ContractCheckMojo extends AbstractMojo {
*/
@Override
public void execute() {
this.compileFiles();

compileFiles();
}

private void compileFiles(){
// 获取当前项目pom.xml文件所在路径
// URL targetClasses = this.getClass().getClassLoader().getResource("");
// File file = new File(targetClasses.getPath());
// String pomXmlPath = file.getParentFile().getParent() + File.separator + "pom.xml";
try (FileInputStream fis = new FileInputStream(project.getFile())) {

FileInputStream fis = null;
try {
// fis = new FileInputStream(new File(pomXmlPath));
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("com.jd.blockchain:contract-maven-plugin");
Plugin plugin = model.getBuild().getPluginsAsMap()
.get(JDCHAIN_PACKAGE + ":" + CONTRACT_MAVEN_PLUGIN);
if(plugin == null){
plugin = model.getBuild().getPluginsAsMap().get("org.apache.maven.plugins:contract-maven-plugin");
plugin = model.getBuild().getPluginsAsMap()
.get(APACHE_MAVEN_PLUGINS + ":" + CONTRACT_MAVEN_PLUGIN);
}

if(plugin == null) {
@@ -86,26 +96,18 @@ public class ContractCheckMojo extends AbstractMojo {
}

model.getBuild().removePlugin(plugin);
// model.getBuild().setPlugins(null);

// ConsoleUtils.info("----- 不携带Plugin -----");
// print(model);

List<Plugin> plugins = new ArrayList<>();
plugins.add(createAssembly());
plugins.add(createCheckImports());
plugins.add(createContractVerify());

model.getBuild().setPlugins(plugins);

ConsoleUtils.info("----- add Plugin -----");
handle(model);

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
LOG.error(e.getMessage());
throw new IllegalStateException(e);
}
}

@@ -116,43 +118,35 @@ public class ContractCheckMojo extends AbstractMojo {
try {
request.setPomFile(file);

request.setGoals( Collections.singletonList( "verify" ) );
// request.setMavenOpts("-DmainClass="+mainClass);
request.setGoals(Collections.singletonList(GOALS_VERIFY));
invoker.setMavenHome(new File(mvnHome));
invoker.execute(request);
} catch (MavenInvocationException e) {
e.printStackTrace();
LOG.error(e.getMessage());
throw new IllegalStateException(e);
}
}

private Plugin createCheckImports() {
private Plugin createContractVerify() {
Plugin plugin = new Plugin();
plugin.setGroupId("com.jd.blockchain");
plugin.setArtifactId("contract-maven-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);

PluginExecution pluginExecution = new PluginExecution();
pluginExecution.setId("make-assembly");
pluginExecution.setPhase("verify");
List <String> goals = new ArrayList<>();
goals.add("JDChain.Verify");
pluginExecution.setGoals(goals);
List<PluginExecution> pluginExecutions = new ArrayList<>();
pluginExecutions.add(pluginExecution);
plugin.setExecutions(pluginExecutions);
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");
plugin.setArtifactId(MAVEN_ASSEMBLY_PLUGIN);

Xpp3Dom configuration = new Xpp3Dom("configuration");

@@ -180,21 +174,28 @@ public class ContractCheckMojo extends AbstractMojo {
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> pluginExecution(String id, String phase, String goal) {
PluginExecution pluginExecution = new PluginExecution();
pluginExecution.setId("make-assembly");
pluginExecution.setPhase("package");
pluginExecution.setId(id);
pluginExecution.setPhase(phase);
List <String> goals = new ArrayList<>();
goals.add("single");
goals.add(goal);
pluginExecution.setGoals(goals);
List<PluginExecution> pluginExecutions = new ArrayList<>();
pluginExecutions.add(pluginExecution);
plugin.setExecutions(pluginExecutions);
return plugin;
return pluginExecutions;
}

private void handle(Model model) throws IOException {

MavenXpp3Writer mavenXpp3Writer = new MavenXpp3Writer();

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
@@ -203,20 +204,10 @@ public class ContractCheckMojo extends AbstractMojo {

byte[] buffer = outputStream.toByteArray();

//输出文件
// File fileOutput = new File("fileOut.xml");
// File fileOutput = File.createTempFile("fileOut",".xml");
File fileOutput = new File(project.getBasedir().getPath(),"fileOut.xml");
fileOutput.createNewFile();

ConsoleUtils.info("fileOutput's path="+fileOutput.getPath());
//创建文件输出流对象
FileOutputStream fos = new FileOutputStream(fileOutput);
//将字节数组fileInput中的内容输出到文件fileOut.xml中;
ConsoleUtils.info(new String(buffer));
fos.write(buffer);
fos.flush();
fos.close();
invokeCompile(fileOutput);
File outPom = new File(project.getBasedir().getPath(), OUT_POM_XML);

FileUtils.writeByteArrayToFile(outPom, buffer);

invokeCompile(outPom);
}
}

+ 321
- 50
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractVerifyMojo.java View File

@@ -20,6 +20,7 @@ import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ResourceUtils;

import java.io.*;
import java.net.URL;
@@ -27,15 +28,15 @@ import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.stream.Collectors;

import static com.jd.blockchain.ContractCheckMojo.CONTRACT_VERIFY;
import static com.jd.blockchain.utils.decompiler.utils.DecompilerUtils.decompileJarFile;
import static com.jd.blockchain.utils.jar.ContractJarUtils.*;

/**
@@ -47,10 +48,10 @@ import static com.jd.blockchain.utils.jar.ContractJarUtils.*;
* by zhaogw
* date 2019-06-05 16:17
*/
@Mojo(name = "JDChain.Verify")
@Mojo(name = CONTRACT_VERIFY)
public class ContractVerifyMojo extends AbstractMojo {

Logger logger = LoggerFactory.getLogger(ContractVerifyMojo.class);
Logger LOG = LoggerFactory.getLogger(ContractVerifyMojo.class);

@Parameter(defaultValue = "${project}", required = true, readonly = true)
private MavenProject project;
@@ -61,53 +62,249 @@ public class ContractVerifyMojo extends AbstractMojo {
@Parameter
private String finalName;

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";

@Override
public void execute() throws MojoFailureException {

List<Path> sources;
try {

File jarFile = copyAndManage();

InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("config.properties");
Properties properties = new Properties();
properties.load(inputStream);
String[] packageBlackList = properties.getProperty("blacklist").split(",");
Path baseDirPath = project.getBasedir().toPath();
sources = Files.find(baseDirPath, Integer.MAX_VALUE, (file, attrs) -> (file.toString().endsWith(".java"))).collect(Collectors.toList());
for (Path path : sources) {
CompilationUnit compilationUnit = JavaParser.parse(path);

compilationUnit.accept(new MethodVisitor(), null);

NodeList<ImportDeclaration> imports = compilationUnit.getImports();
for (ImportDeclaration imp : imports) {
String importName = imp.getName().asString();
for (String item : packageBlackList) {
if (importName.startsWith(item)) {
throw new MojoFailureException("在源码中不允许包含此引入包:" + importName);
Properties config = loadConfig();

List<ContractPackage> blackNameList = blackNameList(config);

List<ContractPackage> blackPackageList = blackPackageList(config);

Set<String> blackClassSet = blackClassSet(config);

LinkedList<String> 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) {
// 获取其包名
String packageName = packageName(clazz);

// 包的名字黑名单,不能打包该类进入Jar包中,或者合约不能命名这样的名字
boolean isNameBlack = false;
for (ContractPackage blackName : blackNameList) {
isNameBlack = verifyPackage(packageName, blackName);
if (isNameBlack) {
break;
}
}

// 假设是黑名单则打印日志
if (isNameBlack) {
// 打印信息供检查
LOG.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()) {
// 表明不是项目中的内容,需要通过反编译获取该文件
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) {
LOG.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;
}

// 解析文件中的内容
CompilationUnit compilationUnit = JavaParser.parse(javaFile);

MethodVisitor methodVisitor = new MethodVisitor();

compilationUnit.accept(methodVisitor, null);

List<String> imports = methodVisitor.importClasses;

if (!imports.isEmpty()) {
for (String importClass : imports) {
if (importClass.endsWith("*")) {
// 导入的是包
for (ContractPackage blackPackage : blackPackageList) {
String importPackageName = importClass.substring(0, importClass.length() - 2);
if (verifyPackage(importPackageName, blackPackage)) {
// 打印信息供检查
LOG.error(String.format("Class[%s]'s import class [%s] belong to BlackPackageList !!!", clazz, importClass));
isOK = false;
break;
}
}
} else {
// 导入的是具体的类,则判断类黑名单 + 包黑名单
if (blackClassSet.contains(importClass)) {
// 包含导入类,该方式无法通过验证
LOG.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)) {
LOG.error(String.format("Class[%s]'s import class [%s] belong to BlackPackageList !!!", clazz, importClass));
isOK = false;
break;
}
}
}
}
}
}
if (isNeedDelete) {
javaFile.delete();
}
}
if (!isOK) {
throw new IllegalStateException("There are many Illegal information, please check !!!");
}

//now we parse the jar;
// 加载main-class,开始校验类型
URL jarURL = jarFile.toURI().toURL();
ClassLoader classLoader = new URLClassLoader(new URL[]{jarURL},this.getClass().getClassLoader());
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);
try {
Class mainClass = classLoader.loadClass(contractMainClass);
ContractType.resolve(mainClass);
} catch (ClassNotFoundException e) {
throw new IllegalDataException(e.getMessage());
}
Class mainClass = classLoader.loadClass(contractMainClass);
ContractType.resolve(mainClass);
} else {
throw new IllegalStateException("There is none class !!!");
}
} catch (Exception e) {
LOG.error(e.getMessage());
throw new MojoFailureException(e.getMessage());
}
}

private List<ContractPackage> blackNameList(Properties config) {
return blackList(config, BLACK_NAME_LIST);
}

private Set<String> blackClassSet(Properties config) {
Set<String> 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 List<ContractPackage> blackPackageList(Properties config) {
return blackList(config, BLACK_PACKAGE_LIST);
}

private List<ContractPackage> blackList(Properties config, String attrName) {
List<ContractPackage> 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;
}
} catch (IOException exception) {
logger.error(exception.getMessage());
throw new MojoFailureException("IO ERROR");
} catch (NullPointerException e) {
logger.error(e.getMessage());
}
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() throws IOException {
@@ -138,30 +335,104 @@ public class ContractVerifyMojo extends AbstractMojo {
return finalJar;
}

private Properties loadConfig() throws Exception {

Properties properties = new Properties();

properties.load(this.getClass().getClassLoader().getResourceAsStream(CONFIG));

return properties;
}

private LinkedList<String> loadAllClass(File file) throws Exception {
JarFile jarFile = new JarFile(file);
LinkedList<String> allClass = new LinkedList<>();
Enumeration<JarEntry> 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<Void> {

private List<String> importClasses = new ArrayList<>();

// @Override
// public void visit(MethodDeclaration n, Void arg) {
// /* here you can access the attributes of the method.
// this method will be called for all methods in this
// CompilationUnit, including inner class methods */
// super.visit(n, arg);
// }
//
// @Override
// public void visit(ClassOrInterfaceDeclaration n, Void arg) {
// super.visit(n, arg);
// }
//
// @Override
// public void visit(PackageDeclaration n, Void arg) {
// super.visit(n, arg);
// }

private class MethodVisitor extends VoidVisitorAdapter<Void> {
@Override
public void visit(MethodDeclaration n, Void arg) {
/* here you can access the attributes of the method.
this method will be called for all methods in this
CompilationUnit, including inner class methods */
logger.info("method:"+n.getName());
public void visit(ImportDeclaration n, Void arg) {
importClasses.add(parseClass(n.toString()));
super.visit(n, arg);
}

@Override
public void visit(ClassOrInterfaceDeclaration n, Void arg) {
logger.info("class:"+n.getName()+" extends:"+n.getExtendedTypes()+" implements:"+n.getImplementedTypes());
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;
}
}

super.visit(n, arg);
private static class ContractPackage {

private String packageName;

private boolean isTotal = false;

public ContractPackage() {
}

@Override
public void visit(PackageDeclaration n, Void arg) {
logger.info("package:"+n.getName());
super.visit(n, arg);
public ContractPackage(String totalPackage) {
if (totalPackage.endsWith("*")) {
this.packageName = totalPackage.substring(0, totalPackage.length() - 2).trim();
this.isTotal = true;
}
this.packageName = totalPackage;
}

public String getPackageName() {
return packageName;
}

public boolean isTotal() {
return isTotal;
}
}
}

+ 8
- 1
source/contract/contract-maven-plugin/src/main/resources/config.properties View File

@@ -1 +1,8 @@
blacklist=java.io,java.net,java.util.Random
#black.name.list:打包为合约Jar后,每个Class文件不允许使用的名称,默认不允许使用com.jd.blockchain.*
black.name.list=com.jd.blockchain.*

#black.package.list:打包为合约中的每个Class都不允许使用的包列表,某个包下面的所有包通过.*表示
black.package.list=java.io.*, java.net.*, org.apache.commons.io.*

#black.class.list:打包为合约中的每个Class都不允许使用的类列表
black.class.list=java.util.Random, com.jd.blockchain.ledger.BlockchainKeyGenerator

+ 0
- 28
source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/CheckImportsMojoTest.java View File

@@ -1,28 +0,0 @@
package com.jd.blockchain.ledger;

import com.jd.blockchain.CheckImportsMojo;
import org.apache.maven.plugin.testing.AbstractMojoTestCase;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;

/**
* @Author zhaogw
* @Date 2019/3/1 21:27
*/
public class CheckImportsMojoTest extends AbstractMojoTestCase {
Logger logger = LoggerFactory.getLogger(CheckImportsMojoTest.class);

@Test
public void test1() throws Exception {
File pom = getTestFile( "src/test/resources/project-to-test/pom.xml" );
assertNotNull( pom );
assertTrue( pom.exists() );

CheckImportsMojo myMojo = (CheckImportsMojo) lookupMojo( "checkImports", pom );
assertNotNull( myMojo );
myMojo.execute();
}
}

+ 50
- 0
source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractTestBase.java View File

@@ -0,0 +1,50 @@
package com.jd.blockchain.ledger;

import org.apache.maven.model.Build;
import org.apache.maven.project.MavenProject;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;

import java.io.File;

public class ContractTestBase {

public static MavenProject mavenProjectInit() {
MavenProject mavenProject = new MavenProject();
mavenProject.setBuild(buildInit());
mavenProject.setFile(file());
return mavenProject;
}

public static File file() {
String resDir = resourceDir();
File file = new File(resDir);
String path = file.getParentFile().getParentFile().getPath();
return new File(path + File.separator + "src");
}

public static Build buildInit() {
Build build = new Build();
build.setDirectory(resourceDir());
return build;
}

public static String resourceDir() {
try {
ClassPathResource classPathResource = new ClassPathResource("complex.jar");
return classPathResource.getFile().getParentFile().getPath();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}

@Test
public void testResourceDir() {
System.out.println(resourceDir());
}

@Test
public void testFile() {
System.out.println(file().getPath());
}
}

+ 1
- 2
source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractVerifyMojoTest.java View File

@@ -1,6 +1,5 @@
package com.jd.blockchain.ledger;

import com.jd.blockchain.CheckImportsMojo;
import com.jd.blockchain.ContractVerifyMojo;
import org.apache.maven.plugin.testing.AbstractMojoTestCase;
import org.junit.Test;
@@ -22,7 +21,7 @@ public class ContractVerifyMojoTest extends AbstractMojoTestCase {
assertNotNull( pom );
assertTrue( pom.exists() );

ContractVerifyMojo myMojo = (ContractVerifyMojo) lookupMojo( "JDChain.Verify", pom );
ContractVerifyMojo myMojo = (ContractVerifyMojo) lookupMojo( "Contract.Verify", pom );
assertNotNull( myMojo );
myMojo.execute();
}


+ 47
- 0
source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractVerifyTest.java View File

@@ -0,0 +1,47 @@
package com.jd.blockchain.ledger;

import com.jd.blockchain.ContractVerifyMojo;
import org.apache.maven.project.MavenProject;
import org.junit.Before;
import org.junit.Test;

import java.lang.reflect.Field;

import static com.jd.blockchain.ledger.ContractTestBase.mavenProjectInit;

public class ContractVerifyTest {

private MavenProject project;

private String finalName;

@Before
public void testInit() {
project = mavenProjectInit();
finalName = "complex.jar";
}

@Test
public void test() throws Exception {
ContractVerifyMojo contractVerifyMojo = contractVerifyMojoConf();
contractVerifyMojo.execute();
}

private ContractVerifyMojo contractVerifyMojoConf() throws Exception {
ContractVerifyMojo contractVerifyMojo = new ContractVerifyMojo();
// 为不影响其内部结构,通过反射进行私有变量赋值
Class<?> clazz = contractVerifyMojo.getClass();
Field projectField = clazz.getDeclaredField("project");
Field finalNameField = clazz.getDeclaredField("finalName");

// 更新权限
projectField.setAccessible(true);
finalNameField.setAccessible(true);

// 设置具体值
projectField.set(contractVerifyMojo, project);
finalNameField.set(contractVerifyMojo, finalName);

return contractVerifyMojo;
}
}

BIN
source/contract/contract-maven-plugin/src/test/resources/complex.jar View File


+ 1
- 16
source/gateway/pom.xml View File

@@ -78,22 +78,7 @@
<version>${commons-io.version}</version>
</dependency>

<dependency>
<groupId>org.bitbucket.mstrobel</groupId>
<artifactId>procyon-core</artifactId>
</dependency>
<dependency>
<groupId>org.bitbucket.mstrobel</groupId>
<artifactId>procyon-expressions</artifactId>
</dependency>
<dependency>
<groupId>org.bitbucket.mstrobel</groupId>
<artifactId>procyon-reflection</artifactId>
</dependency>
<dependency>
<groupId>org.bitbucket.mstrobel</groupId>
<artifactId>procyon-compilertools</artifactId>
</dependency>


<dependency>
<groupId>org.springframework.boot</groupId>


+ 1
- 3
source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayQueryServiceHandler.java View File

@@ -3,11 +3,8 @@ package com.jd.blockchain.gateway.service;
import com.jd.blockchain.consensus.ConsensusProvider;
import com.jd.blockchain.consensus.ConsensusProviders;
import com.jd.blockchain.consensus.ConsensusSettings;
import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider;
import com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider;
import com.jd.blockchain.crypto.HashDigest;
import com.jd.blockchain.gateway.PeerService;
import com.jd.blockchain.gateway.decompiler.utils.DecompilerUtils;
import com.jd.blockchain.ledger.ContractInfo;
import com.jd.blockchain.ledger.LedgerMetadata;
import com.jd.blockchain.ledger.ParticipantNode;
@@ -15,6 +12,7 @@ import com.jd.blockchain.sdk.ContractSettings;
import com.jd.blockchain.sdk.LedgerInitSettings;
import com.jd.blockchain.utils.QueryUtil;
import com.jd.blockchain.utils.codec.HexUtils;
import com.jd.blockchain.utils.decompiler.utils.DecompilerUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;


+ 17
- 0
source/utils/utils-common/pom.xml View File

@@ -43,6 +43,23 @@
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.bitbucket.mstrobel</groupId>
<artifactId>procyon-core</artifactId>
</dependency>
<dependency>
<groupId>org.bitbucket.mstrobel</groupId>
<artifactId>procyon-expressions</artifactId>
</dependency>
<dependency>
<groupId>org.bitbucket.mstrobel</groupId>
<artifactId>procyon-reflection</artifactId>
</dependency>
<dependency>
<groupId>org.bitbucket.mstrobel</groupId>
<artifactId>procyon-compilertools</artifactId>
</dependency>
<!--<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId>
</dependency> -->


source/gateway/src/main/java/com/jd/blockchain/gateway/decompiler/loads/BytesTypeLoader.java → source/utils/utils-common/src/main/java/com/jd/blockchain/utils/decompiler/loads/BytesTypeLoader.java View File

@@ -1,4 +1,4 @@
package com.jd.blockchain.gateway.decompiler.loads;
package com.jd.blockchain.utils.decompiler.loads;

import com.strobel.assembler.ir.ConstantPool;
import com.strobel.assembler.metadata.Buffer;

source/gateway/src/main/java/com/jd/blockchain/gateway/decompiler/utils/DecompilerUtils.java → source/utils/utils-common/src/main/java/com/jd/blockchain/utils/decompiler/utils/DecompilerUtils.java View File

@@ -1,6 +1,6 @@
package com.jd.blockchain.gateway.decompiler.utils;
package com.jd.blockchain.utils.decompiler.utils;

import com.jd.blockchain.gateway.decompiler.loads.BytesTypeLoader;
import com.jd.blockchain.utils.decompiler.loads.BytesTypeLoader;
import com.strobel.assembler.metadata.JarTypeLoader;
import com.strobel.decompiler.Decompiler;
import com.strobel.decompiler.DecompilerSettings;

+ 1
- 1
source/utils/utils-common/src/main/java/com/jd/blockchain/utils/jar/ContractJarUtils.java View File

@@ -15,7 +15,7 @@ import java.util.jar.JarOutputStream;

public class ContractJarUtils {

private static final String JDCHAIN_META = "META-INF/JDCHAIN.TXT";
private static final String JDCHAIN_META = "META-INF/CONTRACT.MF";

private static final int JDCHAIN_HASH_LENGTH = 69;



Loading…
Cancel
Save