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