@@ -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); | |||||
} |