# Conflicts: # source/base/src/main/java/com/jd/blockchain/consts/DataCodes.java # source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java # source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionBatchProcessor.java # source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditerTest.java # source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.javatags/1.0.0
| @@ -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; | |||
| @@ -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<String> execReturn); | |||
| } | |||
| @@ -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<String> execReturn) { | |||
| this.contractEventContext = eventContext; | |||
| codeModule.execute(new ContractExecution()); | |||
| codeModule.execute(new ContractExecution(execReturn)); | |||
| } | |||
| private Object resolveArgs(byte[] args, List<DataContract> dataContractList) { | |||
| @@ -61,6 +63,13 @@ public class JavaContractCode implements ContractCode { | |||
| } | |||
| class ContractExecution implements Runnable { | |||
| private CompletableFuture<String> contractReturn; | |||
| public ContractExecution(CompletableFuture<String> 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) { | |||
| @@ -0,0 +1,62 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
| <parent> | |||
| <artifactId>contract</artifactId> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <version>0.9.0-SNAPSHOT</version> | |||
| </parent> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <artifactId>contract-samples</artifactId> | |||
| <name>contract-samples</name> | |||
| <dependencies> | |||
| <dependency> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>ledger-model</artifactId> | |||
| <version>${project.version}</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>crypto-framework</artifactId> | |||
| <version>${project.version}</version> | |||
| <scope>provided</scope> | |||
| </dependency> | |||
| </dependencies> | |||
| <build> | |||
| <plugins> | |||
| <plugin> | |||
| <artifactId>maven-assembly-plugin</artifactId> | |||
| <configuration> | |||
| <finalName>contract</finalName> | |||
| <appendAssemblyId>false</appendAssemblyId> | |||
| <archive> | |||
| <manifest> | |||
| <mainClass>com.jd.blockchain.contract.ReadContractImpl</mainClass> | |||
| </manifest> | |||
| </archive> | |||
| <descriptorRefs> | |||
| <descriptorRef>jar-with-dependencies</descriptorRef> | |||
| </descriptorRefs> | |||
| </configuration> | |||
| <executions> | |||
| <execution> | |||
| <id>make-assembly</id> | |||
| <phase>package</phase> | |||
| <goals> | |||
| <goal>single</goal> | |||
| </goals> | |||
| </execution> | |||
| </executions> | |||
| </plugin> | |||
| </plugins> | |||
| </build> | |||
| </project> | |||
| @@ -0,0 +1,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); | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -13,6 +13,7 @@ | |||
| <module>contract-framework</module> | |||
| <module>contract-jvm</module> | |||
| <module>contract-maven-plugin</module> | |||
| </modules> | |||
| <module>contract-samples</module> | |||
| </modules> | |||
| </project> | |||
| @@ -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); | |||
| } | |||
| @@ -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); | |||
| /** | |||
| * 抛弃对账本数据的修改,以指定的交易状态提交交易;<br> | |||
| * | |||
| * 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; | |||
| * 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState, TransactionReturnMessage)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; | |||
| * | |||
| * @param txResult | |||
| * @return | |||
| */ | |||
| LedgerTransaction discardAndCommit(TransactionState txResult); | |||
| LedgerTransaction discardAndCommit(TransactionState txResult, TransactionReturnMessage returnMessage); | |||
| /** | |||
| * 回滚事务,抛弃本次事务的所有数据更新; | |||
| @@ -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; | |||
| @@ -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(); | |||
| @@ -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<String> 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 { | |||
| @@ -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<String> 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); | |||
| } | |||
| } | |||
| @@ -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(); | |||
| @@ -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()); | |||
| @@ -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()); | |||
| @@ -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(); | |||
| } | |||
| @@ -0,0 +1,41 @@ | |||
| package com.jd.blockchain.ledger; | |||
| import java.util.concurrent.CompletableFuture; | |||
| public class ContractReturnMessageData implements ContractReturnMessage { | |||
| private int operationIndex; | |||
| private CompletableFuture<String> returnMsgFuture; | |||
| public ContractReturnMessageData() { | |||
| } | |||
| public ContractReturnMessageData(int operationIndex, CompletableFuture<String> returnMsgFuture) { | |||
| this.operationIndex = operationIndex; | |||
| this.returnMsgFuture = returnMsgFuture; | |||
| } | |||
| public void setOperationIndex(int operationIndex) { | |||
| this.operationIndex = operationIndex; | |||
| } | |||
| public void setReturnMsgFuture(CompletableFuture<String> 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); | |||
| } | |||
| } | |||
| } | |||
| @@ -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(); | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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(); | |||
| } | |||
| @@ -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(); | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| package com.jd.blockchain.ledger; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| public class TransactionReturnMessageData implements TransactionReturnMessage { | |||
| private List<ContractReturnMessage> 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); | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| @@ -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 { | |||
| @@ -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); | |||
| } | |||
| } | |||
| }); | |||
| @@ -40,6 +40,13 @@ | |||
| <artifactId>sdk-client</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>contract-samples</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>io.nats</groupId> | |||
| <artifactId>jnats</artifactId> | |||
| @@ -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); | |||
| } | |||
| @@ -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(); | |||
| } | |||
| @@ -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(); | |||
| } | |||