@@ -74,7 +74,7 @@ public class JavaContractCode implements ContractCode { | |||
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(); | |||
// 反序列化参数; | |||
@@ -97,40 +97,40 @@ public class JavaContractCode implements ContractCode { | |||
} | |||
// 得到当前类中相关方法和注解对应关系; | |||
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; | |||
} | |||
} | |||
// 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; | |||
// } | |||
// } | |||
} | |||
} |
@@ -4,6 +4,7 @@ import java.lang.reflect.InvocationHandler; | |||
import java.lang.reflect.Method; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; | |||
public class ContractInvocationProxy implements InvocationHandler { | |||
@@ -25,6 +26,10 @@ public class ContractInvocationProxy implements InvocationHandler { | |||
@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 对象的方法; | |||
@@ -43,7 +48,6 @@ public class ContractInvocationProxy implements InvocationHandler { | |||
private byte[] serializeArgs(Object[] args) { | |||
// TODO 根据方法参数的定义序列化参数; | |||
return null; | |||
return BinarySerializeUtils.serialize(args); | |||
} | |||
} |
@@ -1,9 +1,16 @@ | |||
package com.jd.blockchain.transaction; | |||
import java.lang.annotation.Annotation; | |||
import java.lang.reflect.Method; | |||
import java.lang.reflect.Proxy; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import com.jd.blockchain.contract.ContractEvent; | |||
import com.jd.blockchain.contract.ContractException; | |||
import com.jd.blockchain.utils.BaseConstant; | |||
import com.jd.blockchain.utils.Bytes; | |||
import org.apache.http.annotation.Contract; | |||
public class ContractInvocationProxyBuilder { | |||
@@ -32,14 +39,58 @@ public class ContractInvocationProxyBuilder { | |||
} | |||
// 判断是否是标注了合约的接口类型; | |||
if (!isContractType(contractIntf)){ | |||
return null; | |||
} | |||
// 解析合约事件处理方法,检查是否有重名; | |||
if(!isUniqueEvent(contractIntf)){ | |||
return null; | |||
} | |||
// TODO 检查是否不支持的参数类型; | |||
// TODO 检查返回值类型; | |||
return null; | |||
return ContractType.resolve(contractIntf); | |||
} | |||
private boolean isUniqueEvent(Class<?> contractIntf) { | |||
boolean isUnique = true; | |||
Method[] classMethods = contractIntf.getMethods(); | |||
Map<Method, Annotation[]> methodAnnoMap = new HashMap<Method, Annotation[]>(); | |||
Map<String, Method> annoMethodMap = new HashMap<String, Method>(); | |||
for (int i = 0; i < classMethods.length; i++) { | |||
Annotation[] a = classMethods[i].getDeclaredAnnotations(); | |||
methodAnnoMap.put(classMethods[i], a); | |||
// if current method contains @ContractEvent,then put it in this map; | |||
for (Annotation annotation_ : a) { | |||
if (classMethods[i].isAnnotationPresent(ContractEvent.class)) { | |||
Object obj = classMethods[i].getAnnotation(ContractEvent.class); | |||
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); | |||
//if annoMethodMap has contained the eventName, too many same eventNames exists probably, say NO! | |||
if(annoMethodMap.containsKey(eventName_)){ | |||
isUnique = false; | |||
} | |||
annoMethodMap.put(eventName_, classMethods[i]); | |||
} | |||
} | |||
} | |||
return isUnique; | |||
} | |||
/** | |||
* is contractType really? identified by @Contract; | |||
* @param contractIntf | |||
* @return | |||
*/ | |||
private boolean isContractType(Class<?> contractIntf) { | |||
Annotation annotation = contractIntf.getDeclaredAnnotation(Contract.class); | |||
return annotation != null ? true : false; | |||
} | |||
} |
@@ -1,8 +1,12 @@ | |||
package com.jd.blockchain.transaction; | |||
import com.jd.blockchain.contract.ContractEvent; | |||
import com.jd.blockchain.contract.ContractException; | |||
import com.jd.blockchain.utils.BaseConstant; | |||
import java.lang.annotation.Annotation; | |||
import java.lang.reflect.Method; | |||
import java.util.Set; | |||
import java.util.SortedMap; | |||
import java.util.*; | |||
public class ContractType { | |||
@@ -48,8 +52,40 @@ public class ContractType { | |||
private ContractType() { | |||
} | |||
// public static ContractType resolve(Class<?> contractIntf) { | |||
// | |||
// } | |||
public static ContractType resolve(Class<?> contractIntf) { | |||
ContractType contractType = new ContractType(); | |||
//contractIntf contains @Contract and @ContractEvent; | |||
Method[] classMethods = contractIntf.getMethods(); | |||
Map<Method, Annotation[]> methodAnnoMap = new HashMap<Method, Annotation[]>(); | |||
for (int i = 0; i < classMethods.length; i++) { | |||
Annotation[] a = classMethods[i].getDeclaredAnnotations(); | |||
methodAnnoMap.put(classMethods[i], a); | |||
// if current method contains @ContractEvent,then put it in this map; | |||
for (Annotation annotation_ : a) { | |||
if (classMethods[i].isAnnotationPresent(ContractEvent.class)) { | |||
Object obj = classMethods[i].getAnnotation(ContractEvent.class); | |||
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); | |||
//if annoMethodMap has contained the eventName, too many same eventNames exists probably, say NO! | |||
if(contractType.events.containsKey(eventName_)){ | |||
throw new ContractException("too many same eventNames exists in the contract, check it."); | |||
} | |||
contractType.events.put(eventName_, classMethods[i]); | |||
contractType.handleMethods.put(classMethods[i],eventName_); | |||
} | |||
} | |||
} | |||
return contractType; | |||
} | |||
@Override | |||
public String toString() { | |||
return "ContractType{" + | |||
"name='" + name + '\'' + | |||
", events=" + events + | |||
", handleMethods=" + handleMethods + | |||
'}'; | |||
} | |||
} |
@@ -30,14 +30,14 @@ public class SDKDemo_Contract { | |||
*/ | |||
public static void demoContract() { | |||
// 账本地址; | |||
String ledgerAddress = "ffkjhkeqwiuhivnsh3298josijdocaijsda=="; | |||
String ledgerAddress = "6GgNS3YgtxvZDBMvHEoqDiNZvWdiJ3MMpvRS9kL4DYwr4"; | |||
// 节点地址列表; | |||
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 String GATEWAY_IP = "192.168.151.39"; | |||
final int GATEWAY_PORT = 80; | |||
final boolean SECURE = false; | |||
GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IP, GATEWAY_PORT, SECURE, | |||
@@ -28,6 +28,7 @@ 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 java.io.File; | |||
import java.io.FileInputStream; | |||
@@ -470,21 +471,20 @@ public class IntegrationBase { | |||
txContentHash = ptx.getHash(); | |||
// execute the contract; | |||
testContractExe(adminKey, ledgerHash, userKey, blockchainService, ledgerRepository); | |||
testContractExe(adminKey, ledgerHash, userKey, blockchainService, ledgerRepository, AssetContract.class); | |||
return block; | |||
} | |||
private void testContractExe(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair userKey, | |||
BlockchainService blockchainService,LedgerRepository ledgerRepository) { | |||
private <T> void testContractExe(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair userKey, | |||
BlockchainService blockchainService,LedgerRepository ledgerRepository,Class<T> contractIntf) { | |||
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##123##" + contractDataKey.getAddress()).getBytes()); | |||
txTpl.contract(contractDeployKey.getAddress(),AssetContract.class).issue(10,"abc"); | |||
// 签名; | |||
PreparedTransaction ptx = txTpl.prepare(); | |||
@@ -41,6 +41,7 @@ import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; | |||
import com.jd.blockchain.utils.net.NetworkAddress; | |||
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; | |||
@@ -50,7 +51,7 @@ import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsT | |||
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,8 +316,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(); | |||
@@ -48,6 +48,7 @@ import com.jd.blockchain.utils.codec.HexUtils; | |||
import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; | |||
import com.jd.blockchain.utils.net.NetworkAddress; | |||
import test.com.jd.blockchain.intgr.contract.AssetContract; | |||
import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest; | |||
public class IntegrationTestAll4Redis { | |||
@@ -450,10 +451,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); | |||
} |