@@ -74,7 +74,7 @@ public class JavaContractCode implements ContractCode { | |||||
ReflectionUtils.invokeMethod(beforeMth_, contractMainClassObj, contractEventContext); | ReflectionUtils.invokeMethod(beforeMth_, contractMainClassObj, contractEventContext); | ||||
LOGGER.info("beforeEvent,耗时:" + (System.currentTimeMillis() - startTime)); | LOGGER.info("beforeEvent,耗时:" + (System.currentTimeMillis() - startTime)); | ||||
Method eventMethod = this.getMethodByAnno(contractMainClassObj, contractEventContext.getEvent()); | |||||
// Method eventMethod = this.getMethodByAnno(contractMainClassObj, contractEventContext.getEvent()); | |||||
startTime = System.currentTimeMillis(); | 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 java.lang.reflect.Method; | ||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; | |||||
public class ContractInvocationProxy implements InvocationHandler { | public class ContractInvocationProxy implements InvocationHandler { | ||||
@@ -25,6 +26,10 @@ public class ContractInvocationProxy implements InvocationHandler { | |||||
@Override | @Override | ||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | 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); | String event = contractType.getEvent(method); | ||||
if (event == null) { | if (event == null) { | ||||
// 适配 Object 对象的方法; | // 适配 Object 对象的方法; | ||||
@@ -43,7 +48,6 @@ public class ContractInvocationProxy implements InvocationHandler { | |||||
private byte[] serializeArgs(Object[] args) { | private byte[] serializeArgs(Object[] args) { | ||||
// TODO 根据方法参数的定义序列化参数; | // TODO 根据方法参数的定义序列化参数; | ||||
return null; | |||||
return BinarySerializeUtils.serialize(args); | |||||
} | } | ||||
} | } |
@@ -1,9 +1,16 @@ | |||||
package com.jd.blockchain.transaction; | package com.jd.blockchain.transaction; | ||||
import java.lang.annotation.Annotation; | |||||
import java.lang.reflect.Method; | |||||
import java.lang.reflect.Proxy; | import java.lang.reflect.Proxy; | ||||
import java.util.HashMap; | |||||
import java.util.Map; | 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 com.jd.blockchain.utils.Bytes; | ||||
import org.apache.http.annotation.Contract; | |||||
public class ContractInvocationProxyBuilder { | public class ContractInvocationProxyBuilder { | ||||
@@ -32,14 +39,58 @@ public class ContractInvocationProxyBuilder { | |||||
} | } | ||||
// 判断是否是标注了合约的接口类型; | // 判断是否是标注了合约的接口类型; | ||||
if (!isContractType(contractIntf)){ | |||||
return null; | |||||
} | |||||
// 解析合约事件处理方法,检查是否有重名; | // 解析合约事件处理方法,检查是否有重名; | ||||
if(!isUniqueEvent(contractIntf)){ | |||||
return null; | |||||
} | |||||
// TODO 检查是否不支持的参数类型; | // TODO 检查是否不支持的参数类型; | ||||
// 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; | 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.lang.reflect.Method; | ||||
import java.util.Set; | |||||
import java.util.SortedMap; | |||||
import java.util.*; | |||||
public class ContractType { | public class ContractType { | ||||
@@ -48,8 +52,40 @@ public class ContractType { | |||||
private 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() { | 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 int GATEWAY_PORT = 80; | ||||
final boolean SECURE = false; | final boolean SECURE = false; | ||||
GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IP, GATEWAY_PORT, SECURE, | 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.apache.commons.io.FileUtils; | ||||
import org.junit.Assert; | import org.junit.Assert; | ||||
import org.springframework.core.io.ClassPathResource; | import org.springframework.core.io.ClassPathResource; | ||||
import test.com.jd.blockchain.intgr.contract.AssetContract; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileInputStream; | import java.io.FileInputStream; | ||||
@@ -470,21 +471,20 @@ public class IntegrationBase { | |||||
txContentHash = ptx.getHash(); | txContentHash = ptx.getHash(); | ||||
// execute the contract; | // execute the contract; | ||||
testContractExe(adminKey, ledgerHash, userKey, blockchainService, ledgerRepository); | |||||
testContractExe(adminKey, ledgerHash, userKey, blockchainService, ledgerRepository, AssetContract.class); | |||||
return block; | 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); | LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); | ||||
LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); | LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); | ||||
// 定义交易; | // 定义交易; | ||||
TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | 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(); | PreparedTransaction ptx = txTpl.prepare(); | ||||
@@ -41,6 +41,7 @@ import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; | |||||
import com.jd.blockchain.utils.net.NetworkAddress; | import com.jd.blockchain.utils.net.NetworkAddress; | ||||
import test.com.jd.blockchain.intgr.IntegratedContext.Node; | 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; | ||||
import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest.NodeWebContext; | import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest.NodeWebContext; | ||||
@@ -50,7 +51,7 @@ import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsT | |||||
public class IntegrationTest2 { | public class IntegrationTest2 { | ||||
// 合约测试使用的初始化数据; | // 合约测试使用的初始化数据; | ||||
BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | ||||
private String contractZipName = "AssetContract3.contract"; | |||||
private String contractZipName = "contract.jar"; | |||||
private String eventName = "issue-asset"; | private String eventName = "issue-asset"; | ||||
@Test | @Test | ||||
@@ -315,8 +316,7 @@ public class IntegrationTest2 { | |||||
// 定义交易; | // 定义交易; | ||||
TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | 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(); | 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.concurrent.ThreadInvoker.AsyncCallback; | ||||
import com.jd.blockchain.utils.net.NetworkAddress; | import com.jd.blockchain.utils.net.NetworkAddress; | ||||
import test.com.jd.blockchain.intgr.contract.AssetContract; | |||||
import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest; | import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest; | ||||
public class IntegrationTestAll4Redis { | public class IntegrationTestAll4Redis { | ||||
@@ -450,10 +451,7 @@ public class IntegrationTestAll4Redis { | |||||
// 定义交易; | // 定义交易; | ||||
TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | 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(); | 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); | |||||
} |