@@ -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; | |||
@@ -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 { | |||
@@ -7,7 +7,7 @@ public interface DataType { | |||
*/ | |||
public static final byte NIL = (byte) 0x00; | |||
/** | |||
/**LdeNhjPGzHcHL6rLcJ7whHxUbn9Tv7qSKRfEA | |||
* 布尔; | |||
*/ | |||
public static final byte BOOLEAN = (byte) 0x01; | |||
@@ -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<DataContract> 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 <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; | |||
throw new IllegalDataException(e.getMessage()); | |||
} | |||
} | |||
} | |||
} |
@@ -49,22 +49,22 @@ public class LedgerTestUtils { | |||
return txHandle.txRequest; | |||
} | |||
public static TransactionRequest createContractEventTxRequest(HashDigest ledgerHash, | |||
SignatureFunction signatureFunction, String contractAddress, String event, byte[] args) { | |||
TxHandle txHandle = new TxHandle(); | |||
TxTemplate txTemp = new TxTemplate(ledgerHash, txHandle); | |||
txTemp.contractEvents().send(contractAddress, event, args); | |||
AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | |||
PubKey pubKey = cryptoKeyPair.getPubKey(); | |||
txTemp.users().register(new BlockchainIdentityData(pubKey)); | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
ptx.sign(cryptoKeyPair); | |||
ptx.commit(); | |||
return txHandle.txRequest; | |||
} | |||
// public static TransactionRequest createContractEventTxRequest(HashDigest ledgerHash, | |||
// SignatureFunction signatureFunction, String contractAddress, String event, byte[] args) { | |||
// TxHandle txHandle = new TxHandle(); | |||
// | |||
// TxTemplate txTemp = new TxTemplate(ledgerHash, txHandle); | |||
// | |||
// txTemp.contractEvents().send(contractAddress, event, args); | |||
// | |||
// AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); | |||
// PubKey pubKey = cryptoKeyPair.getPubKey(); | |||
// txTemp.users().register(new BlockchainIdentityData(pubKey)); | |||
// PreparedTransaction ptx = txTemp.prepare(); | |||
// ptx.sign(cryptoKeyPair); | |||
// ptx.commit(); | |||
// return txHandle.txRequest; | |||
// } | |||
public static TransactionStagedSnapshot generateRandomSnapshot() { | |||
TransactionStagedSnapshot txDataSnapshot = new TransactionStagedSnapshot(); | |||
@@ -6,14 +6,12 @@ import static org.junit.Assert.assertNotNull; | |||
import static org.junit.Assert.assertNull; | |||
import static org.junit.Assert.assertTrue; | |||
import java.security.KeyFactory; | |||
import java.util.Random; | |||
import com.jd.blockchain.binaryproto.DataContractRegistry; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import org.junit.Test; | |||
import com.jd.blockchain.binaryproto.DataContractRegistry; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||
import com.jd.blockchain.ledger.ContractCodeDeployOperation; | |||
@@ -25,7 +23,6 @@ import com.jd.blockchain.ledger.DataAccountRegisterOperation; | |||
import com.jd.blockchain.ledger.DigitalSignature; | |||
import com.jd.blockchain.ledger.LedgerTransaction; | |||
import com.jd.blockchain.ledger.Operation; | |||
import com.jd.blockchain.ledger.TransactionBuilder; | |||
import com.jd.blockchain.ledger.TransactionRequest; | |||
import com.jd.blockchain.ledger.TransactionRequestBuilder; | |||
import com.jd.blockchain.ledger.TransactionState; | |||
@@ -62,7 +59,7 @@ public class TransactionSetTest { | |||
// Build transaction request; | |||
HashDigest ledgerHash = LedgerTestUtils.generateRandomHash(); | |||
TransactionBuilder txBuilder = new TxBuilder(ledgerHash); | |||
TxBuilder txBuilder = new TxBuilder(ledgerHash); | |||
BlockchainKeypair userKey = BlockchainKeyGenerator.getInstance().generate(); | |||
UserRegisterOperation userRegOp = txBuilder.users().register(userKey.getIdentity()); | |||
@@ -0,0 +1,196 @@ | |||
package com.jd.blockchain.contract; | |||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
import com.jd.blockchain.binaryproto.DataContract; | |||
import com.jd.blockchain.consts.DataCodes; | |||
import com.jd.blockchain.ledger.*; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.IllegalDataException; | |||
import org.springframework.util.ReflectionUtils; | |||
import java.nio.ByteBuffer; | |||
import java.util.Arrays; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
/** | |||
* @author zhaogw | |||
* date 2019/5/16 18:05 | |||
*/ | |||
public class ContractSerializeUtils { | |||
public static Map<Integer, Class<?>> 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<DataContract> list; | |||
* @param objArr | |||
* @param dataContractList | |||
* @return | |||
*/ | |||
public static byte[] serializeMethodParam(Object[] objArr,List<DataContract> dataContractList) { | |||
byte[][] result = new byte[objArr.length][]; | |||
//将method中形参转换为实体对象,每个形参都必须为@DataContract类型;每个形参使用系统的BinaryProtocol来进行序列化,如果有5个参数,则使用5次序列化; | |||
int sum = 0; | |||
for(int i=0;i<objArr.length;i++){ | |||
DataContract dataContract = dataContractList.get(i); | |||
objArr[i] = regenObj(dataContract,objArr[i]); | |||
//get data interface; | |||
result[i] = BinaryProtocol.encode(objArr[i],getDataIntf().get(dataContract.code())); | |||
sum += result[i].length; | |||
} | |||
/** | |||
* return byte[] format: | |||
return is byte[], but now is byte[][], so we should reduct dimension by adding the header info to the rtnBytes[]; | |||
rtnBytes[]=classTypes.length/first length/second length/third length/result[0]/result[1]/result[2]; | |||
rtnBytes[0]: 4 bytes(classTypes.length); | |||
rtnBytes[1]: 4 bytes(1 param's length); | |||
rtnBytes[2]: 4 bytes(2 param's length); | |||
rtnBytes[3]: 4 bytes(3 param's length); | |||
rtnBytes[...]: result[0][] bytes(1 param's length); | |||
rtnBytes[...]: result[1][] bytes(2 param's length); | |||
rtnBytes[...]: result[2][] bytes(3 param's length); | |||
*/ | |||
int bodyFirstPosition = 4 + 4 * (objArr.length); | |||
ByteBuffer byteBuffer = ByteBuffer.allocate(bodyFirstPosition + sum); | |||
byteBuffer.putInt(objArr.length); | |||
for(int j=0; j<result.length; j++) { | |||
byte[] curResult = result[j]; | |||
byteBuffer.putInt(curResult.length); | |||
} | |||
for(int k=0; k<result.length; k++){ | |||
byteBuffer.put(result[k]); | |||
} | |||
return byteBuffer.array(); | |||
} | |||
/** | |||
* deserialize the params bytes[]; | |||
* params format: nums|first length| second length| third length| ... |bytes[0]| byte[1] | bytes[2]| ... | |||
* @param params | |||
* @param dataContractList | |||
* @return | |||
*/ | |||
public static Object[] deserializeMethodParam(byte[] params, List<DataContract> 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<dataContractList.size(); i++){ | |||
DataContract dataContract = dataContractList.get(i); | |||
int curParamLength = byteBuffer.getInt((i+1)*4); | |||
ByteBuffer byteBuffer1 = ByteBuffer.allocate(curParamLength); | |||
byteBuffer1.put(params,offsetPosition,curParamLength); | |||
offsetPosition += curParamLength; | |||
//if dataContract=primitive type(byte/short/int/long/String),only use its getValues(); | |||
Object object = BinaryProtocol.decodeAs(byteBuffer1.array(), | |||
getDataIntf().get(dataContract.code())); | |||
if(isPrimitiveType(dataContract.code())){ | |||
Class<?> 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 <T> | |||
* @return | |||
*/ | |||
public static <T> Map<Integer, Class<?> > 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; | |||
} | |||
} |
@@ -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(); | |||
} |
@@ -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(); | |||
} |
@@ -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(); | |||
} |
@@ -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(); | |||
} |
@@ -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(); | |||
} |
@@ -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(); | |||
} |
@@ -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(); | |||
} |
@@ -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(); | |||
} |
@@ -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<Operation> 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> T contract(String address, Class<T> contractIntf) { | |||
// TODO Auto-generated method stub | |||
return null; | |||
return contractInvoProxyBuilder.create(address, contractIntf, contractEventSendOpBuilder); | |||
} | |||
@Override | |||
public <T> T contract(Bytes address, Class<T> contractIntf) { | |||
return contractInvoProxyBuilder.create(address, contractIntf, contractEventSendOpBuilder); | |||
} | |||
public Collection<Operation> 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); | |||
@@ -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)); | |||
} | |||
} |
@@ -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<Class<?>, ContractType> contractTypes = new ConcurrentHashMap<>(); | |||
public <T> T create(String address, Class<T> contractIntf, ContractEventSendOperationBuilder contractEventBuilder) { | |||
return create(Bytes.fromBase58(address), contractIntf, contractEventBuilder); | |||
} | |||
@SuppressWarnings("unchecked") | |||
public <T> T create(Bytes address, Class<T> 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; | |||
} | |||
} |
@@ -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<String, Method> events; | |||
private SortedMap<Method, String> handleMethods; | |||
private Map<String, Method> events = new HashMap<>(); | |||
private Map<Method, String> handleMethods = new HashMap<>(); | |||
private Map<Method, List<DataContract>> dataContractMap = new HashMap<>(); | |||
/** | |||
* 返回声明的所有事件; | |||
* | |||
* @return | |||
*/ | |||
Set<String> getEvents() { | |||
public Set<String> getEvents() { | |||
return events.keySet(); | |||
} | |||
public Map<Method, List<DataContract>> getDataContractMap() { | |||
return dataContractMap; | |||
} | |||
/** | |||
* 返回指定方法声明的事件;<br> | |||
* | |||
@@ -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 + | |||
'}'; | |||
} | |||
} |
@@ -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> T contract(String address, Class<T> contractIntf); | |||
/** | |||
* 创建调用合约的代理实例; | |||
@@ -17,6 +28,6 @@ public interface EventOperator { | |||
* @param contractIntf | |||
* @return | |||
*/ | |||
<T> T contract(String address, Class<T> contractIntf); | |||
<T> T contract(Bytes address, Class<T> contractIntf); | |||
} |
@@ -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> T contract(Bytes address, Class<T> contractIntf) { | |||
return opFactory.contract(address, contractIntf); | |||
} | |||
@Override | |||
public <T> T contract(String address, Class<T> contractIntf) { | |||
// TODO Auto-generated method stub | |||
throw new IllegalStateException("Not implemented."); | |||
return opFactory.contract(address, contractIntf); | |||
} | |||
} |
@@ -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> T contract(Bytes address, Class<T> contractIntf) { | |||
return txBuilder.contract(address, contractIntf); | |||
} | |||
@Override | |||
@@ -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(); | |||
} | |||
@@ -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<String> allKeys = pp.stringPropertyNames(); | |||
List<String> 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<String,String> 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<String,String> 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(); | |||
} | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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 准备就绪; | |||
@@ -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<keys.length; i++){ | |||
txTemp.dataAccount(dataAddress).set(keys[i], values[i], version); | |||
} | |||
// TX 准备就绪; | |||
PreparedTransaction prepTx = txTemp.prepare(); | |||
prepTx.sign(signKeyPair); | |||
// 提交交易; | |||
TransactionResponse transactionResponse = prepTx.commit(); | |||
assertTrue(transactionResponse.isSuccess()); | |||
} | |||
private String deploy() throws IOException { | |||
ClassPathResource classPathResource = new ClassPathResource("contract.jar"); | |||
byte[] chainCode = this.getChainCode(classPathResource.getURL().getPath()); | |||
TransactionTemplate txTpl = this.bcsrv.newTransaction(ledgerHash); | |||
BlockchainIdentity contractIdentity = BlockchainKeyGenerator.getInstance().generate().getIdentity(); | |||
txTpl.contracts().deploy(contractIdentity, chainCode); | |||
PreparedTransaction ptx = txTpl.prepare(); | |||
ptx.sign(signKeyPair); | |||
TransactionResponse txResp = ptx.commit(); | |||
System.out.println("contract's address=" + contractIdentity.getAddress()); | |||
String contractAddr = contractIdentity.getAddress().toBase58(); | |||
log.info("contractAddr="+contractAddr); | |||
return contractAddr; | |||
} | |||
public byte[] getChainCode(String path) { | |||
byte[] chainCode = null; | |||
File file = null; | |||
FileInputStream input = null; | |||
try { | |||
file = new File(path); | |||
input = new FileInputStream(file); | |||
chainCode = new byte[input.available()]; | |||
input.read(chainCode); | |||
} catch (IOException var14) { | |||
var14.printStackTrace(); | |||
} finally { | |||
try { | |||
if (input != null) { | |||
input.close(); | |||
} | |||
} catch (IOException var13) { | |||
var13.printStackTrace(); | |||
} | |||
} | |||
return chainCode; | |||
} | |||
/** | |||
* 演示合约执行的过程; | |||
*/ | |||
public static void demoContract() { | |||
// 账本地址; | |||
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) }; | |||
// 创建服务代理; | |||
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); | |||
BlockchainService service = serviceFactory.getBlockchainService(); | |||
HashDigest ledgerHash = getLedgerHash(); | |||
// 发起交易; | |||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
// -------------------------------------- | |||
// 一个贸易账户,贸易结算后的利润将通过一个合约账户来执行利润分配; | |||
// 合约账户被设置为通用的账户,不具备对贸易结算账户的直接权限; | |||
// 只有当前交易发起人具备对贸易账户的直接权限,当交易发起人对交易进行签名之后,权限被间接传递给合约账户; | |||
String commerceAccount = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; | |||
// 处理利润分成的通用业务逻辑的合约账户; | |||
String profitDistributionContract = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; | |||
// 收益人账户; | |||
String receiptorAccount1 = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; | |||
String receiptorAccount2 = "LdeP13gKE6319LvYPyWAT4UXr2brvpitPRBN1"; | |||
// 资产编码; | |||
String assetKey = "RMB-ASSET"; | |||
// 此次待分配利润; | |||
long profit = 1000000; | |||
// 备注信息; | |||
Remark remark = new Remark(); | |||
String remarkJSON = JSONSerializeUtils.serializeToJSON(remark); | |||
AssetContract assetContract = txTemp.contract(profitDistributionContract, AssetContract.class); | |||
assetContract.issue(1000, receiptorAccount1); | |||
assetContract.transfer(receiptorAccount1, receiptorAccount2, 600); | |||
// assetContract. | |||
// -------------------------------------- | |||
// TX 准备就绪; | |||
PreparedTransaction prepTx = txTemp.prepare(); | |||
String txHash = ByteArray.toBase64(prepTx.getHash().toBytes()); | |||
// 使用私钥进行签名; | |||
AsymmetricKeypair keyPair = getSponsorKey(); | |||
prepTx.sign(keyPair); | |||
// 提交交易; | |||
prepTx.commit(); | |||
} | |||
private static HashDigest getLedgerHash() { | |||
return new HashDigest(Base58Utils.decode(ledgerAddress)); | |||
} | |||
/** | |||
* 交易发起人的私钥;<br> | |||
* | |||
* 注:私钥由调用方在本地保管和使用; | |||
* | |||
* @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); | |||
} | |||
} |
@@ -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); | |||
@@ -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 | |||
@@ -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) { | |||
@@ -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 <T> 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 <T> 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; | |||
@@ -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(); | |||
@@ -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(); | |||
@@ -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); | |||
} |
@@ -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); | |||
} |
@@ -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"; | |||