Browse Source

Modify contract-maven-plugin's verification scheme to be implemented by ASM!

tags/1.1.0
shaozhuguang 6 years ago
parent
commit
3a37cded20
23 changed files with 1321 additions and 994 deletions
  1. +6
    -4
      source/contract/contract-maven-plugin/pom.xml
  2. +98
    -0
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/AbstractContract.java
  3. +0
    -216
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractCheckMojo.java
  4. +66
    -0
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractClass.java
  5. +142
    -8
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractCompileMojo.java
  6. +9
    -0
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractConstant.java
  7. +0
    -176
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractDeployExeUtil.java
  8. +0
    -120
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractDeployMojo.java
  9. +43
    -0
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractField.java
  10. +81
    -0
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractMethod.java
  11. +0
    -459
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractResolveEngine.java
  12. +22
    -0
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/asm/ASMClassVisitor.java
  13. +108
    -0
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/asm/ASMMethodVisitor.java
  14. +154
    -0
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/rule/BlackList.java
  15. +93
    -0
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/rule/DependencyExclude.java
  16. +30
    -0
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/rule/WhiteList.java
  17. +133
    -0
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/verify/ResolveEngine.java
  18. +216
    -0
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/verify/VerifyEngine.java
  19. +11
    -0
      source/contract/contract-maven-plugin/src/main/resources/blacks.conf
  20. +0
    -8
      source/contract/contract-maven-plugin/src/main/resources/config.properties
  21. +22
    -0
      source/contract/contract-maven-plugin/src/main/resources/provideds.conf
  22. +1
    -0
      source/contract/contract-maven-plugin/src/main/resources/whites.conf
  23. +86
    -3
      source/ledger/ledger-model/src/main/java/com/jd/blockchain/contract/ContractJarUtils.java

+ 6
- 4
source/contract/contract-maven-plugin/pom.xml View File

@@ -35,10 +35,6 @@
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>


<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-core</artifactId>
</dependency>


<dependency> <dependency>
<groupId>org.apache.maven</groupId> <groupId>org.apache.maven</groupId>
@@ -58,6 +54,12 @@
<version>2.6</version> <version>2.6</version>
</dependency> </dependency>


<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>5.0.4</version>
</dependency>

</dependencies> </dependencies>


<build> <build>


+ 98
- 0
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/AbstractContract.java View File

@@ -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<String> 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<String> 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;
}
}

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

@@ -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<Plugin> 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> pluginExecution(String id, String phase, String goal) {
// PluginExecution pluginExecution = new PluginExecution();
// pluginExecution.setId(id);
// pluginExecution.setPhase(phase);
// List<String> goals = new ArrayList<>();
// goals.add(goal);
// pluginExecution.setGoals(goals);
// List<PluginExecution> 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);
// }
//}

+ 66
- 0
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractClass.java View File

@@ -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<String, ContractMethod> 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<ContractField> fields() {

List<ContractField> 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<String, ContractMethod> getMethods() {
return methods;
}
}

+ 142
- 8
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractCompileMojo.java View File

@@ -1,20 +1,94 @@
package com.jd.blockchain.contract.maven; 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.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.assembly.mojos.SingleAssemblyMojo; import org.apache.maven.plugin.assembly.mojos.SingleAssemblyMojo;
import org.apache.maven.plugins.annotations.Mojo; 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") @Mojo(name = "compile")
public class ContractCompileMojo extends SingleAssemblyMojo { public class ContractCompileMojo extends SingleAssemblyMojo {


public static final String JAR_DEPENDENCE = "jar-with-dependencies"; 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 @Override
public void execute() throws MojoExecutionException, MojoFailureException { 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 // 要求必须有MainClass
String mainClass;
try { try {
String mainClass = super.getJarArchiveConfiguration().getManifest().getMainClass();
mainClass = super.getJarArchiveConfiguration().getManifest().getMainClass();
// 校验MainClass,要求MainClass必须不能为空 // 校验MainClass,要求MainClass必须不能为空
if (mainClass == null || mainClass.length() == 0) { if (mainClass == null || mainClass.length() == 0) {
throw new MojoFailureException("MainClass is NULL !!!"); throw new MojoFailureException("MainClass is NULL !!!");
@@ -23,16 +97,76 @@ public class ContractCompileMojo extends SingleAssemblyMojo {
} catch (Exception e) { } catch (Exception e) {
throw new MojoFailureException("MainClass is null: " + e.getMessage(), 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<Artifact> 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<Artifact> 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);
}
} }
} }

+ 9
- 0
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractConstant.java View File

@@ -0,0 +1,9 @@
package com.jd.blockchain.contract.maven;

public class ContractConstant {

public static final String METHOD_INIT = "<init>";

public static final String METHOD_CLINIT = "<clinit>";

}

+ 0
- 176
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractDeployExeUtil.java View File

@@ -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;
// }
//}

+ 0
- 120
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractDeployMojo.java View File

@@ -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);
// }
//
//}
//
//

+ 43
- 0
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractField.java View File

@@ -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 +
'}';
}
}

+ 81
- 0
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractMethod.java View File

@@ -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<ContractField> fieldList = new ArrayList<>();

private List<ContractMethod> 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<ContractField> getAllFieldList() {
return fieldList;
}

public List<ContractField> getClassFieldList(String cName) {
List<ContractField> classFieldList = new ArrayList<>();
if (!fieldList.isEmpty()) {
for (ContractField field : fieldList) {
if (field.getClassName().equals(cName)) {
classFieldList.add(field);
}
}
}
return classFieldList;
}

public List<ContractMethod> 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 +
'}';
}
}

+ 0
- 459
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/ContractResolveEngine.java View File

@@ -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<ContractPackage> blackNameList;

private static List<ContractPackage> blackPackageList;

private static Set<String> 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<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) {

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<String> 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<ContractPackage> blackNameList(Properties config) {
return blackList(config, BLACK_NAME_LIST);
}

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

private static 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;
}
}
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<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(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;
}
}
}

+ 22
- 0
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/asm/ASMClassVisitor.java View File

@@ -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);
}
}

+ 108
- 0
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/asm/ASMMethodVisitor.java View File

@@ -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<String> paramList = handleTypes(paramsTypeChars);
if (!paramList.isEmpty()) {
this.paramTypes = new String[paramList.size()];
paramList.toArray(this.paramTypes);
}
}

private void initReturnType(String returnTypeChar) {
// 按照分号分隔
List<String> returnList = handleTypes(returnTypeChar);
if (!returnList.isEmpty()) {
this.returnTypes = new String[returnList.size()];
returnList.toArray(this.returnTypes);
}
}

private List<String> handleTypes(String typeChars) {
String[] types = typeChars.split(";");
List<String> 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;
}
}
}

+ 154
- 0
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/rule/BlackList.java View File

@@ -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<String, BlackClass> blackClassMap = new ConcurrentHashMap<>();

private final List<String> 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<Class<?>> 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<Class<?>> 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<String> 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);
}
}
}



+ 93
- 0
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/rule/DependencyExclude.java View File

@@ -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<String, List<Dependency>> DEPENDENCYS = new ConcurrentHashMap<>();

static {
try {
init();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}

private static void init() throws Exception {
List<String> 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<Dependency> dependencies = new ArrayList<>();
dependencies.add(dependency);
DEPENDENCYS.put(groupId, dependencies);
} else {
List<Dependency> dependencies = DEPENDENCYS.get(groupId);
dependencies.add(dependency);
}
}

public boolean isExclude(String groupId, String artifactId) {
List<Dependency> 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);
}
}
}

+ 30
- 0
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/rule/WhiteList.java View File

@@ -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<String> 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;
}
}



+ 133
- 0
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/verify/ResolveEngine.java View File

@@ -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<String> 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<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;
}
}

+ 216
- 0
source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/contract/maven/verify/VerifyEngine.java View File

@@ -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<String> haveManagedMethods = new HashSet<>();

// 代表的是处理的参数
private Set<String> 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<String, ContractClass> allContractClasses = resolveClasses(jarClasses());

// 开始处理MainClass
verify(urlClassLoader, allContractClasses);

}

public void verify(URLClassLoader urlClassLoader, Map<String, ContractClass> 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<String, ContractMethod> methods = mainContractClass.getMethods();
if (!methods.isEmpty()) {
for (Map.Entry<String, ContractMethod> entry : methods.entrySet()) {
ContractMethod method = entry.getValue();
verify(urlClassLoader, allContractClasses, method);
}
}
}

public void verify(URLClassLoader urlClassLoader, Map<String, ContractClass> 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<ContractMethod> 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<ContractField> 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<String, byte[]> jarClasses() throws Exception {
return loadAllClasses(jarFile);
}

private Map<String, ContractClass> resolveClasses(Map<String, byte[]> allClasses) {

Map<String, ContractClass> allContractClasses = new ConcurrentHashMap<>();

for (Map.Entry<String, byte[]> 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() + "-<init>-" + field.getFieldName();
}
}

+ 11
- 0
source/contract/contract-maven-plugin/src/main/resources/blacks.conf View File

@@ -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

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

@@ -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

+ 22
- 0
source/contract/contract-maven-plugin/src/main/resources/provideds.conf View File

@@ -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/*

+ 1
- 0
source/contract/contract-maven-plugin/src/main/resources/whites.conf View File

@@ -0,0 +1 @@
com.jd.blockchain.*

+ 86
- 3
source/ledger/ledger-model/src/main/java/com/jd/blockchain/contract/ContractJarUtils.java View File

@@ -4,21 +4,23 @@ import com.jd.blockchain.crypto.Crypto;
import com.jd.blockchain.crypto.HashDigest; import com.jd.blockchain.crypto.HashDigest;
import com.jd.blockchain.crypto.HashFunction; import com.jd.blockchain.crypto.HashFunction;
import com.jd.blockchain.utils.io.BytesUtils; 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.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;


import java.io.*; import java.io.*;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; 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.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.jar.JarOutputStream; import java.util.jar.JarOutputStream;


public class ContractJarUtils { 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 String CONTRACT_MF = "META-INF/CONTRACT.MF";


private static final HashFunction HASH_FUNCTION = Crypto.getHashFunction("SHA256"); 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); 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<String> loadWhiteConf() {

return resolveConfig(WHITE_CONF);
}

public static List<String> loadBlackConf() {
return resolveConfig(BLACK_CONF);
}

public static List<String> resolveConfig(String fileName) {
List<String> configs = new ArrayList<>();

try {
List<String> 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<String> loadConfig(String fileName) throws Exception {

return IOUtils.readLines(
ContractJarUtils.class.getResourceAsStream(File.separator + fileName));
}

public static Map<String, byte[]> loadAllClasses(final File jar) throws Exception {
Map<String, byte[]> allClasses = new HashMap<>();
JarFile jarFile = new JarFile(jar);
Enumeration<JarEntry> 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) { public static void verify(byte[] chainCode) {
if (chainCode == null || chainCode.length == 0) { if (chainCode == null || chainCode.length == 0) {
throw new IllegalStateException("Contract's chaincode is empty !!!"); throw new IllegalStateException("Contract's chaincode is empty !!!");


Loading…
Cancel
Save