| @@ -88,6 +88,8 @@ public interface DataCodes { | |||||
| public static final int CONTRACT_TEXT = 0xA05; | public static final int CONTRACT_TEXT = 0xA05; | ||||
| public static final int CONTRACT_BINARY = 0xA06; | public static final int CONTRACT_BINARY = 0xA06; | ||||
| public static final int CONTRACT_BIG_INT = 0xA07; | public static final int CONTRACT_BIG_INT = 0xA07; | ||||
| //...0xA19 | |||||
| public static final int CONTRACT_BIZ_CONTENT = 0xA20; | |||||
| public static final int HASH = 0xB00; | public static final int HASH = 0xB00; | ||||
| @@ -7,7 +7,7 @@ public interface DataType { | |||||
| */ | */ | ||||
| public static final byte NIL = (byte) 0x00; | public static final byte NIL = (byte) 0x00; | ||||
| /** | |||||
| /**LdeNhjPGzHcHL6rLcJ7whHxUbn9Tv7qSKRfEA | |||||
| * 布尔; | * 布尔; | ||||
| */ | */ | ||||
| public static final byte BOOLEAN = (byte) 0x01; | public static final byte BOOLEAN = (byte) 0x01; | ||||
| @@ -1,5 +1,6 @@ | |||||
| package com.jd.blockchain.contract.jvm; | package com.jd.blockchain.contract.jvm; | ||||
| import com.jd.blockchain.binaryproto.DataContract; | |||||
| import com.jd.blockchain.contract.ContractEventContext; | import com.jd.blockchain.contract.ContractEventContext; | ||||
| import com.jd.blockchain.contract.ContractSerializeUtils; | import com.jd.blockchain.contract.ContractSerializeUtils; | ||||
| import com.jd.blockchain.contract.engine.ContractCode; | import com.jd.blockchain.contract.engine.ContractCode; | ||||
| @@ -12,6 +13,7 @@ import org.slf4j.LoggerFactory; | |||||
| import org.springframework.util.ReflectionUtils; | import org.springframework.util.ReflectionUtils; | ||||
| import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
| import java.util.List; | |||||
| /** | /** | ||||
| * contract code based jvm | * contract code based jvm | ||||
| @@ -25,7 +27,7 @@ public class JavaContractCode implements ContractCode { | |||||
| private long version; | private long version; | ||||
| private ContractEventContext contractEventContext; | private ContractEventContext contractEventContext; | ||||
| private ContractType contractType ; | |||||
| private ContractType contractType; | |||||
| public JavaContractCode(Bytes address, long version, Module codeModule) { | public JavaContractCode(Bytes address, long version, Module codeModule) { | ||||
| this.address = address; | this.address = address; | ||||
| @@ -49,8 +51,11 @@ public class JavaContractCode implements ContractCode { | |||||
| codeModule.execute(new ContractExecution()); | codeModule.execute(new ContractExecution()); | ||||
| } | } | ||||
| private Object resolveArgs(byte[] args, Method method) { | |||||
| return ContractSerializeUtils.deserializeMethodParam(args,method); | |||||
| private Object resolveArgs(byte[] args, List<DataContract> dataContractList) { | |||||
| if(args == null || args.length == 0){ | |||||
| return null; | |||||
| } | |||||
| return ContractSerializeUtils.deserializeMethodParam(args,dataContractList); | |||||
| } | } | ||||
| class ContractExecution implements Runnable { | class ContractExecution implements Runnable { | ||||
| @@ -73,11 +78,13 @@ public class JavaContractCode implements ContractCode { | |||||
| startTime = System.currentTimeMillis(); | startTime = System.currentTimeMillis(); | ||||
| // 反序列化参数; | // 反序列化参数; | ||||
| Method handleMethod = ContractType.resolve(myClass).getHandleMethod(contractEventContext.getEvent()); | |||||
| contractType = ContractType.resolve(myClass); | |||||
| Method handleMethod = contractType.getHandleMethod(contractEventContext.getEvent()); | |||||
| if (handleMethod == null){ | if (handleMethod == null){ | ||||
| throw new IllegalDataException("don't get this method by it's @ContractEvent."); | throw new IllegalDataException("don't get this method by it's @ContractEvent."); | ||||
| } | } | ||||
| Object args = resolveArgs(contractEventContext.getArgs(), handleMethod); | |||||
| Object args = resolveArgs(contractEventContext.getArgs(), | |||||
| contractType.getDataContractMap().get(handleMethod)); | |||||
| Object[] params = null; | Object[] params = null; | ||||
| if(args.getClass().isArray()){ | if(args.getClass().isArray()){ | ||||
| @@ -97,42 +104,6 @@ public class JavaContractCode implements ContractCode { | |||||
| throw new IllegalDataException(e.getMessage()); | throw new IllegalDataException(e.getMessage()); | ||||
| } | } | ||||
| } | } | ||||
| // 得到当前类中相关方法和注解对应关系; | |||||
| // Method getMethodByAnno(Object classObj, String eventName) { | |||||
| // Class<?> c = classObj.getClass(); | |||||
| // Class<ContractEvent> contractEventClass = null; | |||||
| // try { | |||||
| // contractEventClass = (Class<ContractEvent>) c.getClassLoader().loadClass(ContractEvent.class.getName()); | |||||
| // } catch (ClassNotFoundException e) { | |||||
| // e.printStackTrace(); | |||||
| // } | |||||
| // Method[] classMethods = c.getMethods(); | |||||
| // Map<Method, Annotation[]> methodAnnoMap = new HashMap<Method, Annotation[]>(); | |||||
| // Map<String, Method> annoMethodMap = new HashMap<String, Method>(); | |||||
| // for (int i = 0; i < classMethods.length; i++) { | |||||
| // Annotation[] a = classMethods[i].getDeclaredAnnotations(); | |||||
| // methodAnnoMap.put(classMethods[i], a); | |||||
| // // 如果当前方法中包含@ContractEvent注解,则将其放入Map; | |||||
| // for (Annotation annotation_ : a) { | |||||
| // // 如果是合同事件类型,则放入map; | |||||
| // if (classMethods[i].isAnnotationPresent(contractEventClass)) { | |||||
| // Object obj = classMethods[i].getAnnotation(contractEventClass); | |||||
| // String annoAllName = obj.toString(); | |||||
| // // format:@com.jd.blockchain.contract.model.ContractEvent(name=transfer-asset) | |||||
| // String eventName_ = obj.toString().substring(BaseConstant.CONTRACT_EVENT_PREFIX.length(), | |||||
| // annoAllName.length() - 1); | |||||
| // annoMethodMap.put(eventName_, classMethods[i]); | |||||
| // break; | |||||
| // } | |||||
| // } | |||||
| // } | |||||
| // if (annoMethodMap.containsKey(eventName)) { | |||||
| // return annoMethodMap.get(eventName); | |||||
| // } else { | |||||
| // return null; | |||||
| // } | |||||
| // } | |||||
| } | } | ||||
| } | } | ||||
| @@ -6,13 +6,11 @@ import com.jd.blockchain.consts.DataCodes; | |||||
| import com.jd.blockchain.ledger.*; | import com.jd.blockchain.ledger.*; | ||||
| import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
| import org.springframework.util.ReflectionUtils; | import org.springframework.util.ReflectionUtils; | ||||
| import java.lang.annotation.Annotation; | |||||
| import java.lang.reflect.Method; | |||||
| import java.math.BigDecimal; | import java.math.BigDecimal; | ||||
| import java.nio.ByteBuffer; | import java.nio.ByteBuffer; | ||||
| import java.util.Arrays; | import java.util.Arrays; | ||||
| import java.util.HashMap; | import java.util.HashMap; | ||||
| import java.util.List; | |||||
| import java.util.Map; | import java.util.Map; | ||||
| /** | /** | ||||
| @@ -24,57 +22,20 @@ public class ContractSerializeUtils { | |||||
| public static final Integer[] PRIMITIVE_DATA_CODES = {DataCodes.CONTRACT_INT8, DataCodes.CONTRACT_INT16, DataCodes.CONTRACT_INT32, | public static final Integer[] PRIMITIVE_DATA_CODES = {DataCodes.CONTRACT_INT8, DataCodes.CONTRACT_INT16, DataCodes.CONTRACT_INT32, | ||||
| DataCodes.CONTRACT_INT64, DataCodes.CONTRACT_BIG_INT,DataCodes.CONTRACT_TEXT, DataCodes.CONTRACT_BINARY }; | DataCodes.CONTRACT_INT64, DataCodes.CONTRACT_BIG_INT,DataCodes.CONTRACT_TEXT, DataCodes.CONTRACT_BINARY }; | ||||
| /** | /** | ||||
| * valid then parse the Object by Method params; | |||||
| * @param object | |||||
| * @param method | |||||
| * serialize the Object[] by List<DataContract> list; | |||||
| * @param objArr | |||||
| * @param dataContractList | |||||
| * @return | * @return | ||||
| */ | */ | ||||
| public static byte[] serializeMethodParam(Object object,Method method) { | |||||
| if (object == null) { | |||||
| return null; | |||||
| } | |||||
| Object[] objArr = null; | |||||
| if(object.getClass().isArray()){ | |||||
| objArr = (Object[]) object; | |||||
| } | |||||
| Class<?>[] classTypes = method.getParameterTypes(); | |||||
| byte[][] result = new byte[classTypes.length][]; | |||||
| public static byte[] serializeMethodParam(Object[] objArr,List<DataContract> dataContractList) { | |||||
| byte[][] result = new byte[objArr.length][]; | |||||
| //将method中形参转换为实体对象,每个形参都必须为@DataContract类型;每个形参使用系统的BinaryProtocol来进行序列化,如果有5个参数,则使用5次序列化; | //将method中形参转换为实体对象,每个形参都必须为@DataContract类型;每个形参使用系统的BinaryProtocol来进行序列化,如果有5个参数,则使用5次序列化; | ||||
| int sum = 0; | int sum = 0; | ||||
| for(int i=0;i<classTypes.length;i++){ | |||||
| Class <?> classType = classTypes[i]; | |||||
| DataContract dataContract = classType.getDeclaredAnnotation(DataContract.class); | |||||
| //if the param's class Type don't contain @DataContract, then check parameterAnnotations of this method. | |||||
| if(dataContract == null){ | |||||
| boolean canPass = false; | |||||
| //check by annotation; | |||||
| // Annotation[] annotationArr = annotations[i]; | |||||
| // for(Annotation annotation : annotationArr){ | |||||
| // if(annotation instanceof DataContract){ | |||||
| // dataContract = (DataContract) annotation; | |||||
| // objArr[i] = regenObj(dataContract,objArr[i]); | |||||
| // canPass = true; | |||||
| // } | |||||
| // } | |||||
| //if parameterAnnotations don't contain @DataContract, is it primitive type? | |||||
| Class<?> contractType = getContractTypeByPrimitiveType(classType); | |||||
| dataContract = contractType.getDeclaredAnnotation(DataContract.class); | |||||
| if(dataContract != null){ | |||||
| objArr[i] = regenObj(dataContract,objArr[i]); | |||||
| canPass = true; | |||||
| } | |||||
| if(!canPass){ | |||||
| throw new IllegalArgumentException("must set @DataContract for each param of contract."); | |||||
| } | |||||
| } | |||||
| if(!getDataIntf().containsKey(dataContract.code())){ | |||||
| throw new IllegalArgumentException(String.format( | |||||
| "for now, this @dataContract(code=%s) is forbidden in the param list.",dataContract.code())); | |||||
| } | |||||
| for(int i=0;i<objArr.length;i++){ | |||||
| DataContract dataContract = dataContractList.get(i); | |||||
| objArr[i] = regenObj(dataContract,objArr[i]); | |||||
| //get data interface; | //get data interface; | ||||
| result[i] = BinaryProtocol.encode(objArr[i],getDataIntf().get(dataContract.code())); | result[i] = BinaryProtocol.encode(objArr[i],getDataIntf().get(dataContract.code())); | ||||
| sum += result[i].length; | sum += result[i].length; | ||||
| @@ -91,9 +52,9 @@ public class ContractSerializeUtils { | |||||
| rtnBytes[...]: result[1][] bytes(2 param's length); | rtnBytes[...]: result[1][] bytes(2 param's length); | ||||
| rtnBytes[...]: result[2][] bytes(3 param's length); | rtnBytes[...]: result[2][] bytes(3 param's length); | ||||
| */ | */ | ||||
| int bodyFirstPosition = 4 + 4 * (classTypes.length); | |||||
| int bodyFirstPosition = 4 + 4 * (objArr.length); | |||||
| ByteBuffer byteBuffer = ByteBuffer.allocate(bodyFirstPosition + sum); | ByteBuffer byteBuffer = ByteBuffer.allocate(bodyFirstPosition + sum); | ||||
| byteBuffer.putInt(classTypes.length); | |||||
| byteBuffer.putInt(objArr.length); | |||||
| for(int j=0; j<result.length; j++) { | for(int j=0; j<result.length; j++) { | ||||
| byte[] curResult = result[j]; | byte[] curResult = result[j]; | ||||
| byteBuffer.putInt(curResult.length); | byteBuffer.putInt(curResult.length); | ||||
| @@ -108,59 +69,26 @@ public class ContractSerializeUtils { | |||||
| * deserialize the params bytes[]; | * deserialize the params bytes[]; | ||||
| * params format: nums|first length| second length| third length| ... |bytes[0]| byte[1] | bytes[2]| ... | * params format: nums|first length| second length| third length| ... |bytes[0]| byte[1] | bytes[2]| ... | ||||
| * @param params | * @param params | ||||
| * @param method | |||||
| * @param dataContractList | |||||
| * @return | * @return | ||||
| */ | */ | ||||
| public static Object[] deserializeMethodParam(byte[] params, Method method) { | |||||
| if (params == null) { | |||||
| return null; | |||||
| } | |||||
| Class<?>[] classTypes = method.getParameterTypes(); | |||||
| Object result[] = new Object[classTypes.length]; | |||||
| public static Object[] deserializeMethodParam(byte[] params, List<DataContract> dataContractList) { | |||||
| Object result[] = new Object[dataContractList.size()]; | |||||
| ByteBuffer byteBuffer = ByteBuffer.allocate(params.length); | ByteBuffer byteBuffer = ByteBuffer.allocate(params.length); | ||||
| byteBuffer.put(params); | byteBuffer.put(params); | ||||
| int paramNums = byteBuffer.getInt(0); | int paramNums = byteBuffer.getInt(0); | ||||
| if(paramNums != classTypes.length){ | |||||
| if(paramNums != dataContractList.size()){ | |||||
| throw new IllegalArgumentException("deserialize Method param. params'length in byte[] != method's param length"); | throw new IllegalArgumentException("deserialize Method param. params'length in byte[] != method's param length"); | ||||
| } | } | ||||
| Annotation [][] annotations = method.getParameterAnnotations(); | |||||
| int offsetPosition = (1 + classTypes.length)*4; //start position of real data; | |||||
| for(int i=0; i<classTypes.length; i++){ | |||||
| Class<?> classType = classTypes[i]; | |||||
| int offsetPosition = (1 + dataContractList.size())*4; //start position of real data; | |||||
| for(int i=0; i<dataContractList.size(); i++){ | |||||
| DataContract dataContract = dataContractList.get(i); | |||||
| int curParamLength = byteBuffer.getInt((i+1)*4); | int curParamLength = byteBuffer.getInt((i+1)*4); | ||||
| DataContract dataContract = classType.getDeclaredAnnotation(DataContract.class); | |||||
| if(dataContract == null){ | |||||
| boolean canPass = false; | |||||
| //check by annotation; | |||||
| // Annotation[] annotationArr = annotations[i]; | |||||
| // for(Annotation annotation : annotationArr){ | |||||
| // if(annotation.annotationType().equals(DataContract.class)){ | |||||
| // dataContract = (DataContract) annotation; | |||||
| // canPass = true; | |||||
| // } | |||||
| // } | |||||
| //if parameterAnnotations don't contain @DataContract, is it primitive type? | |||||
| Class<?> contractType = getContractTypeByPrimitiveType(classType); | |||||
| dataContract = contractType.getDeclaredAnnotation(DataContract.class); | |||||
| if(dataContract != null){ | |||||
| canPass = true; | |||||
| } | |||||
| if(!canPass){ | |||||
| throw new IllegalArgumentException("must set annotation in each param of contract."); | |||||
| } | |||||
| } | |||||
| ByteBuffer byteBuffer1 = ByteBuffer.allocate(curParamLength); | ByteBuffer byteBuffer1 = ByteBuffer.allocate(curParamLength); | ||||
| byteBuffer1.put(params,offsetPosition,curParamLength); | byteBuffer1.put(params,offsetPosition,curParamLength); | ||||
| offsetPosition += curParamLength; | offsetPosition += curParamLength; | ||||
| if(!getDataIntf().containsKey(dataContract.code())){ | |||||
| throw new IllegalArgumentException(String.format( | |||||
| "for now, this @dataContract(code=%s) is forbidden in the param list.",dataContract.code())); | |||||
| } | |||||
| //if dataContract=primitive type(byte/short/int/long/String),only use its getValues(); | //if dataContract=primitive type(byte/short/int/long/String),only use its getValues(); | ||||
| Object object = BinaryProtocol.decodeAs(byteBuffer1.array(), | Object object = BinaryProtocol.decodeAs(byteBuffer1.array(), | ||||
| getDataIntf().get(dataContract.code())); | getDataIntf().get(dataContract.code())); | ||||
| @@ -239,4 +167,26 @@ public class ContractSerializeUtils { | |||||
| } | } | ||||
| return null; | return null; | ||||
| } | } | ||||
| public static DataContract parseDataContract(Class<?> classType){ | |||||
| DataContract dataContract = classType.getDeclaredAnnotation(DataContract.class); | |||||
| //if the param's class Type don't contain @DataContract, then check parameterAnnotations of this method. | |||||
| if(dataContract == null){ | |||||
| boolean canPass = false; | |||||
| //if parameterAnnotations don't contain @DataContract, is it primitive type? | |||||
| Class<?> contractType = getContractTypeByPrimitiveType(classType); | |||||
| dataContract = contractType.getDeclaredAnnotation(DataContract.class); | |||||
| if(dataContract != null){ | |||||
| canPass = true; | |||||
| } | |||||
| if(!canPass){ | |||||
| throw new IllegalArgumentException("must set @DataContract for each param of contract."); | |||||
| } | |||||
| } | |||||
| if(!getDataIntf().containsKey(dataContract.code())){ | |||||
| throw new IllegalArgumentException(String.format( | |||||
| "for now, this @dataContract(code=%s) is forbidden in the param list.",dataContract.code())); | |||||
| } | |||||
| return dataContract; | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,30 @@ | |||||
| package com.jd.blockchain.ledger; | |||||
| import com.jd.blockchain.binaryproto.DataContract; | |||||
| import com.jd.blockchain.binaryproto.DataField; | |||||
| import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
| import com.jd.blockchain.consts.DataCodes; | |||||
| import com.jd.blockchain.crypto.HashDigest; | |||||
| /** | |||||
| * build complex param Object; | |||||
| */ | |||||
| @DataContract(code = DataCodes.CONTRACT_BIZ_CONTENT) | |||||
| public interface ContractBizContent { | |||||
| /** | |||||
| * 执行交易的账本地址; | |||||
| * 注:除了账本的创世交易之外,任何交易的账本地址都不允许为 null; | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| @DataField(order = 1, primitiveType = PrimitiveType.BYTES) | |||||
| HashDigest getLedgerHash(); | |||||
| /** | |||||
| * 操作列表; | |||||
| * @return | |||||
| */ | |||||
| @DataField(order = 2, list = true, refContract = true, genericContract = true) | |||||
| Operation[] getOperations(); | |||||
| } | |||||
| @@ -47,6 +47,9 @@ public class ContractInvocationProxy implements InvocationHandler { | |||||
| private byte[] serializeArgs(Object[] args, Method method) { | private byte[] serializeArgs(Object[] args, Method method) { | ||||
| // TODO 根据方法参数的定义序列化参数; | // TODO 根据方法参数的定义序列化参数; | ||||
| return ContractSerializeUtils.serializeMethodParam(args,method); | |||||
| if(args == null || args.length==0){ | |||||
| return null; | |||||
| } | |||||
| return ContractSerializeUtils.serializeMethodParam(args,contractType.getDataContractMap().get(method)); | |||||
| } | } | ||||
| } | } | ||||
| @@ -38,19 +38,6 @@ public class ContractInvocationProxyBuilder { | |||||
| if (contractType != null) { | if (contractType != null) { | ||||
| return contractType; | return contractType; | ||||
| } | } | ||||
| // 判断是否是标注了合约的接口类型; | |||||
| if (!isContractType(contractIntf)){ | |||||
| throw new IllegalDataException("is not Contract Type, becaust there is not @Contract."); | |||||
| } | |||||
| // 解析合约事件处理方法,检查是否有重名; | |||||
| if(!isUniqueEvent(contractIntf)){ | |||||
| throw new IllegalDataException("there is repeat definition of contractEvent to @ContractEvent."); | |||||
| } | |||||
| // TODO 检查是否不支持的参数类型; | |||||
| // TODO 检查返回值类型; | // TODO 检查返回值类型; | ||||
| ContractType contractType1 = ContractType.resolve(contractIntf); | ContractType contractType1 = ContractType.resolve(contractIntf); | ||||
| @@ -59,33 +46,6 @@ public class ContractInvocationProxyBuilder { | |||||
| } | } | ||||
| private boolean isUniqueEvent(Class<?> contractIntf) { | |||||
| boolean isUnique = true; | |||||
| Method[] classMethods = contractIntf.getMethods(); | |||||
| Map<Method, Annotation[]> methodAnnoMap = new HashMap<Method, Annotation[]>(); | |||||
| Map<String, Method> annoMethodMap = new HashMap<String, Method>(); | |||||
| for (int i = 0; i < classMethods.length; i++) { | |||||
| Annotation[] annotations = classMethods[i].getDeclaredAnnotations(); | |||||
| methodAnnoMap.put(classMethods[i], annotations); | |||||
| // if current method contains @ContractEvent,then put it in this map; | |||||
| Method curMethod = classMethods[i]; | |||||
| ContractEvent contractEvent = curMethod.getAnnotation(ContractEvent.class); | |||||
| if (contractEvent != null) { | |||||
| Object obj = classMethods[i].getAnnotation(ContractEvent.class); | |||||
| String annoAllName = obj.toString(); | |||||
| // format:@com.jd.blockchain.contract.model.ContractEvent(name=transfer-asset) | |||||
| String eventName_ = contractEvent.name(); | |||||
| //if annoMethodMap has contained the eventName, too many same eventNames exists probably, say NO! | |||||
| if(annoMethodMap.containsKey(eventName_)){ | |||||
| isUnique = false; | |||||
| } | |||||
| annoMethodMap.put(eventName_, classMethods[i]); | |||||
| } | |||||
| } | |||||
| return isUnique; | |||||
| } | |||||
| /** | /** | ||||
| * is contractType really? identified by @Contract; | * is contractType really? identified by @Contract; | ||||
| * @param contractIntf | * @param contractIntf | ||||
| @@ -1,20 +1,22 @@ | |||||
| package com.jd.blockchain.transaction; | package com.jd.blockchain.transaction; | ||||
| import com.jd.blockchain.binaryproto.DataContract; | |||||
| import com.jd.blockchain.contract.Contract; | |||||
| import com.jd.blockchain.contract.ContractEvent; | import com.jd.blockchain.contract.ContractEvent; | ||||
| import com.jd.blockchain.contract.ContractException; | import com.jd.blockchain.contract.ContractException; | ||||
| import com.jd.blockchain.contract.ContractSerializeUtils; | |||||
| import com.jd.blockchain.utils.IllegalDataException; | |||||
| import java.lang.annotation.Annotation; | |||||
| import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| import java.util.Set; | |||||
| import java.util.*; | |||||
| public class ContractType { | public class ContractType { | ||||
| private String name; | private String name; | ||||
| private Map<String, Method> events = new HashMap<>(); | private Map<String, Method> events = new HashMap<>(); | ||||
| private Map<Method, String> handleMethods = new HashMap<>();; | |||||
| private Map<Method, String> handleMethods = new HashMap<>(); | |||||
| private Map<Method, List<DataContract>> dataContractMap = new HashMap<>(); | |||||
| /** | /** | ||||
| * 返回声明的所有事件; | * 返回声明的所有事件; | ||||
| @@ -25,6 +27,10 @@ public class ContractType { | |||||
| return events.keySet(); | return events.keySet(); | ||||
| } | } | ||||
| public Map<Method, List<DataContract>> getDataContractMap() { | |||||
| return dataContractMap; | |||||
| } | |||||
| /** | /** | ||||
| * 返回指定方法声明的事件;<br> | * 返回指定方法声明的事件;<br> | ||||
| * | * | ||||
| @@ -54,6 +60,15 @@ public class ContractType { | |||||
| public static ContractType resolve(Class<?> contractIntf){ | public static ContractType resolve(Class<?> contractIntf){ | ||||
| ContractType contractType = new ContractType(); | ContractType contractType = new ContractType(); | ||||
| Annotation annotation = contractIntf.getDeclaredAnnotation(Contract.class); | |||||
| //contains: @Contract? | |||||
| boolean isContractType = annotation != null ? true : false; | |||||
| if(!isContractType){ | |||||
| throw new IllegalDataException("is not Contract Type, becaust there is not @Contract."); | |||||
| } | |||||
| //contractIntf contains @Contract and @ContractEvent; | //contractIntf contains @Contract and @ContractEvent; | ||||
| Method[] classMethods = contractIntf.getDeclaredMethods(); | Method[] classMethods = contractIntf.getDeclaredMethods(); | ||||
| for (Method method : classMethods) { | for (Method method : classMethods) { | ||||
| @@ -63,8 +78,19 @@ public class ContractType { | |||||
| String eventName_ = contractEvent.name(); | String eventName_ = contractEvent.name(); | ||||
| //if annoMethodMap has contained the eventName, too many same eventNames exists probably, say NO! | //if annoMethodMap has contained the eventName, too many same eventNames exists probably, say NO! | ||||
| if(contractType.events.containsKey(eventName_)){ | if(contractType.events.containsKey(eventName_)){ | ||||
| throw new ContractException("too many same eventNames exists in the contract, check it."); | |||||
| throw new ContractException("there is repeat definition of contractEvent to @ContractEvent."); | |||||
| } | |||||
| //check param's type is fit for need. | |||||
| Class<?>[] paramTypes = method.getParameterTypes(); | |||||
| List dataContractList = new ArrayList(); | |||||
| for(Class<?> curParamType : paramTypes){ | |||||
| DataContract dataContract = ContractSerializeUtils.parseDataContract(curParamType); | |||||
| dataContractList.add(dataContract); | |||||
| } | } | ||||
| if(dataContractList.size()>0){ | |||||
| contractType.dataContractMap.put(method,dataContractList); | |||||
| } | |||||
| contractType.events.put(eventName_, method); | contractType.events.put(eventName_, method); | ||||
| contractType.handleMethods.put(method,eventName_); | contractType.handleMethods.put(method,eventName_); | ||||
| } | } | ||||
| @@ -68,7 +68,7 @@ public class SDK_Contract_Test { | |||||
| public void demoContract1() { | public void demoContract1() { | ||||
| // 发起交易; | // 发起交易; | ||||
| TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); | TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); | ||||
| String contractAddress = "LdeNw7PsVrXTrffQMfYLTvqscDyQ8QCUPruTS"; | |||||
| String contractAddress = "LdeNhjPGzHcHL6rLcJ7whHxUbn9Tv7qSKRfEA"; | |||||
| AssetContract2 assetContract = txTemp.contract(contractAddress, AssetContract2.class); | AssetContract2 assetContract = txTemp.contract(contractAddress, AssetContract2.class); | ||||
| TransactionContentBody transactionContentBody = new TransactionContentBody() { | TransactionContentBody transactionContentBody = new TransactionContentBody() { | ||||
| @Override | @Override | ||||
| @@ -29,6 +29,7 @@ import org.apache.commons.io.FileUtils; | |||||
| import org.junit.Assert; | import org.junit.Assert; | ||||
| import org.springframework.core.io.ClassPathResource; | import org.springframework.core.io.ClassPathResource; | ||||
| import test.com.jd.blockchain.intgr.contract.AssetContract; | import test.com.jd.blockchain.intgr.contract.AssetContract; | ||||
| import test.com.jd.blockchain.intgr.contract.AssetContract2; | |||||
| import java.io.File; | import java.io.File; | ||||
| import java.io.FileInputStream; | import java.io.FileInputStream; | ||||
| @@ -431,17 +432,13 @@ public class IntegrationBase { | |||||
| } | } | ||||
| // 合约测试使用的初始化数据; | // 合约测试使用的初始化数据; | ||||
| BlockchainKeypair contractDataKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
| BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
| static BlockchainKeypair contractDataKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
| static BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
| // 保存资产总数的键; | // 保存资产总数的键; | ||||
| private static final String KEY_TOTAL = "TOTAL"; | |||||
| // 第二个参数; | // 第二个参数; | ||||
| private static final String KEY_ABC = "abc"; | |||||
| private String contractZipName = "Example1.jar"; | |||||
| HashDigest txContentHash; | |||||
| String pubKeyVal = "jd.com"+System.currentTimeMillis(); | |||||
| private String eventName = "issue-asset"; | |||||
| public LedgerBlock testSDK_Contract(AsymmetricKeypair adminKey, HashDigest ledgerHash, | |||||
| private static String contractZipName = "contract.jar"; | |||||
| static HashDigest txContentHash; | |||||
| public static LedgerBlock testSDK_Contract(AsymmetricKeypair adminKey, HashDigest ledgerHash, | |||||
| BlockchainService blockchainService,LedgerRepository ledgerRepository) { | BlockchainService blockchainService,LedgerRepository ledgerRepository) { | ||||
| System.out.println("adminKey="+ AddressEncoding.generateAddress(adminKey.getPubKey())); | System.out.println("adminKey="+ AddressEncoding.generateAddress(adminKey.getPubKey())); | ||||
| BlockchainKeypair userKey = BlockchainKeyGenerator.getInstance().generate(); | BlockchainKeypair userKey = BlockchainKeyGenerator.getInstance().generate(); | ||||
| @@ -476,7 +473,7 @@ public class IntegrationBase { | |||||
| return block; | return block; | ||||
| } | } | ||||
| private <T> void testContractExe(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair userKey, | |||||
| private static <T> void testContractExe(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair userKey, | |||||
| BlockchainService blockchainService,LedgerRepository ledgerRepository,Class<T> contractIntf) { | BlockchainService blockchainService,LedgerRepository ledgerRepository,Class<T> contractIntf) { | ||||
| LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); | LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); | ||||
| LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); | LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); | ||||
| @@ -484,7 +481,9 @@ public class IntegrationBase { | |||||
| // 定义交易; | // 定义交易; | ||||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | ||||
| txTpl.contract(contractDeployKey.getAddress(),AssetContract.class).issue(10,"abc"); | |||||
| Byte byteObj = Byte.parseByte("127"); | |||||
| txTpl.contract(contractDeployKey.getAddress(),AssetContract2.class).issue(byteObj, | |||||
| contractDeployKey.getAddress().toBase58(),321123); | |||||
| // 签名; | // 签名; | ||||
| PreparedTransaction ptx = txTpl.prepare(); | PreparedTransaction ptx = txTpl.prepare(); | ||||
| @@ -503,7 +502,7 @@ public class IntegrationBase { | |||||
| * | * | ||||
| * @return | * @return | ||||
| */ | */ | ||||
| private byte[] getChainCodeBytes() { | |||||
| private static byte[] getChainCodeBytes() { | |||||
| // 构建合约的字节数组; | // 构建合约的字节数组; | ||||
| byte[] contractCode = null; | byte[] contractCode = null; | ||||
| File file = null; | File file = null; | ||||
| @@ -0,0 +1,53 @@ | |||||
| package test.com.jd.blockchain.intgr.contract; | |||||
| import com.jd.blockchain.binaryproto.DataContract; | |||||
| import com.jd.blockchain.consts.DataCodes; | |||||
| import com.jd.blockchain.contract.Contract; | |||||
| import com.jd.blockchain.contract.ContractEvent; | |||||
| import com.jd.blockchain.ledger.TransactionContentBody; | |||||
| import com.jd.blockchain.utils.Bytes; | |||||
| /** | |||||
| * 示例:一个“资产管理”智能合约; | |||||
| * | |||||
| * @author zhaogw | |||||
| */ | |||||
| @Contract | |||||
| public interface AssetContract2 { | |||||
| /** | |||||
| * 发行资产; | |||||
| * 新发行的资产数量; | |||||
| * @param assetHolderAddress | |||||
| * 新发行的资产的持有账户; | |||||
| */ | |||||
| @ContractEvent(name = "issue-asset-0") | |||||
| void issue(@DataContract(code = DataCodes.TX_CONTENT_BODY) TransactionContentBody transactionContentBody, | |||||
| @DataContract(code = DataCodes.CONTRACT_TEXT) String assetHolderAddress); | |||||
| /** | |||||
| * issue asset; | |||||
| * @param transactionContentBody | |||||
| * @param assetHolderAddress | |||||
| * @param cashNumber | |||||
| */ | |||||
| @ContractEvent(name = "issue-asset") | |||||
| public void issue(@DataContract(code = DataCodes.TX_CONTENT_BODY) TransactionContentBody transactionContentBody, | |||||
| @DataContract(code = DataCodes.CONTRACT_TEXT) String assetHolderAddress, | |||||
| @DataContract(code = DataCodes.CONTRACT_INT64) long cashNumber); | |||||
| /** | |||||
| * Bytes can bring the byte[]; | |||||
| * @param bytes | |||||
| * @param assetHolderAddress | |||||
| * @param cashNumber | |||||
| */ | |||||
| @ContractEvent(name = "issue-asset-2") | |||||
| void issue(Bytes bytes, String assetHolderAddress, long cashNumber); | |||||
| @ContractEvent(name = "issue-asset-3") | |||||
| void issue(Byte bytes, String assetHolderAddress, long cashNumber); | |||||
| @ContractEvent(name = "issue-asset-4") | |||||
| void issue1(byte[] bytes, String assetHolderAddress, long cashNumber); | |||||
| } | |||||