2021SC@SDUSC
目录
- 一、合约模型的接口
- 1.ContractAware
- 2.ContractEventContext
- 3.ContractLifeCycleAware
- 4.ContractProcessor
- 二、合约模型的类
- 1.ContractType
- 2.ContractJarUtils
一、合约模型的接口
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);}}// 最起码有一个ContractEventif (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();}