Browse Source

SDK增加合约校验功能!

tags/1.1.0
shaozhuguang 5 years ago
parent
commit
5fadf9bdb8
7 changed files with 222 additions and 63 deletions
  1. +3
    -63
      source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractVerifyMojo.java
  2. +13
    -0
      source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/BlockchainOperationFactory.java
  3. +6
    -0
      source/utils/utils-common/pom.xml
  4. +136
    -0
      source/utils/utils-common/src/main/java/com/jd/blockchain/utils/jar/ContractJarUtils.java
  5. BIN
      source/utils/utils-common/src/main/resources/complex.jar
  6. +64
    -0
      source/utils/utils-common/src/test/java/test/my/utils/ContractJarUtilsTest.java
  7. BIN
      source/utils/utils-common/src/test/resources/complex.jar

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

@@ -36,6 +36,8 @@ import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.stream.Collectors;

import static com.jd.blockchain.utils.jar.ContractJarUtils.*;

/**
* 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".
@@ -50,8 +52,6 @@ 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;

@@ -130,75 +130,15 @@ public class ContractVerifyMojo extends AbstractMojo {

File finalJar = new File(finalJarPath);

copy(dstJar, finalJar, new JarEntry(JDCHAIN_META), txtBytes, null);
copy(dstJar, finalJar, jdChainMetaTxtJarEntry(), 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<JarEntry> 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<Void> {
@Override


+ 13
- 0
source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/BlockchainOperationFactory.java View File

@@ -1,8 +1,15 @@
package com.jd.blockchain.transaction;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import com.jd.blockchain.ledger.BlockchainIdentity;
import com.jd.blockchain.ledger.BytesValue;
@@ -16,6 +23,9 @@ import com.jd.blockchain.ledger.LedgerInitSetting;
import com.jd.blockchain.ledger.Operation;
import com.jd.blockchain.ledger.UserRegisterOperation;
import com.jd.blockchain.utils.Bytes;
import com.jd.blockchain.utils.jar.ContractJarUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
/**
* @author huanghaiquan
@@ -250,6 +260,9 @@ 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);
return op;


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

@@ -24,6 +24,12 @@
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>net.i2p.crypto</groupId>
<artifactId>eddsa</artifactId>


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

@@ -0,0 +1,136 @@
package com.jd.blockchain.utils.jar;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.Random;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;

public class ContractJarUtils {

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

private static final int JDCHAIN_HASH_LENGTH = 69;

private static final Random FILE_RANDOM = new Random();

public static void verify(byte[] chainCode) {
// 首先生成合约文件
File jarFile = newJarFile();
try {
FileUtils.writeByteArrayToFile(jarFile, chainCode);
// 校验合约文件
verify(jarFile);
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
// 删除文件
try {
FileUtils.forceDelete(jarFile);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
}

private static void verify(File jarFile) throws Exception {
// 首先判断jarFile中是否含有META-INF/JDCHAIN.TXT,并将其读出
URL jarUrl = new URL("jar:file:" + jarFile.getPath() + "!/" + JDCHAIN_META);
InputStream inputStream = jarUrl.openStream();
if (inputStream == null) {
throw new IllegalStateException(JDCHAIN_META + " IS NULL !!!");
}
byte[] bytes = IOUtils.toByteArray(inputStream);
if (bytes == null || bytes.length != JDCHAIN_HASH_LENGTH) {
throw new IllegalStateException(JDCHAIN_META + " IS Illegal !!!");
}
// 获取对应的Hash内容
String txt = new String(bytes, StandardCharsets.UTF_8);

// 生成新的Jar包文件,该文件路径与JarFile基本一致
File tempJar = newJarFile();

// 复制除JDCHAIN.TXT之外的部分
copy(jarFile, tempJar, null, null, JDCHAIN_META);

// 生成新Jar包对应的Hash内容
String verifyTxt = jdChainTxt(FileUtils.readFileToByteArray(tempJar));

// 删除临时文件
FileUtils.forceDelete(tempJar);

// 校验Jar包内容
if (!txt.equals(verifyTxt)) {
throw new IllegalStateException(String.format("Jar [%s] verify Illegal !!!", jarFile.getName()));
}
}

public static void copy(File srcJar, File dstJar) throws IOException {
copy(srcJar, dstJar, null, null, null);
}

public static void copy(File srcJar, File dstJar, JarEntry addEntry, byte[] addBytes, String filter) throws IOException {
JarFile jarFile = new JarFile(srcJar);
Enumeration<JarEntry> 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;
}
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();
}

public static String jdChainTxt(byte[] content) {
// hash=Hex(hash(content))
return "hash:" + DigestUtils.sha256Hex(content);
}

public static JarEntry jdChainMetaTxtJarEntry() {
return new JarEntry(JDCHAIN_META);
}

private static 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 static File newJarFile() {
return new File("contract-" +
System.currentTimeMillis() + "-" +
System.nanoTime() + "-" +
FILE_RANDOM.nextInt(1024) +
".jar");
}
}

BIN
source/utils/utils-common/src/main/resources/complex.jar View File


+ 64
- 0
source/utils/utils-common/src/test/java/test/my/utils/ContractJarUtilsTest.java View File

@@ -0,0 +1,64 @@
package test.my.utils;

import org.apache.commons.io.FileUtils;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;

import java.io.File;
import java.nio.charset.StandardCharsets;

import static com.jd.blockchain.utils.jar.ContractJarUtils.*;
import static org.junit.Assert.fail;

public class ContractJarUtilsTest {

private String jarName = "complex";

@Test
public void test() {

byte[] chainCode = null;
try {
ClassPathResource classPathResource = new ClassPathResource(jarName + ".jar");
String classPath = classPathResource.getFile().getParentFile().getPath();

// 首先将Jar包转换为指定的格式
String srcJarPath = classPath +
File.separator + jarName + ".jar";

String dstJarPath = classPath +
File.separator + jarName + "-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 = classPath +
File.separator + jarName + "-jdchain.jar";

File finalJar = new File(finalJarPath);

copy(dstJar, finalJar, jdChainMetaTxtJarEntry(), txtBytes, null);

// 删除临时文件
FileUtils.forceDelete(dstJar);

// 读取finalJar中的内容
chainCode = FileUtils.readFileToByteArray(finalJar);

FileUtils.forceDelete(finalJar);
} catch (Exception e) {
e.printStackTrace();
}
try {
verify(chainCode);
System.out.println("Verify Success !!!");
} catch (Exception e) {
fail("Verify Fail !!");
}

}
}

BIN
source/utils/utils-common/src/test/resources/complex.jar View File


Loading…
Cancel
Save