| @@ -103,27 +103,27 @@ | |||
| <artifactId>maven-plugin-plugin</artifactId> | |||
| <version>3.5</version> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-compiler-plugin</artifactId> | |||
| <configuration> | |||
| <source>1.8</source> | |||
| <target>1.8</target> | |||
| <encoding>UTF-8</encoding> | |||
| <optimize>false</optimize> | |||
| <debug>true</debug> | |||
| <showDeprecation>false</showDeprecation> | |||
| <showWarnings>false</showWarnings> | |||
| </configuration> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-surefire-plugin</artifactId> | |||
| <configuration> | |||
| <skipTests>true</skipTests> | |||
| </configuration> | |||
| </plugin> | |||
| </plugins> | |||
| </build> | |||
| <!-- | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-compiler-plugin</artifactId> | |||
| <configuration> | |||
| <source>1.8</source> | |||
| <target>1.8</target> | |||
| <encoding>UTF-8</encoding> | |||
| <optimize>false</optimize> | |||
| <debug>true</debug> | |||
| <showDeprecation>false</showDeprecation> | |||
| <showWarnings>false</showWarnings> | |||
| </configuration> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-surefire-plugin</artifactId> | |||
| <configuration> | |||
| <skipTests>true</skipTests> | |||
| </configuration> | |||
| </plugin>--> | |||
| </plugins> | |||
| </build> | |||
| </project> | |||
| @@ -1,6 +1,5 @@ | |||
| package com.jd.blockchain; | |||
| import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||
| import org.apache.commons.io.FileUtils; | |||
| import org.apache.maven.model.Model; | |||
| import org.apache.maven.model.Plugin; | |||
| @@ -8,6 +7,7 @@ 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; | |||
| @@ -22,11 +22,12 @@ import java.util.Collections; | |||
| import java.util.List; | |||
| @Mojo(name = "Contract.Check") | |||
| @Mojo(name = "contractCheck") | |||
| public class ContractCheckMojo extends AbstractMojo { | |||
| Logger LOG = LoggerFactory.getLogger(ContractCheckMojo.class); | |||
| public static final String CONTRACT_VERIFY = "Contract.Verify"; | |||
| public static final String CONTRACT_VERIFY = "contractVerify"; | |||
| private static final String CONTRACT_MAVEN_PLUGIN = "contract-maven-plugin"; | |||
| @@ -36,11 +37,11 @@ public class ContractCheckMojo extends AbstractMojo { | |||
| private static final String APACHE_MAVEN_PLUGINS = "org.apache.maven.plugins"; | |||
| private static final String GOALS_VERIFY = "verify"; | |||
| private static final String GOALS_VERIFY = "package"; | |||
| private static final String GOALS_PACKAGE = "package"; | |||
| private static final String OUT_POM_XML = "OutPom.xml"; | |||
| private static final String OUT_POM_XML = "ContractPom.xml"; | |||
| @Parameter(defaultValue = "${project}", required = true, readonly = true) | |||
| private MavenProject project; | |||
| @@ -51,7 +52,6 @@ public class ContractCheckMojo extends AbstractMojo { | |||
| @Parameter | |||
| private String finalName; | |||
| /** | |||
| * mainClass; | |||
| */ | |||
| @@ -73,11 +73,11 @@ public class ContractCheckMojo extends AbstractMojo { | |||
| * first compile the class, then parse it; | |||
| */ | |||
| @Override | |||
| public void execute() { | |||
| public void execute() throws MojoFailureException { | |||
| compileFiles(); | |||
| } | |||
| private void compileFiles(){ | |||
| private void compileFiles() throws MojoFailureException { | |||
| try (FileInputStream fis = new FileInputStream(project.getFile())) { | |||
| MavenXpp3Reader reader = new MavenXpp3Reader(); | |||
| @@ -107,7 +107,7 @@ public class ContractCheckMojo extends AbstractMojo { | |||
| } catch (Exception e) { | |||
| LOG.error(e.getMessage()); | |||
| throw new IllegalStateException(e); | |||
| throw new MojoFailureException(e.getMessage()); | |||
| } | |||
| } | |||
| @@ -185,7 +185,7 @@ public class ContractCheckMojo extends AbstractMojo { | |||
| PluginExecution pluginExecution = new PluginExecution(); | |||
| pluginExecution.setId(id); | |||
| pluginExecution.setPhase(phase); | |||
| List <String> goals = new ArrayList<>(); | |||
| List<String> goals = new ArrayList<>(); | |||
| goals.add(goal); | |||
| pluginExecution.setGoals(goals); | |||
| List<PluginExecution> pluginExecutions = new ArrayList<>(); | |||
| @@ -3,37 +3,25 @@ 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.plugin.MojoExecutionException; | |||
| 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 org.springframework.util.ResourceUtils; | |||
| 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.*; | |||
| 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; | |||
| @@ -78,12 +66,15 @@ public class ContractVerifyMojo extends AbstractMojo { | |||
| private static final String BLACK_NAME_LIST = "black.name.list"; | |||
| @Override | |||
| public void execute() throws MojoFailureException { | |||
| public void execute() throws MojoExecutionException { | |||
| try { | |||
| File jarFile = copyAndManage(); | |||
| // 首先校验MainClass | |||
| verifyMainClass(jarFile); | |||
| Properties config = loadConfig(); | |||
| List<ContractPackage> blackNameList = blackNameList(config); | |||
| @@ -193,23 +184,25 @@ public class ContractVerifyMojo extends AbstractMojo { | |||
| if (!isOK) { | |||
| throw new IllegalStateException("There are many Illegal information, please check !!!"); | |||
| } | |||
| // 加载main-class,开始校验类型 | |||
| 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); | |||
| } else { | |||
| throw new IllegalStateException("There is none class !!!"); | |||
| } | |||
| } catch (Exception e) { | |||
| LOG.error(e.getMessage()); | |||
| throw new MojoFailureException(e.getMessage()); | |||
| throw new MojoExecutionException(e.getMessage()); | |||
| } | |||
| } | |||
| private void verifyMainClass(File jarFile) throws Exception { | |||
| // 加载main-class,开始校验类型 | |||
| 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); | |||
| } | |||
| private List<ContractPackage> blackNameList(Properties config) { | |||
| return blackList(config, BLACK_NAME_LIST); | |||
| } | |||
| @@ -320,14 +313,14 @@ public class ContractVerifyMojo extends AbstractMojo { | |||
| // 首先进行Copy处理 | |||
| copy(srcJar, dstJar); | |||
| byte[] txtBytes = jdChainTxt(FileUtils.readFileToByteArray(dstJar)).getBytes(StandardCharsets.UTF_8); | |||
| 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, jdChainMetaTxtJarEntry(), txtBytes, null); | |||
| copy(dstJar, finalJar, contractMFJarEntry(), txtBytes, null); | |||
| // 删除临时文件 | |||
| FileUtils.forceDelete(dstJar); | |||
| @@ -423,8 +416,9 @@ public class ContractVerifyMojo extends AbstractMojo { | |||
| if (totalPackage.endsWith("*")) { | |||
| this.packageName = totalPackage.substring(0, totalPackage.length() - 2).trim(); | |||
| this.isTotal = true; | |||
| } else { | |||
| this.packageName = totalPackage; | |||
| } | |||
| this.packageName = totalPackage; | |||
| } | |||
| public String getPackageName() { | |||
| @@ -1,8 +1,8 @@ | |||
| #black.name.list:打包为合约Jar后,每个Class文件不允许使用的名称,默认不允许使用com.jd.blockchain.* | |||
| black.name.list=com.jd.blockchain.* | |||
| #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.net.*, org.apache.commons.io.* | |||
| 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 | |||
| @@ -18,7 +18,7 @@ public class ContractVerifyTest { | |||
| @Before | |||
| public void testInit() { | |||
| project = mavenProjectInit(); | |||
| finalName = "complex.jar"; | |||
| finalName = "complex"; | |||
| } | |||
| @Test | |||
| @@ -5,6 +5,7 @@ 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 com.jd.blockchain.utils.jar.ContractJarUtils; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Service; | |||
| @@ -29,14 +30,7 @@ public class GatewayInterceptServiceHandler implements GatewayInterceptService { | |||
| } | |||
| private void contractCheck(final ContractCodeDeployOperation contractOP) { | |||
| // | |||
| byte[] chainCode = contractOP.getChainCode(); | |||
| if (chainCode == null || chainCode.length == 0) { | |||
| throw new IllegalDataException("Contract's content is empty !!!"); | |||
| } | |||
| // 校验chainCode | |||
| ContractJarUtils.verify(contractOP.getChainCode()); | |||
| } | |||
| } | |||
| @@ -1,5 +1,6 @@ | |||
| package com.jd.blockchain.gateway.web; | |||
| import com.jd.blockchain.gateway.service.GatewayInterceptService; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.web.bind.annotation.RequestBody; | |||
| import org.springframework.web.bind.annotation.RequestMapping; | |||
| @@ -30,9 +31,15 @@ public class TxProcessingController implements TransactionService { | |||
| @Autowired | |||
| private PeerService peerService; | |||
| @Autowired | |||
| private GatewayInterceptService interceptService; | |||
| @RequestMapping(path = "rpc/tx", method = RequestMethod.POST, consumes = BinaryMessageConverter.CONTENT_TYPE_VALUE, produces = BinaryMessageConverter.CONTENT_TYPE_VALUE) | |||
| @Override | |||
| public @ResponseBody TransactionResponse process(@RequestBody TransactionRequest txRequest) { | |||
| // 拦截请求进行校验 | |||
| interceptService.intercept(txRequest); | |||
| // 检查交易请求的信息是否完整; | |||
| HashDigest ledgerHash = txRequest.getTransactionContent().getLedgerHash(); | |||
| if (ledgerHash == null) { | |||
| @@ -1,5 +1,6 @@ | |||
| package com.jd.blockchain.ledger.core.impl.handles; | |||
| import com.jd.blockchain.utils.jar.ContractJarUtils; | |||
| import org.springframework.stereotype.Service; | |||
| import com.jd.blockchain.ledger.BytesValue; | |||
| @@ -22,8 +23,12 @@ public class ContractCodeDeployOperationHandle implements OperationHandle { | |||
| // TODO: 请求者应该提供合约账户的公钥签名,已确定注册的地址的唯一性; | |||
| byte[] chainCode = contractOP.getChainCode(); | |||
| // 校验合约代码,不通过会抛出异常 | |||
| ContractJarUtils.verify(chainCode); | |||
| dataset.getContractAccountSet().deploy(contractOP.getContractID().getAddress(), | |||
| contractOP.getContractID().getPubKey(), contractOP.getAddressSignature(), contractOP.getChainCode()); | |||
| contractOP.getContractID().getPubKey(), contractOP.getAddressSignature(), chainCode); | |||
| return null; | |||
| } | |||
| @@ -37,5 +42,4 @@ public class ContractCodeDeployOperationHandle implements OperationHandle { | |||
| public boolean support(Class<?> operationType) { | |||
| return ContractCodeDeployOperation.class.isAssignableFrom(operationType); | |||
| } | |||
| } | |||
| @@ -260,8 +260,6 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||
| private class ContractCodeDeployOperationBuilderFilter implements ContractCodeDeployOperationBuilder { | |||
| @Override | |||
| public ContractCodeDeployOperation deploy(BlockchainIdentity id, byte[] chainCode) { | |||
| // 校验chainCode | |||
| ContractJarUtils.verify(chainCode); | |||
| // 校验成功后发布 | |||
| ContractCodeDeployOperation op = CONTRACT_CODE_DEPLOY_OP_BUILDER.deploy(id, chainCode); | |||
| operationList.add(op); | |||
| @@ -15,13 +15,16 @@ import java.util.jar.JarOutputStream; | |||
| public class ContractJarUtils { | |||
| private static final String JDCHAIN_META = "META-INF/CONTRACT.MF"; | |||
| private static final String CONTRACT_MF = "META-INF/CONTRACT.MF"; | |||
| private static final int JDCHAIN_HASH_LENGTH = 69; | |||
| private static final Random FILE_RANDOM = new Random(); | |||
| public static void verify(byte[] chainCode) { | |||
| if (chainCode == null || chainCode.length == 0) { | |||
| throw new IllegalStateException("ChainCode is empty !!!"); | |||
| } | |||
| // 首先生成合约文件 | |||
| File jarFile = newJarFile(); | |||
| try { | |||
| @@ -42,14 +45,14 @@ public class ContractJarUtils { | |||
| private static void verify(File jarFile) throws Exception { | |||
| // 首先判断jarFile中是否含有META-INF/JDCHAIN.TXT,并将其读出 | |||
| URL jarUrl = new URL("jar:file:" + jarFile.getPath() + "!/" + JDCHAIN_META); | |||
| URL jarUrl = new URL("jar:file:" + jarFile.getPath() + "!/" + CONTRACT_MF); | |||
| InputStream inputStream = jarUrl.openStream(); | |||
| if (inputStream == null) { | |||
| throw new IllegalStateException(JDCHAIN_META + " IS NULL !!!"); | |||
| throw new IllegalStateException(CONTRACT_MF + " IS NULL !!!"); | |||
| } | |||
| byte[] bytes = IOUtils.toByteArray(inputStream); | |||
| if (bytes == null || bytes.length != JDCHAIN_HASH_LENGTH) { | |||
| throw new IllegalStateException(JDCHAIN_META + " IS Illegal !!!"); | |||
| throw new IllegalStateException(CONTRACT_MF + " IS Illegal !!!"); | |||
| } | |||
| // 获取对应的Hash内容 | |||
| String txt = new String(bytes, StandardCharsets.UTF_8); | |||
| @@ -58,10 +61,10 @@ public class ContractJarUtils { | |||
| File tempJar = newJarFile(); | |||
| // 复制除JDCHAIN.TXT之外的部分 | |||
| copy(jarFile, tempJar, null, null, JDCHAIN_META); | |||
| copy(jarFile, tempJar, null, null, CONTRACT_MF); | |||
| // 生成新Jar包对应的Hash内容 | |||
| String verifyTxt = jdChainTxt(FileUtils.readFileToByteArray(tempJar)); | |||
| String verifyTxt = contractMF(FileUtils.readFileToByteArray(tempJar)); | |||
| // 删除临时文件 | |||
| FileUtils.forceDelete(tempJar); | |||
| @@ -103,13 +106,13 @@ public class ContractJarUtils { | |||
| jarFile.close(); | |||
| } | |||
| public static String jdChainTxt(byte[] content) { | |||
| public static String contractMF(byte[] content) { | |||
| // hash=Hex(hash(content)) | |||
| return "hash:" + DigestUtils.sha256Hex(content); | |||
| } | |||
| public static JarEntry jdChainMetaTxtJarEntry() { | |||
| return new JarEntry(JDCHAIN_META); | |||
| public static JarEntry contractMFJarEntry() { | |||
| return new JarEntry(CONTRACT_MF); | |||
| } | |||
| private static byte[] readStream(InputStream inputStream) { | |||
| @@ -34,14 +34,14 @@ public class ContractJarUtilsTest { | |||
| // 首先进行Copy处理 | |||
| copy(srcJar, dstJar); | |||
| byte[] txtBytes = jdChainTxt(FileUtils.readFileToByteArray(dstJar)).getBytes(StandardCharsets.UTF_8); | |||
| byte[] txtBytes = contractMF(FileUtils.readFileToByteArray(dstJar)).getBytes(StandardCharsets.UTF_8); | |||
| String finalJarPath = classPath + | |||
| File.separator + jarName + "-jdchain.jar"; | |||
| File finalJar = new File(finalJarPath); | |||
| copy(dstJar, finalJar, jdChainMetaTxtJarEntry(), txtBytes, null); | |||
| copy(dstJar, finalJar, contractMFJarEntry(), txtBytes, null); | |||
| // 删除临时文件 | |||
| FileUtils.forceDelete(dstJar); | |||