2021SC@SDUSC
目录
一、合约模型的接口
1.ContractAware
合约实现的子接口可以监听运行时的生命周期事件。
public interface ContractAware {
}
2.ContractEventContext
接口import了HashDigest的接口,本方法用于获得当前账本哈希。
HashDigest getCurrentLedgerHash();
执行合约事件的交易请求
TransactionRequest getTransactionRequest();
代码返回交易的签署人集合
Set<BlockchainIdentity> getTxSigners();
代码返回事件名
String getEvent();
代码返回参数列表
BytesValueList getArgs();
账本操作上下文
LedgerContext getLedger();
合约拥有者集合,合约拥有者是签署和约时签名者的集合
Set<BlockchainIdentity> getContractOwners();
我们知道每个合约或者账本都要有获得最新版本的方法,这个方法是用于获得该合约的最新版本。
long getVersion();
当前包含未提交区块数据账本操作上下文。
LedgerQueryService getUncommittedLedger();
3.ContractLifeCycleAware
合约实现此接口可以监听合约应用的生命周期事件
public interface ContractLifecycleAware extends ContractAware {
void postConstruct();
void beforeDestroy();
}
4.ContractProcessor
这个是一个合约处理器的接口,主要用于合约合法性校验,合约分析以及 提供合约入口以及反编译
合约合法性校验,第一个是输入一个用户名就可以进行校验,方法会给你反馈一个信息,第二个则是输入块密码,同样会获得一个反馈。
boolean verify(File carFile) throws Exception;
boolean verify(byte[] chainCode) throws Exception;
这两个方法是合约入口,两个分别输入用户名和密码,就可以进入合约
ContractEntrance analyse(File carFile) throws Exception;
ContractEntrance analyse(byte[] chainCode) throws Exception;
这两个是反编译入口
String decompileEntranceClass(File carFile) throws Exception;
String decompileEntranceClass(byte[] chainCode) throws Exception;
二、合约模型的类
1.ContractType
这是它的import
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.jd.blockchain.ledger.BytesValueEncoding;
import utils.IllegalDataException;
我们可以看到类创造了三个私有变量,分别是合约名,合约的两张哈希图
private String name;
private Map<String, Method> events = new HashMap<>();
private Map<Method, String> handleMethods = new HashMap<>();
这个方法用于获得合约的名字。
public String getName() {
return name;
}
方法返回声明的所有事件
public Set<String> getEvents() {
return events.keySet();
}
代码返回指定方法申明的事件,如果不存在则返回null
public String getEvent(Method method) {
return handleMethods.get(method);
}
代码返回事件处理的方法,没有就返回null
public Method getHandleMethod(String event) {
return events.get(event);
}
解析合约的申明,输入合约的内容代码就会执行它的解析工作,方法先检查合约方法申明的类型和返回值类型,如果是class则会先获得其接口,由于代码过于长,我将其他部分的解析写进了注释里面
public static ContractType resolve(Class<?> contractIntf) {
if (!contractIntf.isInterface()) {
Class<?> realIntf = null;
Class<?>[] interfaces = contractIntf.getInterfaces();
for (Class<?> intf : interfaces) {
if (intf.isAnnotationPresent(Contract.class)) {
realIntf = intf;
break;
}
}
if (realIntf == null) {
throw new IllegalDataException(String
.format("%s is not a Contract Type, because there is not @Contract !", contractIntf.getName()));
}
contractIntf = realIntf;
}
// 接口上必须有注解
Contract contract = contractIntf.getAnnotation(Contract.class);
if (contract == null) {
throw new IllegalDataException("It is not a Contract Type, because there is not @Contract !");
}
Method[] classMethods = contractIntf.getDeclaredMethods();
if (classMethods.length == 0) {
throw new IllegalDataException("This interface have not any methods !");
}
ContractType contractType = new ContractType();
// 设置合约显示名字为
contractType.name = contract.name();
for (Method method : classMethods) {
// if current method contains @ContractEvent,then put it in this map;
ContractEvent contractEvent = method.getAnnotation(ContractEvent.class);
if (contractEvent != null) {
String eventName = contractEvent.name();
// if annoMethodMap has contained the eventName, too many same eventNames exists
// probably, say NO!
if (contractType.events.containsKey(eventName)) {
throw new ContractException("there is repeat definition of contractEvent to @ContractEvent.");
}
// check param's type is fit for need.
Class<?>[] paramTypes = method.getParameterTypes();
for (Class<?> currParamType : paramTypes) {
if (!BytesValueEncoding.supportType(currParamType)) {
throw new IllegalStateException(
String.format("Param Type = %s can not support !!!", currParamType.getName()));
}
}
// 判断返回值是否可序列化
Class<?> returnType = method.getReturnType();
if (!BytesValueEncoding.supportType(returnType)) {
throw new IllegalStateException(
String.format("Return Type = %s can not support !!!", returnType.getName()));
}
contractType.events.put(eventName, method);
contractType.handleMethods.put(method, eventName);
}
}
// 最起码有一个ContractEvent
if (contractType.events.isEmpty()) {
throw new IllegalStateException(
String.format("Contract Interface[%s] have none method for annotation[@ContractEvent] !", contractIntf.getName()));
}
return contractType;
}
这个代码会返回合约类型
public String toString() {
return "ContractType{" + "name='" + name + '\'' + ", events=" + events + ", handleMethods=" + handleMethods
+ '}';
}
2.ContractJarUtils
这个类在开头定义了这些变量以及固定值,我们在接下来的方法中会用到。
public static final String BLACK_CONF = "filter.black.conf";
private static final String CONTRACT_MF = "META-INF/CONTRACT.MF";
private static final Random FILE_RANDOM = new Random();
private static final byte[] JDCHAIN_MARK = "JDChain".getBytes(StandardCharsets.UTF_8);
public static final String JDCHAIN_PACKAGE = "com.jd.blockchain";
这个方法是用于判断一个包是否是jdchain的包,输入包的名字,代码通过条件语句if判断,如果是则返回true,如果不是就返回JDCHAIN_PACKAGE.
public static boolean isJDChainPackage(String packageName) {
if (packageName.equals(JDCHAIN_PACKAGE)) {
return true;
}
return packageName.startsWith(JDCHAIN_PACKAGE + ".");
}
这个方法由于解决合约的配置问题,输入文件的名字,方法会新建一个数组列表,将对应的文件导入方法,判断其是否为空,不为空的话,文件的每一句都会被循环一遍,然后加上“,”的分隔符,最终会全部加载到代码开端新建的一个列表里面,如果文件为空则返回错误信息。
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;
}
这个方法用于加载合约的配置,,同样是输入文件的名字,代码会调用I OUtils中的方法将文件信息打印出来
public static List<String> loadConfig(String fileName) throws Exception {
return IOUtils.readLines(
ContractJarUtils.class.getResourceAsStream(File.separator + fileName));
}
这个代码用于加载所有的class,输入file的jare包,就可以进入方法的循环,获取元素,获取名字,然后将验证名字后,就可以不断的打印出class的信息。
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;
}
这个方法用来在class前加上一个点(.)
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;
}
这个方法输入file的各个jar等,用于复制合约中的信息。
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();
}