| @@ -3,12 +3,14 @@ package com.jd.blockchain.contract.engine; | |||||
| import com.jd.blockchain.contract.ContractEventContext; | import com.jd.blockchain.contract.ContractEventContext; | ||||
| import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
| import java.util.concurrent.CompletableFuture; | |||||
| import java.util.concurrent.Future; | |||||
| public interface ContractCode { | public interface ContractCode { | ||||
| Bytes getAddress(); | Bytes getAddress(); | ||||
| long getVersion(); | long getVersion(); | ||||
| void processEvent(ContractEventContext eventContext); | |||||
| void processEvent(ContractEventContext eventContext, CompletableFuture<String> execReturn); | |||||
| } | } | ||||
| @@ -14,6 +14,8 @@ import org.springframework.util.ReflectionUtils; | |||||
| import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
| import java.util.List; | import java.util.List; | ||||
| import java.util.concurrent.CompletableFuture; | |||||
| import java.util.concurrent.Future; | |||||
| /** | /** | ||||
| * contract code based jvm | * contract code based jvm | ||||
| @@ -46,9 +48,9 @@ public class JavaContractCode implements ContractCode { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public void processEvent(ContractEventContext eventContext) { | |||||
| public void processEvent(ContractEventContext eventContext, CompletableFuture<String> execReturn) { | |||||
| this.contractEventContext = eventContext; | this.contractEventContext = eventContext; | ||||
| codeModule.execute(new ContractExecution()); | |||||
| codeModule.execute(new ContractExecution(execReturn)); | |||||
| } | } | ||||
| private Object resolveArgs(byte[] args, List<DataContract> dataContractList) { | private Object resolveArgs(byte[] args, List<DataContract> dataContractList) { | ||||
| @@ -59,6 +61,13 @@ public class JavaContractCode implements ContractCode { | |||||
| } | } | ||||
| class ContractExecution implements Runnable { | class ContractExecution implements Runnable { | ||||
| private CompletableFuture<String> contractReturn; | |||||
| public ContractExecution(CompletableFuture<String> contractReturn) { | |||||
| this.contractReturn = contractReturn; | |||||
| } | |||||
| public void run() { | public void run() { | ||||
| LOGGER.info("ContractThread execute()."); | LOGGER.info("ContractThread execute()."); | ||||
| try { | try { | ||||
| @@ -66,12 +75,16 @@ public class JavaContractCode implements ContractCode { | |||||
| long startTime = System.currentTimeMillis(); | long startTime = System.currentTimeMillis(); | ||||
| String contractClassName = codeModule.getMainClass(); | String contractClassName = codeModule.getMainClass(); | ||||
| Class myClass = codeModule.loadClass(contractClassName); | Class myClass = codeModule.loadClass(contractClassName); | ||||
| Object contractMainClassObj = myClass.newInstance();// 合约主类生成的类实例; | Object contractMainClassObj = myClass.newInstance();// 合约主类生成的类实例; | ||||
| Method beforeMth_ = myClass.getMethod("beforeEvent", | Method beforeMth_ = myClass.getMethod("beforeEvent", | ||||
| codeModule.loadClass(ContractEventContext.class.getName())); | codeModule.loadClass(ContractEventContext.class.getName())); | ||||
| 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()); | ||||
| @@ -79,25 +92,38 @@ public class JavaContractCode implements ContractCode { | |||||
| // 反序列化参数; | // 反序列化参数; | ||||
| contractType = ContractType.resolve(myClass); | contractType = ContractType.resolve(myClass); | ||||
| Method handleMethod = contractType.getHandleMethod(contractEventContext.getEvent()); | Method handleMethod = contractType.getHandleMethod(contractEventContext.getEvent()); | ||||
| if (handleMethod == null){ | if (handleMethod == null){ | ||||
| throw new IllegalDataException("don't get this method by it's @ContractEvent."); | throw new IllegalDataException("don't get this method by it's @ContractEvent."); | ||||
| } | } | ||||
| Object args = resolveArgs(contractEventContext.getArgs(), | Object args = resolveArgs(contractEventContext.getArgs(), | ||||
| contractType.getDataContractMap().get(handleMethod)); | contractType.getDataContractMap().get(handleMethod)); | ||||
| Object[] params = null; | Object[] params = null; | ||||
| if(args.getClass().isArray()){ | if(args.getClass().isArray()){ | ||||
| params = (Object[])args; | params = (Object[])args; | ||||
| } | } | ||||
| ReflectionUtils.invokeMethod(handleMethod, contractMainClassObj, params); | |||||
| String contractReturn = ReflectionUtils.invokeMethod(handleMethod, contractMainClassObj, params).toString(); | |||||
| LOGGER.info("合约执行,耗时:" + (System.currentTimeMillis() - startTime)); | LOGGER.info("合约执行,耗时:" + (System.currentTimeMillis() - startTime)); | ||||
| Method mth2 = myClass.getMethod("postEvent"); | Method mth2 = myClass.getMethod("postEvent"); | ||||
| startTime = System.currentTimeMillis(); | startTime = System.currentTimeMillis(); | ||||
| ReflectionUtils.invokeMethod(mth2, contractMainClassObj); | ReflectionUtils.invokeMethod(mth2, contractMainClassObj); | ||||
| LOGGER.info("postEvent,耗时:" + (System.currentTimeMillis() - startTime)); | LOGGER.info("postEvent,耗时:" + (System.currentTimeMillis() - startTime)); | ||||
| // 填充return结果 | |||||
| if (this.contractReturn != null) { | |||||
| this.contractReturn.complete(contractReturn); | |||||
| } | |||||
| } catch (NoSuchMethodException e) { | } catch (NoSuchMethodException e) { | ||||
| throw new IllegalArgumentException(e.getMessage()); | throw new IllegalArgumentException(e.getMessage()); | ||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| @@ -0,0 +1,62 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <parent> | |||||
| <artifactId>contract</artifactId> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <version>0.9.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <artifactId>contract-samples</artifactId> | |||||
| <name>contract-samples</name> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>ledger-model</artifactId> | |||||
| <version>${project.version}</version> | |||||
| <scope>provided</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>crypto-framework</artifactId> | |||||
| <version>${project.version}</version> | |||||
| <scope>provided</scope> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <build> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <artifactId>maven-assembly-plugin</artifactId> | |||||
| <configuration> | |||||
| <finalName>contract</finalName> | |||||
| <appendAssemblyId>false</appendAssemblyId> | |||||
| <archive> | |||||
| <manifest> | |||||
| <mainClass>com.jd.blockchain.contract.ReadContractImpl</mainClass> | |||||
| </manifest> | |||||
| </archive> | |||||
| <descriptorRefs> | |||||
| <descriptorRef>jar-with-dependencies</descriptorRef> | |||||
| </descriptorRefs> | |||||
| </configuration> | |||||
| <executions> | |||||
| <execution> | |||||
| <id>make-assembly</id> | |||||
| <phase>package</phase> | |||||
| <goals> | |||||
| <goal>single</goal> | |||||
| </goals> | |||||
| </execution> | |||||
| </executions> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| </project> | |||||
| @@ -0,0 +1,10 @@ | |||||
| package com.jd.blockchain.contract; | |||||
| @Contract | |||||
| public interface ReadContract { | |||||
| @ContractEvent(name = "read-key") | |||||
| String read(String address, String key); | |||||
| } | |||||
| @@ -0,0 +1,43 @@ | |||||
| package com.jd.blockchain.contract; | |||||
| import com.jd.blockchain.crypto.HashDigest; | |||||
| import com.jd.blockchain.ledger.KVDataEntry; | |||||
| @Contract | |||||
| public class ReadContractImpl implements EventProcessingAwire, ReadContract { | |||||
| private ContractEventContext eventContext; | |||||
| @Override | |||||
| public void beforeEvent(ContractEventContext eventContext) { | |||||
| this.eventContext = eventContext; | |||||
| } | |||||
| @Override | |||||
| public void postEvent(ContractEventContext eventContext, ContractException error) { | |||||
| } | |||||
| @Override | |||||
| public void postEvent(ContractException error) { | |||||
| } | |||||
| @Override | |||||
| public void postEvent() { | |||||
| } | |||||
| @Override | |||||
| @ContractEvent(name = "read-key") | |||||
| public String read(String address, String key) { | |||||
| HashDigest ledgerHash = eventContext.getCurrentLedgerHash(); | |||||
| KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, key); | |||||
| if (kvDataEntries != null && kvDataEntries.length == 1) { | |||||
| return kvDataEntries[0].getValue().toString(); | |||||
| } | |||||
| return null; | |||||
| } | |||||
| } | |||||
| @@ -13,6 +13,7 @@ | |||||
| <module>contract-framework</module> | <module>contract-framework</module> | ||||
| <module>contract-jvm</module> | <module>contract-jvm</module> | ||||
| <module>contract-maven-plugin</module> | <module>contract-maven-plugin</module> | ||||
| </modules> | |||||
| <module>contract-samples</module> | |||||
| </modules> | |||||
| </project> | </project> | ||||
| @@ -3,7 +3,9 @@ package com.jd.blockchain.ledger.core.impl; | |||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.Iterator; | import java.util.Iterator; | ||||
| import java.util.List; | import java.util.List; | ||||
| import java.util.concurrent.CompletableFuture; | |||||
| import com.jd.blockchain.ledger.core.impl.handles.ContractEventSendOperationHandle; | |||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
| @@ -74,6 +76,7 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
| // 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | // 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | ||||
| LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | ||||
| TransactionState result; | TransactionState result; | ||||
| List<CompletableFuture<String>> contractReturn = new ArrayList<>(); | |||||
| try { | try { | ||||
| LedgerDataSet dataset = txCtx.getDataSet(); | LedgerDataSet dataset = txCtx.getDataSet(); | ||||
| TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | ||||
| @@ -102,7 +105,14 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
| OperationHandle opHandle; | OperationHandle opHandle; | ||||
| for (Operation op : ops) { | for (Operation op : ops) { | ||||
| opHandle = opHandles.getHandle(op.getClass()); | opHandle = opHandles.getHandle(op.getClass()); | ||||
| opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); | |||||
| // 合约执行需要填充执行结果 | |||||
| if (opHandle instanceof ContractEventSendOperationHandle) { | |||||
| CompletableFuture<String> currContractReturn = new CompletableFuture<>(); | |||||
| contractReturn.add(currContractReturn); | |||||
| ((ContractEventSendOperationHandle) opHandle).process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService, currContractReturn); | |||||
| } else { | |||||
| opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); | |||||
| } | |||||
| } | } | ||||
| // 提交交易(事务); | // 提交交易(事务); | ||||
| @@ -120,8 +130,21 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
| LOGGER.warn(String.format("Transaction rollback caused by the system exception! --[TxHash=%s] --%s", | LOGGER.warn(String.format("Transaction rollback caused by the system exception! --[TxHash=%s] --%s", | ||||
| request.getHash().toBase58(), e.getMessage()), e); | request.getHash().toBase58(), e.getMessage()), e); | ||||
| } | } | ||||
| TxResponseHandle resp = new TxResponseHandle(request, result); | TxResponseHandle resp = new TxResponseHandle(request, result); | ||||
| if (!contractReturn.isEmpty()) { | |||||
| // 获取结果中的字符串 | |||||
| String[] returnValue = new String[contractReturn.size()]; | |||||
| try { | |||||
| for (int i = 0; i < contractReturn.size(); i++) { | |||||
| returnValue[i] = contractReturn.get(i).get(); | |||||
| } | |||||
| resp.setContractReturn(returnValue); | |||||
| } catch (Exception e) { | |||||
| throw new IllegalStateException(e); | |||||
| } | |||||
| } | |||||
| responseList.add(resp); | responseList.add(resp); | ||||
| return resp; | return resp; | ||||
| @@ -201,6 +224,8 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
| private TransactionState result; | private TransactionState result; | ||||
| private String[] contractReturn; | |||||
| public TxResponseHandle(TransactionRequest request, TransactionState result) { | public TxResponseHandle(TransactionRequest request, TransactionState result) { | ||||
| this.request = request; | this.request = request; | ||||
| this.result = result; | this.result = result; | ||||
| @@ -231,6 +256,14 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
| return globalResult == null ? result == TransactionState.SUCCESS : globalResult == TransactionState.SUCCESS; | return globalResult == null ? result == TransactionState.SUCCESS : globalResult == TransactionState.SUCCESS; | ||||
| } | } | ||||
| @Override | |||||
| public String[] getContractReturn() { | |||||
| return contractReturn; | |||||
| } | |||||
| public void setContractReturn(String[] contractReturn) { | |||||
| this.contractReturn = contractReturn; | |||||
| } | |||||
| } | } | ||||
| private class TransactionBatchResultImpl implements TransactionBatchResult { | private class TransactionBatchResultImpl implements TransactionBatchResult { | ||||
| @@ -20,6 +20,9 @@ import com.jd.blockchain.ledger.core.TransactionRequestContext; | |||||
| import com.jd.blockchain.ledger.core.impl.LedgerQueryService; | import com.jd.blockchain.ledger.core.impl.LedgerQueryService; | ||||
| import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
| import java.util.concurrent.CompletableFuture; | |||||
| import java.util.concurrent.Future; | |||||
| @Service | @Service | ||||
| public class ContractEventSendOperationHandle implements OperationHandle { | public class ContractEventSendOperationHandle implements OperationHandle { | ||||
| @@ -32,6 +35,18 @@ public class ContractEventSendOperationHandle implements OperationHandle { | |||||
| @Override | @Override | ||||
| public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | ||||
| LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, LedgerService ledgerService) { | LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, LedgerService ledgerService) { | ||||
| process(op, dataset, requestContext, previousBlockDataset, opHandleContext, ledgerService, null); | |||||
| } | |||||
| @Override | |||||
| public boolean support(Class<?> operationType) { | |||||
| return ContractEventSendOperation.class.isAssignableFrom(operationType); | |||||
| } | |||||
| public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
| LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, | |||||
| LedgerService ledgerService, CompletableFuture<String> contractReturn) { | |||||
| ContractEventSendOperation contractOP = (ContractEventSendOperation) op; | ContractEventSendOperation contractOP = (ContractEventSendOperation) op; | ||||
| // 先从账本校验合约的有效性; | // 先从账本校验合约的有效性; | ||||
| // 注意:必须在前一个区块的数据集中进行校验,因为那是经过共识的数据;从当前新区块链数据集校验则会带来攻击风险:未经共识的合约得到执行; | // 注意:必须在前一个区块的数据集中进行校验,因为那是经过共识的数据;从当前新区块链数据集校验则会带来攻击风险:未经共识的合约得到执行; | ||||
| @@ -66,12 +81,8 @@ public class ContractEventSendOperationHandle implements OperationHandle { | |||||
| } | } | ||||
| // 处理合约事件; | // 处理合约事件; | ||||
| contractCode.processEvent(localContractEventContext); | |||||
| contractCode.processEvent(localContractEventContext, contractReturn); | |||||
| } | } | ||||
| @Override | |||||
| public boolean support(Class<?> operationType) { | |||||
| return ContractEventSendOperation.class.isAssignableFrom(operationType); | |||||
| } | |||||
| } | } | ||||
| @@ -27,6 +27,8 @@ public class TransactionRespHandle implements TransactionResponse { | |||||
| private TransactionState globalResult; | private TransactionState globalResult; | ||||
| private String[] contractReturn; | |||||
| public TransactionRespHandle(TransactionRequest request, TransactionState result, TransactionState globalResult) { | public TransactionRespHandle(TransactionRequest request, TransactionState result, TransactionState globalResult) { | ||||
| this.request = request; | this.request = request; | ||||
| this.result = result; | this.result = result; | ||||
| @@ -49,6 +51,10 @@ public class TransactionRespHandle implements TransactionResponse { | |||||
| this.result = result; | this.result = result; | ||||
| } | } | ||||
| public void setContractReturn(String[] contractReturn) { | |||||
| this.contractReturn = contractReturn; | |||||
| } | |||||
| public LedgerBlock getBlock() { | public LedgerBlock getBlock() { | ||||
| return block; | return block; | ||||
| } | } | ||||
| @@ -89,4 +95,9 @@ public class TransactionRespHandle implements TransactionResponse { | |||||
| public boolean isSuccess() { | public boolean isSuccess() { | ||||
| return globalResult == null ? result == TransactionState.SUCCESS : globalResult == TransactionState.SUCCESS; | return globalResult == null ? result == TransactionState.SUCCESS : globalResult == TransactionState.SUCCESS; | ||||
| } | } | ||||
| @Override | |||||
| public String[] getContractReturn() { | |||||
| return contractReturn; | |||||
| } | |||||
| } | } | ||||
| @@ -49,8 +49,20 @@ public interface TransactionResponse { | |||||
| */ | */ | ||||
| @DataField(order=4, primitiveType=PrimitiveType.INT64) | @DataField(order=4, primitiveType=PrimitiveType.INT64) | ||||
| long getBlockHeight(); | long getBlockHeight(); | ||||
| /** | |||||
| * 交易是否执行成功 | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| @DataField(order=5, primitiveType=PrimitiveType.BOOLEAN) | @DataField(order=5, primitiveType=PrimitiveType.BOOLEAN) | ||||
| boolean isSuccess(); | boolean isSuccess(); | ||||
| /** | |||||
| * 合约返回值 | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| @DataField(order=6, list=true, primitiveType=PrimitiveType.TEXT) | |||||
| String[] getContractReturn(); | |||||
| } | } | ||||
| @@ -17,6 +17,8 @@ public class TxResponseMessage implements TransactionResponse { | |||||
| private long blockHeight; | private long blockHeight; | ||||
| private TransactionState executionState; | private TransactionState executionState; | ||||
| private String[] contractReturn; | |||||
| public TxResponseMessage() { | public TxResponseMessage() { | ||||
| } | } | ||||
| @@ -39,9 +41,6 @@ public class TxResponseMessage implements TransactionResponse { | |||||
| this.executionState = executionState; | this.executionState = executionState; | ||||
| } | } | ||||
| /* (non-Javadoc) | |||||
| * @see com.jd.blockchain.ledger.TransactionResponse#getBlockHash() | |||||
| */ | |||||
| @Override | @Override | ||||
| public HashDigest getBlockHash() { | public HashDigest getBlockHash() { | ||||
| return blockHash; | return blockHash; | ||||
| @@ -55,13 +54,23 @@ public class TxResponseMessage implements TransactionResponse { | |||||
| public long getBlockHeight() { | public long getBlockHeight() { | ||||
| return blockHeight; | return blockHeight; | ||||
| } | } | ||||
| public void setBlockHeight(long blockHeight) { | public void setBlockHeight(long blockHeight) { | ||||
| this.blockHeight = blockHeight; | this.blockHeight = blockHeight; | ||||
| } | } | ||||
| public void setContractReturn(String[] contractReturn) { | |||||
| this.contractReturn = contractReturn; | |||||
| } | |||||
| @Override | @Override | ||||
| public boolean isSuccess() { | public boolean isSuccess() { | ||||
| return blockHash != null & executionState == TransactionState.SUCCESS; | return blockHash != null & executionState == TransactionState.SUCCESS; | ||||
| } | } | ||||
| @Override | |||||
| public String[] getContractReturn() { | |||||
| return contractReturn; | |||||
| } | |||||
| } | } | ||||
| @@ -307,6 +307,11 @@ public class ConsensusMessageDispatcher implements MessageHandle { | |||||
| public boolean isSuccess() { | public boolean isSuccess() { | ||||
| return this.txResp.isSuccess(); | return this.txResp.isSuccess(); | ||||
| } | } | ||||
| @Override | |||||
| public String[] getContractReturn() { | |||||
| return txResp.getContractReturn(); | |||||
| } | |||||
| } | } | ||||
| private final class BlockStateSnapshot implements StateSnapshot { | private final class BlockStateSnapshot implements StateSnapshot { | ||||
| @@ -50,15 +50,12 @@ public abstract class AbstractModule implements Module { | |||||
| if (origClassLoader != moduleClassLoader) { | if (origClassLoader != moduleClassLoader) { | ||||
| Thread.currentThread().setContextClassLoader(moduleClassLoader); | Thread.currentThread().setContextClassLoader(moduleClassLoader); | ||||
| } | } | ||||
| return CompletableAsyncFuture.runAsync(new Runnable() { | |||||
| @Override | |||||
| public void run() { | |||||
| try { | |||||
| runnable.run(); | |||||
| } finally { | |||||
| if (origClassLoader != Thread.currentThread().getContextClassLoader()) { | |||||
| Thread.currentThread().setContextClassLoader(origClassLoader); | |||||
| } | |||||
| return CompletableAsyncFuture.runAsync(() -> { | |||||
| try { | |||||
| runnable.run(); | |||||
| } finally { | |||||
| if (origClassLoader != Thread.currentThread().getContextClassLoader()) { | |||||
| Thread.currentThread().setContextClassLoader(origClassLoader); | |||||
| } | } | ||||
| } | } | ||||
| }); | }); | ||||
| @@ -40,6 +40,13 @@ | |||||
| <artifactId>sdk-client</artifactId> | <artifactId>sdk-client</artifactId> | ||||
| <version>${project.version}</version> | <version>${project.version}</version> | ||||
| </dependency> | </dependency> | ||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>contract-samples</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | <dependency> | ||||
| <groupId>io.nats</groupId> | <groupId>io.nats</groupId> | ||||
| <artifactId>jnats</artifactId> | <artifactId>jnats</artifactId> | ||||
| @@ -9,6 +9,7 @@ | |||||
| package test.com.jd.blockchain.intgr; | package test.com.jd.blockchain.intgr; | ||||
| import com.jd.blockchain.binaryproto.DataContractRegistry; | import com.jd.blockchain.binaryproto.DataContractRegistry; | ||||
| import com.jd.blockchain.contract.ReadContract; | |||||
| import com.jd.blockchain.crypto.AddressEncoding; | import com.jd.blockchain.crypto.AddressEncoding; | ||||
| import com.jd.blockchain.crypto.AsymmetricKeypair; | import com.jd.blockchain.crypto.AsymmetricKeypair; | ||||
| import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
| @@ -441,7 +442,7 @@ public class IntegrationBase { | |||||
| static BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | static BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | ||||
| // 保存资产总数的键; | // 保存资产总数的键; | ||||
| // 第二个参数; | // 第二个参数; | ||||
| private static String contractZipName = "contract.jar"; | |||||
| private static String contractZipName = "contract-read.jar"; | |||||
| static HashDigest txContentHash; | static HashDigest txContentHash; | ||||
| public static LedgerBlock testSDK_Contract(AsymmetricKeypair adminKey, HashDigest ledgerHash, | public static LedgerBlock testSDK_Contract(AsymmetricKeypair adminKey, HashDigest ledgerHash, | ||||
| BlockchainService blockchainService,LedgerRepository ledgerRepository) { | BlockchainService blockchainService,LedgerRepository ledgerRepository) { | ||||
| @@ -473,9 +474,11 @@ public class IntegrationBase { | |||||
| .getChainCode(); | .getChainCode(); | ||||
| assertArrayEquals(contractCode, contractCodeInDb); | assertArrayEquals(contractCode, contractCodeInDb); | ||||
| testExeReadContract(adminKey, ledgerHash, blockchainService); | |||||
| // execute the contract; | // execute the contract; | ||||
| testContractExe(adminKey, ledgerHash, keyPairResponse.keyPair, blockchainService, ledgerRepository); | |||||
| testContractExe1(adminKey, ledgerHash, keyPairResponse.keyPair, blockchainService, ledgerRepository); | |||||
| // testContractExe(adminKey, ledgerHash, keyPairResponse.keyPair, blockchainService, ledgerRepository); | |||||
| // testContractExe1(adminKey, ledgerHash, keyPairResponse.keyPair, blockchainService, ledgerRepository); | |||||
| return block; | return block; | ||||
| } | } | ||||
| @@ -509,7 +512,7 @@ public class IntegrationBase { | |||||
| assertEquals("100",kvDataEntries[0].getValue().toString()); | assertEquals("100",kvDataEntries[0].getValue().toString()); | ||||
| } | } | ||||
| private static <T> void testContractExe1(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair dataKey, | |||||
| private static <T> void testContractExe1(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair dataKey, | |||||
| BlockchainService blockchainService,LedgerRepository ledgerRepository) { | BlockchainService blockchainService,LedgerRepository ledgerRepository) { | ||||
| 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); | ||||
| @@ -537,6 +540,68 @@ public class IntegrationBase { | |||||
| assertEquals(888L,kvDataEntries[1].getValue()); | assertEquals(888L,kvDataEntries[1].getValue()); | ||||
| } | } | ||||
| private static <T> void testExeReadContract(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainService blockchainService) { | |||||
| // 首先注册一个数据账户 | |||||
| BlockchainKeypair newDataAccount = BlockchainKeyGenerator.getInstance().generate(); | |||||
| TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||||
| txTpl.dataAccounts().register(newDataAccount.getIdentity()); | |||||
| PreparedTransaction ptx = txTpl.prepare(); | |||||
| ptx.sign(adminKey); | |||||
| // 提交并等待共识返回; | |||||
| ptx.commit(); | |||||
| // 再提交一个KV写入 | |||||
| String key1 = "JingDong", value1 = "www.jd.com"; | |||||
| String key2 = "JD", value2 = "JingDong"; | |||||
| TransactionTemplate txKv = blockchainService.newTransaction(ledgerHash); | |||||
| txKv.dataAccount(newDataAccount.getAddress()).set(key1, value1, -1).set(key2, value2, -1); | |||||
| PreparedTransaction kvPtx = txKv.prepare(); | |||||
| kvPtx.sign(adminKey); | |||||
| // 提交并等待共识返回; | |||||
| kvPtx.commit(); | |||||
| // 下面才是执行Read交易 | |||||
| // 定义交易; | |||||
| TransactionTemplate txContract = blockchainService.newTransaction(ledgerHash); | |||||
| ReadContract readContract1 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | |||||
| readContract1.read(newDataAccount.getAddress().toBase58(), key1); | |||||
| ReadContract readContract2 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | |||||
| readContract2.read(newDataAccount.getAddress().toBase58(), key2); | |||||
| // 签名; | |||||
| PreparedTransaction contractPtx = txContract.prepare(); | |||||
| contractPtx.sign(adminKey); | |||||
| // 提交并等待共识返回; | |||||
| TransactionResponse readTxResp = contractPtx.commit(); | |||||
| String[] contractReturn = readTxResp.getContractReturn(); | |||||
| // 打印结果 | |||||
| for (String cr : contractReturn) { | |||||
| System.out.printf("----- Return Value = [%s] ----- \r\n", cr); | |||||
| } | |||||
| // 验证结果 | |||||
| assertNotNull(contractReturn); | |||||
| assertEquals(contractReturn.length, 2); | |||||
| String returnVal1 = contractReturn[0]; | |||||
| assertEquals(value1, returnVal1); | |||||
| String returnVal2 = contractReturn[1]; | |||||
| assertEquals(value2, returnVal2); | |||||
| } | |||||
| /** | /** | ||||
| * 根据合约构建字节数组; | * 根据合约构建字节数组; | ||||
| * | * | ||||
| @@ -15,9 +15,11 @@ import com.jd.blockchain.tools.keygen.KeyGenCommand; | |||||
| import com.jd.blockchain.utils.concurrent.ThreadInvoker; | import com.jd.blockchain.utils.concurrent.ThreadInvoker; | ||||
| import org.junit.Test; | import org.junit.Test; | ||||
| import org.springframework.core.io.ClassPathResource; | |||||
| import test.com.jd.blockchain.intgr.initializer.LedgerInitializeTest; | import test.com.jd.blockchain.intgr.initializer.LedgerInitializeTest; | ||||
| import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4Nodes; | import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4Nodes; | ||||
| import java.io.File; | |||||
| import java.util.concurrent.CountDownLatch; | import java.util.concurrent.CountDownLatch; | ||||
| import java.util.concurrent.ExecutorService; | import java.util.concurrent.ExecutorService; | ||||
| import java.util.concurrent.Executors; | import java.util.concurrent.Executors; | ||||
| @@ -85,6 +87,20 @@ public class IntegrationTest4Contract { | |||||
| BlockchainService blockchainService = gwsrvFact.getBlockchainService(); | BlockchainService blockchainService = gwsrvFact.getBlockchainService(); | ||||
| if(isContractDeployAndExe){ | if(isContractDeployAndExe){ | ||||
| // 合约测试需要runtime路径 | |||||
| try { | |||||
| ClassPathResource contractPath = new ClassPathResource(""); | |||||
| File file = new File(contractPath.getURI()); | |||||
| String runTimePath = file.getParentFile().getParent() + File.separator + "runtime"; | |||||
| File runTime = new File(runTimePath); | |||||
| if (!runTime.exists()) { | |||||
| runTime.mkdir(); | |||||
| } | |||||
| } catch (Exception e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| IntegrationBase.testSDK_Contract(adminKey,ledgerHash,blockchainService,ledgerRepository); | IntegrationBase.testSDK_Contract(adminKey,ledgerHash,blockchainService,ledgerRepository); | ||||
| } | } | ||||