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 e5ffe4bf..423d59b5 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; @@ -79,6 +81,8 @@ public interface DataCodes { public static final int DATA = 0x900; + 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-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 97b3af56..8130f1bd 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 com.jd.blockchain.runtime.Module; import com.jd.blockchain.transaction.ContractType; import com.jd.blockchain.utils.Bytes; import com.jd.blockchain.utils.IllegalDataException; +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) { @@ -61,6 +63,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 { @@ -68,12 +77,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()); @@ -81,7 +94,9 @@ 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."); } @@ -98,9 +113,17 @@ public class JavaContractCode implements ContractCode { 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..8f97fb10 --- /dev/null +++ b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContract.java @@ -0,0 +1,13 @@ +package com.jd.blockchain.contract; + + +@Contract +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 new file mode 100644 index 00000000..b32e8d37 --- /dev/null +++ b/source/contract/contract-samples/src/main/java/com/jd/blockchain/contract/ReadContractImpl.java @@ -0,0 +1,55 @@ +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; + + private HashDigest ledgerHash; + + @Override + public void beforeEvent(ContractEventContext eventContext) { + this.eventContext = eventContext; + this.ledgerHash = eventContext.getCurrentLedgerHash(); + } + + @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) { + KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, key); + + if (kvDataEntries != null && kvDataEntries.length == 1) { + return kvDataEntries[0].getValue().toString(); + } + 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/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/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 2adda2f6..0cd6beeb 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,17 +3,15 @@ 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.*; +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.LedgerException; -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.LedgerService; @@ -74,6 +72,8 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { // 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); TransactionState result; + TransactionReturnMessageData returnMessageData = new TransactionReturnMessageData(); + try { LedgerDataSet dataset = txCtx.getDataSet(); TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); @@ -100,28 +100,53 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { } }; OperationHandle opHandle; + int contractOpIndex = 0; 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<>(); + 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); + } } // 提交交易(事务); 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 (!returnMessageData.isContractReturnEmpty()) { + + ContractReturnMessage[] contractReturnMessages = returnMessageData.getContractReturn(); + + // 获取结果中的字符串 + String[] returnValue = new String[contractReturnMessages.length]; + try { + for (int i = 0; i < contractReturnMessages.length; i++) { + returnValue[i] = contractReturnMessages[i].getReturnMessage(); + } + resp.setContractReturn(returnValue); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + responseList.add(resp); return resp; @@ -201,6 +226,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 +258,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 5680846c..a8882d5d 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-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 d99ed706..9e53ae24 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 83ea8f9a..c39ea445 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/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/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/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/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 00000000..ddf9c668 Binary files /dev/null and b/source/test/test-integration/src/test/resources/contract-read.jar differ diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitializeWebController.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitializeWebController.java index 4c9af288..ecf5f640 100644 --- a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitializeWebController.java +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitializeWebController.java @@ -425,7 +425,7 @@ public class LedgerInitializeWebController implements LedgerInitProcess, LedgerI userRegOP.getUserID().getPubKey()); } - txCtx.commit(TransactionState.SUCCESS); + txCtx.commit(TransactionState.SUCCESS, null); return ledgerEditor.prepare(); } diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/MockerLedgerInitializer.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/MockerLedgerInitializer.java index b03d88af..570ccfc4 100644 --- a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/MockerLedgerInitializer.java +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/MockerLedgerInitializer.java @@ -293,7 +293,7 @@ public class MockerLedgerInitializer implements LedgerInitProcess, LedgerInitCon userRegOP.getUserID().getPubKey()); } - txCtx.commit(TransactionState.SUCCESS); + txCtx.commit(TransactionState.SUCCESS, null); return ledgerEditor.prepare(); }