From bcbe8ae214acc9c4e61c15ec254fd73c0a17de99 Mon Sep 17 00:00:00 2001 From: huanghaiquan Date: Mon, 1 Jul 2019 16:45:31 +0800 Subject: [PATCH] Fixed the bug where abnormal transaction processing was not reported; --- source/base/src/main/resources/log4j2.xml | 62 ++++++++ source/ledger/ledger-core/pom.xml | 66 ++++---- .../blockchain/ledger/core/LedgerEditor.java | 18 ++- .../core/impl/LedgerRepositoryImpl.java | 14 +- .../core/impl/LedgerTransactionalEditor.java | 46 +++++- .../core/impl/TransactionBatchProcessor.java | 145 +++++++++++++++--- .../ledger/ContractInvokingTest.java | 2 +- .../blockchain/ledger/LedgerEditerTest.java | 18 ++- .../blockchain/ledger/LedgerManagerTest.java | 11 +- .../jd/blockchain/ledger/LedgerTestUtils.java | 63 ++++++-- .../ledger/TransactionBatchProcessorTest.java | 139 ++++++++++++++++- .../blockchain/ledger/TransactionSetTest.java | 3 +- .../blockchain/ledger/TransactionState.java | 15 +- .../jd/blockchain/transaction/TxBuilder.java | 14 +- .../transaction/TxRequestBuilder.java | 10 +- source/pom.xml | 47 +++--- .../jd/blockchain/utils/io/BytesUtils.java | 3 + 17 files changed, 550 insertions(+), 126 deletions(-) create mode 100644 source/base/src/main/resources/log4j2.xml diff --git a/source/base/src/main/resources/log4j2.xml b/source/base/src/main/resources/log4j2.xml new file mode 100644 index 00000000..c889aa69 --- /dev/null +++ b/source/base/src/main/resources/log4j2.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/ledger/ledger-core/pom.xml b/source/ledger/ledger-core/pom.xml index 34497874..f246b6d2 100644 --- a/source/ledger/ledger-core/pom.xml +++ b/source/ledger/ledger-core/pom.xml @@ -63,7 +63,7 @@ test - + @@ -78,42 +78,32 @@ - + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerEditor.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerEditor.java index efa3e750..ced98b45 100644 --- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerEditor.java +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerEditor.java @@ -1,5 +1,6 @@ package com.jd.blockchain.ledger.core; +import com.jd.blockchain.crypto.HashDigest; import com.jd.blockchain.ledger.LedgerBlock; import com.jd.blockchain.ledger.LedgerException; import com.jd.blockchain.ledger.LedgerTransaction; @@ -18,6 +19,20 @@ import com.jd.blockchain.ledger.TransactionRequest; */ public interface LedgerEditor { + /** + * 账本Hash; + * + * @return + */ + HashDigest getLedgerHash(); + + /** + * 新区块的高度; + * + * @return + */ + long getBlockHeight(); + /** * 开始新事务;
* @@ -32,7 +47,8 @@ public interface LedgerEditor { * 或者全部回滚(通过方法 {@link LedgerTransactionContext#rollback()}),以此实现原子性写入; *

* - * 每一次事务性的账本写入操作在提交后,都会记录该事务相关的系统全局快照,以交易对象 {@link LedgerTransaction} 进行保存;

+ * 每一次事务性的账本写入操作在提交后,都会记录该事务相关的系统全局快照,以交易对象 {@link LedgerTransaction} 进行保存; + *

* * 注:方法不解析、不执行交易中的操作; * diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerRepositoryImpl.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerRepositoryImpl.java index 69e3223f..7b9b4a9a 100644 --- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerRepositoryImpl.java +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerRepositoryImpl.java @@ -401,7 +401,7 @@ public class LedgerRepositoryImpl implements LedgerRepository { "A new block is in process, cann't create another one until it finish by committing or canceling."); } LedgerBlock previousBlock = getLatestBlock(); - LedgerTransactionalEditor editor = LedgerTransactionalEditor.createEditor( + LedgerTransactionalEditor editor = LedgerTransactionalEditor.createEditor(ledgerHash, getAdminInfo().getMetadata().getSetting(), previousBlock, keyPrefix, exPolicyStorage, versioningStorage); NewBlockCommittingMonitor committingMonitor = new NewBlockCommittingMonitor(editor, this); @@ -479,7 +479,7 @@ public class LedgerRepositoryImpl implements LedgerRepository { } static TransactionSet newTransactionSet(LedgerSetting ledgerSetting, String keyPrefix, - ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { + ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { // TransactionSet transactionSet = new // TransactionSet(ledgerSetting.getCryptoSetting(), // PrefixAppender.prefix(TRANSACTION_SET_PREFIX, ledgerExStorage), @@ -576,6 +576,16 @@ public class LedgerRepositoryImpl implements LedgerRepository { this.ledgerRepo = ledgerRepo; } + @Override + public HashDigest getLedgerHash() { + return editor.getLedgerHash(); + } + + @Override + public long getBlockHeight() { + return editor.getBlockHeight(); + } + @Override public LedgerTransactionContext newTransaction(TransactionRequest txRequest) { return editor.newTransaction(txRequest); 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 d939c865..a84d7692 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 @@ -26,6 +26,11 @@ public class LedgerTransactionalEditor implements LedgerEditor { System.out.println("------ [[ parallel-dbwrite=" + PARALLEL_DB_WRITE + " ]] ------"); } + /** + * 账本Hash,创世区块的编辑器则返回 null; + */ + private HashDigest ledgerHash; + private final String ledgerKeyPrefix; private CryptoSetting cryptoSetting; @@ -49,8 +54,9 @@ public class LedgerTransactionalEditor implements LedgerEditor { private LedgerDataContext newTxCtx; - private LedgerTransactionalEditor(CryptoSetting cryptoSetting, LedgerBlockData newlyBlock, + private LedgerTransactionalEditor(HashDigest ledgerHash, CryptoSetting cryptoSetting, LedgerBlockData newlyBlock, StagedSnapshot startingPoint, String ledgerKeyPrefix, BufferedKVStorage bufferedStorage) { + this.ledgerHash = ledgerHash; this.ledgerKeyPrefix = ledgerKeyPrefix; this.cryptoSetting = cryptoSetting; this.newlyBlock = newlyBlock; @@ -59,8 +65,9 @@ public class LedgerTransactionalEditor implements LedgerEditor { this.stagedSnapshots.push(startingPoint); } - public static LedgerTransactionalEditor createEditor(LedgerSetting ledgerSetting, LedgerBlock previousBlock, - String ledgerKeyPrefix, ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { + public static LedgerTransactionalEditor createEditor(HashDigest ledgerHash, LedgerSetting ledgerSetting, + LedgerBlock previousBlock, String ledgerKeyPrefix, ExPolicyKVStorage ledgerExStorage, + VersioningKVStorage ledgerVerStorage) { // new block; LedgerBlockData currBlock = new LedgerBlockData(previousBlock.getHeight() + 1, previousBlock.getLedgerHash(), previousBlock.getHash()); @@ -71,7 +78,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { StagedSnapshot startingPoint = new TxSnapshot(previousBlock, previousBlock.getTransactionSetHash()); // instantiate editor; - return new LedgerTransactionalEditor(ledgerSetting.getCryptoSetting(), currBlock, startingPoint, + return new LedgerTransactionalEditor(ledgerHash, ledgerSetting.getCryptoSetting(), currBlock, startingPoint, ledgerKeyPrefix, txStagedStorage); } @@ -81,7 +88,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { StagedSnapshot startingPoint = new GenesisSnapshot(initSetting); // init storage; BufferedKVStorage txStagedStorage = new BufferedKVStorage(ledgerExStorage, ledgerVerStorage, false); - return new LedgerTransactionalEditor(initSetting.getCryptoSetting(), genesisBlock, startingPoint, + return new LedgerTransactionalEditor(null, initSetting.getCryptoSetting(), genesisBlock, startingPoint, ledgerKeyPrefix, txStagedStorage); } @@ -102,12 +109,39 @@ public class LedgerTransactionalEditor implements LedgerEditor { // return lastTxCtx.getDataSet(); // } - public LedgerBlock getNewlyBlock() { + LedgerBlock getNewlyBlock() { return newlyBlock; } + @Override + public long getBlockHeight() { + return newlyBlock.getHeight(); + } + + @Override + public HashDigest getLedgerHash() { + return ledgerHash; + } + + private boolean isRequestedLedger(TransactionRequest txRequest) { + HashDigest reqLedgerHash = txRequest.getTransactionContent().getLedgerHash(); + if (ledgerHash == reqLedgerHash) { + return true; + } + if (ledgerHash == null || reqLedgerHash == null) { + return false; + } + return ledgerHash.equals(reqLedgerHash); + } + @Override public LedgerTransactionContext newTransaction(TransactionRequest txRequest) { + // 验证账本是否; + if (!isRequestedLedger(txRequest)) { + throw new LedgerException("This ledger is not the target ledger of transaction request[" + + txRequest.getTransactionContent().getHash() + "]!"); + } + checkState(); // TODO:验证交易签名; 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 b4795c01..b14b3678 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 @@ -9,11 +9,13 @@ import org.slf4j.LoggerFactory; import com.jd.blockchain.crypto.HashDigest; import com.jd.blockchain.ledger.BytesValue; +import com.jd.blockchain.ledger.DigitalSignature; import com.jd.blockchain.ledger.LedgerBlock; import com.jd.blockchain.ledger.LedgerException; import com.jd.blockchain.ledger.Operation; import com.jd.blockchain.ledger.OperationResult; import com.jd.blockchain.ledger.OperationResultData; +import com.jd.blockchain.ledger.TransactionContent; import com.jd.blockchain.ledger.TransactionRequest; import com.jd.blockchain.ledger.TransactionResponse; import com.jd.blockchain.ledger.TransactionState; @@ -26,6 +28,9 @@ import com.jd.blockchain.ledger.core.TransactionRequestContext; import com.jd.blockchain.service.TransactionBatchProcess; import com.jd.blockchain.service.TransactionBatchResult; import com.jd.blockchain.service.TransactionBatchResultHandle; +import com.jd.blockchain.transaction.TxBuilder; +import com.jd.blockchain.transaction.TxRequestBuilder; +import com.jd.blockchain.transaction.TxResponseMessage; import com.jd.blockchain.utils.Bytes; public class TransactionBatchProcessor implements TransactionBatchProcess { @@ -50,12 +55,9 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { private TransactionBatchResult batchResult; /** - * @param newBlockEditor - * 新区块的数据编辑器; - * @param previousBlockDataset - * 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; - * @param opHandles - * 操作处理对象注册表; + * @param newBlockEditor 新区块的数据编辑器; + * @param previousBlockDataset 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; + * @param opHandles 操作处理对象注册表; */ public TransactionBatchProcessor(LedgerEditor newBlockEditor, LedgerDataSet previousBlockDataset, OperationHandleRegisteration opHandles, LedgerService ledgerService) { @@ -65,6 +67,18 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { this.ledgerService = ledgerService; } + private boolean isRequestedLedger(TransactionRequest txRequest) { + HashDigest currLedgerHash = newBlockEditor.getLedgerHash(); + HashDigest reqLedgerHash = txRequest.getTransactionContent().getLedgerHash(); + if (currLedgerHash == reqLedgerHash) { + return true; + } + if (currLedgerHash == null || reqLedgerHash == null) { + return false; + } + return currLedgerHash.equals(reqLedgerHash); + } + /* * (non-Javadoc) * @@ -74,14 +88,61 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { */ @Override public TransactionResponse schedule(TransactionRequest request) { - // 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; - LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); - TransactionState result; + TransactionResponse resp; + try { + if (!isRequestedLedger(request)) { + // 抛弃不属于当前账本的交易请求; + resp = discard(request, TransactionState.DISCARD_BY_WRONG_LEDGER); + responseList.add(resp); + return resp; + } + if (!verifyTxContent(request)) { + // 抛弃哈希和签名校验失败的交易请求; + resp = discard(request, TransactionState.DISCARD_BY_WRONG_CONTENT_SIGNATURE); + responseList.add(resp); + return resp; + } - List operationResults = new ArrayList<>(); + LOGGER.debug("Start handling transaction... --[BlockHeight=%s][RequestHash=%s][TxHash=%s]", + newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash()); + // 创建交易上下文; + // 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; + LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); + + // 处理交易; + resp = handleTx(request, txCtx); + + LOGGER.debug("Complete handling transaction. --[BlockHeight=%s][RequestHash=%s][TxHash=%s]", + newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash()); + + responseList.add(resp); + return resp; + } catch (Exception e) { + // 抛弃发生处理异常的交易请求; + resp = discard(request, TransactionState.SYSTEM_ERROR); + LOGGER.error(String.format( + "Discard transaction rollback caused by the system exception! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", + newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), + e.getMessage()), e); + + responseList.add(resp); + return resp; + } + } + /** + * 处理交易;
+ * + * 此方法会处理所有的异常,以不同结果的 {@link TransactionResponse} 返回; + * + * @param request + * @param txCtx + * @return + */ + private TransactionResponse handleTx(TransactionRequest request, LedgerTransactionContext txCtx) { + TransactionState result; + List operationResults = new ArrayList<>(); try { - LedgerDataSet dataset = txCtx.getDataSet(); TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); // TODO: 验证签名者的有效性; @@ -101,7 +162,8 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { OperationHandleContext handleContext = new OperationHandleContext() { @Override public void handle(Operation operation) { - //assert; Instance of operation are one of User related operations or DataAccount related operations; + // assert; Instance of operation are one of User related operations or + // DataAccount related operations; OperationHandle hdl = opHandles.getHandle(operation.getClass()); hdl.process(operation, dataset, reqCtx, previousBlockDataset, this, ledgerService); } @@ -110,7 +172,8 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { int opIndex = 0; for (Operation op : ops) { opHandle = opHandles.getHandle(op.getClass()); - BytesValue opResult = opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); + BytesValue opResult = opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, + ledgerService); if (opResult != null) { operationResults.add(new OperationResultData(opIndex, opResult)); } @@ -119,19 +182,22 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { // 提交交易(事务); result = TransactionState.SUCCESS; - txCtx.commit(result, operationResults); } catch (LedgerException e) { // TODO: 识别更详细的异常类型以及执行对应的处理; result = TransactionState.LEDGER_ERROR; txCtx.discardAndCommit(TransactionState.LEDGER_ERROR, operationResults); - LOGGER.warn(String.format("Transaction rollback caused by the ledger exception! --[TxHash=%s] --%s", - request.getHash().toBase58(), e.getMessage()), e); + LOGGER.error(String.format( + "Transaction rollback caused by the ledger exception! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", + newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), + e.getMessage()), e); } catch (Exception e) { result = TransactionState.SYSTEM_ERROR; txCtx.discardAndCommit(TransactionState.SYSTEM_ERROR, operationResults); - LOGGER.warn(String.format("Transaction rollback caused by the system exception! --[TxHash=%s] --%s", - request.getHash().toBase58(), e.getMessage()), e); + LOGGER.error(String.format( + "Transaction rollback caused by the system exception! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", + newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), + e.getMessage()), e); } TxResponseHandle resp = new TxResponseHandle(request, result); @@ -139,9 +205,50 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { OperationResult[] operationResultArray = new OperationResult[operationResults.size()]; resp.setOperationResults(operationResults.toArray(operationResultArray)); } + return resp; + } - responseList.add(resp); + private boolean verifyTxContent(TransactionRequest request) { + TransactionContent txContent = request.getTransactionContent(); + if (!TxBuilder.verifyTxContentHash(txContent, txContent.getHash())) { + return false; + } + DigitalSignature[] endpointSignatures = request.getEndpointSignatures(); + if (endpointSignatures != null) { + for (DigitalSignature signature : endpointSignatures) { + if (!TxRequestBuilder.verifyHashSignature(txContent.getHash(), signature.getDigest(), + signature.getPubKey())) { + return false; + } + } + } + DigitalSignature[] nodeSignatures = request.getNodeSignatures(); + if (nodeSignatures != null) { + for (DigitalSignature signature : nodeSignatures) { + if (!TxRequestBuilder.verifyHashSignature(txContent.getHash(), signature.getDigest(), + signature.getPubKey())) { + return false; + } + } + } + return true; + } + /** + * 直接丢弃交易; + * + * @param request + * @param txState + * @return 丢弃交易的回复;只包含原始请求中的交易内容哈希和交易被丢弃的原因,而不包含区块信息; + */ + private TransactionResponse discard(TransactionRequest request, TransactionState txState) { + // 丢弃交易的回复;只返回请求的交易内容哈希和交易被丢弃的原因, + TxResponseMessage resp = new TxResponseMessage(request.getTransactionContent().getHash()); + resp.setExecutionState(txState); + + LOGGER.error("Discard transaction request! --[BlockHeight=%s][RequestHash=%s][TxHash=%s][ResponseState=%s]", + newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), + resp.getExecutionState()); return resp; } diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingTest.java index eab81350..daffa40b 100644 --- a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingTest.java +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingTest.java @@ -184,7 +184,7 @@ public class ContractInvokingTest { // 创建账本; LedgerEditor ldgEdt = LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); - TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); + TransactionRequest genesisTxReq = LedgerTestUtils.createLedgerInitTxRequest(partiKeys); LedgerTransactionContext genisisTxCtx = ldgEdt.newTransaction(genesisTxReq); LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditerTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditerTest.java index c7f3d6e6..dfab92c9 100644 --- a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditerTest.java +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditerTest.java @@ -16,6 +16,7 @@ import com.jd.blockchain.crypto.SignatureFunction; import com.jd.blockchain.crypto.service.classic.ClassicAlgorithm; import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; import com.jd.blockchain.crypto.service.sm.SMCryptoService; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; import com.jd.blockchain.ledger.BlockchainKeypair; import com.jd.blockchain.ledger.BytesValue; import com.jd.blockchain.ledger.DataType; @@ -52,6 +53,13 @@ public class LedgerEditerTest { private static final String LEDGER_KEY_PREFIX = "LDG://"; private SignatureFunction signatureFunction; + private BlockchainKeypair parti0 = BlockchainKeyGenerator.getInstance().generate(); + private BlockchainKeypair parti1 = BlockchainKeyGenerator.getInstance().generate(); + private BlockchainKeypair parti2 = BlockchainKeyGenerator.getInstance().generate(); + private BlockchainKeypair parti3 = BlockchainKeyGenerator.getInstance().generate(); + + private BlockchainKeypair[] participants = { parti0, parti1, parti2, parti3 }; + /** * 初始化一个; */ @@ -74,8 +82,8 @@ public class LedgerEditerTest { return LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); } - private LedgerTransactionContext createGenisisTx(LedgerEditor ldgEdt) { - TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); + private LedgerTransactionContext createGenisisTx(LedgerEditor ldgEdt, BlockchainKeypair[] partis) { + TransactionRequest genesisTxReq = LedgerTestUtils.createLedgerInitTxRequest(partis); LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); @@ -86,7 +94,7 @@ public class LedgerEditerTest { @Test public void testWriteDataAccoutKvOp() { LedgerEditor ldgEdt = createLedgerInitEditor(); - LedgerTransactionContext genisisTxCtx = createGenisisTx(ldgEdt); + LedgerTransactionContext genisisTxCtx = createGenisisTx(ldgEdt, participants); LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); @@ -119,7 +127,7 @@ public class LedgerEditerTest { @Test public void testGennesisBlockCreation() { LedgerEditor ldgEdt = createLedgerInitEditor(); - LedgerTransactionContext genisisTxCtx = createGenisisTx(ldgEdt); + LedgerTransactionContext genisisTxCtx = createGenisisTx(ldgEdt, participants); LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); AsymmetricKeypair cryptoKeyPair = signatureFunction.generateKeypair(); @@ -146,5 +154,5 @@ public class LedgerEditerTest { ldgEdt.commit(); } - + } 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 9639602c..73e4a8c1 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 @@ -63,6 +63,13 @@ public class LedgerManagerTest { public static final String[] SUPPORTED_PROVIDERS = { ClassicCryptoService.class.getName(), SMCryptoService.class.getName() }; + private BlockchainKeypair parti0 = BlockchainKeyGenerator.getInstance().generate(); + private BlockchainKeypair parti1 = BlockchainKeyGenerator.getInstance().generate(); + private BlockchainKeypair parti2 = BlockchainKeyGenerator.getInstance().generate(); + private BlockchainKeypair parti3 = BlockchainKeyGenerator.getInstance().generate(); + + private BlockchainKeypair[] participants = { parti0, parti1, parti2, parti3 }; + private SignatureFunction signatureFunction; @Before @@ -83,13 +90,13 @@ public class LedgerManagerTest { LedgerEditor ldgEdt = ledgerManager.newLedger(initSetting, storage); // 创建一个模拟的创世交易; - TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); + TransactionRequest genesisTxReq = LedgerTestUtils.createLedgerInitTxRequest(participants); // 记录交易,注册用户; LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); LedgerDataSet ldgDS = txCtx.getDataSet(); BlockchainKeypair userKP = BlockchainKeyGenerator.getInstance().generate(); - + UserAccount userAccount = ldgDS.getUserAccountSet().register(userKP.getAddress(), userKP.getPubKey()); userAccount.setProperty("Name", "孙悟空", -1); userAccount.setProperty("Age", "10000", -1); diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerTestUtils.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerTestUtils.java index 3a631ad3..ca405b35 100644 --- a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerTestUtils.java +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerTestUtils.java @@ -38,10 +38,10 @@ public class LedgerTestUtils { private static Random rand = new Random(); - public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash) { - BlockchainKeypair key = BlockchainKeyGenerator.getInstance().generate(ED25519); - return createTxRequest_UserReg(ledgerHash, key); - } +// public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash) { +// BlockchainKeypair key = BlockchainKeyGenerator.getInstance().generate(ED25519); +// return createTxRequest_UserReg(ledgerHash, key); +// } public static LedgerInitSetting createLedgerInitSetting() { BlockchainKeypair[] partiKeys = new BlockchainKeypair[2]; @@ -81,22 +81,61 @@ public class LedgerTestUtils { return initSetting; } - public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash, BlockchainKeypair userKeypair) { - return createTxRequest_UserReg(ledgerHash, userKeypair, null); +// public static TransactionRequest createTxRequest_UserReg(BlockchainKeypair userKeypair, HashDigest ledgerHash, BlockchainKeypair... partiKeys) { +// return createTxRequest_UserReg(userKeypair, ledgerHash, null, null); +// } + + public static TransactionRequest createLedgerInitTxRequest(BlockchainKeypair... participants) { + TxBuilder txBuilder = new TxBuilder(null); + + for (BlockchainKeypair parti : participants) { + txBuilder.users().register(parti.getIdentity()); + } + + TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); + for (BlockchainKeypair parti : participants) { + txReqBuilder.signAsNode(parti); + } + + return txReqBuilder.buildRequest(); + } + + public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash, + BlockchainKeypair nodeKeypair, BlockchainKeypair... signers) { + return createTxRequest_UserReg(BlockchainKeyGenerator.getInstance().generate(), ledgerHash, nodeKeypair, + signers); } - public static TransactionRequest createTxRequest_UserReg(HashDigest ledgerHash, BlockchainKeypair userKeypair, - BlockchainKeypair gatewayKeypair) { + public static TransactionRequest createTxRequest_UserReg(BlockchainKeypair userKeypair, HashDigest ledgerHash, + BlockchainKeypair nodeKeypair, BlockchainKeypair... signers) { TxBuilder txBuilder = new TxBuilder(ledgerHash); txBuilder.users().register(userKeypair.getIdentity()); TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); - txReqBuilder.signAsEndpoint(userKeypair); - if (gatewayKeypair != null) { - txReqBuilder.signAsNode(gatewayKeypair); + txReqBuilder.signAsEndpoint(nodeKeypair); + if (nodeKeypair != null) { + txReqBuilder.signAsNode(nodeKeypair); } - + + return txReqBuilder.buildRequest(); + } + + public static TransactionRequest createTxRequest_MultiOPs_WithError(HashDigest ledgerHash, + BlockchainKeypair userKeypair, BlockchainKeypair nodeKeypair) { + TxBuilder txBuilder = new TxBuilder(ledgerHash); + + txBuilder.users().register(userKeypair.getIdentity()); + + BlockchainKeypair testKey = BlockchainKeyGenerator.getInstance().generate(); + txBuilder.dataAccount(testKey.getAddress()).setBytes("AA", "Value".getBytes(), 1); + + TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); + txReqBuilder.signAsEndpoint(nodeKeypair); + if (nodeKeypair != null) { + txReqBuilder.signAsNode(nodeKeypair); + } + return txReqBuilder.buildRequest(); } diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionBatchProcessorTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionBatchProcessorTest.java index 72c8a848..1172b3c4 100644 --- a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionBatchProcessorTest.java +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionBatchProcessorTest.java @@ -1,6 +1,7 @@ package test.com.jd.blockchain.ledger; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -54,19 +55,22 @@ public class TransactionBatchProcessorTest { private BlockchainKeypair parti2 = BlockchainKeyGenerator.getInstance().generate(); private BlockchainKeypair parti3 = BlockchainKeyGenerator.getInstance().generate(); + private BlockchainKeypair[] participants = { parti0, parti1, parti2, parti3 }; + private TransactionRequest transactionRequest; - // 采用基于内存的 Storage; - private MemoryKVStorage storage = new MemoryKVStorage(); + // TODO: 验证无效签名会被拒绝; @Test - public void testTxReqProcess() { + public void testSingleTxProcess() { + final MemoryKVStorage STORAGE = new MemoryKVStorage(); + // 初始化账本到指定的存储库; - ledgerHash = initLedger(storage, parti0, parti1, parti2, parti3); + ledgerHash = initLedger(STORAGE, parti0, parti1, parti2, parti3); // 加载账本; LedgerManager ledgerManager = new LedgerManager(); - LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, storage); + LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); // 验证参与方账户的存在; LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); @@ -85,18 +89,137 @@ public class TransactionBatchProcessorTest { // 注册新用户; BlockchainKeypair userKeypair = BlockchainKeyGenerator.getInstance().generate(); transactionRequest = LedgerTestUtils.createTxRequest_UserReg(ledgerHash, userKeypair, parti0); - txbatchProcessor.schedule(transactionRequest); + TransactionResponse txResp = txbatchProcessor.schedule(transactionRequest); + + LedgerBlock newBlock = newBlockEditor.prepare(); + newBlockEditor.commit(); + + // 验证正确性; + ledgerManager = new LedgerManager(); + ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); + + LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); + assertEquals(newBlock.getHash(), latestBlock.getHash()); + assertEquals(1, newBlock.getHeight()); + + assertEquals(TransactionState.SUCCESS, txResp.getExecutionState()); + } + + @Test + public void testMultiTxsProcess() { + final MemoryKVStorage STORAGE = new MemoryKVStorage(); + + // 初始化账本到指定的存储库; + ledgerHash = initLedger(STORAGE, parti0, parti1, parti2, parti3); + + // 加载账本; + LedgerManager ledgerManager = new LedgerManager(); + LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); + + // 验证参与方账户的存在; + LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); + UserAccount user0 = previousBlockDataset.getUserAccountSet().getUser(parti0.getAddress()); + assertNotNull(user0); + boolean partiRegistered = previousBlockDataset.getUserAccountSet().contains(parti0.getAddress()); + assertTrue(partiRegistered); + + // 生成新区块; + LedgerEditor newBlockEditor = ledgerRepo.createNextBlock(); + + OperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration(); + TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, + opReg, ledgerManager); + + // 注册新用户; + BlockchainKeypair userKeypair1 = BlockchainKeyGenerator.getInstance().generate(); + transactionRequest = LedgerTestUtils.createTxRequest_UserReg(ledgerHash, userKeypair1, parti0); + TransactionResponse txResp1 = txbatchProcessor.schedule(transactionRequest); + +// BlockchainKeypair userKeypair2 = BlockchainKeyGenerator.getInstance().generate(); +// transactionRequest = LedgerTestUtils.createTxRequest_UserReg(ledgerHash, userKeypair2, parti0); +// TransactionResponse txResp2 = txbatchProcessor.schedule(transactionRequest); + + LedgerBlock newBlock = newBlockEditor.prepare(); + newBlockEditor.commit(); + + assertEquals(TransactionState.SUCCESS, txResp1.getExecutionState()); +// assertEquals(TransactionState.SUCCESS, txResp2.getExecutionState()); + + // 验证正确性; + ledgerManager = new LedgerManager(); + ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); + + LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); + assertEquals(newBlock.getHash(), latestBlock.getHash()); + assertEquals(1, newBlock.getHeight()); + + LedgerDataSet ledgerDS = ledgerRepo.getDataSet(latestBlock); + boolean existUser1 = ledgerDS.getUserAccountSet().contains(userKeypair1.getAddress()); +// boolean existUser2 = ledgerDS.getUserAccountSet().contains(userKeypair2.getAddress()); + assertTrue(existUser1); +// assertTrue(existUser2); + } + + @Test + public void testTxRollback() { + final MemoryKVStorage STORAGE = new MemoryKVStorage(); + + // 初始化账本到指定的存储库; + ledgerHash = initLedger(STORAGE, parti0, parti1, parti2, parti3); + + // 加载账本; + LedgerManager ledgerManager = new LedgerManager(); + LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); + + // 验证参与方账户的存在; + LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); + UserAccount user0 = previousBlockDataset.getUserAccountSet().getUser(parti0.getAddress()); + assertNotNull(user0); + boolean partiRegistered = previousBlockDataset.getUserAccountSet().contains(parti0.getAddress()); + assertTrue(partiRegistered); + + // 生成新区块; + LedgerEditor newBlockEditor = ledgerRepo.createNextBlock(); + + OperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration(); + TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, + opReg, ledgerManager); + + // 注册新用户; + BlockchainKeypair userKeypair1 = BlockchainKeyGenerator.getInstance().generate(); + transactionRequest = LedgerTestUtils.createTxRequest_UserReg(ledgerHash, userKeypair1, parti0); + TransactionResponse txResp1 = txbatchProcessor.schedule(transactionRequest); + + BlockchainKeypair userKeypair2 = BlockchainKeyGenerator.getInstance().generate(); + transactionRequest = LedgerTestUtils.createTxRequest_MultiOPs_WithError(ledgerHash, userKeypair2, parti0); + TransactionResponse txResp2 = txbatchProcessor.schedule(transactionRequest); + + BlockchainKeypair userKeypair3 = BlockchainKeyGenerator.getInstance().generate(); + transactionRequest = LedgerTestUtils.createTxRequest_UserReg(ledgerHash, userKeypair3, parti0); + TransactionResponse txResp3 = txbatchProcessor.schedule(transactionRequest); LedgerBlock newBlock = newBlockEditor.prepare(); newBlockEditor.commit(); + assertEquals(TransactionState.SUCCESS, txResp1.getExecutionState()); + assertEquals(TransactionState.LEDGER_ERROR, txResp2.getExecutionState()); + assertEquals(TransactionState.SUCCESS, txResp3.getExecutionState()); + // 验证正确性; ledgerManager = new LedgerManager(); - ledgerRepo = ledgerManager.register(ledgerHash, storage); + ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); assertEquals(newBlock.getHash(), latestBlock.getHash()); assertEquals(1, newBlock.getHeight()); + + LedgerDataSet ledgerDS = ledgerRepo.getDataSet(latestBlock); + boolean existUser1 = ledgerDS.getUserAccountSet().contains(userKeypair1.getAddress()); + boolean existUser2 = ledgerDS.getUserAccountSet().contains(userKeypair2.getAddress()); + boolean existUser3 = ledgerDS.getUserAccountSet().contains(userKeypair3.getAddress()); + assertTrue(existUser1); + assertFalse(existUser2); + assertTrue(existUser3); } private HashDigest initLedger(MemoryKVStorage storage, BlockchainKeypair... partiKeys) { @@ -106,7 +229,7 @@ public class TransactionBatchProcessorTest { // 创建账本; LedgerEditor ldgEdt = LedgerTransactionalEditor.createEditor(initSetting, LEDGER_KEY_PREFIX, storage, storage); - TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest_UserReg(null); + TransactionRequest genesisTxReq = LedgerTestUtils.createLedgerInitTxRequest(partiKeys); LedgerTransactionContext genisisTxCtx = ldgEdt.newTransaction(genesisTxReq); LedgerDataSet ldgDS = genisisTxCtx.getDataSet(); 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 a4b719a7..f023a987 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 @@ -102,8 +102,7 @@ public class TransactionSetTest { txSnapshot.setContractAccountSetHash(contractAccountSetHash); long blockHeight = 8922L; - LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txReq, TransactionState.SUCCESS, txSnapshot, - null); + LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txReq, TransactionState.SUCCESS, txSnapshot); txset.add(tx); assertTrue(txset.isUpdated()); diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionState.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionState.java index e89d41b6..9d9b1691 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionState.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionState.java @@ -11,7 +11,7 @@ import com.jd.blockchain.consts.DataCodes; * @author huanghaiquan * */ -@EnumContract(code= DataCodes.ENUM_TYPE_TRANSACTION_STATE) +@EnumContract(code = DataCodes.ENUM_TYPE_TRANSACTION_STATE) public enum TransactionState { /** @@ -23,16 +23,21 @@ public enum TransactionState { * 共识错误; */ CONSENSUS_ERROR((byte) 1), - + /** * 账本错误; */ LEDGER_ERROR((byte) 2), /** - * 数据序列更新错误; + * 由于在错误的账本上执行交易而被丢弃; + */ + DISCARD_BY_WRONG_LEDGER((byte) 3), + + /** + * 由于交易内容的验签失败而丢弃; */ - DATA_SEQUENCE_UPDATE_ERROR((byte) 3), + DISCARD_BY_WRONG_CONTENT_SIGNATURE((byte) 4), /** * 系统错误; @@ -44,7 +49,7 @@ public enum TransactionState { */ TIMEOUT((byte) 0x81); - @EnumField(type= PrimitiveType.INT8) + @EnumField(type = PrimitiveType.INT8) public final byte CODE; private TransactionState(byte code) { diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxBuilder.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxBuilder.java index 35e09d78..1ff23a2f 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxBuilder.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxBuilder.java @@ -55,12 +55,22 @@ public class TxBuilder implements TransactionBuilder { txContent.addOperations(opFactory.getOperations()); txContent.setTime(time); - byte[] contentBodyBytes = BinaryProtocol.encode(txContent, TransactionContentBody.class); - HashDigest contentHash = Crypto.getHashFunction(DEFAULT_HASH_ALGORITHM).hash(contentBodyBytes); + HashDigest contentHash = computeTxContentHash(txContent); txContent.setHash(contentHash); return txContent; } + + public static HashDigest computeTxContentHash(TransactionContent txContent) { + byte[] contentBodyBytes = BinaryProtocol.encode(txContent, TransactionContentBody.class); + HashDigest contentHash = Crypto.getHashFunction(DEFAULT_HASH_ALGORITHM).hash(contentBodyBytes); + return contentHash; + } + + public static boolean verifyTxContentHash(TransactionContent txContent, HashDigest verifiedHash) { + HashDigest hash = computeTxContentHash(txContent); + return hash.equals(verifiedHash); + } public Collection getReturnValuehandlers() { return opFactory.getReturnValuetHandlers(); diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxRequestBuilder.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxRequestBuilder.java index 7d6701a5..87326bb1 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxRequestBuilder.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxRequestBuilder.java @@ -75,8 +75,14 @@ public class TxRequestBuilder implements TransactionRequestBuilder { } public static boolean verifySignature(TransactionContent txContent, SignatureDigest signDigest, PubKey pubKey) { - return Crypto.getSignatureFunction(pubKey.getAlgorithm()).verify(signDigest, pubKey, - txContent.getHash().toBytes()); + if (!TxBuilder.verifyTxContentHash(txContent, txContent.getHash())) { + return false; + } + return verifyHashSignature(txContent.getHash(), signDigest, pubKey); + } + + public static boolean verifyHashSignature(HashDigest hash, SignatureDigest signDigest, PubKey pubKey) { + return Crypto.getSignatureFunction(pubKey.getAlgorithm()).verify(signDigest, pubKey, hash.toBytes()); } @Override diff --git a/source/pom.xml b/source/pom.xml index f2a8ac32..4dca787c 100644 --- a/source/pom.xml +++ b/source/pom.xml @@ -8,11 +8,11 @@ spring-boot-starter-parent 2.0.6.RELEASE - - - - - + + + + + com.jd.blockchain jdchain-root @@ -92,6 +92,11 @@ mockito-core test + + org.springframework.boot + spring-boot-starter-log4j2 + test + @@ -446,22 +451,22 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesUtils.java index 93520e0e..3f0a7256 100644 --- a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesUtils.java +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesUtils.java @@ -46,6 +46,9 @@ public class BytesUtils { if (bytes1 == bytes2) { return true; } + if (bytes1 == null || bytes2 == null) { + return false; + } if (bytes1.length != bytes2.length) { return false; }