From cffab08fa9032d49d024f0f9aa1e8249b82166c5 Mon Sep 17 00:00:00 2001 From: shaozhuguang Date: Mon, 10 Jun 2019 16:38:32 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=90=88=E7=BA=A6?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=80=BC=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contract/engine/ContractCode.java | 6 +- .../contract/jvm/JavaContractCode.java | 32 +++++++- source/contract/contract-samples/pom.xml | 62 +++++++++++++++ .../jd/blockchain/contract/ReadContract.java | 10 +++ .../blockchain/contract/ReadContractImpl.java | 43 +++++++++++ source/contract/pom.xml | 3 +- .../core/impl/TransactionBatchProcessor.java | 37 ++++++++- .../ContractEventSendOperationHandle.java | 21 +++-- .../ledger/TransactionRespHandle.java | 11 +++ .../ledger/TransactionResponse.java | 14 +++- .../transaction/TxResponseMessage.java | 17 +++- .../consensus/ConsensusMessageDispatcher.java | 5 ++ .../jd/blockchain/runtime/AbstractModule.java | 15 ++-- source/test/test-integration/pom.xml | 7 ++ .../jd/blockchain/intgr/IntegrationBase.java | 73 +++++++++++++++++- .../intgr/IntegrationTest4Contract.java | 16 ++++ .../src/test/resources/contract-read.jar | Bin 0 -> 3924 bytes 17 files changed, 341 insertions(+), 31 deletions(-) create mode 100644 source/contract/contract-samples/pom.xml create mode 100644 source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContract.java create mode 100644 source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContractImpl.java create mode 100644 source/test/test-integration/src/test/resources/contract-read.jar diff --git a/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/engine/ContractCode.java b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/engine/ContractCode.java index 891acd4c..94ded100 100644 --- a/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/engine/ContractCode.java +++ b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/engine/ContractCode.java @@ -3,12 +3,14 @@ package com.jd.blockchain.contract.engine; import com.jd.blockchain.contract.ContractEventContext; import com.jd.blockchain.utils.Bytes; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; + public interface ContractCode { Bytes getAddress(); long getVersion(); - void processEvent(ContractEventContext eventContext); - + void processEvent(ContractEventContext eventContext, CompletableFuture execReturn); } diff --git a/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java index 6bf5e859..4dcab78f 100644 --- a/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java +++ b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java @@ -14,6 +14,8 @@ import org.springframework.util.ReflectionUtils; import java.lang.reflect.Method; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; /** * contract code based jvm @@ -46,9 +48,9 @@ public class JavaContractCode implements ContractCode { } @Override - public void processEvent(ContractEventContext eventContext) { + public void processEvent(ContractEventContext eventContext, CompletableFuture execReturn) { this.contractEventContext = eventContext; - codeModule.execute(new ContractExecution()); + codeModule.execute(new ContractExecution(execReturn)); } private Object resolveArgs(byte[] args, List dataContractList) { @@ -59,6 +61,13 @@ public class JavaContractCode implements ContractCode { } class ContractExecution implements Runnable { + + private CompletableFuture contractReturn; + + public ContractExecution(CompletableFuture contractReturn) { + this.contractReturn = contractReturn; + } + public void run() { LOGGER.info("ContractThread execute()."); try { @@ -66,12 +75,16 @@ public class JavaContractCode implements ContractCode { long startTime = System.currentTimeMillis(); String contractClassName = codeModule.getMainClass(); + Class myClass = codeModule.loadClass(contractClassName); + Object contractMainClassObj = myClass.newInstance();// 合约主类生成的类实例; Method beforeMth_ = myClass.getMethod("beforeEvent", codeModule.loadClass(ContractEventContext.class.getName())); + ReflectionUtils.invokeMethod(beforeMth_, contractMainClassObj, contractEventContext); + LOGGER.info("beforeEvent,耗时:" + (System.currentTimeMillis() - startTime)); // Method eventMethod = this.getMethodByAnno(contractMainClassObj, contractEventContext.getEvent()); @@ -79,25 +92,38 @@ public class JavaContractCode implements ContractCode { // 反序列化参数; contractType = ContractType.resolve(myClass); + Method handleMethod = contractType.getHandleMethod(contractEventContext.getEvent()); + if (handleMethod == null){ throw new IllegalDataException("don't get this method by it's @ContractEvent."); } + Object args = resolveArgs(contractEventContext.getArgs(), contractType.getDataContractMap().get(handleMethod)); Object[] params = null; + if(args.getClass().isArray()){ params = (Object[])args; } - ReflectionUtils.invokeMethod(handleMethod, contractMainClassObj, params); + + String contractReturn = ReflectionUtils.invokeMethod(handleMethod, contractMainClassObj, params).toString(); LOGGER.info("合约执行,耗时:" + (System.currentTimeMillis() - startTime)); Method mth2 = myClass.getMethod("postEvent"); + startTime = System.currentTimeMillis(); + ReflectionUtils.invokeMethod(mth2, contractMainClassObj); + LOGGER.info("postEvent,耗时:" + (System.currentTimeMillis() - startTime)); + + // 填充return结果 + if (this.contractReturn != null) { + this.contractReturn.complete(contractReturn); + } } catch (NoSuchMethodException e) { throw new IllegalArgumentException(e.getMessage()); } catch (Exception e) { diff --git a/source/contract/contract-samples/pom.xml b/source/contract/contract-samples/pom.xml new file mode 100644 index 00000000..b229b7bc --- /dev/null +++ b/source/contract/contract-samples/pom.xml @@ -0,0 +1,62 @@ + + + + + contract + com.jd.blockchain + 0.9.0-SNAPSHOT + + 4.0.0 + + contract-samples + + contract-samples + + + + com.jd.blockchain + ledger-model + ${project.version} + provided + + + + com.jd.blockchain + crypto-framework + ${project.version} + provided + + + + + + + maven-assembly-plugin + + contract + false + + + com.jd.blockchain.contract.ReadContractImpl + + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + + + + diff --git a/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContract.java b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContract.java new file mode 100644 index 00000000..1f82491f --- /dev/null +++ b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContract.java @@ -0,0 +1,10 @@ +package com.jd.blockchain.contract; + + +@Contract +public interface ReadContract { + + @ContractEvent(name = "read-key") + String read(String address, String key); +} + diff --git a/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContractImpl.java b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContractImpl.java new file mode 100644 index 00000000..65310b6e --- /dev/null +++ b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContractImpl.java @@ -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; + } +} diff --git a/source/contract/pom.xml b/source/contract/pom.xml index 835b06aa..80283489 100644 --- a/source/contract/pom.xml +++ b/source/contract/pom.xml @@ -13,6 +13,7 @@ contract-framework contract-jvm contract-maven-plugin - + contract-samples + \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionBatchProcessor.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionBatchProcessor.java index 7a0670b8..0e58fba7 100644 --- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionBatchProcessor.java +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionBatchProcessor.java @@ -3,7 +3,9 @@ package com.jd.blockchain.ledger.core.impl; import java.util.ArrayList; import java.util.Iterator; 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.LoggerFactory; @@ -74,6 +76,7 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { // 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); TransactionState result; + List> contractReturn = new ArrayList<>(); try { LedgerDataSet dataset = txCtx.getDataSet(); TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); @@ -102,7 +105,14 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { OperationHandle opHandle; for (Operation op : ops) { opHandle = opHandles.getHandle(op.getClass()); - opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); + // 合约执行需要填充执行结果 + if (opHandle instanceof ContractEventSendOperationHandle) { + CompletableFuture 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", request.getHash().toBase58(), e.getMessage()), e); } - 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); return resp; @@ -201,6 +224,8 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { private TransactionState result; + private String[] contractReturn; + public TxResponseHandle(TransactionRequest request, TransactionState result) { this.request = request; this.result = result; @@ -231,6 +256,14 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { 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 { diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/ContractEventSendOperationHandle.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/ContractEventSendOperationHandle.java index e1930a35..1ab90547 100644 --- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/ContractEventSendOperationHandle.java +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/ContractEventSendOperationHandle.java @@ -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.OperationHandleContext; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; + @Service public class ContractEventSendOperationHandle implements OperationHandle { @@ -32,6 +35,18 @@ public class ContractEventSendOperationHandle implements OperationHandle { @Override public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, 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 contractReturn) { + 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); - } } diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRespHandle.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRespHandle.java index 2a67b606..8695ab7e 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRespHandle.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRespHandle.java @@ -27,6 +27,8 @@ public class TransactionRespHandle implements TransactionResponse { private TransactionState globalResult; + private String[] contractReturn; + public TransactionRespHandle(TransactionRequest request, TransactionState result, TransactionState globalResult) { this.request = request; this.result = result; @@ -49,6 +51,10 @@ public class TransactionRespHandle implements TransactionResponse { this.result = result; } + public void setContractReturn(String[] contractReturn) { + this.contractReturn = contractReturn; + } + public LedgerBlock getBlock() { return block; } @@ -89,4 +95,9 @@ public class TransactionRespHandle implements TransactionResponse { public boolean isSuccess() { return globalResult == null ? result == TransactionState.SUCCESS : globalResult == TransactionState.SUCCESS; } + + @Override + public String[] getContractReturn() { + return contractReturn; + } } \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionResponse.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionResponse.java index f83a8961..e7aa1197 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionResponse.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionResponse.java @@ -49,8 +49,20 @@ public interface TransactionResponse { */ @DataField(order=4, primitiveType=PrimitiveType.INT64) long getBlockHeight(); - + + /** + * 交易是否执行成功 + * + * @return + */ @DataField(order=5, primitiveType=PrimitiveType.BOOLEAN) boolean isSuccess(); + /** + * 合约返回值 + * + * @return + */ + @DataField(order=6, list=true, primitiveType=PrimitiveType.TEXT) + String[] getContractReturn(); } \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxResponseMessage.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxResponseMessage.java index 8a912d21..5d143898 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxResponseMessage.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxResponseMessage.java @@ -17,6 +17,8 @@ public class TxResponseMessage implements TransactionResponse { private long blockHeight; private TransactionState executionState; + + private String[] contractReturn; public TxResponseMessage() { } @@ -39,9 +41,6 @@ public class TxResponseMessage implements TransactionResponse { this.executionState = executionState; } - /* (non-Javadoc) - * @see com.jd.blockchain.ledger.TransactionResponse#getBlockHash() - */ @Override public HashDigest getBlockHash() { return blockHash; @@ -55,13 +54,23 @@ public class TxResponseMessage implements TransactionResponse { public long getBlockHeight() { return blockHeight; } + public void setBlockHeight(long blockHeight) { this.blockHeight = blockHeight; } - + + public void setContractReturn(String[] contractReturn) { + this.contractReturn = contractReturn; + } + @Override public boolean isSuccess() { return blockHash != null & executionState == TransactionState.SUCCESS; } + @Override + public String[] getContractReturn() { + return contractReturn; + } + } diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusMessageDispatcher.java b/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusMessageDispatcher.java index 41cf14c6..ae62e2d6 100644 --- a/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusMessageDispatcher.java +++ b/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusMessageDispatcher.java @@ -307,6 +307,11 @@ public class ConsensusMessageDispatcher implements MessageHandle { public boolean isSuccess() { return this.txResp.isSuccess(); } + + @Override + public String[] getContractReturn() { + return txResp.getContractReturn(); + } } private final class BlockStateSnapshot implements StateSnapshot { diff --git a/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/AbstractModule.java b/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/AbstractModule.java index fbd2df3f..e7045d68 100644 --- a/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/AbstractModule.java +++ b/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/AbstractModule.java @@ -50,15 +50,12 @@ public abstract class AbstractModule implements Module { if (origClassLoader != 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); } } }); diff --git a/source/test/test-integration/pom.xml b/source/test/test-integration/pom.xml index 4901c7d4..380f82cc 100644 --- a/source/test/test-integration/pom.xml +++ b/source/test/test-integration/pom.xml @@ -40,6 +40,13 @@ sdk-client ${project.version} + + + com.jd.blockchain + contract-samples + ${project.version} + + io.nats jnats diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java index 3065da89..a06cc1e5 100644 --- a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java @@ -9,6 +9,7 @@ package test.com.jd.blockchain.intgr; import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.contract.ReadContract; import com.jd.blockchain.crypto.AddressEncoding; import com.jd.blockchain.crypto.AsymmetricKeypair; import com.jd.blockchain.crypto.HashDigest; @@ -441,7 +442,7 @@ public class IntegrationBase { static BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); // 保存资产总数的键; // 第二个参数; - private static String contractZipName = "contract.jar"; + private static String contractZipName = "contract-read.jar"; static HashDigest txContentHash; public static LedgerBlock testSDK_Contract(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainService blockchainService,LedgerRepository ledgerRepository) { @@ -473,9 +474,11 @@ public class IntegrationBase { .getChainCode(); assertArrayEquals(contractCode, contractCodeInDb); + testExeReadContract(adminKey, ledgerHash, blockchainService); + // 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; } @@ -509,7 +512,7 @@ public class IntegrationBase { assertEquals("100",kvDataEntries[0].getValue().toString()); } - private static void testContractExe1(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair dataKey, + private static void testContractExe1(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainKeypair dataKey, BlockchainService blockchainService,LedgerRepository ledgerRepository) { LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); @@ -537,6 +540,68 @@ public class IntegrationBase { assertEquals(888L,kvDataEntries[1].getValue()); } + private static 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); + } + /** * 根据合约构建字节数组; * diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest4Contract.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest4Contract.java index c0c29be7..b2067f62 100644 --- a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest4Contract.java +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest4Contract.java @@ -15,9 +15,11 @@ import com.jd.blockchain.tools.keygen.KeyGenCommand; import com.jd.blockchain.utils.concurrent.ThreadInvoker; 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.LedgerInitializeWeb4Nodes; +import java.io.File; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -85,6 +87,20 @@ public class IntegrationTest4Contract { BlockchainService blockchainService = gwsrvFact.getBlockchainService(); 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); } diff --git a/source/test/test-integration/src/test/resources/contract-read.jar b/source/test/test-integration/src/test/resources/contract-read.jar new file mode 100644 index 0000000000000000000000000000000000000000..38f81951da1fa98c3b3fe1a08f47642ea9afc7cb GIT binary patch literal 3924 zcmbW32|SeR7stmE6Oov*C0sGt#!eJs3^T^qg)C*?#xA=wiQ*C=d%~49b&F(QuWhog ziR_dnYd4j`{~Z!n%cpR$GswqgsJCYlnAT6Z~?3~ap~coCD^ssZVqL!boh=+JY|ZSPe&WyiU4IK_Cl5C>3y+@&4+6rkXXa?; zY4x*JIKOoUa)sGj!hUI!qQ4^F-m{>)nWGEV%6)V1TaC20)`CAzTHDIZ68@zEFbk}i zyL*Ht22CbH7f^V=+;Po5aviipCeC>93iObyUhI(yn?iWzRX$>=F2!57h?^Xa z*N}M?!gVb!ity;bFxB&Oi`3&Tk3700KVyxIdi^I;B4y4kD7i{XR%z+whCNRXPIMr| zF2N`qGs@OTz4$$~eDA9J()qsVeCa@_?G*|d_cNlFPXce4xOcUwo?MNrHelc2{N%__ zz2RSHq4hA785fmyDi+_4eS&-s`itLbS zy;0feiJePo&^!IhjbLm`;klkg=VgX&b#}N-Qsi3aMz8vUs;G#v)j!^uCNC=A4;R`j z-1_{!I?x~V_w}~Z%O$trv|dyte4+9KEB%QU-}BA%^^5QU_dZd0vAB_iFyAG( za7u1j>a#b+1jS1o1i?pYx&hao*c+FvN=vfX5798qXy8AoE-DMXFZ9etNK;>2YK;HG zvhHsK841TW4}W-$WA#!rdgO>Ia^fc811?O9kgb9_yx4z;ZF<$T*$j!CMTZ3U1b3tE zT6i5#8lmac$^%$@?_fUF4wX0vdbMb zXJ20FpAKA+O|WRGV-7cBYDfsxqaMvLH=`lwCI`Jv^2UzuPfhZ+9+x&X$T1Hxn}-Cr zlfNrpz2TCBpJM1W3 zQQ~Zs4G$pm!1D64V;#->kq=uN6yC;}K=$#;8IGuQl-J3#&+BIi--R*CWM9FFB}=jE zlyrK&Hx*4X@?mto9VtR9lXGTZIm+-k`j!X#>h)b38zl#&aq8{53uF_(qKcVcdY4D}%;6((ztds8S&OmeJyA&rgPYF@4YoaG3{pE#t(Q zF%VXNJ_0-sW(e_S?h`CRaVJDh)pQpo*o)c3I@(#%>k2l_-V{fbM}Kp?5_8 zA>e{j zh}^bJToW*Selhe8Qtl2 Date: Tue, 11 Jun 2019 10:57:00 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=90=88=E7=BA=A6?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=80=BC=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/jd/blockchain/consts/DataCodes.java | 4 ++ .../jd/blockchain/contract/ReadContract.java | 3 ++ .../blockchain/contract/ReadContractImpl.java | 16 ++++++- .../gateway/web/BlockBrowserController.java | 2 +- .../ledger/core/LedgerTransactionContext.java | 9 ++-- .../core/impl/LedgerTransactionData.java | 16 ++++--- .../core/impl/LedgerTransactionalEditor.java | 8 ++-- .../core/impl/TransactionBatchProcessor.java | 30 +++++++------ .../blockchain/ledger/LedgerEditerTest.java | 4 +- .../blockchain/ledger/LedgerManagerTest.java | 4 +- .../ledger/LedgerTransactionDataTest.java | 2 +- .../blockchain/ledger/TransactionSetTest.java | 2 +- .../ledger/ContractReturnMessage.java | 17 ++++++++ .../ledger/ContractReturnMessageData.java | 41 ++++++++++++++++++ .../com/jd/blockchain/ledger/Transaction.java | 7 +++ .../ledger/TransactionReturnMessage.java | 17 ++++++++ .../ledger/TransactionReturnMessageData.java | 26 +++++++++++ .../jd/blockchain/intgr/IntegrationBase.java | 8 +++- .../src/test/resources/contract-read.jar | Bin 3924 -> 4077 bytes .../web/LedgerInitializeWebController.java | 2 +- .../mocker/MockerLedgerInitializer.java | 2 +- 21 files changed, 181 insertions(+), 39 deletions(-) create mode 100644 source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractReturnMessage.java create mode 100644 source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractReturnMessageData.java create mode 100644 source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionReturnMessage.java create mode 100644 source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionReturnMessageData.java diff --git a/source/base/src/main/java/com/jd/blockchain/consts/DataCodes.java b/source/base/src/main/java/com/jd/blockchain/consts/DataCodes.java index 48cffbc7..577dd433 100644 --- a/source/base/src/main/java/com/jd/blockchain/consts/DataCodes.java +++ b/source/base/src/main/java/com/jd/blockchain/consts/DataCodes.java @@ -30,6 +30,8 @@ public interface DataCodes { public static final int TX_CONTENT_BODY = 0x220; + public static final int TX_RETURN_MESSAGE = 0x230; + public static final int TX_OP = 0x300; public static final int TX_OP_LEDGER_INIT = 0x301; @@ -92,6 +94,8 @@ public interface DataCodes { public static final int CONTRACT_BIZ_CONTENT = 0xA20; public static final int CONTRACT_ARGS = 0xA21; + public static final int CONTRACT_RETURN = 0xA22; + public static final int HASH = 0xB00; public static final int HASH_OBJECT = 0xB10; diff --git a/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContract.java b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContract.java index 1f82491f..8f97fb10 100644 --- a/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContract.java +++ b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContract.java @@ -6,5 +6,8 @@ public interface ReadContract { @ContractEvent(name = "read-key") String read(String address, String key); + + @ContractEvent(name = "version-key") + Long readVersion(String address, String key); } diff --git a/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContractImpl.java b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContractImpl.java index 65310b6e..b32e8d37 100644 --- a/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContractImpl.java +++ b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContractImpl.java @@ -8,9 +8,12 @@ public class ReadContractImpl implements EventProcessingAwire, ReadContract { private ContractEventContext eventContext; + private HashDigest ledgerHash; + @Override public void beforeEvent(ContractEventContext eventContext) { this.eventContext = eventContext; + this.ledgerHash = eventContext.getCurrentLedgerHash(); } @Override @@ -31,8 +34,6 @@ public class ReadContractImpl implements EventProcessingAwire, ReadContract { @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) { @@ -40,4 +41,15 @@ public class ReadContractImpl implements EventProcessingAwire, ReadContract { } return null; } + + @Override + @ContractEvent(name = "version-key") + public Long readVersion(String address, String key) { + KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, key); + + if (kvDataEntries != null && kvDataEntries.length == 1) { + return kvDataEntries[0].getVersion(); + } + return -1L; + } } diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/BlockBrowserController.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/BlockBrowserController.java index 66f94b97..9b41bcc0 100644 --- a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/BlockBrowserController.java +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/BlockBrowserController.java @@ -55,7 +55,7 @@ public class BlockBrowserController implements BlockchainExtendQueryService { // @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/participants") @Override - public ParticipantNode[] getConsensusParticipants(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + public ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash) { return peerService.getQueryService().getConsensusParticipants(ledgerHash); } diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerTransactionContext.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerTransactionContext.java index 9b653ce8..c7be08e5 100644 --- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerTransactionContext.java +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerTransactionContext.java @@ -1,5 +1,6 @@ package com.jd.blockchain.ledger.core; +import com.jd.blockchain.ledger.TransactionReturnMessage; import com.jd.blockchain.ledger.TransactionState; import com.jd.blockchain.ledger.LedgerTransaction; import com.jd.blockchain.ledger.TransactionRequest; @@ -30,19 +31,21 @@ public interface LedgerTransactionContext { * 提交对账本数据的修改,以指定的交易状态提交交易; * * @param txResult + * @param returnMessage + * * @return */ - LedgerTransaction commit(TransactionState txResult); + LedgerTransaction commit(TransactionState txResult, TransactionReturnMessage returnMessage); /** * 抛弃对账本数据的修改,以指定的交易状态提交交易;
* - * 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; + * 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState, TransactionReturnMessage)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; * * @param txResult * @return */ - LedgerTransaction discardAndCommit(TransactionState txResult); + LedgerTransaction discardAndCommit(TransactionState txResult, TransactionReturnMessage returnMessage); /** * 回滚事务,抛弃本次事务的所有数据更新; diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionData.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionData.java index 349e3040..a7c3d480 100644 --- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionData.java +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionData.java @@ -1,11 +1,7 @@ package com.jd.blockchain.ledger.core.impl; import com.jd.blockchain.crypto.HashDigest; -import com.jd.blockchain.ledger.DigitalSignature; -import com.jd.blockchain.ledger.LedgerTransaction; -import com.jd.blockchain.ledger.TransactionContent; -import com.jd.blockchain.ledger.TransactionRequest; -import com.jd.blockchain.ledger.TransactionState; +import com.jd.blockchain.ledger.*; public class LedgerTransactionData implements LedgerTransaction { @@ -23,6 +19,8 @@ public class LedgerTransactionData implements LedgerTransaction { private long blockHeight; + private TransactionReturnMessage returnMessage; + // private HashDigest adminAccountHash; // // private HashDigest userAccountSetHash; @@ -49,7 +47,7 @@ public class LedgerTransactionData implements LedgerTransaction { * 交易级的系统快照; */ public LedgerTransactionData(long blockHeight, TransactionRequest txReq, TransactionState execState, - TransactionStagedSnapshot txSnapshot) { + TransactionStagedSnapshot txSnapshot, TransactionReturnMessage returnMessage) { this.blockHeight = blockHeight; // this.txSnapshot = txSnapshot == null ? new TransactionStagedSnapshot() : txSnapshot; this.txSnapshot = txSnapshot; @@ -57,6 +55,7 @@ public class LedgerTransactionData implements LedgerTransaction { this.endpointSignatures = txReq.getEndpointSignatures(); this.nodeSignatures = txReq.getNodeSignatures(); this.executionState = execState; + this.returnMessage = returnMessage; } @Override @@ -74,6 +73,11 @@ public class LedgerTransactionData implements LedgerTransaction { return executionState; } + @Override + public TransactionReturnMessage getReturnMessage() { + return returnMessage; + } + @Override public TransactionContent getTransactionContent() { return this.transactionContent; diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionalEditor.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionalEditor.java index 5bb29512..deec6200 100644 --- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionalEditor.java +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionalEditor.java @@ -350,7 +350,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { } @Override - public LedgerTransaction commit(TransactionState txResult) { + public LedgerTransaction commit(TransactionState txResult, TransactionReturnMessage returnMessage) { checkTxState(); // capture snapshot @@ -359,7 +359,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, // txResult, txDataSnapshot); - LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null); + LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null, returnMessage); this.txset.add(tx); // this.txset.commit(); @@ -376,7 +376,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { } @Override - public LedgerTransaction discardAndCommit(TransactionState txResult) { + public LedgerTransaction discardAndCommit(TransactionState txResult, TransactionReturnMessage returnMessage) { checkTxState(); // 未处理 @@ -385,7 +385,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { // TransactionStagedSnapshot txDataSnapshot = takeSnapshot(); // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, // txResult, txDataSnapshot); - LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null); + LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null, returnMessage); this.txset.add(tx); // this.txset.commit(); diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionBatchProcessor.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionBatchProcessor.java index 0e58fba7..ce2af7df 100644 --- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionBatchProcessor.java +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionBatchProcessor.java @@ -5,16 +5,12 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.CompletableFuture; +import com.jd.blockchain.ledger.*; import com.jd.blockchain.ledger.core.impl.handles.ContractEventSendOperationHandle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.jd.blockchain.crypto.HashDigest; -import com.jd.blockchain.ledger.LedgerBlock; -import com.jd.blockchain.ledger.Operation; -import com.jd.blockchain.ledger.TransactionRequest; -import com.jd.blockchain.ledger.TransactionResponse; -import com.jd.blockchain.ledger.TransactionState; import com.jd.blockchain.ledger.core.LedgerDataSet; import com.jd.blockchain.ledger.core.LedgerEditor; import com.jd.blockchain.ledger.core.LedgerException; @@ -76,7 +72,8 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { // 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); TransactionState result; - List> contractReturn = new ArrayList<>(); + TransactionReturnMessageData returnMessageData = new TransactionReturnMessageData(); + try { LedgerDataSet dataset = txCtx.getDataSet(); TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); @@ -103,12 +100,14 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { } }; OperationHandle opHandle; + int contractOpIndex = 0; for (Operation op : ops) { opHandle = opHandles.getHandle(op.getClass()); // 合约执行需要填充执行结果 if (opHandle instanceof ContractEventSendOperationHandle) { CompletableFuture currContractReturn = new CompletableFuture<>(); - contractReturn.add(currContractReturn); + ContractReturnMessageData crmd = new ContractReturnMessageData(contractOpIndex++, currContractReturn); + returnMessageData.addContractReturnMessage(crmd); ((ContractEventSendOperationHandle) opHandle).process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService, currContractReturn); } else { opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); @@ -117,27 +116,30 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { // 提交交易(事务); result = TransactionState.SUCCESS; - txCtx.commit(result); + txCtx.commit(result, returnMessageData); } catch (LedgerException e) { // TODO: 识别更详细的异常类型以及执行对应的处理; result = TransactionState.LEDGER_ERROR; - txCtx.discardAndCommit(TransactionState.LEDGER_ERROR); + txCtx.discardAndCommit(TransactionState.LEDGER_ERROR, returnMessageData); LOGGER.warn(String.format("Transaction rollback caused by the ledger exception! --[TxHash=%s] --%s", request.getHash().toBase58(), e.getMessage()), e); } catch (Exception e) { result = TransactionState.SYSTEM_ERROR; - txCtx.discardAndCommit(TransactionState.SYSTEM_ERROR); + txCtx.discardAndCommit(TransactionState.SYSTEM_ERROR, returnMessageData); LOGGER.warn(String.format("Transaction rollback caused by the system exception! --[TxHash=%s] --%s", request.getHash().toBase58(), e.getMessage()), e); } TxResponseHandle resp = new TxResponseHandle(request, result); - if (!contractReturn.isEmpty()) { + if (!returnMessageData.isContractReturnEmpty()) { + + ContractReturnMessage[] contractReturnMessages = returnMessageData.getContractReturn(); + // 获取结果中的字符串 - String[] returnValue = new String[contractReturn.size()]; + String[] returnValue = new String[contractReturnMessages.length]; try { - for (int i = 0; i < contractReturn.size(); i++) { - returnValue[i] = contractReturn.get(i).get(); + for (int i = 0; i < contractReturnMessages.length; i++) { + returnValue[i] = contractReturnMessages[i].getReturnMessage(); } resp.setContractReturn(returnValue); } catch (Exception e) { diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditerTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditerTest.java index 3ae78c32..08f6d134 100644 --- a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditerTest.java +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditerTest.java @@ -76,7 +76,7 @@ public class LedgerEditerTest { dataAccount.setBytes(Bytes.fromString("A"), "abc".getBytes(), -1); - LedgerTransaction tx = txCtx.commit(TransactionState.SUCCESS); + LedgerTransaction tx = txCtx.commit(TransactionState.SUCCESS, null); LedgerBlock block = ldgEdt.prepare(); // 提交数据,写入存储; ldgEdt.commit(); @@ -96,7 +96,7 @@ public class LedgerEditerTest { userAccount.setProperty("Name", "孙悟空", -1); userAccount.setProperty("Age", "10000", -1); - LedgerTransaction tx = txCtx.commit(TransactionState.SUCCESS); + LedgerTransaction tx = txCtx.commit(TransactionState.SUCCESS, null); assertEquals(genesisTxReq.getTransactionContent().getHash(), tx.getTransactionContent().getHash()); assertEquals(0, tx.getBlockHeight()); diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerManagerTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerManagerTest.java index cdbece82..f8297824 100644 --- a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerManagerTest.java +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerManagerTest.java @@ -97,7 +97,7 @@ public class LedgerManagerTest { System.out.println("UserAddress=" + userAccount.getAddress()); // 提交交易结果; - LedgerTransaction tx = txCtx.commit(TransactionState.SUCCESS); + LedgerTransaction tx = txCtx.commit(TransactionState.SUCCESS, null); assertEquals(genesisTxReq.getTransactionContent().getHash(), tx.getTransactionContent().getHash()); assertEquals(0, tx.getBlockHeight()); @@ -137,7 +137,7 @@ public class LedgerManagerTest { LedgerTransactionContext txCtx1 = editor1.newTransaction(txRequest); txCtx1.getDataSet().getDataAccountSet().register(dataKey.getAddress(), dataKey.getPubKey(), null); - txCtx1.commit(TransactionState.SUCCESS); + txCtx1.commit(TransactionState.SUCCESS, null); LedgerBlock block1 = editor1.prepare(); editor1.commit(); diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerTransactionDataTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerTransactionDataTest.java index ba533a4a..e64a26a1 100644 --- a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerTransactionDataTest.java +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerTransactionDataTest.java @@ -68,7 +68,7 @@ public class LedgerTransactionDataTest { long blockHeight = 9986L; data = new LedgerTransactionData(blockHeight, txRequestMessage, TransactionState.SUCCESS, - initTransactionStagedSnapshot()); + initTransactionStagedSnapshot(), null); HashDigest hash = new HashDigest(ClassicAlgorithm.SHA256, "zhangsan".getBytes()); HashDigest adminAccountHash = new HashDigest(ClassicAlgorithm.SHA256, "lisi".getBytes()); diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionSetTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionSetTest.java index 41a9d3b5..85c5f277 100644 --- a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionSetTest.java +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionSetTest.java @@ -98,7 +98,7 @@ public class TransactionSetTest { txSnapshot.setContractAccountSetHash(contractAccountSetHash); long blockHeight = 8922L; - LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txReq, TransactionState.SUCCESS, txSnapshot); + LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txReq, TransactionState.SUCCESS, txSnapshot, null); txset.add(tx); assertTrue(txset.isUpdated()); diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractReturnMessage.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractReturnMessage.java new file mode 100644 index 00000000..3556c95c --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractReturnMessage.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.binaryproto.PrimitiveType; +import com.jd.blockchain.consts.DataCodes; + +@DataContract(code= DataCodes.CONTRACT_RETURN) +public interface ContractReturnMessage { + + @DataField(order=1, primitiveType = PrimitiveType.INT32) + int getOperationIndex(); + + @DataField(order=2, primitiveType = PrimitiveType.TEXT) + String getReturnMessage(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractReturnMessageData.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractReturnMessageData.java new file mode 100644 index 00000000..70282725 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractReturnMessageData.java @@ -0,0 +1,41 @@ +package com.jd.blockchain.ledger; + +import java.util.concurrent.CompletableFuture; + +public class ContractReturnMessageData implements ContractReturnMessage { + + private int operationIndex; + + private CompletableFuture returnMsgFuture; + + public ContractReturnMessageData() { + } + + public ContractReturnMessageData(int operationIndex, CompletableFuture returnMsgFuture) { + this.operationIndex = operationIndex; + this.returnMsgFuture = returnMsgFuture; + } + + public void setOperationIndex(int operationIndex) { + this.operationIndex = operationIndex; + } + + public void setReturnMsgFuture(CompletableFuture returnMsgFuture) { + this.returnMsgFuture = returnMsgFuture; + } + + @Override + public int getOperationIndex() { + return operationIndex; + } + + @Override + public String getReturnMessage() { + try { + return returnMsgFuture.get(); + } catch (Exception e) { + e.printStackTrace(); + throw new IllegalStateException(e); + } + } +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/Transaction.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/Transaction.java index a2442521..402024e2 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/Transaction.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/Transaction.java @@ -45,4 +45,11 @@ public interface Transaction extends NodeRequest, HashObject { @DataField(order=3, refEnum=true) TransactionState getExecutionState(); + /** + * 交易的返回结果 + * + * @return + */ + @DataField(order=4, refContract=true) + TransactionReturnMessage getReturnMessage(); } diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionReturnMessage.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionReturnMessage.java new file mode 100644 index 00000000..2d2fbfc7 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionReturnMessage.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.consts.DataCodes; + +@DataContract(code= DataCodes.TX_RETURN_MESSAGE) +public interface TransactionReturnMessage { + + /** + * 合约返回值列表 + * + * @return + */ + @DataField(order=1, list = true, refContract=true) + ContractReturnMessage[] getContractReturn(); +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionReturnMessageData.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionReturnMessageData.java new file mode 100644 index 00000000..646b7090 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionReturnMessageData.java @@ -0,0 +1,26 @@ +package com.jd.blockchain.ledger; + +import java.util.ArrayList; +import java.util.List; + +public class TransactionReturnMessageData implements TransactionReturnMessage { + + private List contractReturnMessages = new ArrayList<>(); + + public void addContractReturnMessage(ContractReturnMessage contractReturnMessage) { + contractReturnMessages.add(contractReturnMessage); + } + + public boolean isContractReturnEmpty() { + return contractReturnMessages.isEmpty(); + } + + @Override + public ContractReturnMessage[] getContractReturn() { + if (isContractReturnEmpty()) { + return null; + } + ContractReturnMessage[] crms = new ContractReturnMessage[contractReturnMessages.size()]; + return contractReturnMessages.toArray(crms); + } +} diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java index a06cc1e5..ba027f3d 100644 --- a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java @@ -577,6 +577,9 @@ public class IntegrationBase { ReadContract readContract2 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); readContract2.read(newDataAccount.getAddress().toBase58(), key2); + ReadContract readContract3 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); + readContract3.readVersion(newDataAccount.getAddress().toBase58(), key2); + // 签名; PreparedTransaction contractPtx = txContract.prepare(); contractPtx.sign(adminKey); @@ -593,13 +596,16 @@ public class IntegrationBase { // 验证结果 assertNotNull(contractReturn); - assertEquals(contractReturn.length, 2); + assertEquals(contractReturn.length, 3); String returnVal1 = contractReturn[0]; assertEquals(value1, returnVal1); String returnVal2 = contractReturn[1]; assertEquals(value2, returnVal2); + + String returnVal3 = contractReturn[2]; + assertEquals("0", returnVal3); } /** diff --git a/source/test/test-integration/src/test/resources/contract-read.jar b/source/test/test-integration/src/test/resources/contract-read.jar index 38f81951da1fa98c3b3fe1a08f47642ea9afc7cb..ddf9c66802c4fa96d6bad111a772ab2165168bfb 100644 GIT binary patch delta 1637 zcmYM#eKga190%|{cqrSvFmUhQF1k3PbR=XhE2 z8hSgIe!wH;h9u8{DHt{?xM*BO4E%8_qqWAR^-E5O=b)Y>jXO!xG_=ymZoZXWJ-V@_ zX}>piT86HixqrWHD-|7HQH<8svJIncEOav{j3_8&DC2`L6t^n|Mtib~_T!C)l&n~7 zOiYMS)>inZiojmsEp!J}d6lAU-ef%#0?~y*AYkd&Kvu2HmFo4uU04Nl=5dLu+UaNX zqF3}`vX3)$bE;TcYZDPv`=a>1!p6V0K12LLyws??x53scvWxJvztIr8lRmmd z>*={yvVBwO43DW`R;0A4)p6Ok{T2!LB*Ac)n9?j6iv65BF{`QC!Z{n&-=A`RU&Y>~ z8x420OG^7n$|o}O7t=U6?rwH8$G>#w^li&U?OZnWl{>0qXIgW;--u5z`Q(5(Y7FOM z&fi4zI9b-LU8L9|4=7K(D)D^~!=WEWJ<{5zWt@K2G5%Jo_sD+ z8T}2@QrEJ;sviwo@7g3SUME@a++Rli;wiY9=(;d=KC?tGDgsq&d#G5;Y`Vs~8!CJNt1>lT-j{Fcj z-EQtbLTd<>+^3J%)rEX&1>*ey$Tli_L`K(D;tx&#u*C1$-NUMOJ+S|;Y+c@Jwf8N zw3_P{fq)}!e9I&1yXH->yrz@u;60M#6;h^Hyg=oH~?UG<$96mirlGWk7WA+Q%ZZb#uL`u^KbkX^kkwz@ajS1xvl}xd@8F z_ZV~^BQp6_=-~D#IccN;y*=PV7h!7jkJU5S_~5U)q5ITXM{^wU(FTQ{7k7$TGkHmC z1I_qOxs{if;d2xIVl+^aauw@M{E^W^w2SK_B#G*^sHr%D+=k>)f0~Pnvp#P!-4SHA z*U&?yXUR?Mg?UCQrzQVP%gBh`c$}E8K7XeNi+Ay}7~nnJzDvpar&hz_!A=gwB~Rw> znmJYo2)16Fa^2#RJXHLN?z>sPF6#f~zwEBR_ujm*tVXy&bs8?+5B3iJvL_U)0D(lR zR6kbkfq^|YOJx9N2XMm>mMe{$dmZ5fhBAaX2o`Ap;(>Gk0g&z>y+}I{Rg@1%5Q?En z0iMu)btP^W%10lJemw?(5dZxj{@aR^N8#KI*kkqbU8{r~C zgt7}ogeWEqx474m>sNpDzUO^E=REIo&U?=D$9Gk1OcZHKfU{baf4BM-)X%gPiEb78^)+LQEagyx<#nglc)k>qkya9y{GWvm?tND)uj%DM6SW9=D;lw0AhoLNEvf%vJydKir4o z*@WUK_bz)`Oz8_~dm&^fhRh(DD{tX80qoG*num=7~*u|hUfrOwuqg@d0jYBmq3 zkhQ0Z^m;|ytmk_}ulDeDvTzfDZf1G9>C11|2AAupBW`YVXPtEIcudkb=hGh)j;ru#+qg@Z5j3J0 z%%?N|r6!p%%kjixtZ`nE#T|zpCj*0JLR{=5s>o;=PCE(z=5gwbJGl#sYn2uzN)&85=FWxk5&xbwYpoF(pDu$39ZOkT zs|o0uNNkXUQJT31&5epHyluV4JnY*UxU;4536 zqT^vlNRG