@@ -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 { | |||
@@ -5,7 +5,7 @@ import com.jd.blockchain.contract.engine.ContractCode; | |||
import com.jd.blockchain.runtime.Module; | |||
import com.jd.blockchain.transaction.ContractType; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.serialize.binary.ContractSerializeUtils; | |||
import com.jd.blockchain.contract.ContractSerializeUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.util.ReflectionUtils; | |||
@@ -0,0 +1,202 @@ | |||
package com.jd.blockchain.contract; | |||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
import com.jd.blockchain.binaryproto.DataContract; | |||
import com.jd.blockchain.binaryproto.DataField; | |||
import com.jd.blockchain.consts.DataCodes; | |||
import com.jd.blockchain.ledger.*; | |||
import com.jd.blockchain.utils.ArrayUtils; | |||
import com.jd.blockchain.utils.io.ByteArray; | |||
import com.jd.blockchain.utils.io.BytesUtils; | |||
import org.springframework.util.SerializationUtils; | |||
import java.lang.annotation.Annotation; | |||
import java.lang.reflect.Field; | |||
import java.lang.reflect.Method; | |||
import java.math.BigDecimal; | |||
import java.nio.ByteBuffer; | |||
import java.util.ArrayList; | |||
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<?>> dataContractMap = new HashMap<>(); | |||
public static final Class[] confirmedType = {CONTRACT_INT8.class,CONTRACT_INT16.class,CONTRACT_INT32.class,CONTRACT_INT64.class, | |||
CONTRACT_TEXT.class,CONTRACT_BINARY.class,CONTRACT_BIG_INT.class}; | |||
/** | |||
* valid then parse the Object by Method params; | |||
* @param object | |||
* @param method | |||
* @return | |||
*/ | |||
public static byte[] serializeMethodParam(Object object,Method method) { | |||
if (object == null) { | |||
return null; | |||
} | |||
Object[] objArr = null; | |||
if(object.getClass().isArray()){ | |||
objArr = (Object[]) object; | |||
} | |||
Class<?>[] classTypes = method.getParameterTypes(); | |||
Annotation [][] annotations = method.getParameterAnnotations(); | |||
byte[][] result = new byte[classTypes.length][]; | |||
//将method中形参转换为实体对象,每个形参都必须为@DataContract类型;每个形参使用系统的BinaryProtocol来进行序列化,如果有5个参数,则使用5次序列化; | |||
int sum = 0; | |||
for(int i=0;i<classTypes.length;i++){ | |||
Class <?> classType = classTypes[i]; | |||
DataContract dataContract = classType.getDeclaredAnnotation(DataContract.class); | |||
if(dataContract == null){ | |||
boolean canPass = false; | |||
//check by annotation; | |||
Annotation[] annotationArr = annotations[i]; | |||
for(Annotation annotation : annotationArr){ | |||
if(annotation.annotationType().equals(DataContract.class)){ | |||
dataContract = (DataContract) annotation; | |||
objArr[i] = regenObj(dataContract,objArr[i]); | |||
canPass = true; | |||
} | |||
} | |||
if(!canPass){ | |||
throw new IllegalArgumentException("must set annotation in each param of contract."); | |||
} | |||
} | |||
//get data interface; | |||
result[i] = BinaryProtocol.encode(objArr[i],getDataIntf().get(dataContract.code())); | |||
sum += result[i].length; | |||
} | |||
/** | |||
* //return is byte[], but now is byte[][], so we should reduct dimension, use the header info to the first byte(length=classTypes.length); | |||
format:result[][]={{1,2,3},{4,5},{6,7}}; newResult[]=classTypes.length/first length/second length/3 length/result[0]/result[1]/result[2]; | |||
rtnBytes[0]: 4 bytes(classTypes.length, <255); | |||
rtnBytes[1]: 4 bytes(each param's length, ) | |||
rtnBytes[2]: 4 bytes(each param's length, ) | |||
rtnBytes[3]: 4 bytes(each param's length, ) | |||
rtnBytes[4...]: result[0][] bytes(each param's length) | |||
rtnBytes[5...]: result[1][] bytes(each param's length) | |||
rtnBytes[6...]: result[2][] bytes(each param's length) | |||
*/ | |||
int bodyFirstPosition = 4 + 4*(classTypes.length); | |||
ByteBuffer byteBuffer = ByteBuffer.allocate(bodyFirstPosition + sum); | |||
byteBuffer.putInt(classTypes.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],0, result[k].length); | |||
} | |||
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 method | |||
* @return | |||
*/ | |||
public static Object[] deserializeMethodParam(byte[] params, Method method) { | |||
if (params == null) { | |||
return null; | |||
} | |||
Class<?>[] classTypes = method.getParameterTypes(); | |||
Object result[] = new Object[classTypes.length]; | |||
ByteBuffer byteBuffer = ByteBuffer.allocate(params.length); | |||
int paramNums = byteBuffer.getInt(0); | |||
if(paramNums != classTypes.length){ | |||
throw new IllegalArgumentException("deserializeMethodparm. params'length in byte[] != method's param length"); | |||
} | |||
for(int i=0; i<classTypes.length; i++){ | |||
Class<?> classType = classTypes[i]; | |||
int curParamLength = byteBuffer.get(i+1); | |||
DataContract dataContract = classType.getDeclaredAnnotation(DataContract.class); | |||
if(dataContract == null){ | |||
throw new IllegalArgumentException("must set annotation in each param of contract."); | |||
} | |||
result [i] = BinaryProtocol.decodeAs( | |||
byteBuffer.get(params,(i + 1 + classTypes.length)*4,curParamLength).array(), | |||
getDataIntf().get(dataContract.code())); | |||
} | |||
return result; | |||
} | |||
public static <T> Map<Integer, Class<?> > getDataIntf(){ | |||
dataContractMap.put(DataCodes.CONTRACT_INT8, CONTRACT_INT8.class); | |||
dataContractMap.put(DataCodes.CONTRACT_INT16, CONTRACT_INT16.class); | |||
dataContractMap.put(DataCodes.CONTRACT_INT32, CONTRACT_INT32.class); | |||
dataContractMap.put(DataCodes.CONTRACT_INT64, CONTRACT_INT64.class); | |||
dataContractMap.put(DataCodes.CONTRACT_TEXT, CONTRACT_TEXT.class); | |||
dataContractMap.put(DataCodes.CONTRACT_BINARY, CONTRACT_BINARY.class); | |||
dataContractMap.put(DataCodes.CONTRACT_BIG_INT, CONTRACT_BIG_INT.class); | |||
dataContractMap.put(DataCodes.TX_CONTENT_BODY, TransactionContentBody.class); | |||
return dataContractMap; | |||
} | |||
private static Object regenObj(DataContract dataContract, Object object){ | |||
if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT8.class)){ | |||
return new CONTRACT_INT8() { | |||
@Override | |||
public int getValue() { | |||
return Integer.parseInt(object.toString()); | |||
} | |||
}; | |||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT16.class)){ | |||
return new CONTRACT_INT16() { | |||
@Override | |||
public int getValue() { | |||
return Integer.parseInt(object.toString()); | |||
} | |||
}; | |||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT32.class)){ | |||
return new CONTRACT_INT32() { | |||
@Override | |||
public int getValue() { | |||
return Integer.parseInt(object.toString()); | |||
} | |||
}; | |||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT64.class)){ | |||
return new CONTRACT_INT64() { | |||
@Override | |||
public long getValue() { | |||
return Long.parseLong(object.toString()); | |||
} | |||
}; | |||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_TEXT.class)){ | |||
return new CONTRACT_TEXT() { | |||
@Override | |||
public String getValue() { | |||
return object.toString(); | |||
} | |||
}; | |||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_BINARY.class)){ | |||
return new CONTRACT_BINARY() { | |||
@Override | |||
public Byte getValue() { | |||
return (Byte) object; | |||
} | |||
}; | |||
}else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_BIG_INT.class)){ | |||
return new CONTRACT_BIG_INT() { | |||
@Override | |||
public BigDecimal getValue() { | |||
return new BigDecimal(object.toString()); | |||
} | |||
}; | |||
} | |||
return null; | |||
} | |||
} |
@@ -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_BINARY) | |||
public interface CONTRACT_BIG_INT { | |||
@DataField(order=2, primitiveType= PrimitiveType.BIG_INT) | |||
BigDecimal 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 Binary; | |||
* @author zhaogw | |||
* date 2019-05-17 15:32 | |||
*/ | |||
@DataContract(code = DataCodes.CONTRACT_BINARY) | |||
public interface CONTRACT_BINARY { | |||
@DataField(order=2, primitiveType= PrimitiveType.BYTES) | |||
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 int16; | |||
* @author zhaogw | |||
* date 2019-05-17 15:32 | |||
*/ | |||
@DataContract(code = DataCodes.CONTRACT_INT16) | |||
public interface CONTRACT_INT16 { | |||
@DataField(order=2, primitiveType= PrimitiveType.INT16) | |||
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 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) | |||
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 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,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,27 @@ | |||
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.CONTRACT_TEXT; | |||
import com.jd.blockchain.ledger.TransactionContentBody; | |||
/** | |||
* 示例:一个“资产管理”智能合约; | |||
* | |||
* @author zhaogw | |||
*/ | |||
@Contract | |||
public interface AssetContract2 { | |||
/** | |||
* 发行资产; | |||
* 新发行的资产数量; | |||
* @param assetHolderAddress | |||
* 新发行的资产的持有账户; | |||
*/ | |||
@ContractEvent(name = "issue-asset") | |||
void issue(@DataContract(code = DataCodes.TX_CONTENT_BODY) TransactionContentBody transactionContentBody, | |||
@DataContract(code = DataCodes.CONTRACT_TEXT) String assetHolderAddress); | |||
} |
@@ -0,0 +1,68 @@ | |||
package com.jd.blockchain.contract.samples; | |||
import com.jd.blockchain.contract.ContractEventContext; | |||
import com.jd.blockchain.contract.ContractException; | |||
import com.jd.blockchain.contract.EventProcessingAwire; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.KVDataObject; | |||
import com.jd.blockchain.ledger.TransactionContentBody; | |||
import com.jd.blockchain.utils.io.BytesUtils; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.Set; | |||
/** | |||
* 示例:一个“资产管理”智能合约的实现; | |||
* | |||
* 注: 1、实现 EventProcessingAwire 接口以便合约实例在运行时可以从上下文获得合约生命周期事件的通知; 2、实现 | |||
* AssetContract 接口定义的合约方法; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public class AssetContractImpl2 implements EventProcessingAwire, AssetContract2 { | |||
// 合约事件上下文; | |||
private ContractEventContext eventContext; | |||
@Override | |||
public void issue(TransactionContentBody transactionContentBody, String assetHolderAddress) { | |||
System.out.println(transactionContentBody.toString()); | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see | |||
* com.jd.blockchain.contract.model.EventProcessingAwire#beforeEvent(com.jd. | |||
* blockchain.contract.model.ContractEventContext) | |||
*/ | |||
@Override | |||
public void beforeEvent(ContractEventContext eventContext) { | |||
this.eventContext = eventContext; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.jd.blockchain.contract.model.EventProcessingAwire#postEvent(com.jd. | |||
* blockchain.contract.model.ContractEventContext, | |||
* com.jd.blockchain.contract.model.ContractError) | |||
*/ | |||
@Override | |||
public void postEvent(ContractEventContext eventContext, ContractException error) { | |||
this.eventContext = null; | |||
} | |||
@Override | |||
public void postEvent(ContractException error) { | |||
} | |||
@Override | |||
public void postEvent() { | |||
} | |||
} |
@@ -0,0 +1,301 @@ | |||
package test.com.jd.blockchain.sdk.test; | |||
import com.jd.blockchain.contract.samples.AssetContract; | |||
import com.jd.blockchain.contract.samples.AssetContract2; | |||
import com.jd.blockchain.crypto.*; | |||
import com.jd.blockchain.crypto.service.classic.ClassicAlgorithm; | |||
import com.jd.blockchain.ledger.*; | |||
import com.jd.blockchain.sdk.BlockchainService; | |||
import com.jd.blockchain.sdk.BlockchainServiceFactory; | |||
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.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 = "j5rpuGWVxSuUbU3gK7MDREfui797AjfdHzvAMiSaSzydu7"; | |||
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() { | |||
// 发起交易; | |||
TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); | |||
String contractAddress = "LdeNfBcbQWqVge1sVyLq3EHBJhMMjwQY3uJJE"; | |||
AssetContract2 assetContract = txTemp.contract(contractAddress, AssetContract2.class); | |||
TransactionContentBody transactionContentBody = new TransactionContentBody() { | |||
@Override | |||
public HashDigest getLedgerHash() { | |||
return new HashDigest(ClassicAlgorithm.SHA256, "zhaogw".getBytes()); | |||
} | |||
@Override | |||
public Operation[] getOperations() { | |||
return new Operation[0]; | |||
} | |||
}; | |||
assetContract.issue(transactionContentBody,contractAddress); | |||
// TX 准备就绪; | |||
PreparedTransaction prepTx = txTemp.prepare(); | |||
prepTx.sign(signKeyPair); | |||
// 提交交易; | |||
prepTx.commit(); | |||
} | |||
@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 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()); | |||
} | |||
@Test | |||
public void 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); | |||
} | |||
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; | |||
} | |||
} | |||
} |
@@ -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,51 +0,0 @@ | |||
package com.jd.blockchain.utils.serialize.binary; | |||
import com.jd.blockchain.utils.ArrayUtils; | |||
import com.jd.blockchain.utils.io.BytesUtils; | |||
import org.springframework.util.SerializationUtils; | |||
import java.lang.reflect.Method; | |||
import java.math.BigDecimal; | |||
/** | |||
* @author zhaogw | |||
* date 2019/5/16 18:05 | |||
*/ | |||
public class ContractSerializeUtils { | |||
public static final Class[] confirmedType = {Integer.class, Long.class, Double.class, | |||
int.class,long.class,double.class,String.class, BigDecimal.class}; | |||
/** | |||
* valid then parse the Object by Method params; | |||
* @param object | |||
* @param method | |||
* @return | |||
*/ | |||
public static byte[] serializeMethodParam(Object object,Method method) { | |||
if (object == null) { | |||
return BytesUtils.EMPTY_BYTES; | |||
} | |||
Class<?>[] classType = method.getParameterTypes(); | |||
for(Class<?> curClass : classType){ | |||
if(!ArrayUtils.asList(confirmedType).contains(curClass)){ | |||
throw new IllegalArgumentException("not support this type="+curClass.toString()); | |||
} | |||
} | |||
return SerializationUtils.serialize(object); | |||
} | |||
public static Object deserializeMethodParam(byte[] bytes,Method method) { | |||
if (bytes == null) { | |||
return null; | |||
} | |||
Class<?>[] classType = method.getParameterTypes(); | |||
for(Class<?> curClass : classType){ | |||
if(!ArrayUtils.asList(confirmedType).contains(curClass)){ | |||
throw new IllegalArgumentException("not support this type="+curClass.toString()); | |||
} | |||
} | |||
return SerializationUtils.deserialize(bytes); | |||
} | |||
} |