| @@ -30,6 +30,8 @@ public interface DataCodes { | |||||
| public static final int TX_CONTENT_BODY = 0x220; | 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 = 0x300; | ||||
| public static final int TX_OP_LEDGER_INIT = 0x301; | 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_BIZ_CONTENT = 0xA20; | ||||
| public static final int CONTRACT_ARGS = 0xA21; | 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 = 0xB00; | ||||
| public static final int HASH_OBJECT = 0xB10; | public static final int HASH_OBJECT = 0xB10; | ||||
| @@ -6,5 +6,8 @@ public interface ReadContract { | |||||
| @ContractEvent(name = "read-key") | @ContractEvent(name = "read-key") | ||||
| String read(String address, String key); | String read(String address, String key); | ||||
| @ContractEvent(name = "version-key") | |||||
| Long readVersion(String address, String key); | |||||
| } | } | ||||
| @@ -8,9 +8,12 @@ public class ReadContractImpl implements EventProcessingAwire, ReadContract { | |||||
| private ContractEventContext eventContext; | private ContractEventContext eventContext; | ||||
| private HashDigest ledgerHash; | |||||
| @Override | @Override | ||||
| public void beforeEvent(ContractEventContext eventContext) { | public void beforeEvent(ContractEventContext eventContext) { | ||||
| this.eventContext = eventContext; | this.eventContext = eventContext; | ||||
| this.ledgerHash = eventContext.getCurrentLedgerHash(); | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -31,8 +34,6 @@ public class ReadContractImpl implements EventProcessingAwire, ReadContract { | |||||
| @Override | @Override | ||||
| @ContractEvent(name = "read-key") | @ContractEvent(name = "read-key") | ||||
| public String read(String address, String key) { | public String read(String address, String key) { | ||||
| HashDigest ledgerHash = eventContext.getCurrentLedgerHash(); | |||||
| KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, key); | KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, key); | ||||
| if (kvDataEntries != null && kvDataEntries.length == 1) { | if (kvDataEntries != null && kvDataEntries.length == 1) { | ||||
| @@ -40,4 +41,15 @@ public class ReadContractImpl implements EventProcessingAwire, ReadContract { | |||||
| } | } | ||||
| return null; | 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; | |||||
| } | |||||
| } | } | ||||
| @@ -55,7 +55,7 @@ public class BlockBrowserController implements BlockchainExtendQueryService { | |||||
| // @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/participants") | // @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/participants") | ||||
| @Override | @Override | ||||
| public ParticipantNode[] getConsensusParticipants(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { | |||||
| public ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash) { | |||||
| return peerService.getQueryService().getConsensusParticipants(ledgerHash); | return peerService.getQueryService().getConsensusParticipants(ledgerHash); | ||||
| } | } | ||||
| @@ -1,5 +1,6 @@ | |||||
| package com.jd.blockchain.ledger.core; | package com.jd.blockchain.ledger.core; | ||||
| import com.jd.blockchain.ledger.TransactionReturnMessage; | |||||
| import com.jd.blockchain.ledger.TransactionState; | import com.jd.blockchain.ledger.TransactionState; | ||||
| import com.jd.blockchain.ledger.LedgerTransaction; | import com.jd.blockchain.ledger.LedgerTransaction; | ||||
| import com.jd.blockchain.ledger.TransactionRequest; | import com.jd.blockchain.ledger.TransactionRequest; | ||||
| @@ -30,19 +31,21 @@ public interface LedgerTransactionContext { | |||||
| * 提交对账本数据的修改,以指定的交易状态提交交易; | * 提交对账本数据的修改,以指定的交易状态提交交易; | ||||
| * | * | ||||
| * @param txResult | * @param txResult | ||||
| * @param returnMessage | |||||
| * | |||||
| * @return | * @return | ||||
| */ | */ | ||||
| LedgerTransaction commit(TransactionState txResult); | |||||
| LedgerTransaction commit(TransactionState txResult, TransactionReturnMessage returnMessage); | |||||
| /** | /** | ||||
| * 抛弃对账本数据的修改,以指定的交易状态提交交易;<br> | * 抛弃对账本数据的修改,以指定的交易状态提交交易;<br> | ||||
| * | * | ||||
| * 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; | |||||
| * 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState, TransactionReturnMessage)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; | |||||
| * | * | ||||
| * @param txResult | * @param txResult | ||||
| * @return | * @return | ||||
| */ | */ | ||||
| LedgerTransaction discardAndCommit(TransactionState txResult); | |||||
| LedgerTransaction discardAndCommit(TransactionState txResult, TransactionReturnMessage returnMessage); | |||||
| /** | /** | ||||
| * 回滚事务,抛弃本次事务的所有数据更新; | * 回滚事务,抛弃本次事务的所有数据更新; | ||||
| @@ -1,11 +1,7 @@ | |||||
| package com.jd.blockchain.ledger.core.impl; | package com.jd.blockchain.ledger.core.impl; | ||||
| import com.jd.blockchain.crypto.HashDigest; | 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 { | public class LedgerTransactionData implements LedgerTransaction { | ||||
| @@ -23,6 +19,8 @@ public class LedgerTransactionData implements LedgerTransaction { | |||||
| private long blockHeight; | private long blockHeight; | ||||
| private TransactionReturnMessage returnMessage; | |||||
| // private HashDigest adminAccountHash; | // private HashDigest adminAccountHash; | ||||
| // | // | ||||
| // private HashDigest userAccountSetHash; | // private HashDigest userAccountSetHash; | ||||
| @@ -49,7 +47,7 @@ public class LedgerTransactionData implements LedgerTransaction { | |||||
| * 交易级的系统快照; | * 交易级的系统快照; | ||||
| */ | */ | ||||
| public LedgerTransactionData(long blockHeight, TransactionRequest txReq, TransactionState execState, | public LedgerTransactionData(long blockHeight, TransactionRequest txReq, TransactionState execState, | ||||
| TransactionStagedSnapshot txSnapshot) { | |||||
| TransactionStagedSnapshot txSnapshot, TransactionReturnMessage returnMessage) { | |||||
| this.blockHeight = blockHeight; | this.blockHeight = blockHeight; | ||||
| // this.txSnapshot = txSnapshot == null ? new TransactionStagedSnapshot() : txSnapshot; | // this.txSnapshot = txSnapshot == null ? new TransactionStagedSnapshot() : txSnapshot; | ||||
| this.txSnapshot = txSnapshot; | this.txSnapshot = txSnapshot; | ||||
| @@ -57,6 +55,7 @@ public class LedgerTransactionData implements LedgerTransaction { | |||||
| this.endpointSignatures = txReq.getEndpointSignatures(); | this.endpointSignatures = txReq.getEndpointSignatures(); | ||||
| this.nodeSignatures = txReq.getNodeSignatures(); | this.nodeSignatures = txReq.getNodeSignatures(); | ||||
| this.executionState = execState; | this.executionState = execState; | ||||
| this.returnMessage = returnMessage; | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -74,6 +73,11 @@ public class LedgerTransactionData implements LedgerTransaction { | |||||
| return executionState; | return executionState; | ||||
| } | } | ||||
| @Override | |||||
| public TransactionReturnMessage getReturnMessage() { | |||||
| return returnMessage; | |||||
| } | |||||
| @Override | @Override | ||||
| public TransactionContent getTransactionContent() { | public TransactionContent getTransactionContent() { | ||||
| return this.transactionContent; | return this.transactionContent; | ||||
| @@ -350,7 +350,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public LedgerTransaction commit(TransactionState txResult) { | |||||
| public LedgerTransaction commit(TransactionState txResult, TransactionReturnMessage returnMessage) { | |||||
| checkTxState(); | checkTxState(); | ||||
| // capture snapshot | // capture snapshot | ||||
| @@ -359,7 +359,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
| // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | ||||
| // txResult, txDataSnapshot); | // 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.add(tx); | ||||
| // this.txset.commit(); | // this.txset.commit(); | ||||
| @@ -376,7 +376,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
| } | } | ||||
| @Override | @Override | ||||
| public LedgerTransaction discardAndCommit(TransactionState txResult) { | |||||
| public LedgerTransaction discardAndCommit(TransactionState txResult, TransactionReturnMessage returnMessage) { | |||||
| checkTxState(); | checkTxState(); | ||||
| // 未处理 | // 未处理 | ||||
| @@ -385,7 +385,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
| // TransactionStagedSnapshot txDataSnapshot = takeSnapshot(); | // TransactionStagedSnapshot txDataSnapshot = takeSnapshot(); | ||||
| // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | ||||
| // txResult, txDataSnapshot); | // 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.add(tx); | ||||
| // this.txset.commit(); | // this.txset.commit(); | ||||
| @@ -5,16 +5,12 @@ import java.util.Iterator; | |||||
| import java.util.List; | import java.util.List; | ||||
| import java.util.concurrent.CompletableFuture; | import java.util.concurrent.CompletableFuture; | ||||
| import com.jd.blockchain.ledger.*; | |||||
| import com.jd.blockchain.ledger.core.impl.handles.ContractEventSendOperationHandle; | import com.jd.blockchain.ledger.core.impl.handles.ContractEventSendOperationHandle; | ||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
| import com.jd.blockchain.crypto.HashDigest; | 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.LedgerDataSet; | ||||
| import com.jd.blockchain.ledger.core.LedgerEditor; | import com.jd.blockchain.ledger.core.LedgerEditor; | ||||
| import com.jd.blockchain.ledger.core.LedgerException; | import com.jd.blockchain.ledger.core.LedgerException; | ||||
| @@ -76,7 +72,8 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
| // 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | // 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | ||||
| LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | ||||
| TransactionState result; | TransactionState result; | ||||
| List<CompletableFuture<String>> contractReturn = new ArrayList<>(); | |||||
| TransactionReturnMessageData returnMessageData = new TransactionReturnMessageData(); | |||||
| try { | try { | ||||
| LedgerDataSet dataset = txCtx.getDataSet(); | LedgerDataSet dataset = txCtx.getDataSet(); | ||||
| TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | ||||
| @@ -103,12 +100,14 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
| } | } | ||||
| }; | }; | ||||
| OperationHandle opHandle; | OperationHandle opHandle; | ||||
| int contractOpIndex = 0; | |||||
| for (Operation op : ops) { | for (Operation op : ops) { | ||||
| opHandle = opHandles.getHandle(op.getClass()); | opHandle = opHandles.getHandle(op.getClass()); | ||||
| // 合约执行需要填充执行结果 | // 合约执行需要填充执行结果 | ||||
| if (opHandle instanceof ContractEventSendOperationHandle) { | if (opHandle instanceof ContractEventSendOperationHandle) { | ||||
| CompletableFuture<String> currContractReturn = new CompletableFuture<>(); | CompletableFuture<String> 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); | ((ContractEventSendOperationHandle) opHandle).process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService, currContractReturn); | ||||
| } else { | } else { | ||||
| opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); | opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); | ||||
| @@ -117,27 +116,30 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
| // 提交交易(事务); | // 提交交易(事务); | ||||
| result = TransactionState.SUCCESS; | result = TransactionState.SUCCESS; | ||||
| txCtx.commit(result); | |||||
| txCtx.commit(result, returnMessageData); | |||||
| } catch (LedgerException e) { | } catch (LedgerException e) { | ||||
| // TODO: 识别更详细的异常类型以及执行对应的处理; | // TODO: 识别更详细的异常类型以及执行对应的处理; | ||||
| result = TransactionState.LEDGER_ERROR; | 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", | LOGGER.warn(String.format("Transaction rollback caused by the ledger exception! --[TxHash=%s] --%s", | ||||
| request.getHash().toBase58(), e.getMessage()), e); | request.getHash().toBase58(), e.getMessage()), e); | ||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| result = TransactionState.SYSTEM_ERROR; | 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", | LOGGER.warn(String.format("Transaction rollback caused by the system exception! --[TxHash=%s] --%s", | ||||
| request.getHash().toBase58(), e.getMessage()), e); | request.getHash().toBase58(), e.getMessage()), e); | ||||
| } | } | ||||
| TxResponseHandle resp = new TxResponseHandle(request, result); | TxResponseHandle resp = new TxResponseHandle(request, result); | ||||
| if (!contractReturn.isEmpty()) { | |||||
| if (!returnMessageData.isContractReturnEmpty()) { | |||||
| ContractReturnMessage[] contractReturnMessages = returnMessageData.getContractReturn(); | |||||
| // 获取结果中的字符串 | // 获取结果中的字符串 | ||||
| String[] returnValue = new String[contractReturn.size()]; | |||||
| String[] returnValue = new String[contractReturnMessages.length]; | |||||
| try { | 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); | resp.setContractReturn(returnValue); | ||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| @@ -76,7 +76,7 @@ public class LedgerEditerTest { | |||||
| dataAccount.setBytes(Bytes.fromString("A"), "abc".getBytes(), -1); | 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(); | LedgerBlock block = ldgEdt.prepare(); | ||||
| // 提交数据,写入存储; | // 提交数据,写入存储; | ||||
| ldgEdt.commit(); | ldgEdt.commit(); | ||||
| @@ -96,7 +96,7 @@ public class LedgerEditerTest { | |||||
| userAccount.setProperty("Name", "孙悟空", -1); | userAccount.setProperty("Name", "孙悟空", -1); | ||||
| userAccount.setProperty("Age", "10000", -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(genesisTxReq.getTransactionContent().getHash(), tx.getTransactionContent().getHash()); | ||||
| assertEquals(0, tx.getBlockHeight()); | assertEquals(0, tx.getBlockHeight()); | ||||
| @@ -97,7 +97,7 @@ public class LedgerManagerTest { | |||||
| System.out.println("UserAddress=" + userAccount.getAddress()); | 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(genesisTxReq.getTransactionContent().getHash(), tx.getTransactionContent().getHash()); | ||||
| assertEquals(0, tx.getBlockHeight()); | assertEquals(0, tx.getBlockHeight()); | ||||
| @@ -137,7 +137,7 @@ public class LedgerManagerTest { | |||||
| LedgerTransactionContext txCtx1 = editor1.newTransaction(txRequest); | LedgerTransactionContext txCtx1 = editor1.newTransaction(txRequest); | ||||
| txCtx1.getDataSet().getDataAccountSet().register(dataKey.getAddress(), dataKey.getPubKey(), null); | txCtx1.getDataSet().getDataAccountSet().register(dataKey.getAddress(), dataKey.getPubKey(), null); | ||||
| txCtx1.commit(TransactionState.SUCCESS); | |||||
| txCtx1.commit(TransactionState.SUCCESS, null); | |||||
| LedgerBlock block1 = editor1.prepare(); | LedgerBlock block1 = editor1.prepare(); | ||||
| editor1.commit(); | editor1.commit(); | ||||
| @@ -68,7 +68,7 @@ public class LedgerTransactionDataTest { | |||||
| long blockHeight = 9986L; | long blockHeight = 9986L; | ||||
| data = new LedgerTransactionData(blockHeight, txRequestMessage, TransactionState.SUCCESS, | data = new LedgerTransactionData(blockHeight, txRequestMessage, TransactionState.SUCCESS, | ||||
| initTransactionStagedSnapshot()); | |||||
| initTransactionStagedSnapshot(), null); | |||||
| HashDigest hash = new HashDigest(ClassicAlgorithm.SHA256, "zhangsan".getBytes()); | HashDigest hash = new HashDigest(ClassicAlgorithm.SHA256, "zhangsan".getBytes()); | ||||
| HashDigest adminAccountHash = new HashDigest(ClassicAlgorithm.SHA256, "lisi".getBytes()); | HashDigest adminAccountHash = new HashDigest(ClassicAlgorithm.SHA256, "lisi".getBytes()); | ||||
| @@ -98,7 +98,7 @@ public class TransactionSetTest { | |||||
| txSnapshot.setContractAccountSetHash(contractAccountSetHash); | txSnapshot.setContractAccountSetHash(contractAccountSetHash); | ||||
| long blockHeight = 8922L; | 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); | txset.add(tx); | ||||
| assertTrue(txset.isUpdated()); | 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) | @DataField(order=3, refEnum=true) | ||||
| TransactionState getExecutionState(); | TransactionState getExecutionState(); | ||||
| /** | |||||
| * 交易的返回结果 | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| @DataField(order=4, refContract=true) | |||||
| TransactionReturnMessage getReturnMessage(); | |||||
| } | } | ||||
| @@ -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); | |||||
| } | |||||
| } | |||||
| @@ -577,6 +577,9 @@ public class IntegrationBase { | |||||
| ReadContract readContract2 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | ReadContract readContract2 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | ||||
| readContract2.read(newDataAccount.getAddress().toBase58(), key2); | readContract2.read(newDataAccount.getAddress().toBase58(), key2); | ||||
| ReadContract readContract3 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | |||||
| readContract3.readVersion(newDataAccount.getAddress().toBase58(), key2); | |||||
| // 签名; | // 签名; | ||||
| PreparedTransaction contractPtx = txContract.prepare(); | PreparedTransaction contractPtx = txContract.prepare(); | ||||
| contractPtx.sign(adminKey); | contractPtx.sign(adminKey); | ||||
| @@ -593,13 +596,16 @@ public class IntegrationBase { | |||||
| // 验证结果 | // 验证结果 | ||||
| assertNotNull(contractReturn); | assertNotNull(contractReturn); | ||||
| assertEquals(contractReturn.length, 2); | |||||
| assertEquals(contractReturn.length, 3); | |||||
| String returnVal1 = contractReturn[0]; | String returnVal1 = contractReturn[0]; | ||||
| assertEquals(value1, returnVal1); | assertEquals(value1, returnVal1); | ||||
| String returnVal2 = contractReturn[1]; | String returnVal2 = contractReturn[1]; | ||||
| assertEquals(value2, returnVal2); | assertEquals(value2, returnVal2); | ||||
| String returnVal3 = contractReturn[2]; | |||||
| assertEquals("0", returnVal3); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -425,7 +425,7 @@ public class LedgerInitializeWebController implements LedgerInitProcess, LedgerI | |||||
| userRegOP.getUserID().getPubKey()); | userRegOP.getUserID().getPubKey()); | ||||
| } | } | ||||
| txCtx.commit(TransactionState.SUCCESS); | |||||
| txCtx.commit(TransactionState.SUCCESS, null); | |||||
| return ledgerEditor.prepare(); | return ledgerEditor.prepare(); | ||||
| } | } | ||||
| @@ -293,7 +293,7 @@ public class MockerLedgerInitializer implements LedgerInitProcess, LedgerInitCon | |||||
| userRegOP.getUserID().getPubKey()); | userRegOP.getUserID().getPubKey()); | ||||
| } | } | ||||
| txCtx.commit(TransactionState.SUCCESS); | |||||
| txCtx.commit(TransactionState.SUCCESS, null); | |||||
| return ledgerEditor.prepare(); | return ledgerEditor.prepare(); | ||||
| } | } | ||||