diff --git a/source/base/src/main/java/com/jd/blockchain/consts/DataCodes.java b/source/base/src/main/java/com/jd/blockchain/consts/DataCodes.java index 6b47a505..a7382d0a 100644 --- a/source/base/src/main/java/com/jd/blockchain/consts/DataCodes.java +++ b/source/base/src/main/java/com/jd/blockchain/consts/DataCodes.java @@ -79,7 +79,17 @@ public interface DataCodes { public static final int DATA = 0x900; + //contract related; public static final int CONTRACT = 0xA00; + public static final int CONTRACT_INT8 = 0xA01; + public static final int CONTRACT_INT16 = 0xA02; + public static final int CONTRACT_INT32 = 0xA03; + public static final int CONTRACT_INT64 = 0xA04; + public static final int CONTRACT_TEXT = 0xA05; + public static final int CONTRACT_BINARY = 0xA06; + public static final int CONTRACT_BIG_INT = 0xA07; + //...0xA19 + public static final int CONTRACT_BIZ_CONTENT = 0xA20; public static final int HASH = 0xB00; diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContract.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContract.java index dee872e3..6c0fc8e2 100644 --- a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContract.java +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContract.java @@ -27,7 +27,7 @@ import java.lang.annotation.Target; * @author huanghaiquan * */ -@Target({ ElementType.TYPE }) +@Target({ ElementType.TYPE, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) public @interface DataContract { diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataType.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataType.java index 9df9f7bd..1129518b 100644 --- a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataType.java +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataType.java @@ -7,7 +7,7 @@ public interface DataType { */ public static final byte NIL = (byte) 0x00; - /** + /**LdeNhjPGzHcHL6rLcJ7whHxUbn9Tv7qSKRfEA * 布尔; */ public static final byte BOOLEAN = (byte) 0x01; diff --git a/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java index 90f68d5a..6bf5e859 100644 --- a/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java +++ b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java @@ -1,24 +1,24 @@ package com.jd.blockchain.contract.jvm; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.ReflectionUtils; - -import com.jd.blockchain.contract.ContractEvent; +import com.jd.blockchain.binaryproto.DataContract; import com.jd.blockchain.contract.ContractEventContext; +import com.jd.blockchain.contract.ContractSerializeUtils; import com.jd.blockchain.contract.engine.ContractCode; import com.jd.blockchain.runtime.Module; -import com.jd.blockchain.utils.BaseConstant; +import com.jd.blockchain.transaction.ContractType; import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.IllegalDataException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Method; +import java.util.List; /** * contract code based jvm - * @author zhaogw + * + * @author zhaogw */ public class JavaContractCode implements ContractCode { private static final Logger LOGGER = LoggerFactory.getLogger(JavaContractCode.class); @@ -26,7 +26,9 @@ public class JavaContractCode implements ContractCode { private Bytes address; private long version; private ContractEventContext contractEventContext; - + + private ContractType contractType; + public JavaContractCode(Bytes address, long version, Module codeModule) { this.address = address; this.version = version; @@ -37,7 +39,7 @@ public class JavaContractCode implements ContractCode { public Bytes getAddress() { return address; } - + @Override public long getVersion() { return version; @@ -46,75 +48,62 @@ public class JavaContractCode implements ContractCode { @Override public void processEvent(ContractEventContext eventContext) { this.contractEventContext = eventContext; - codeModule.execute(new ContractThread()); + codeModule.execute(new ContractExecution()); } - class ContractThread implements Runnable{ - public void run(){ + private Object resolveArgs(byte[] args, List dataContractList) { + if(args == null || args.length == 0){ + return null; + } + return ContractSerializeUtils.deserializeMethodParam(args,dataContractList); + } + + class ContractExecution implements Runnable { + public void run() { LOGGER.info("ContractThread execute()."); try { - //执行预处理; + // 执行预处理; long startTime = System.currentTimeMillis(); String contractClassName = codeModule.getMainClass(); Class myClass = codeModule.loadClass(contractClassName); - Object contractMainClassObj = myClass.newInstance();//合约主类生成的类实例; + Object contractMainClassObj = myClass.newInstance();// 合约主类生成的类实例; - Method beforeMth_ = myClass.getMethod("beforeEvent",codeModule.loadClass(ContractEventContext.class.getName())); - ReflectionUtils.invokeMethod(beforeMth_,contractMainClassObj,contractEventContext); - LOGGER.info("beforeEvent,耗时:"+(System.currentTimeMillis()-startTime)); + Method beforeMth_ = myClass.getMethod("beforeEvent", + codeModule.loadClass(ContractEventContext.class.getName())); + ReflectionUtils.invokeMethod(beforeMth_, contractMainClassObj, contractEventContext); + LOGGER.info("beforeEvent,耗时:" + (System.currentTimeMillis() - startTime)); - Method eventMethod = this.getMethodByAnno(contractMainClassObj,contractEventContext.getEvent()); +// Method eventMethod = this.getMethodByAnno(contractMainClassObj, contractEventContext.getEvent()); startTime = System.currentTimeMillis(); - ReflectionUtils.invokeMethod(eventMethod,contractMainClassObj,contractEventContext); + // 反序列化参数; + contractType = ContractType.resolve(myClass); + Method handleMethod = contractType.getHandleMethod(contractEventContext.getEvent()); + if (handleMethod == null){ + throw new IllegalDataException("don't get this method by it's @ContractEvent."); + } + Object args = resolveArgs(contractEventContext.getArgs(), + contractType.getDataContractMap().get(handleMethod)); - LOGGER.info("合约执行,耗时:"+(System.currentTimeMillis()-startTime)); + Object[] params = null; + if(args.getClass().isArray()){ + params = (Object[])args; + } + ReflectionUtils.invokeMethod(handleMethod, contractMainClassObj, params); + + LOGGER.info("合约执行,耗时:" + (System.currentTimeMillis() - startTime)); Method mth2 = myClass.getMethod("postEvent"); startTime = System.currentTimeMillis(); - ReflectionUtils.invokeMethod(mth2,contractMainClassObj); - LOGGER.info("postEvent,耗时:"+(System.currentTimeMillis()-startTime)); + ReflectionUtils.invokeMethod(mth2, contractMainClassObj); + LOGGER.info("postEvent,耗时:" + (System.currentTimeMillis() - startTime)); } catch (NoSuchMethodException e) { - e.printStackTrace(); + throw new IllegalArgumentException(e.getMessage()); } catch (Exception e) { - e.printStackTrace(); - } - } - - //得到当前类中相关方法和注解对应关系; - Method getMethodByAnno(Object classObj, String eventName){ - Class c = classObj.getClass(); - Class contractEventClass = null; - try { - contractEventClass = (Class )c.getClassLoader().loadClass(ContractEvent.class.getName()); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } - Method[] classMethods = c.getMethods(); - Map methodAnnoMap = new HashMap(); - Map annoMethodMap = new HashMap(); - for(int i = 0;i> DATA_CONTRACT_MAP = new HashMap<>(); + 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 }; + + /** + * serialize the Object[] by List list; + * @param objArr + * @param dataContractList + * @return + */ + public static byte[] serializeMethodParam(Object[] objArr,List dataContractList) { + byte[][] result = new byte[objArr.length][]; + //将method中形参转换为实体对象,每个形参都必须为@DataContract类型;每个形参使用系统的BinaryProtocol来进行序列化,如果有5个参数,则使用5次序列化; + int sum = 0; + + for(int i=0;i dataContractList) { + Object result[] = new Object[dataContractList.size()]; + ByteBuffer byteBuffer = ByteBuffer.allocate(params.length); + byteBuffer.put(params); + int paramNums = byteBuffer.getInt(0); + + if(paramNums != dataContractList.size()){ + throw new IllegalArgumentException("deserialize Method param. params'length in byte[] != method's param length"); + } + + int offsetPosition = (1 + dataContractList.size())*4; //start position of real data; + for(int i=0; i classObj = getDataIntf().get(dataContract.code()); + try { + result[i] = ReflectionUtils.invokeMethod(classObj.getMethod("getValue"),object); + } catch (NoSuchMethodException e) { + throw new IllegalStateException("no getValue(). detail="+e.getMessage()); + } + }else { + result[i] = object; + } + byteBuffer1.clear(); + } + + return result; + } + + + /** + * the param types that we can support; + * @param + * @return + */ + public static Map > getDataIntf(){ + DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT8, CONTRACT_INT8.class); + DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT16, CONTRACT_INT16.class); + DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT32, CONTRACT_INT32.class); + DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT64, CONTRACT_INT64.class); + DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_TEXT, CONTRACT_TEXT.class); + DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_BINARY, CONTRACT_BINARY.class); + DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_BIG_INT, CONTRACT_BIG_INT.class); + DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_BIZ_CONTENT, ContractBizContent.class); + return DATA_CONTRACT_MAP; + } + + public static boolean isPrimitiveType(int dataContractCode){ + return Arrays.asList(PRIMITIVE_DATA_CODES).contains(dataContractCode); + } + + private static Object regenObj(DataContract dataContract, Object object){ + if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT8.class)){ + + return (CONTRACT_INT8) () -> Byte.parseByte(object.toString()); + }else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT16.class)){ + return (CONTRACT_INT16) () -> Short.parseShort(object.toString()); + }else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT32.class)){ + return (CONTRACT_INT32) () -> Integer.parseInt(object.toString()); + }else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT64.class)){ + return (CONTRACT_INT64) () -> Long.parseLong(object.toString()); + }else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_TEXT.class)){ + return (CONTRACT_TEXT) () -> object.toString(); + }else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_BINARY.class)){ + return (CONTRACT_BINARY) () -> (Bytes) object; + }else if(getDataIntf().get(dataContract.code()).equals(ContractBizContent.class)){ + ContractBizContent contractBizContent = (ContractBizContent)object; + return contractBizContent; + }else { + throw new IllegalDataException("cann't get new Object by dataContract and object."); + } + } + + /** + * get contractType(contain @DataContract) by primitive class type; + * some class type can be supported default (byte/char/int/long/String/Bytes, and so on). + * in other words, need not contain the @DataContract in its class for contract param's serialization or deserialization. + * @param classType + * @return + */ + private static Class getContractTypeByPrimitiveType(Class classType) { + if(classType.equals(byte.class) || classType.equals(Byte.class)){ + return CONTRACT_INT8.class; + }else if(classType.equals(char.class) || classType.equals(Character.class)){ + return CONTRACT_INT16.class; + }else if(classType.equals(int.class) || classType.equals(Integer.class)){ + return CONTRACT_INT32.class; + }else if(classType.equals(long.class) || classType.equals(Long.class)){ + return CONTRACT_INT64.class; + }else if(classType.equals(String.class)){ + return CONTRACT_TEXT.class; + }else if(classType.equals(Bytes.class)){ + return CONTRACT_BINARY.class; + }else { + throw new IllegalDataException(String.format("no support the classType=%s, please check @DataContract.",classType.toString())); + } + } + + public static DataContract parseDataContract(Class classType){ + DataContract dataContract = classType.getAnnotation(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.getAnnotation(DataContract.class); + } + 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; + } +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_BIG_INT.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_BIG_INT.java new file mode 100644 index 00000000..fad0e208 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_BIG_INT.java @@ -0,0 +1,20 @@ +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 java.math.BigDecimal; + +/** + * contract args for BIG_INT; + * @author zhaogw + * date 2019-05-17 15:32 + */ +@DataContract(code = DataCodes.CONTRACT_BIG_INT) +public interface CONTRACT_BIG_INT { + + @DataField(order=2, primitiveType= PrimitiveType.BIG_INT) + BigDecimal getValue(); +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_BINARY.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_BINARY.java new file mode 100644 index 00000000..7661b852 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_BINARY.java @@ -0,0 +1,19 @@ +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.utils.Bytes; + +/** + * contract args for Binary; + * @author zhaogw + * date 2019-05-17 15:32 + */ +@DataContract(code = DataCodes.CONTRACT_BINARY) +public interface CONTRACT_BINARY { + + @DataField(order=2, primitiveType= PrimitiveType.BYTES) + Bytes getValue(); +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_INT16.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_INT16.java new file mode 100644 index 00000000..b6666cc0 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_INT16.java @@ -0,0 +1,18 @@ +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; + +/** + * contract args for int16; + * @author zhaogw + * date 2019-05-17 15:32 + */ +@DataContract(code = DataCodes.CONTRACT_INT16) +public interface CONTRACT_INT16 { + + @DataField(order=2, primitiveType= PrimitiveType.INT16) + short getValue(); +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_INT32.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_INT32.java new file mode 100644 index 00000000..02fdb07e --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_INT32.java @@ -0,0 +1,18 @@ +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; + +/** + * contract args for int32; + * @author zhaogw + * date 2019-05-17 15:32 + */ +@DataContract(code = DataCodes.CONTRACT_INT32) +public interface CONTRACT_INT32 { + + @DataField(order=2, primitiveType= PrimitiveType.INT32) + int getValue(); +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_INT64.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_INT64.java new file mode 100644 index 00000000..ad092edd --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_INT64.java @@ -0,0 +1,18 @@ +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; + +/** + * contract args for int64; + * @author zhaogw + * date 2019-05-17 15:32 + */ +@DataContract(code = DataCodes.CONTRACT_INT64) +public interface CONTRACT_INT64 { + + @DataField(order=2, primitiveType= PrimitiveType.INT64) + long getValue(); +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_INT8.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_INT8.java new file mode 100644 index 00000000..f77d3de2 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_INT8.java @@ -0,0 +1,18 @@ +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; + +/** + * contract args for int8; + * @author zhaogw + * date 2019-05-17 15:32 + */ +@DataContract(code = DataCodes.CONTRACT_INT8) +public interface CONTRACT_INT8 { + + @DataField(order=2, primitiveType= PrimitiveType.INT8) + Byte getValue(); +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_TEXT.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_TEXT.java new file mode 100644 index 00000000..8f5e4a4c --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CONTRACT_TEXT.java @@ -0,0 +1,18 @@ +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; + +/** + * contract args for String; + * @author zhaogw + * date 2019-05-17 15:32 + */ +@DataContract(code = DataCodes.CONTRACT_TEXT) +public interface CONTRACT_TEXT { + + @DataField(order=2, primitiveType= PrimitiveType.TEXT) + String getValue(); +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractBizContent.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractBizContent.java new file mode 100644 index 00000000..56e6bc24 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractBizContent.java @@ -0,0 +1,20 @@ +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; + +/** + * build complex param Object, provide more String attributes; + */ +@DataContract(code = DataCodes.CONTRACT_BIZ_CONTENT) +public interface ContractBizContent { + /** + * param lists; + * @return + */ + @DataField(order = 1, list = true, primitiveType = PrimitiveType.TEXT, genericContract = true) + String[] getAttrs(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/BlockchainOperationFactory.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/BlockchainOperationFactory.java index bd1bfd5b..c8769428 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/BlockchainOperationFactory.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/BlockchainOperationFactory.java @@ -31,7 +31,6 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe private static final ContractEventSendOperationBuilderImpl CONTRACT_EVENT_SEND_OP_BUILDER = new ContractEventSendOperationBuilderImpl(); - private LedgerInitOperationBuilder ledgerInitOpBuilder = new LedgerInitOperationBuilderFilter(); private UserRegisterOperationBuilder userRegOpBuilder = new UserRegisterOperationBuilderFilter(); @@ -42,6 +41,8 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe private ContractEventSendOperationBuilder contractEventSendOpBuilder = new ContractEventSendOperationBuilderFilter(); + private ContractInvocationProxyBuilder contractInvoProxyBuilder = new ContractInvocationProxyBuilder(); + private List operationList = new ArrayList<>(); @Override @@ -74,15 +75,18 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe return contractCodeDeployOpBuilder; } - @Override public ContractEventSendOperationBuilder contractEvents() { return contractEventSendOpBuilder; } - + @Override public T contract(String address, Class contractIntf) { - // TODO Auto-generated method stub - return null; + return contractInvoProxyBuilder.create(address, contractIntf, contractEventSendOpBuilder); + } + + @Override + public T contract(Bytes address, Class contractIntf) { + return contractInvoProxyBuilder.create(address, contractIntf, contractEventSendOpBuilder); } public Collection getOperations() { @@ -153,6 +157,7 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe } return this; } + @Override public DataAccountKVSetOperationBuilder set(String key, String value, long expVersion) { innerBuilder.set(key, value, expVersion); @@ -162,6 +167,7 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe } return this; } + @Override public DataAccountKVSetOperationBuilder set(String key, long value, long expVersion) { innerBuilder.set(key, value, expVersion); @@ -171,6 +177,7 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe } return this; } + @Override public DataAccountKVSetOperationBuilder set(String key, Bytes value, long expVersion) { innerBuilder.set(key, value, expVersion); diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxy.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxy.java index 00495bdb..f5f83708 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxy.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxy.java @@ -1,7 +1,8 @@ package com.jd.blockchain.transaction; +import com.jd.blockchain.contract.ContractSerializeUtils; import com.jd.blockchain.utils.Bytes; -import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; +import com.jd.blockchain.utils.IllegalDataException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; @@ -19,17 +20,15 @@ public class ContractInvocationProxy implements InvocationHandler { public ContractInvocationProxy(Bytes contractAddress, ContractType contractType, ContractEventSendOperationBuilder sendOpBuilder) { this.contractAddress = contractAddress; + if(contractType == null){ + throw new IllegalDataException("contractType == null, no invoke really."); + } this.contractType = contractType; this.sendOpBuilder = sendOpBuilder; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - - if(contractType == null){ - return "contractType == null, no invoke really."; - } - String event = contractType.getEvent(method); if (event == null) { // 适配 Object 对象的方法; @@ -39,15 +38,17 @@ public class ContractInvocationProxy implements InvocationHandler { // hashCode 方法; } // 合约方法; - byte[] argBytes = serializeArgs(args); + byte[] argBytes = serializeArgs(args,method); sendOpBuilder.send(contractAddress, event, argBytes); // TODO: 暂时未考虑有返回值的情况; return null; } - private byte[] serializeArgs(Object[] args) { - // TODO 根据方法参数的定义序列化参数; - return BinarySerializeUtils.serialize(args); + private byte[] serializeArgs(Object[] args, Method method) { + if(args == null || args.length==0){ + return null; + } + return ContractSerializeUtils.serializeMethodParam(args,contractType.getDataContractMap().get(method)); } } diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxyBuilder.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxyBuilder.java index 2bc6c9ea..06c6b386 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxyBuilder.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxyBuilder.java @@ -1,6 +1,58 @@ package com.jd.blockchain.transaction; -class ContractInvocationProxyBuilder { +import com.jd.blockchain.contract.Contract; +import com.jd.blockchain.contract.ContractEvent; +import com.jd.blockchain.utils.BaseConstant; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.IllegalDataException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +public class ContractInvocationProxyBuilder { + + private Map, ContractType> contractTypes = new ConcurrentHashMap<>(); + + public T create(String address, Class contractIntf, ContractEventSendOperationBuilder contractEventBuilder) { + return create(Bytes.fromBase58(address), contractIntf, contractEventBuilder); + } + + @SuppressWarnings("unchecked") + public T create(Bytes address, Class contractIntf, ContractEventSendOperationBuilder contractEventBuilder) { + ContractType contractType = resolveContractType(contractIntf); + + ContractInvocationProxy proxyHandler = new ContractInvocationProxy(address, contractType, + contractEventBuilder); + T proxy = (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), + new Class[] { contractIntf }, proxyHandler); + + return (T) proxy; + } + + private ContractType resolveContractType(Class contractIntf) { + ContractType contractType = contractTypes.get(contractIntf); + if (contractType != null) { + return contractType; + } + // TODO 检查返回值类型; + + ContractType contractType1 = ContractType.resolve(contractIntf); + contractTypes.put(contractIntf,contractType1); + return contractType1; + } + + + /** + * is contractType really? identified by @Contract; + * @param contractIntf + * @return + */ + private boolean isContractType(Class contractIntf) { + Annotation annotation = contractIntf.getDeclaredAnnotation(Contract.class); + return annotation != null ? true : false; + } } diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractType.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractType.java index 27d75e25..8110fc33 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractType.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractType.java @@ -1,26 +1,36 @@ 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.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.util.Set; -import java.util.SortedMap; +import java.util.*; + +public class ContractType { -class ContractType { - private String name; - - private SortedMap events; - - private SortedMap handleMethods; + private Map events = new HashMap<>(); + private Map handleMethods = new HashMap<>(); + private Map> dataContractMap = new HashMap<>(); /** * 返回声明的所有事件; * * @return */ - Set getEvents() { + public Set getEvents() { return events.keySet(); } + public Map> getDataContractMap() { + return dataContractMap; + } + /** * 返回指定方法声明的事件;
* @@ -29,7 +39,7 @@ class ContractType { * @param method * @return */ - String getEvent(Method method) { + public String getEvent(Method method) { return handleMethods.get(method); } @@ -41,16 +51,59 @@ class ContractType { * @param event * @return */ - Method getHandleMethod(String event) { + public Method getHandleMethod(String event) { return events.get(event); } - + private ContractType() { } - - -// public static ContractType resolve(Class contractIntf) { -// -// } + public static ContractType resolve(Class contractIntf){ + 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; + Method[] classMethods = contractIntf.getDeclaredMethods(); + 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(); + 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.handleMethods.put(method,eventName_); + } + } + return contractType; + } + + @Override + public String toString() { + return "ContractType{" + + "name='" + name + '\'' + + ", events=" + events + + ", handleMethods=" + handleMethods + + '}'; + } } diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/EventOperator.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/EventOperator.java index 687c204d..fd001fb6 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/EventOperator.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/EventOperator.java @@ -1,14 +1,25 @@ package com.jd.blockchain.transaction; +import com.jd.blockchain.utils.Bytes; + public interface EventOperator { + // /** + // * 合约事件; + // * + // * @return + // */ + // @Deprecated + // ContractEventSendOperationBuilder contractEvents(); + /** - * 部署合约; + * 创建调用合约的代理实例; * + * @param address + * @param contractIntf * @return */ - @Deprecated - ContractEventSendOperationBuilder contractEvents(); + T contract(String address, Class contractIntf); /** * 创建调用合约的代理实例; @@ -17,6 +28,6 @@ public interface EventOperator { * @param contractIntf * @return */ - T contract(String address, Class contractIntf); + T contract(Bytes address, Class contractIntf); } \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxBuilder.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxBuilder.java index 19d0020a..12fe552f 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxBuilder.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxBuilder.java @@ -19,7 +19,7 @@ public class TxBuilder implements TransactionBuilder { private BlockchainOperationFactory opFactory = new BlockchainOperationFactory(); private static final String DEFAULT_HASH_ALGORITHM = "SHA256"; - + private HashDigest ledgerHash; public TxBuilder(HashDigest ledgerHash) { @@ -36,16 +36,16 @@ public class TxBuilder implements TransactionBuilder { TransactionContent txContent = prepareContent(); return new TxRequestBuilder(txContent); } - + @Override public TransactionContent prepareContent() { TxContentBlob txContent = new TxContentBlob(ledgerHash); txContent.addOperations(opFactory.getOperations()); - + byte[] contentBodyBytes = BinaryProtocol.encode(txContent, TransactionContentBody.class); HashDigest contentHash = Crypto.getHashFunction(DEFAULT_HASH_ALGORITHM).hash(contentBodyBytes); txContent.setHash(contentHash); - + return txContent; } @@ -53,7 +53,7 @@ public class TxBuilder implements TransactionBuilder { public LedgerInitOperationBuilder ledgers() { return opFactory.ledgers(); } - + @Override public UserRegisterOperationBuilder users() { return opFactory.users(); @@ -63,7 +63,7 @@ public class TxBuilder implements TransactionBuilder { public DataAccountRegisterOperationBuilder dataAccounts() { return opFactory.dataAccounts(); } - + @Override public DataAccountKVSetOperationBuilder dataAccount(String accountAddress) { return opFactory.dataAccount(accountAddress); @@ -79,15 +79,18 @@ public class TxBuilder implements TransactionBuilder { return opFactory.contracts(); } - @Override public ContractEventSendOperationBuilder contractEvents() { return opFactory.contractEvents(); } - + + @Override + public T contract(Bytes address, Class contractIntf) { + return opFactory.contract(address, contractIntf); + } + @Override public T contract(String address, Class contractIntf) { - // TODO Auto-generated method stub - throw new IllegalStateException("Not implemented."); + return opFactory.contract(address, contractIntf); } } diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxTemplate.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxTemplate.java index f19906db..984ecf07 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxTemplate.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxTemplate.java @@ -53,9 +53,14 @@ public class TxTemplate implements TransactionTemplate { return txBuilder.contracts(); } +// @Override +// public ContractEventSendOperationBuilder contractEvents() { +// return txBuilder.contractEvents(); +// } + @Override - public ContractEventSendOperationBuilder contractEvents() { - return txBuilder.contractEvents(); + public T contract(Bytes address, Class contractIntf) { + return txBuilder.contract(address, contractIntf); } @Override diff --git a/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/GatewayServiceFactory.java b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/GatewayServiceFactory.java index 5396a430..666677d8 100644 --- a/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/GatewayServiceFactory.java +++ b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/GatewayServiceFactory.java @@ -56,6 +56,14 @@ public class GatewayServiceFactory implements BlockchainServiceFactory, Closeabl DataContractRegistry.register(ClientIdentifications.class); DataContractRegistry.register(ClientIdentification.class); + DataContractRegistry.register(CONTRACT_INT8.class); + DataContractRegistry.register(CONTRACT_INT16.class); + DataContractRegistry.register(CONTRACT_INT32.class); + DataContractRegistry.register(CONTRACT_INT64.class); + DataContractRegistry.register(CONTRACT_TEXT.class); + DataContractRegistry.register(CONTRACT_BINARY.class); + DataContractRegistry.register(ContractBizContent.class); + ByteArrayObjectUtil.init(); } diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/ContractConfigure.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/ContractConfigure.java new file mode 100644 index 00000000..61dbb10c --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/ContractConfigure.java @@ -0,0 +1,93 @@ +package com.jd.blockchain.contract; + +import com.jd.blockchain.utils.BaseConstant; +import com.jd.blockchain.utils.ConsoleUtils; +import com.jd.blockchain.utils.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ClassPathResource; + +import java.io.*; +import java.util.*; + +import static com.jd.blockchain.utils.BaseConstant.SYS_CONTRACT_CONF; +import static com.jd.blockchain.utils.BaseConstant.SYS_CONTRACT_PROPS_NAME; + +/** + * + * @author zhaogw + * date 2019/3/15 18:22 + */ +public enum ContractConfigure { + instance(); + private static final Logger LOGGER = LoggerFactory.getLogger(ContractConfigure.class); + static Properties pp; + + ContractConfigure(){ + init(); + } + + private void init(){ + String contractConfPath = System.getProperty(SYS_CONTRACT_CONF); + System.out.println("contractConfPath="+contractConfPath); + try { + if (contractConfPath == null) { + ConsoleUtils.info("Load build-in default contractConf in ContractConfigure ..."); + ClassPathResource contractConfigResource = new ClassPathResource(SYS_CONTRACT_PROPS_NAME); + InputStream in = contractConfigResource.getInputStream(); + pp = FileUtils.readProperties(in, BaseConstant.CHARSET_UTF_8); + } else { + ConsoleUtils.info("Load configuration in ContractConfigure,contractConfPath="+contractConfPath); + File file = new File(contractConfPath); + pp = FileUtils.readProperties(file, BaseConstant.CHARSET_UTF_8); + } + } catch (Exception e) { + LOGGER.info(SYS_CONTRACT_PROPS_NAME+"文件异常!"+e.getMessage()); + } + } + + public String values(String key) { + if(pp == null){ + init(); + } + return pp.getProperty(key); + } + + public String allValues() { + if(pp == null){ + init(); + } + Set allKeys = pp.stringPropertyNames(); + List propList = new ArrayList(); + for(String _key : allKeys){ + String value = pp.getProperty(_key); + propList.add(_key+": "+value); + LOGGER.info("key={}, value={}",_key,value); + } + return propList.toString(); + } + + //写入资源文件信息 + public static void writeProperties(String fileAllName, String comments, Map map){ + Properties properties=new Properties(); + try { + File file = new File(fileAllName); + if (!file.getParentFile().exists()) { + boolean result = file.getParentFile().mkdirs(); + if (!result) { + System.out.println("文件创建失败."); + } + } + OutputStream outputStream=new FileOutputStream(file); + for(Map.Entry entry : map.entrySet()){ + properties.setProperty(entry.getKey(), entry.getValue()); + } + properties.store(outputStream, comments); + outputStream.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/samples/AssetContract2.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/samples/AssetContract2.java new file mode 100644 index 00000000..03e6e720 --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/samples/AssetContract2.java @@ -0,0 +1,53 @@ +package com.jd.blockchain.contract.samples; + +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.ContractBizContent; +import com.jd.blockchain.ledger.TransactionContentBody; +import com.jd.blockchain.utils.Bytes; + +import java.math.BigDecimal; + +/** + * 示例:一个“资产管理”智能合约; + * + * @author zhaogw + */ +@Contract +public interface AssetContract2 { + + /** + * 发行资产; + * 新发行的资产数量; + * @param assetHolderAddress + * 新发行的资产的持有账户; + */ + @ContractEvent(name = "issue-asset-0") + void issue(ContractBizContent contractBizContent, String assetHolderAddress); + + /** + * issue asset; + * @param contractBizContent + * @param assetHolderAddress + * @param cashNumber + */ + @ContractEvent(name = "issue-asset") + public void issue(ContractBizContent contractBizContent, String assetHolderAddress, 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 issue(Byte byteObj, String assetHolderAddress, Bytes cashNumber); +} \ No newline at end of file diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Contract.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Contract.java index f88e1cf9..3059aaac 100644 --- a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Contract.java +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Contract.java @@ -25,20 +25,24 @@ public class SDKDemo_Contract { public static BlockchainKeypair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate("ED25519"); + public static void main(String[] args) { + demoContract(); + } + /** * 演示合约执行的过程; */ public static void demoContract() { // 账本地址; - String ledgerAddress = "ffkjhkeqwiuhivnsh3298josijdocaijsda=="; + String ledgerAddress = "j5rpuGWVxSuUbU3gK7MDREfui797AjfdHzvAMiSaSzydu7"; // 节点地址列表; - NetworkAddress[] peerAddrs = { new NetworkAddress("192.168.10.10", 8080), - new NetworkAddress("192.168.10.11", 8080), new NetworkAddress("192.168.10.12", 8080), - new NetworkAddress("192.168.10.13", 8080) }; +// NetworkAddress[] peerAddrs = { new NetworkAddress("192.168.10.10", 8080), +// new NetworkAddress("192.168.10.11", 8080), new NetworkAddress("192.168.10.12", 8080), +// new NetworkAddress("192.168.10.13", 8080) }; // 创建服务代理; - final String GATEWAY_IP = "127.0.0.1"; - final int GATEWAY_PORT = 80; + final String GATEWAY_IP = "localhost"; + final int GATEWAY_PORT = 11000; final boolean SECURE = false; GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IP, GATEWAY_PORT, SECURE, CLIENT_CERT); @@ -52,13 +56,13 @@ public class SDKDemo_Contract { // 一个贸易账户,贸易结算后的利润将通过一个合约账户来执行利润分配; // 合约账户被设置为通用的账户,不具备对贸易结算账户的直接权限; // 只有当前交易发起人具备对贸易账户的直接权限,当交易发起人对交易进行签名之后,权限被间接传递给合约账户; - String commerceAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; + String commerceAccount = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; // 处理利润分成的通用业务逻辑的合约账户; - String profitDistributionContract = "AAdfe4346fHhefe34fwf343kaeER4678RT=="; + String profitDistributionContract = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; // 收益人账户; - String receiptorAccount1 = "MMMEy902jkjjJJDkshreGeasdfassdfajjf=="; - String receiptorAccount2 = "Kjfe8832hfa9jjjJJDkshrFjksjdlkfj93F=="; + String receiptorAccount1 = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; + String receiptorAccount2 = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; // 资产编码; String assetKey = "RMB-ASSET"; // 此次待分配利润; @@ -68,9 +72,12 @@ public class SDKDemo_Contract { Remark remark = new Remark(); String remarkJSON = JSONSerializeUtils.serializeToJSON(remark); -// AssetContract assetContract = txTemp.contract("", AssetContract.class); -// txTemp.contractInvocation(assetContract.issue(amount, assetHolderAddress)) - + AssetContract assetContract = txTemp.contract(profitDistributionContract, AssetContract.class); + assetContract.issue(1000, receiptorAccount1); + assetContract.transfer(receiptorAccount1, receiptorAccount2, 600); + +// assetContract. + // -------------------------------------- // TX 准备就绪; diff --git a/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_Contract_Test.java b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_Contract_Test.java new file mode 100644 index 00000000..76bdceed --- /dev/null +++ b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_Contract_Test.java @@ -0,0 +1,361 @@ +package test.com.jd.blockchain.sdk.test; + +import com.jd.blockchain.binaryproto.BinaryProtocol; +import com.jd.blockchain.contract.samples.AssetContract; +import com.jd.blockchain.contract.samples.AssetContract2; +import com.jd.blockchain.crypto.*; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.sdk.samples.SDKDemo_Contract; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.net.NetworkAddress; +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ClassPathResource; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * 演示合约执行的过程; + * + * @author zhaogw + * 2019-05-21 11:03 + */ +public class SDK_Contract_Test { + public static Logger log = LoggerFactory.getLogger(SDKDemo_Contract.class); + + public static BlockchainKeypair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate("ED25519"); + // 账本地址; + public static String ledgerAddress; + private PrivKey privKey; + private PubKey pubKey; + BlockchainService bcsrv; + AsymmetricKeypair signKeyPair; + HashDigest ledgerHash; + + @Before + public void init(){ + ledgerAddress = "j5qHcS8jG6XwpE5wXv9HYMeGTb5Fs2gQao3TPQ3irqFpQL"; + ledgerHash = getLedgerHash(); + pubKey = SDK_GateWay_KeyPair_Para.pubKey0; + privKey = SDK_GateWay_KeyPair_Para.privkey0; + // 使用私钥进行签名; + signKeyPair = new BlockchainKeypair(pubKey, privKey); + + // 创建服务代理; + final String GATEWAY_IP = "localhost"; + final int GATEWAY_PORT = 11000; + NetworkAddress addr = new NetworkAddress(GATEWAY_IP,GATEWAY_PORT); + GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(addr); + bcsrv = serviceFactory.getBlockchainService(); + } + + /** + * 演示合约执行的过程; + */ + @Test + public void demoContract1() { + String dataAddress = registerData4Contract(); + // 发起交易; + TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); + String contractAddress = "LdeNg8JHFCKABJt6AaRNVCZPgY4ofGPd8MgcR"; + AssetContract2 assetContract = txTemp.contract(contractAddress, AssetContract2.class); +// assetContract.issue(transactionContentBody,contractAddress); +// assetContract.issue(transactionContentBody,contractAddress,888888); +// assetContract.issue(Bytes.fromString("zhaogw, contract based interface is OK!"),contractAddress,77777); +// assetContract.issue(Bytes.fromString("zhaogw, contract based interface is OK!"),contractAddress,77777); + Byte byteObj = Byte.parseByte("127"); + assetContract.issue(byteObj,dataAddress,321123); +// assetContract.issue(contractBizContent,dataAddress); + assetContract.issue(Byte.parseByte("126"),dataAddress,Bytes.fromString("100.234")); + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + prepTx.sign(signKeyPair); + // 提交交易; + TransactionResponse transactionResponse = prepTx.commit(); + + //check; + KVDataEntry[] dataEntries = bcsrv.getDataEntries(ledgerHash,dataAddress,"total"); + assertEquals("100",dataEntries[0].getValue().toString()); + } + + /** + * 演示合约执行的过程; + */ + @Test + public void demoContract2() throws IOException { + String contractAddress = deploy(); + String dataAddress = registerData4Contract(); + System.out.println("dataAddress="+dataAddress); + // 发起交易; + TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); + + AssetContract2 assetContract = txTemp.contract(contractAddress, AssetContract2.class); + ContractBizContent contractBizContent = () -> new String[]{"param1","param2"}; + assetContract.issue(contractBizContent,dataAddress,123456); + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + prepTx.sign(signKeyPair); + // 提交交易; + TransactionResponse transactionResponse = prepTx.commit(); + + //check; + assertTrue(transactionResponse.isSuccess()); + KVDataEntry[] dataEntries = bcsrv.getDataEntries(ledgerHash,dataAddress,contractBizContent.getAttrs()[0],contractBizContent.getAttrs()[1]); + assertEquals("value1",dataEntries[0].getValue().toString()); + assertEquals(888,dataEntries[1].getValue()); + } + + @Test + public void registerData(){ + // 在本地定义 TX 模板 + TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); + BlockchainKeypair dataAccount = BlockchainKeyGenerator.getInstance().generate(); + txTemp.dataAccounts().register(dataAccount.getIdentity()); + + String key1 = "jd_key1"; + String val1 = "www.jd1.com"; + String key2 = "jd_key2"; + String val2 = "www.jd2.com"; + // 定义交易,传输最简单的数字、字符串、提取合约中的地址; + txTemp.dataAccount(dataAccount.getAddress()).set(key1, val1, -1); + txTemp.dataAccount(dataAccount.getAddress()).set(key2, val2, -1); + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + prepTx.sign(signKeyPair); + // 提交交易; + TransactionResponse transactionResponse = prepTx.commit(); + + assertTrue(transactionResponse.isSuccess()); + + //repeat; + String[] keys = {key1,key2}; + String[] values = {"www.jd1.com.v1","www.jd2.com.v1"}; + this.setDataInDataAddress(dataAccount.getAddress(),keys,values,0); + String[] values2 = {"www.jd1.com.v2","www.jd2.com.v2"}; + this.setDataInDataAddress(dataAccount.getAddress(),keys,values2,1); + } + + private String registerData4Contract(){ + // 在本地定义 TX 模板 + TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); + BlockchainKeypair dataAccount = BlockchainKeyGenerator.getInstance().generate(); + txTemp.dataAccounts().register(dataAccount.getIdentity()); + txTemp.dataAccount(dataAccount.getAddress()).set("total", 200, -1); + txTemp.dataAccount(dataAccount.getAddress()).set("param1", "v", -1); + txTemp.dataAccount(dataAccount.getAddress()).set("param2", 123, -1); + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + prepTx.sign(signKeyPair); + // 提交交易; + TransactionResponse transactionResponse = prepTx.commit(); + assertTrue(transactionResponse.isSuccess()); + + return dataAccount.getAddress().toBase58(); + } + + private void setDataInDataAddress(Bytes dataAddress, String[] keys, String[] values, long version){ + // 在本地定义 TX 模板 + TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); + BlockchainKeypair dataAccount = BlockchainKeyGenerator.getInstance().generate(); + + for(int i=0; i + * + * 注:私钥由调用方在本地保管和使用; + * + * @return + */ + private static AsymmetricKeypair getSponsorKey() { + SignatureFunction signatureFunction = Crypto.getSignatureFunction("ED25519"); + return signatureFunction.generateKeypair(); + } + + /** + * 商品信息; + * + * @author huanghaiquan + * + */ + public static class Remark { + + private String code; + + private String name; + + private String venderAddress; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVenderAddress() { + return venderAddress; + } + + public void setVenderAddress(String venderAddress) { + this.venderAddress = venderAddress; + } + + } + + @Test + public void testStringArr(){ + String[] strArr = {"1","2","you are welcome!"}; + ContractBizContent contractBizContent = new ContractBizContent() { + @Override + public String[] getAttrs() { + return strArr; + } + }; + byte[] bizBytes = BinaryProtocol.encode(contractBizContent,ContractBizContent.class); + ContractBizContent actualObj = BinaryProtocol.decodeAs(bizBytes,ContractBizContent.class); + assertEquals(contractBizContent,actualObj); + } +} diff --git a/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_KeyPair_Para.java b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_KeyPair_Para.java index 916a3460..450e5ff4 100644 --- a/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_KeyPair_Para.java +++ b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_KeyPair_Para.java @@ -10,16 +10,16 @@ import com.jd.blockchain.tools.keygen.KeyGenCommand; public class SDK_GateWay_KeyPair_Para { public static final String PASSWORD = "abc"; - public static final String[] PUB_KEYS = { "endPsK36imXrY66pru6ttZ8dZ3TynWekmdqoM1K7ZRRoRBBiYVzM", - "endPsK36jQE1uYpdVRSnwQXVYhgAMWTaMJiAqii7URiULoBDLUUN", - "endPsK36fc7FSecKAJCJdFhTejbPHMLaGcihJVQCv95czCq4tW5n", - "endPsK36m1grx8mkTMgh8XQHiiaNzajdC5hkuqP6pAuLmMbYkzd4" }; + public static final String[] PUB_KEYS = { "3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9", + "3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX", + "3snPdw7i7PZi6TStiyc6mzjprnNhgs2atSGNS8wPYzhbKaUWGFJt7x", + "3snPdw7i7PifPuRX7fu3jBjsb3rJRfDe9GtbDfvFJaJ4V4hHXQfhwk" }; public static final String[] PRIV_KEYS = { - "177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X", - "177gjwQwTdXthkutDKVgKwiq6wWfLWYuxhji1U2N1C5MzqLRWCLZXo3i2g4vpfcEAQUPG8H", - "177gjvLHUjxvAWsqVcGgV8eHgVNBvJZYDfpP9FLjTouR1gEJNiamYu1qjTNDh18XWyLg8or", - "177gk2VtYeGbK5TS2xWhbSZA4BsT9Xj5Fb8hqCzxzgbojVVcqaDSFFrFPsLbZBx7rszyCNy" }; + "177gjzHTznYdPgWqZrH43W3yp37onm74wYXT4v9FukpCHBrhRysBBZh7Pzdo5AMRyQGJD7x", + "177gju9p5zrNdHJVEQnEEKF4ZjDDYmAXyfG84V5RPGVc5xFfmtwnHA7j51nyNLUFffzz5UT", + "177gjtwLgmSx5v1hFb46ijh7L9kdbKUpJYqdKVf9afiEmAuLgo8Rck9yu5UuUcHknWJuWaF", + "177gk1pudweTq5zgJTh8y3ENCTwtSFsKyX7YnpuKPo7rKgCkCBXVXh5z2syaTCPEMbuWRns" }; public static PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[0], PASSWORD); public static PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[1], PASSWORD); diff --git a/source/sdk/sdk-samples/src/test/resources/contract.jar b/source/sdk/sdk-samples/src/test/resources/contract.jar new file mode 100644 index 00000000..3ff8a821 Binary files /dev/null and b/source/sdk/sdk-samples/src/test/resources/contract.jar differ diff --git a/source/sdk/sdk-samples/src/test/resources/sys-contract.properties b/source/sdk/sdk-samples/src/test/resources/sys-contract.properties new file mode 100644 index 00000000..a1457b06 --- /dev/null +++ b/source/sdk/sdk-samples/src/test/resources/sys-contract.properties @@ -0,0 +1,13 @@ +#ledger info; +ownerPubPath=F:\\jdsk\\jdsk-files\\github\\jdchain-starter\\conf\\jd-com.pub +ownerPrvPath=F:\\jdsk\\jdsk-files\\github\\jdchain-starter\\conf\\jd-com.priv +ownerPassword=F:\\jdsk\\jdsk-files\\github\\jdchain-starter\\conf\\jd-com.pwd +ledgerHash=j5rpuGWVxSuUbU3gK7MDREfui797AjfdHzvAMiSaSzydu7 +host=localhost +port=11000 +#execute contract; +event = issue-asset +chainCodePath=F:\\jdsk\\jdsk-files\\github\\jdchain-starter\\contract-compile\\target\\contract.jar +contractArgs=1000##fromAddr##LdeNvLeq6MB6y8CoyawYxgCCYCJkrp5xoVGUw +#contractArgs=fromAddr##toAddr##30##LdeNvLeq6MB6y8CoyawYxgCCYCJkrp5xoVGUw + diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/IntegrationTest.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/IntegrationTest.java index fb64faec..c3c80a9a 100644 --- a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/IntegrationTest.java +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/IntegrationTest.java @@ -1,38 +1,13 @@ package test.com.jd.blockchain.intgr; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; -import java.util.Random; -import java.util.concurrent.CountDownLatch; - -import org.springframework.core.io.ClassPathResource; - import com.jd.blockchain.consensus.ConsensusProvider; import com.jd.blockchain.consensus.ConsensusProviders; import com.jd.blockchain.consensus.ConsensusSettings; -import com.jd.blockchain.crypto.AddressEncoding; import com.jd.blockchain.crypto.AsymmetricKeypair; -import com.jd.blockchain.crypto.Crypto; import com.jd.blockchain.crypto.HashDigest; import com.jd.blockchain.crypto.PrivKey; -import com.jd.blockchain.crypto.PubKey; import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; -import com.jd.blockchain.ledger.AccountHeader; -import com.jd.blockchain.ledger.BlockchainKeyGenerator; -import com.jd.blockchain.ledger.BlockchainKeypair; -import com.jd.blockchain.ledger.DataAccountKVSetOperation; -import com.jd.blockchain.ledger.KVDataEntry; -import com.jd.blockchain.ledger.LedgerBlock; -import com.jd.blockchain.ledger.LedgerInfo; -import com.jd.blockchain.ledger.ParticipantNode; -import com.jd.blockchain.ledger.PreparedTransaction; -import com.jd.blockchain.ledger.TransactionResponse; -import com.jd.blockchain.ledger.TransactionTemplate; -import com.jd.blockchain.ledger.UserInfo; -import com.jd.blockchain.ledger.core.DataAccountSet; +import com.jd.blockchain.ledger.*; import com.jd.blockchain.ledger.core.LedgerManage; import com.jd.blockchain.ledger.core.LedgerRepository; import com.jd.blockchain.ledger.core.impl.LedgerManager; @@ -48,10 +23,18 @@ import com.jd.blockchain.utils.Bytes; import com.jd.blockchain.utils.codec.HexUtils; import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; import com.jd.blockchain.utils.net.NetworkAddress; - +import org.springframework.core.io.ClassPathResource; import test.com.jd.blockchain.intgr.IntegratedContext.Node; import test.com.jd.blockchain.intgr.perf.LedgerInitializeWebTest; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.Random; +import java.util.concurrent.CountDownLatch; + public class IntegrationTest { // 合约测试使用的初始化数据; BlockchainKeypair contractDataKey = BlockchainKeyGenerator.getInstance().generate(); @@ -547,87 +530,87 @@ public class IntegrationTest { } } - private LedgerBlock testSDK_Contract(AsymmetricKeypair adminKey, HashDigest ledgerHash, - BlockchainService blockchainService, IntegratedContext context) { - // valid the basic data in contract; - prepareContractData(adminKey, ledgerHash, blockchainService, context); - - BlockchainKeypair userKey = BlockchainKeyGenerator.getInstance().generate(); - - // 定义交易; - TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); - byte[] contractCode = getChainCodeBytes(); - - txTpl.users().register(userKey.getIdentity()); - - txTpl.contracts().deploy(contractDeployKey.getIdentity(), contractCode); - - // 签名; - PreparedTransaction ptx = txTpl.prepare(); - ptx.sign(adminKey); - - // 提交并等待共识返回; - TransactionResponse txResp = ptx.commit(); - - // 验证结果; - txResp.getContentHash(); - - Node node0 = context.getNode(0); - LedgerRepository ledgerOfNode0 = node0.getLedgerManager().getLedger(ledgerHash); - LedgerBlock block = ledgerOfNode0.getBlock(txResp.getBlockHeight()); - byte[] contractCodeInDb = ledgerOfNode0.getContractAccountSet(block).getContract(contractDeployKey.getAddress()) - .getChainCode(); - txContentHash = ptx.getHash(); - - // execute the contract; - testContractExe(adminKey, ledgerHash, userKey, blockchainService, context); - - return block; - } - - private void testContractExe(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair userKey, - BlockchainService blockchainService, IntegratedContext context) { - LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); - LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); - - // 定义交易; - TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); - - txTpl.contractEvents().send(contractDeployKey.getAddress(), eventName, - ("888##abc##" + contractDataKey.getAddress() + "##" + previousBlock.getHash().toBase58() + "##" - + userKey.getAddress() + "##" + contractDeployKey.getAddress() + "##" + txContentHash.toBase58() - + "##SOME-VALUE").getBytes()); - - // 签名; - PreparedTransaction ptx = txTpl.prepare(); - ptx.sign(adminKey); - - // 提交并等待共识返回; - TransactionResponse txResp = ptx.commit(); - - // 验证结果; - txResp.getContentHash(); - - LedgerInfo latestLedgerInfo = blockchainService.getLedger(ledgerHash); - - Node node0 = context.getNode(0); - LedgerRepository ledgerOfNode0 = node0.getLedgerManager().getLedger(ledgerHash); - LedgerBlock backgroundLedgerBlock = ledgerOfNode0.retrieveLatestBlock(); - - // 验证合约中的赋值,外部可以获得; - DataAccountSet dataAccountSet = ledgerOfNode0.getDataAccountSet(backgroundLedgerBlock); - AsymmetricKeypair key = Crypto.getSignatureFunction("ED25519").generateKeypair(); - PubKey pubKey = key.getPubKey(); - Bytes dataAddress = AddressEncoding.generateAddress(pubKey); - - // 验证userAccount,从合约内部赋值,然后外部验证;由于目前不允许输入重复的key,所以在内部合约中构建的key,不便于在外展示,屏蔽之; - // UserAccountSet userAccountSet = - // ledgerOfNode0.getUserAccountSet(backgroundLedgerBlock); - // PubKey userPubKey = new PubKey(CryptoAlgorithm.ED25519, - // userPubKeyVal.getBytes()); - // String userAddress = AddressEncoding.generateAddress(userPubKey); - // assertEquals(userAddress, userAccountSet.getUser(userAddress).getAddress()); - } +// private LedgerBlock testSDK_Contract(AsymmetricKeypair adminKey, HashDigest ledgerHash, +// BlockchainService blockchainService, IntegratedContext context) { +// // valid the basic data in contract; +// prepareContractData(adminKey, ledgerHash, blockchainService, context); +// +// BlockchainKeypair userKey = BlockchainKeyGenerator.getInstance().generate(); +// +// // 定义交易; +// TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); +// byte[] contractCode = getChainCodeBytes(); +// +// txTpl.users().register(userKey.getIdentity()); +// +// txTpl.contracts().deploy(contractDeployKey.getIdentity(), contractCode); +// +// // 签名; +// PreparedTransaction ptx = txTpl.prepare(); +// ptx.sign(adminKey); +// +// // 提交并等待共识返回; +// TransactionResponse txResp = ptx.commit(); +// +// // 验证结果; +// txResp.getContentHash(); +// +// Node node0 = context.getNode(0); +// LedgerRepository ledgerOfNode0 = node0.getLedgerManager().getLedger(ledgerHash); +// LedgerBlock block = ledgerOfNode0.getBlock(txResp.getBlockHeight()); +// byte[] contractCodeInDb = ledgerOfNode0.getContractAccountSet(block).getContract(contractDeployKey.getAddress()) +// .getChainCode(); +// txContentHash = ptx.getHash(); +// +// // execute the contract; +// testContractExe(adminKey, ledgerHash, userKey, blockchainService, context); +// +// return block; +// } + +// private void testContractExe(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair userKey, +// BlockchainService blockchainService, IntegratedContext context) { +// LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); +// LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); +// +// // 定义交易; +// TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); +// +// txTpl.contractEvents().send(contractDeployKey.getAddress(), eventName, +// ("888##abc##" + contractDataKey.getAddress() + "##" + previousBlock.getHash().toBase58() + "##" +// + userKey.getAddress() + "##" + contractDeployKey.getAddress() + "##" + txContentHash.toBase58() +// + "##SOME-VALUE").getBytes()); +// +// // 签名; +// PreparedTransaction ptx = txTpl.prepare(); +// ptx.sign(adminKey); +// +// // 提交并等待共识返回; +// TransactionResponse txResp = ptx.commit(); +// +// // 验证结果; +// txResp.getContentHash(); +// +// LedgerInfo latestLedgerInfo = blockchainService.getLedger(ledgerHash); +// +// Node node0 = context.getNode(0); +// LedgerRepository ledgerOfNode0 = node0.getLedgerManager().getLedger(ledgerHash); +// LedgerBlock backgroundLedgerBlock = ledgerOfNode0.retrieveLatestBlock(); +// +// // 验证合约中的赋值,外部可以获得; +// DataAccountSet dataAccountSet = ledgerOfNode0.getDataAccountSet(backgroundLedgerBlock); +// AsymmetricKeypair key = Crypto.getSignatureFunction("ED25519").generateKeypair(); +// PubKey pubKey = key.getPubKey(); +// Bytes dataAddress = AddressEncoding.generateAddress(pubKey); +// +// // 验证userAccount,从合约内部赋值,然后外部验证;由于目前不允许输入重复的key,所以在内部合约中构建的key,不便于在外展示,屏蔽之; +// // UserAccountSet userAccountSet = +// // ledgerOfNode0.getUserAccountSet(backgroundLedgerBlock); +// // PubKey userPubKey = new PubKey(CryptoAlgorithm.ED25519, +// // userPubKeyVal.getBytes()); +// // String userAddress = AddressEncoding.generateAddress(userPubKey); +// // assertEquals(userAddress, userAccountSet.getUser(userAddress).getAddress()); +// } private void prepareContractData(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainService blockchainService, IntegratedContext context) { diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java index 17e6ad7f..3065da89 100644 --- a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java @@ -28,11 +28,14 @@ import com.jd.blockchain.utils.net.NetworkAddress; import org.apache.commons.io.FileUtils; import org.junit.Assert; import org.springframework.core.io.ClassPathResource; +import test.com.jd.blockchain.intgr.contract.AssetContract; +import test.com.jd.blockchain.intgr.contract.AssetContract2; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.math.BigDecimal; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; @@ -52,6 +55,7 @@ import static org.junit.Assert.*; */ public class IntegrationBase { + public static String KEY_TOTAL = "total"; static { DataContractRegistry.register(LedgerInitOperation.class); @@ -105,6 +109,9 @@ public class IntegrationBase { // 定义交易; TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); txTpl.dataAccounts().register(dataAccount.getIdentity()); + txTpl.dataAccount(dataAccount.getAddress()).set("total", 200, -1); +// txTpl.dataAccount(dataAccount.getAddress()).set("param1", "v", -1); +// txTpl.dataAccount(dataAccount.getAddress()).set("param2", 200, -1); // 签名; PreparedTransaction ptx = txTpl.prepare(); @@ -430,18 +437,16 @@ 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) { + KeyPairResponse keyPairResponse = testSDK_RegisterDataAccount(adminKey,ledgerHash,blockchainService); + System.out.println("adminKey="+ AddressEncoding.generateAddress(adminKey.getPubKey())); BlockchainKeypair userKey = BlockchainKeyGenerator.getInstance().generate(); System.out.println("userKey="+userKey.getAddress()); @@ -460,22 +465,22 @@ public class IntegrationBase { TransactionResponse txResp = ptx.commit(); assertTrue(txResp.isSuccess()); - // 验证结果; - txResp.getContentHash(); + // 验证结果hash;请求的hash=相应的内容hash; + assertEquals(ptx.getHash(),txResp.getContentHash()); LedgerBlock block = ledgerRepository.getBlock(txResp.getBlockHeight()); byte[] contractCodeInDb = ledgerRepository.getContractAccountSet(block).getContract(contractDeployKey.getAddress()) .getChainCode(); assertArrayEquals(contractCode, contractCodeInDb); - txContentHash = ptx.getHash(); // execute the contract; - testContractExe(adminKey, ledgerHash, userKey, blockchainService, ledgerRepository); + testContractExe(adminKey, ledgerHash, keyPairResponse.keyPair, blockchainService, ledgerRepository); + testContractExe1(adminKey, ledgerHash, keyPairResponse.keyPair, blockchainService, ledgerRepository); return block; } - private void testContractExe(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair userKey, + private static void testContractExe(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair dataKey, BlockchainService blockchainService,LedgerRepository ledgerRepository) { LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); @@ -483,8 +488,38 @@ public class IntegrationBase { // 定义交易; TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); - txTpl.contractEvents().send(contractDeployKey.getAddress(), eventName, - ("888##123##" + contractDataKey.getAddress()).getBytes()); + Byte byteObj = Byte.parseByte("123"); +// txTpl.contract(contractDeployKey.getAddress(),AssetContract2.class).issue(byteObj, +// contractDeployKey.getAddress().toBase58(),321123); + txTpl.contract(contractDeployKey.getAddress(),AssetContract2.class).issue(byteObj, + dataKey.getAddress().toBase58(),Bytes.fromString("123321")); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + + // 验证结果; + Assert.assertTrue(txResp.isSuccess()); + assertEquals(ptx.getHash(),txResp.getContentHash()); + LedgerBlock block = ledgerRepository.getBlock(txResp.getBlockHeight()); + KVDataEntry[] kvDataEntries = ledgerRepository.getDataAccountSet(block).getDataAccount(dataKey.getAddress()).getDataEntries(0,1); + assertEquals("100",kvDataEntries[0].getValue().toString()); + } + + private static void testContractExe1(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair dataKey, + BlockchainService blockchainService,LedgerRepository ledgerRepository) { + LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); + LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); + + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + + AssetContract2 assetContract = txTpl.contract(contractDeployKey.getAddress(), AssetContract2.class); + ContractBizContent contractBizContent = () -> new String[]{"param1","param2"}; + assetContract.issue(contractBizContent,dataKey.getAddress().toBase58(),123456); // 签名; PreparedTransaction ptx = txTpl.prepare(); @@ -494,8 +529,12 @@ public class IntegrationBase { TransactionResponse txResp = ptx.commit(); // 验证结果; - txResp.getContentHash(); Assert.assertTrue(txResp.isSuccess()); + assertEquals(ptx.getHash(),txResp.getContentHash()); + LedgerBlock block = ledgerRepository.getBlock(txResp.getBlockHeight()); + KVDataEntry[] kvDataEntries = ledgerRepository.getDataAccountSet(block).getDataAccount(dataKey.getAddress()).getDataEntries(1,2); + assertEquals("value1",kvDataEntries[0].getValue().toString()); + assertEquals(888L,kvDataEntries[1].getValue()); } /** @@ -503,7 +542,7 @@ public class IntegrationBase { * * @return */ - private byte[] getChainCodeBytes() { + private static byte[] getChainCodeBytes() { // 构建合约的字节数组; byte[] contractCode = null; File file = null; diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest2.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest2.java index a6f2f30a..1da5341b 100644 --- a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest2.java +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest2.java @@ -1,34 +1,12 @@ package test.com.jd.blockchain.intgr; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; -import java.util.concurrent.CountDownLatch; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.core.io.ClassPathResource; - import com.jd.blockchain.consensus.ConsensusProvider; -import com.jd.blockchain.consensus.ConsensusProviders; import com.jd.blockchain.consensus.ConsensusSettings; import com.jd.blockchain.crypto.AsymmetricKeypair; import com.jd.blockchain.crypto.HashDigest; import com.jd.blockchain.crypto.PrivKey; import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; -import com.jd.blockchain.ledger.BlockchainKeyGenerator; -import com.jd.blockchain.ledger.BlockchainKeypair; -import com.jd.blockchain.ledger.LedgerBlock; -import com.jd.blockchain.ledger.LedgerInfo; -import com.jd.blockchain.ledger.PreparedTransaction; -import com.jd.blockchain.ledger.TransactionResponse; -import com.jd.blockchain.ledger.TransactionTemplate; +import com.jd.blockchain.ledger.*; import com.jd.blockchain.ledger.core.LedgerRepository; import com.jd.blockchain.sdk.BlockchainService; import com.jd.blockchain.sdk.client.GatewayServiceFactory; @@ -39,18 +17,29 @@ import com.jd.blockchain.tools.initializer.Prompter; import com.jd.blockchain.tools.keygen.KeyGenCommand; import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; import com.jd.blockchain.utils.net.NetworkAddress; - +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; import test.com.jd.blockchain.intgr.IntegratedContext.Node; +import test.com.jd.blockchain.intgr.contract.AssetContract; import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest; import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest.NodeWebContext; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.*; + /** * 测试合约,提交后不立即进行验证,因为此时可能还没有完成正式结块; */ public class IntegrationTest2 { // 合约测试使用的初始化数据; BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); - private String contractZipName = "AssetContract3.contract"; + private String contractZipName = "contract.jar"; private String eventName = "issue-asset"; @Test @@ -315,7 +304,7 @@ public class IntegrationTest2 { // 定义交易; TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); - txTpl.contractEvents().send(contractDeployKey.getAddress(), eventName, ("888##999##abc").getBytes()); + txTpl.contract(contractDeployKey.getAddress(), AssetContract.class).issue(10,"abc"); // 签名; PreparedTransaction ptx = txTpl.prepare(); diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTestAll4Redis.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTestAll4Redis.java index 1396f0c5..d6daf44b 100644 --- a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTestAll4Redis.java +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTestAll4Redis.java @@ -1,36 +1,8 @@ package test.com.jd.blockchain.intgr; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Random; - -import org.junit.Test; -import org.springframework.core.io.ClassPathResource; - -import com.jd.blockchain.crypto.AddressEncoding; -import com.jd.blockchain.crypto.AsymmetricKeypair; -import com.jd.blockchain.crypto.Crypto; -import com.jd.blockchain.crypto.HashDigest; -import com.jd.blockchain.crypto.PrivKey; -import com.jd.blockchain.crypto.PubKey; +import com.jd.blockchain.crypto.*; import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; -import com.jd.blockchain.ledger.BlockchainKeyGenerator; -import com.jd.blockchain.ledger.BlockchainKeypair; -import com.jd.blockchain.ledger.DataAccountKVSetOperation; -import com.jd.blockchain.ledger.KVDataEntry; -import com.jd.blockchain.ledger.LedgerBlock; -import com.jd.blockchain.ledger.LedgerInfo; -import com.jd.blockchain.ledger.PreparedTransaction; -import com.jd.blockchain.ledger.TransactionResponse; -import com.jd.blockchain.ledger.TransactionState; -import com.jd.blockchain.ledger.TransactionTemplate; +import com.jd.blockchain.ledger.*; import com.jd.blockchain.ledger.core.DataAccount; import com.jd.blockchain.ledger.core.DataAccountSet; import com.jd.blockchain.ledger.core.LedgerManage; @@ -47,9 +19,19 @@ import com.jd.blockchain.utils.Bytes; import com.jd.blockchain.utils.codec.HexUtils; import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; import com.jd.blockchain.utils.net.NetworkAddress; - +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; +import test.com.jd.blockchain.intgr.contract.AssetContract; import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Random; + +import static org.junit.Assert.*; + public class IntegrationTestAll4Redis { public static final String PASSWORD = "abc"; @@ -450,10 +432,7 @@ public class IntegrationTestAll4Redis { // 定义交易; TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); - txTpl.contractEvents().send(contractDeployKey.getAddress(), eventName, - ("888##abc##" + contractDataKey.getAddress() + "##" + previousBlock.getHash().toBase58() + "##" - + userKey.getAddress() + "##" + contractDeployKey.getAddress() + "##" + txContentHash.toBase58() - + "##" + pubKeyVal).getBytes()); + txTpl.contract(contractDeployKey.getAddress(), AssetContract.class).issue(10,"abc"); // 签名; PreparedTransaction ptx = txTpl.prepare(); diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/contract/AssetContract.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/contract/AssetContract.java new file mode 100644 index 00000000..68d223fb --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/contract/AssetContract.java @@ -0,0 +1,39 @@ +package test.com.jd.blockchain.intgr.contract; + +import com.jd.blockchain.contract.Contract; +import com.jd.blockchain.contract.ContractEvent; + +/** + * 示例:一个“资产管理”智能合约; + * + * @author huanghaiquan + * + */ +@Contract +public interface AssetContract { + + /** + * 发行资产; + * + * @param amount + * 新发行的资产数量; + * @param assetHolderAddress + * 新发行的资产的持有账户; + */ + @ContractEvent(name = "issue-asset") + void issue(long amount, String assetHolderAddress); + + /** + * 转移资产 + * + * @param fromAddress + * 转出账户; + * @param toAddress + * 转入账户; + * @param amount + * 转移的资产数额; + */ + @ContractEvent(name = "transfer-asset") + void transfer(String fromAddress, String toAddress, long amount); + +} \ No newline at end of file diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/contract/AssetContract2.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/contract/AssetContract2.java new file mode 100644 index 00000000..a70142fb --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/contract/AssetContract2.java @@ -0,0 +1,44 @@ +package test.com.jd.blockchain.intgr.contract; + +import com.jd.blockchain.contract.Contract; +import com.jd.blockchain.contract.ContractEvent; +import com.jd.blockchain.ledger.ContractBizContent; +import com.jd.blockchain.utils.Bytes; + +import java.math.BigDecimal; + +/** + * 示例:一个“资产管理”智能合约; + * + * @author zhaogw + */ +@Contract +public interface AssetContract2 { + + /** + * 发行资产; + * 新发行的资产数量; + * @param assetHolderAddress + * 新发行的资产的持有账户; + */ + @ContractEvent(name = "issue-asset-0") + void issue(ContractBizContent contractBizContent, String assetHolderAddress); + + /** + * 发行资产; + * 新发行的资产数量; + * @param assetHolderAddress + * 新发行的资产的持有账户; + */ + @ContractEvent(name = "issue-asset") + void issue(ContractBizContent contractBizContent, String assetHolderAddress, long cashNumber); + + @ContractEvent(name = "issue-asset-2") + void issue(Bytes bytes, String assetHolderAddress, long cashNumber); + + @ContractEvent(name = "issue-asset-3") + void issue(Byte byteObj, String assetHolderAddress, long cashNumber); + + @ContractEvent(name = "issue-asset-4") + void issue(Byte byteObj, String assetHolderAddress, Bytes cashNumber); +} \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/contract.jar b/source/test/test-integration/src/test/resources/contract.jar new file mode 100644 index 00000000..7c172d0e Binary files /dev/null and b/source/test/test-integration/src/test/resources/contract.jar differ diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/BaseConstant.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/BaseConstant.java index 303f82ee..4548dbad 100644 --- a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/BaseConstant.java +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/BaseConstant.java @@ -22,7 +22,7 @@ public class BaseConstant { public static final String SYS_CONTRACT_PROPS_NAME = "sys-contract.properties"; public static final String CONTRACT_MAIN_CLASS_KEY = "contract"; - public static final String CONTRACT_EVENT_PREFIX="@com.jd.blockchain.contract.model.ContractEvent(name="; +// public static final String CONTRACT_EVENT_PREFIX="@com.jd.blockchain.contract.ContractEvent(name="; // 编译时引用包黑名单 public static final String PACKAGE_BLACKLIST = "BLACKLIST";