From 34c602991dcbe7e49900f3c8d322a1cef7c33471 Mon Sep 17 00:00:00 2001 From: shaozhuguang Date: Sat, 29 Jun 2019 16:33:21 +0800 Subject: [PATCH] Modify Contract Plugin's Check Function! --- .../com/jd/blockchain/CheckImportsMojo.java | 1 + .../com/jd/blockchain/ContractCheckMojo.java | 5 +- .../com/jd/blockchain/ContractVerifyMojo.java | 227 ++++++++++++++++++ .../ledger/ContractVerifyMojoTest.java | 29 +++ source/contract/contract-samples/pom.xml | 10 +- .../blockchain/contract/ComplexContract.java | 7 + .../contract/ComplexContractImpl.java | 12 + .../service/GatewayInterceptService.java | 8 + .../GatewayInterceptServiceHandler.java | 42 ++++ 9 files changed, 337 insertions(+), 4 deletions(-) create mode 100644 source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractVerifyMojo.java create mode 100644 source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractVerifyMojoTest.java create mode 100644 source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ComplexContract.java create mode 100644 source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ComplexContractImpl.java create mode 100644 source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayInterceptService.java create mode 100644 source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayInterceptServiceHandler.java diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/CheckImportsMojo.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/CheckImportsMojo.java index 461917a4..01e3be99 100644 --- a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/CheckImportsMojo.java +++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/CheckImportsMojo.java @@ -42,6 +42,7 @@ import java.util.stream.Collectors; */ @Mojo(name = "checkImports") public class CheckImportsMojo extends AbstractMojo { + Logger logger = LoggerFactory.getLogger(CheckImportsMojo.class); @Parameter(defaultValue = "${project}", required = true, readonly = true) diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractCheckMojo.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractCheckMojo.java index a47e4eb6..7d823d95 100644 --- a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractCheckMojo.java +++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractCheckMojo.java @@ -141,7 +141,7 @@ public class ContractCheckMojo extends AbstractMojo { pluginExecution.setId("make-assembly"); pluginExecution.setPhase("verify"); List goals = new ArrayList<>(); - goals.add("checkImports"); + goals.add("JDChain.Verify"); pluginExecution.setGoals(goals); List pluginExecutions = new ArrayList<>(); pluginExecutions.add(pluginExecution); @@ -215,7 +215,8 @@ public class ContractCheckMojo extends AbstractMojo { //将字节数组fileInput中的内容输出到文件fileOut.xml中; ConsoleUtils.info(new String(buffer)); fos.write(buffer); - invokeCompile(fileOutput); + fos.flush(); fos.close(); + invokeCompile(fileOutput); } } diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractVerifyMojo.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractVerifyMojo.java new file mode 100644 index 00000000..7dd78300 --- /dev/null +++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractVerifyMojo.java @@ -0,0 +1,227 @@ +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.commons.codec.digest.DigestUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +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.*; +import java.net.URL; +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.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +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 = "JDChain.Verify") +public class ContractVerifyMojo extends AbstractMojo { + + Logger logger = LoggerFactory.getLogger(ContractVerifyMojo.class); + + private static final String JDCHAIN_META = "META-INF/JDCHAIN.TXT"; + + @Parameter(defaultValue = "${project}", required = true, readonly = true) + private MavenProject project; + + /** + * jar's name; + */ + @Parameter + private String finalName; + + @Override + public void execute() throws MojoFailureException { + + List 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 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; + 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 File copyAndManage() 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); + + // 首先进行Copy处理 + copy(srcJar, dstJar); + + byte[] txtBytes = jdChainTxt(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, new JarEntry(JDCHAIN_META), txtBytes, null); + + // 删除临时文件 + FileUtils.forceDelete(dstJar); + + return finalJar; + // 删除srcJar + + // 删除finalJar +// FileUtils.forceDelete(finalJar); +// // 删除srcJar +// srcJar.deleteOnExit(); +// +// // 修改名字 +// finalJar.renameTo(srcJar); + } + + private void copy(File srcJar, File dstJar) throws IOException { + copy(srcJar, dstJar, null, null, null); + } + + private void copy(File srcJar, File dstJar, JarEntry addEntry, byte[] addBytes, String filter) throws IOException { + JarFile jarFile = new JarFile(srcJar); + Enumeration jarEntries = jarFile.entries(); + JarOutputStream jarOut = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(dstJar))); + + while(jarEntries.hasMoreElements()){ + JarEntry jarEntry = jarEntries.nextElement(); + String entryName = jarEntry.getName(); + if (filter != null && filter.equals(entryName)) { + continue; + } + System.out.println(entryName); + jarOut.putNextEntry(jarEntry); + jarOut.write(readStream(jarFile.getInputStream(jarEntry))); + jarOut.closeEntry(); + } + if (addEntry != null) { + jarOut.putNextEntry(addEntry); + jarOut.write(addBytes); + jarOut.closeEntry(); + } + + jarOut.flush(); + jarOut.finish(); + jarOut.close(); + jarFile.close(); + } + + private String jdChainTxt(byte[] content) { + // hash=Hex(hash(content)) + String hashTxt = "hash:" + DigestUtils.sha256Hex(content); + System.out.println(hashTxt); + return hashTxt; + } + + private byte[] readStream(InputStream inputStream) { + try (ByteArrayOutputStream outSteam = new ByteArrayOutputStream()) { + byte[] buffer = new byte[1024]; + int len; + while ((len = inputStream.read(buffer)) != -1) { + outSteam.write(buffer, 0, len); + } + inputStream.close(); + return outSteam.toByteArray(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + private class MethodVisitor extends VoidVisitorAdapter { + @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); + } + + } +} diff --git a/source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractVerifyMojoTest.java b/source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractVerifyMojoTest.java new file mode 100644 index 00000000..4cfc66b5 --- /dev/null +++ b/source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractVerifyMojoTest.java @@ -0,0 +1,29 @@ +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; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +/** + * @Author zhaogw + * @Date 2019/3/1 21:27 + */ +public class ContractVerifyMojoTest extends AbstractMojoTestCase { + Logger logger = LoggerFactory.getLogger(ContractVerifyMojoTest.class); + + @Test + public void test1() throws Exception { + File pom = getTestFile( "src/test/resources/project-to-test/pom.xml" ); + assertNotNull( pom ); + assertTrue( pom.exists() ); + + ContractVerifyMojo myMojo = (ContractVerifyMojo) lookupMojo( "JDChain.Verify", pom ); + assertNotNull( myMojo ); + myMojo.execute(); + } +} diff --git a/source/contract/contract-samples/pom.xml b/source/contract/contract-samples/pom.xml index ef3d3407..0a7be66c 100644 --- a/source/contract/contract-samples/pom.xml +++ b/source/contract/contract-samples/pom.xml @@ -27,6 +27,12 @@ ${project.version} provided + + + com.alibaba + fastjson + + @@ -34,11 +40,11 @@ maven-assembly-plugin - transfer + complex false - com.jd.blockchain.contract.TransferContractImpl + com.jd.blockchain.contract.ComplexContractImpl diff --git a/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ComplexContract.java b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ComplexContract.java new file mode 100644 index 00000000..b23511f3 --- /dev/null +++ b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ComplexContract.java @@ -0,0 +1,7 @@ +package com.jd.blockchain.contract; + +@Contract +public interface ComplexContract { + @ContractEvent(name = "read-key") + String read(String address, String key); +} diff --git a/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ComplexContractImpl.java b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ComplexContractImpl.java new file mode 100644 index 00000000..15763954 --- /dev/null +++ b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ComplexContractImpl.java @@ -0,0 +1,12 @@ +package com.jd.blockchain.contract; + + +import com.alibaba.fastjson.JSON; + +public class ComplexContractImpl implements ComplexContract { + @Override + public String read(String address, String key) { + String json = JSON.toJSONString(address); + return System.currentTimeMillis() + "" + json; + } +} diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayInterceptService.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayInterceptService.java new file mode 100644 index 00000000..ca998b89 --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayInterceptService.java @@ -0,0 +1,8 @@ +package com.jd.blockchain.gateway.service; + +import com.jd.blockchain.ledger.TransactionRequest; + +public interface GatewayInterceptService { + + void intercept(TransactionRequest txRequest); +} diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayInterceptServiceHandler.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayInterceptServiceHandler.java new file mode 100644 index 00000000..b70bee5d --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayInterceptServiceHandler.java @@ -0,0 +1,42 @@ +package com.jd.blockchain.gateway.service; + +import com.jd.blockchain.gateway.PeerService; +import com.jd.blockchain.ledger.ContractCodeDeployOperation; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.utils.IllegalDataException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class GatewayInterceptServiceHandler implements GatewayInterceptService { + + @Autowired + private PeerService peerService; + + @Override + public void intercept(TransactionRequest txRequest) { + // 当前仅处理合约发布的请求 + Operation[] operations = txRequest.getTransactionContent().getOperations(); + if (operations != null && operations.length > 0) { + for (Operation op : operations) { + if (ContractCodeDeployOperation.class.isAssignableFrom(op.getClass())) { + // 发布合约请求 + contractCheck((ContractCodeDeployOperation)op); + } + } + } + } + + private void contractCheck(final ContractCodeDeployOperation contractOP) { + // + byte[] chainCode = contractOP.getChainCode(); + if (chainCode == null || chainCode.length == 0) { + throw new IllegalDataException("Contract's content is empty !!!"); + } + + + + + } +}