Browse Source

intf-contract dev first edition.

tags/1.0.0
zhaoguangwei 5 years ago
parent
commit
19012ae8c4
10 changed files with 188 additions and 60 deletions
  1. +35
    -35
      source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java
  2. +6
    -2
      source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxy.java
  3. +52
    -1
      source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxyBuilder.java
  4. +41
    -5
      source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractType.java
  5. +5
    -5
      source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Contract.java
  6. +5
    -5
      source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java
  7. +3
    -3
      source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest2.java
  8. +2
    -4
      source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTestAll4Redis.java
  9. +39
    -0
      source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/contract/AssetContract.java
  10. BIN
      source/test/test-integration/src/test/resources/contract.jar

+ 35
- 35
source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java View File

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

}

+ 6
- 2
source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxy.java View File

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

}

+ 52
- 1
source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxyBuilder.java View File

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

+ 41
- 5
source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractType.java View File

@@ -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 +
'}';
}
}

+ 5
- 5
source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Contract.java View File

@@ -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,


+ 5
- 5
source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java View File

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


+ 3
- 3
source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest2.java View File

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


+ 2
- 4
source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTestAll4Redis.java View File

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


+ 39
- 0
source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/contract/AssetContract.java View File

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

}

BIN
source/test/test-integration/src/test/resources/contract.jar View File


Loading…
Cancel
Save