From c727a3562660a6ac3524488acefa2831ebdd3134 Mon Sep 17 00:00:00 2001 From: huanghaiquan Date: Fri, 5 Jul 2019 12:17:33 +0800 Subject: [PATCH] =?UTF-8?q?Implemented=20transaction=20rollback=20mechanis?= =?UTF-8?q?m=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bftsmart/service/BftsmartNodeServer.java | 1 + .../jd/blockchain/ledger/core/AccountSet.java | 4 +- .../blockchain/ledger/core/LedgerEditor.java | 6 +- .../ledger/core/LedgerTransactionContext.java | 2 +- .../ledger/core/SettingContext.java | 51 ++ .../ledger/core/impl/LedgerBlockData.java | 14 +- .../core/impl/LedgerRepositoryImpl.java | 24 +- .../core/impl/LedgerTransactionData.java | 66 +-- .../core/impl/LedgerTransactionalEditor.java | 501 +++++++++++------- .../core/impl/TransactionBatchProcessor.java | 104 ++-- .../blockchain/ledger/LedgerEditorTest.java | 2 +- .../blockchain/ledger/LedgerManagerTest.java | 6 +- .../jd/blockchain/ledger/LedgerTestUtils.java | 39 ++ .../blockchain/ledger/MerkleDataSetTest.java | 6 + .../ledger/TransactionBatchProcessorTest.java | 135 ++++- .../com/jd/blockchain/ledger/BlockBody.java | 3 + .../ledger/BlockRollbackException.java | 33 ++ .../ledger/IllegalTransactionException.java | 35 ++ .../ledger/TransactionContentBody.java | 2 +- .../ledger/TransactionException.java | 22 - .../ledger/TransactionRollbackException.java | 16 + .../blockchain/ledger/TransactionState.java | 54 +- .../blockchain/transaction/TxContentBlob.java | 2 +- .../storage/service/VersioningKVStorage.java | 4 +- 24 files changed, 782 insertions(+), 350 deletions(-) create mode 100644 source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/SettingContext.java create mode 100644 source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockRollbackException.java create mode 100644 source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/IllegalTransactionException.java delete mode 100644 source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionException.java create mode 100644 source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRollbackException.java diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServer.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServer.java index 162ff130..ba330e04 100644 --- a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServer.java +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServer.java @@ -243,6 +243,7 @@ public class BftsmartNodeServer extends DefaultRecoverable implements NodeServer messageHandle.commitBatch(realmName, batchId); } catch (Exception e) { // todo 需要处理应答码 404 + LOGGER.error("Error occurred while processing ordered messages! --" + e.getMessage(), e); messageHandle.rollbackBatch(realmName, batchId, TransactionState.CONSENSUS_ERROR.CODE); } diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountSet.java index a28a5fb5..bb57cd3c 100644 --- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountSet.java +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountSet.java @@ -333,9 +333,9 @@ public class AccountSet implements Transactional, MerkleProvable { if (!updated) { return; } - String[] addresses = new String[latestAccountsCache.size()]; + Bytes[] addresses = new Bytes[latestAccountsCache.size()]; latestAccountsCache.keySet().toArray(addresses); - for (String address : addresses) { + for (Bytes address : addresses) { VersioningAccount acc = latestAccountsCache.remove(address); // cancel; if (acc.isUpdated()) { 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 ced98b45..46c21655 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 @@ -50,9 +50,11 @@ public interface LedgerEditor { * 每一次事务性的账本写入操作在提交后,都会记录该事务相关的系统全局快照,以交易对象 {@link LedgerTransaction} 进行保存; *

* - * 注:方法不解析、不执行交易中的操作; * - * @param txRequest + * + * 注:方法不解析、不执行交易中的操作;

+ * + * @param txRequest 交易请求; * @return */ LedgerTransactionContext newTransaction(TransactionRequest txRequest); diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerTransactionContext.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerTransactionContext.java index 33f589c5..a4feb79e 100644 --- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerTransactionContext.java +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerTransactionContext.java @@ -24,7 +24,7 @@ public interface LedgerTransactionContext { * * @return */ - TransactionRequest getRequestTX(); + TransactionRequest getTransactionRequest(); /** * 提交对账本数据的修改,以指定的交易状态提交交易; diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/SettingContext.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/SettingContext.java new file mode 100644 index 00000000..516b2acf --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/SettingContext.java @@ -0,0 +1,51 @@ +package com.jd.blockchain.ledger.core; + +public class SettingContext { + + private static final TxSettingContext txSettings = new TxSettingContext(); + + private static final QueryingSettingContext queryingSettings = new QueryingSettingContext(); + + public static TxSettingContext txSettings() { + return txSettings; + } + + public static QueryingSettingContext queryingSettings() { + return queryingSettings; + } + + /** + * 与交易处理相关的设置; + * @author huanghaiquan + * + */ + public static class TxSettingContext { + + public boolean verifyLedger() { + return true; + } + + public boolean verifySignature() { + return true; + } + + } + + /** + * 与账本查询相关的设置; + * @author huanghaiquan + * + */ + public static class QueryingSettingContext { + + /** + * 查询区块等具有 hash 标识符的对象时是否重新校验哈希; + * @return + */ + public boolean verifyHash() { + return false; + } + + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerBlockData.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerBlockData.java index 24b20401..1e8865b7 100644 --- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerBlockData.java +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerBlockData.java @@ -33,7 +33,9 @@ public class LedgerBlockData implements LedgerBlock { // private HashDigest contractPrivilegeHash; private HashDigest transactionSetHash; - + + private long timestamp; + public LedgerBlockData() { } @@ -155,4 +157,14 @@ public class LedgerBlockData implements LedgerBlock { this.ledgerHash = ledgerHash; } + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + + } \ No newline at end of file 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 7b9b4a9a..1fe559b7 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 @@ -15,6 +15,7 @@ import com.jd.blockchain.ledger.core.LedgerDataSet; import com.jd.blockchain.ledger.core.LedgerEditor; import com.jd.blockchain.ledger.core.LedgerRepository; import com.jd.blockchain.ledger.core.LedgerTransactionContext; +import com.jd.blockchain.ledger.core.SettingContext; import com.jd.blockchain.ledger.core.TransactionSet; import com.jd.blockchain.ledger.core.UserAccountSet; import com.jd.blockchain.storage.service.ExPolicyKVStorage; @@ -77,7 +78,7 @@ public class LedgerRepositoryImpl implements LedgerRepository { this.ledgerIndexKey = encodeLedgerIndexKey(ledgerHash); if (getLatestBlockHeight() < 0) { - throw new LedgerException("Ledger doesn't exist!"); + throw new RuntimeException("Ledger doesn't exist!"); } } @@ -205,15 +206,14 @@ public class LedgerRepositoryImpl implements LedgerRepository { LedgerBlockData block = new LedgerBlockData(deserialize(blockBytes)); if (!blockHash.equals(block.getHash())) { - throw new LedgerException("Block hash not equals to it's storage key!"); + throw new RuntimeException("Block hash not equals to it's storage key!"); } // verify hash; // boolean requiredVerifyHash = // adminAccount.getMetadata().getSetting().getCryptoSetting().getAutoVerifyHash(); // TODO: 未实现从配置中加载是否校验 Hash 的设置; - boolean requiredVerifyHash = false; - if (requiredVerifyHash) { + if (SettingContext.queryingSettings().verifyHash()) { byte[] blockBodyBytes = null; if (block.getHeight() == 0) { // 计算创世区块的 hash 时,不包括 ledgerHash 字段; @@ -227,14 +227,14 @@ public class LedgerRepositoryImpl implements LedgerRepository { HashFunction hashFunc = Crypto.getHashFunction(blockHash.getAlgorithm()); boolean pass = hashFunc.verify(blockHash, blockBodyBytes); if (!pass) { - throw new LedgerException("Block hash verification fail!"); + throw new RuntimeException("Block hash verification fail!"); } } // verify height; HashDigest indexedHash = getBlockHash(block.getHeight()); if (indexedHash == null || !indexedHash.equals(blockHash)) { - throw new LedgerException( + throw new RuntimeException( "Illegal ledger state in storage that ledger height index doesn't match it's block data in height[" + block.getHeight() + "] and block hash[" + Base58Utils.encode(blockHash.toBytes()) + "] !"); @@ -394,15 +394,15 @@ public class LedgerRepositoryImpl implements LedgerRepository { @Override public synchronized LedgerEditor createNextBlock() { if (closed) { - throw new LedgerException("Ledger repository has been closed!"); + throw new RuntimeException("Ledger repository has been closed!"); } if (this.nextBlockEditor != null) { - throw new LedgerException( + throw new RuntimeException( "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(ledgerHash, - getAdminInfo().getMetadata().getSetting(), previousBlock, keyPrefix, exPolicyStorage, + LedgerTransactionalEditor editor = LedgerTransactionalEditor.createEditor(previousBlock, + getAdminInfo().getMetadata().getSetting(), keyPrefix, exPolicyStorage, versioningStorage); NewBlockCommittingMonitor committingMonitor = new NewBlockCommittingMonitor(editor, this); this.nextBlockEditor = committingMonitor; @@ -420,7 +420,7 @@ public class LedgerRepositoryImpl implements LedgerRepository { return; } if (this.nextBlockEditor != null) { - throw new LedgerException("A new block is in process, cann't close the ledger repository!"); + throw new RuntimeException("A new block is in process, cann't close the ledger repository!"); } closed = true; } @@ -600,7 +600,7 @@ public class LedgerRepositoryImpl implements LedgerRepository { public void commit() { try { editor.commit(); - LedgerBlock latestBlock = editor.getNewlyBlock(); + LedgerBlock latestBlock = editor.getCurrentBlock(); ledgerRepo.latestState = new LedgerState(latestBlock); } finally { ledgerRepo.nextBlockEditor = null; diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionData.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionData.java index 6e231b44..432c24e1 100644 --- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionData.java +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionData.java @@ -1,8 +1,5 @@ package com.jd.blockchain.ledger.core.impl; -import java.util.Arrays; -import java.util.Comparator; - import com.jd.blockchain.crypto.HashDigest; import com.jd.blockchain.ledger.DigitalSignature; import com.jd.blockchain.ledger.LedgerTransaction; @@ -29,48 +26,27 @@ public class LedgerTransactionData implements LedgerTransaction { private OperationResult[] operationResults; - // private HashDigest adminAccountHash; - // - // private HashDigest userAccountSetHash; - // - // private HashDigest dataAccountSetHash; - // - // private HashDigest contractAccountSetHash; - /** * Declare a private no-arguments constructor for deserializing purpose; */ + @SuppressWarnings("unused") private LedgerTransactionData() { -// this.txSnapshot = new TransactionStagedSnapshot(); } /** - * @param blockHeight - * 区块链高度; - * @param txReq - * 交易请求; - * @param execState - * 执行状态; - * @param txSnapshot - * 交易级的系统快照; + * @param blockHeight 区块链高度; + * @param txReq 交易请求; + * @param execState 执行状态; + * @param txSnapshot 交易级的系统快照; */ public LedgerTransactionData(long blockHeight, TransactionRequest txReq, TransactionState execState, TransactionStagedSnapshot txSnapshot, OperationResult... opResults) { this.blockHeight = blockHeight; -// this.txSnapshot = txSnapshot == null ? new TransactionStagedSnapshot() : txSnapshot; this.txSnapshot = txSnapshot; this.transactionContent = txReq.getTransactionContent(); this.endpointSignatures = txReq.getEndpointSignatures(); this.nodeSignatures = txReq.getNodeSignatures(); this.executionState = execState; - if (opResults != null) { - Arrays.sort(opResults, new Comparator() { - @Override - public int compare(OperationResult o1, OperationResult o2) { - return o1.getIndex() - o2.getIndex(); - } - }); - } this.operationResults = opResults; } @@ -116,17 +92,17 @@ public class LedgerTransactionData implements LedgerTransaction { @Override public HashDigest getUserAccountSetHash() { - return txSnapshot == null ? null :txSnapshot.getUserAccountSetHash(); + return txSnapshot == null ? null : txSnapshot.getUserAccountSetHash(); } @Override public HashDigest getDataAccountSetHash() { - return txSnapshot == null ? null :txSnapshot.getDataAccountSetHash(); + return txSnapshot == null ? null : txSnapshot.getDataAccountSetHash(); } @Override public HashDigest getContractAccountSetHash() { - return txSnapshot == null ? null :txSnapshot.getContractAccountSetHash(); + return txSnapshot == null ? null : txSnapshot.getContractAccountSetHash(); } public void setTxSnapshot(TransactionStagedSnapshot txSnapshot) { @@ -140,20 +116,22 @@ public class LedgerTransactionData implements LedgerTransaction { this.transactionContent = content; } - public void setEndpointSignatures(Object[] participantSignatures) { - int length = participantSignatures.length; - this.endpointSignatures = new DigitalSignature[length]; - for (int i = 0; i < length; i++) { - this.endpointSignatures[i] = (DigitalSignature) participantSignatures[i]; - } + public void setEndpointSignatures(DigitalSignature[] participantSignatures) { + this.endpointSignatures = participantSignatures; +// int length = participantSignatures.length; +// this.endpointSignatures = new DigitalSignature[length]; +// for (int i = 0; i < length; i++) { +// this.endpointSignatures[i] = (DigitalSignature) participantSignatures[i]; +// } } - public void setNodeSignatures(Object[] nodeSignatures) { - int length = nodeSignatures.length; - this.nodeSignatures = new DigitalSignature[length]; - for (int i = 0; i < length; i++) { - this.nodeSignatures[i] = (DigitalSignature) nodeSignatures[i]; - } + public void setNodeSignatures(DigitalSignature[] nodeSignatures) { + this.nodeSignatures = nodeSignatures; +// int length = nodeSignatures.length; +// this.nodeSignatures = new DigitalSignature[length]; +// for (int i = 0; i < length; i++) { +// this.nodeSignatures[i] = (DigitalSignature) nodeSignatures[i]; +// } } public void setExecutionState(TransactionState executionState) { 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 5113dde4..235d08b8 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 @@ -1,19 +1,35 @@ package com.jd.blockchain.ledger.core.impl; import java.util.List; -import java.util.Stack; import com.jd.blockchain.binaryproto.BinaryProtocol; import com.jd.blockchain.crypto.Crypto; import com.jd.blockchain.crypto.HashDigest; -import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.BlockBody; +import com.jd.blockchain.ledger.BlockRollbackException; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.ledger.IllegalTransactionException; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.LedgerDataSnapshot; +import com.jd.blockchain.ledger.LedgerInitSetting; +import com.jd.blockchain.ledger.LedgerSetting; +import com.jd.blockchain.ledger.LedgerTransaction; +import com.jd.blockchain.ledger.OperationResult; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionRollbackException; +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.LedgerTransactionContext; +import com.jd.blockchain.ledger.core.SettingContext; import com.jd.blockchain.ledger.core.TransactionSet; import com.jd.blockchain.storage.service.ExPolicyKVStorage; import com.jd.blockchain.storage.service.VersioningKVStorage; import com.jd.blockchain.storage.service.utils.BufferedKVStorage; +import com.jd.blockchain.transaction.TxBuilder; +import com.jd.blockchain.transaction.TxRequestBuilder; import com.jd.blockchain.utils.Bytes; import com.jd.blockchain.utils.codec.Base58Utils; @@ -35,9 +51,9 @@ public class LedgerTransactionalEditor implements LedgerEditor { private CryptoSetting cryptoSetting; - private LedgerBlockData newlyBlock; + private LedgerBlockData currentBlock; - private Stack stagedSnapshots = new Stack<>(); +// private Stack stagedSnapshots = new Stack<>(); private boolean prepared = false; @@ -45,43 +61,70 @@ public class LedgerTransactionalEditor implements LedgerEditor { private boolean committed = false; -// private BufferedKVStorage baseStorage; - private BufferedKVStorage bufferedStorage; + private StagedSnapshot startingPoint; /** - * 最近一个交易上下文; + * 当前区块的存储; */ - private LedgerDataContext lastTxCtx; + private BufferedKVStorage baseStorage; - private LedgerDataContext newTxCtx; + /** + * 上一个交易的上下文; + */ +// private LedgerTransactionContextImpl previousTxCtx; + + private TxSnapshot previousTxSnapshot; + + /** + * 当前交易的上下文; + */ + private volatile LedgerTransactionContextImpl currentTxCtx; - private LedgerTransactionalEditor(HashDigest ledgerHash, CryptoSetting cryptoSetting, LedgerBlockData newlyBlock, + /** + * @param ledgerHash + * @param cryptoSetting + * @param currentBlock + * @param startingPoint + * @param ledgerKeyPrefix + * @param bufferedStorage + * @param verifyTx 是否校验交易请求;当外部调用者在调用前已经实施了验证时,将次参数设置为 false 能够提升性能; + */ + private LedgerTransactionalEditor(HashDigest ledgerHash, CryptoSetting cryptoSetting, LedgerBlockData currentBlock, StagedSnapshot startingPoint, String ledgerKeyPrefix, BufferedKVStorage bufferedStorage) { this.ledgerHash = ledgerHash; this.ledgerKeyPrefix = ledgerKeyPrefix; this.cryptoSetting = cryptoSetting; - this.newlyBlock = newlyBlock; - this.bufferedStorage = bufferedStorage; + this.currentBlock = currentBlock; + this.baseStorage = bufferedStorage; + + this.startingPoint = startingPoint; - this.stagedSnapshots.push(startingPoint); +// this.stagedSnapshots.push(startingPoint); } /** * 创建账本新区块的编辑器; * - * @param ledgerHash - * @param ledgerSetting - * @param previousBlock - * @param ledgerKeyPrefix - * @param ledgerExStorage - * @param ledgerVerStorage + * @param ledgerHash 账本哈希; + * @param ledgerSetting 账本设置; + * @param previousBlock 前置区块; + * @param ledgerKeyPrefix 账本数据前缀; + * @param ledgerExStorage 账本数据存储; + * @param ledgerVerStorage 账本数据版本化存储; + * @param verifyTx 是否校验交易请求;当外部调用者在调用前已经实施了验证时,将次参数设置为 false 能够提升性能; * @return */ - public static LedgerTransactionalEditor createEditor(HashDigest ledgerHash, LedgerSetting ledgerSetting, - LedgerBlock previousBlock, String ledgerKeyPrefix, ExPolicyKVStorage ledgerExStorage, - VersioningKVStorage ledgerVerStorage) { + public static LedgerTransactionalEditor createEditor(LedgerBlock previousBlock, LedgerSetting ledgerSetting, + String ledgerKeyPrefix, ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { // new block; - LedgerBlockData currBlock = new LedgerBlockData(previousBlock.getHeight() + 1, previousBlock.getLedgerHash(), + HashDigest ledgerHash = previousBlock.getLedgerHash(); + if (ledgerHash == null) { + ledgerHash = previousBlock.getHash(); + } + if (ledgerHash == null) { + throw new IllegalArgumentException("Illegal previous block was specified!"); + } + LedgerBlockData currBlock = new LedgerBlockData(previousBlock.getHeight() + 1, ledgerHash, previousBlock.getHash()); // init storage; @@ -101,6 +144,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { * @param ledgerKeyPrefix * @param ledgerExStorage * @param ledgerVerStorage + * @param verifyTx 是否校验交易请求;当外部调用者在调用前已经实施了验证时,将次参数设置为 false 能够提升性能; * @return */ public static LedgerTransactionalEditor createEditor(LedgerInitSetting initSetting, String ledgerKeyPrefix, @@ -114,29 +158,21 @@ public class LedgerTransactionalEditor implements LedgerEditor { } private void commitTxSnapshot(TxSnapshot snapshot) { - lastTxCtx = newTxCtx; - newTxCtx = null; - stagedSnapshots.push(snapshot); + previousTxSnapshot = snapshot; + currentTxCtx = null; } - private void rollbackNewTx() { - newTxCtx = null; + private void rollbackCurrentTx() { + currentTxCtx = null; } - // public LedgerDataSet getLatestDataSet() { - // if (lastTxCtx == null) { - // return null; - // } - // return lastTxCtx.getDataSet(); - // } - - LedgerBlock getNewlyBlock() { - return newlyBlock; + LedgerBlock getCurrentBlock() { + return currentBlock; } @Override public long getBlockHeight() { - return newlyBlock.getHeight(); + return currentBlock.getHeight(); } @Override @@ -161,134 +197,182 @@ public class LedgerTransactionalEditor implements LedgerEditor { return ledgerHash.equals(reqLedgerHash); } + 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; + } + @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() + "]!"); + public synchronized LedgerTransactionContext newTransaction(TransactionRequest txRequest) { + if (SettingContext.txSettings().verifyLedger() && !isRequestedLedger(txRequest)) { + throw new IllegalTransactionException( + "Transaction request is dispatched to a wrong ledger! --[TxHash=" + + txRequest.getTransactionContent().getHash() + "]!", + TransactionState.IGNORED_BY_WRONG_LEDGER); + } + + // TODO: 把验签和创建交易并行化; + if (SettingContext.txSettings().verifySignature() && !verifyTxContent(txRequest)) { + // 抛弃哈希和签名校验失败的交易请求; + throw new IllegalTransactionException( + "Wrong transaction signature! --[TxHash=" + txRequest.getTransactionContent().getHash() + "]!", + TransactionState.IGNORED_BY_WRONG_CONTENT_SIGNATURE); + } + + if (currentTxCtx != null) { + throw new IllegalStateException( + "Unable to open another new transaction before the current transaction is completed! --[TxHash=" + + txRequest.getTransactionContent().getHash() + "]!"); } // 检查状态是否允许创建新的交易请求;; checkState(); - BufferedKVStorage txBuffStorage = null; + // init storage of new transaction; + BufferedKVStorage txBufferedStorage = new BufferedKVStorage(baseStorage, baseStorage, false); + LedgerDataSetImpl txDataset = null; TransactionSet txset = null; - if (lastTxCtx == null) { - // init storage of new transaction; - // txBuffStorage = new BufferedKVStorage(bufferedStorage, bufferedStorage, - // false); - txBuffStorage = bufferedStorage; - + if (previousTxSnapshot == null) { // load the starting point of the new transaction; - StagedSnapshot previousSnapshot = stagedSnapshots.peek(); - if (previousSnapshot instanceof GenesisSnapshot) { + if (startingPoint instanceof GenesisSnapshot) { // 准备生成创世区块; - GenesisSnapshot snpht = (GenesisSnapshot) previousSnapshot; - txDataset = LedgerRepositoryImpl.newDataSet(snpht.initSetting, ledgerKeyPrefix, txBuffStorage, - txBuffStorage); + GenesisSnapshot snpht = (GenesisSnapshot) startingPoint; + txDataset = LedgerRepositoryImpl.newDataSet(snpht.initSetting, ledgerKeyPrefix, txBufferedStorage, + txBufferedStorage); txset = LedgerRepositoryImpl.newTransactionSet(txDataset.getAdminAccount().getSetting(), - ledgerKeyPrefix, txBuffStorage, txBuffStorage); - } else { + ledgerKeyPrefix, txBufferedStorage, txBufferedStorage); + } else if (startingPoint instanceof TxSnapshot) { // 新的区块; // TxSnapshot; reload dataset and txset; - TxSnapshot snpht = (TxSnapshot) previousSnapshot; + TxSnapshot snpht = (TxSnapshot) startingPoint; // load dataset; - txDataset = LedgerRepositoryImpl.loadDataSet(snpht.dataSnapshot, ledgerKeyPrefix, txBuffStorage, - txBuffStorage, false); + txDataset = LedgerRepositoryImpl.loadDataSet(snpht.dataSnapshot, ledgerKeyPrefix, txBufferedStorage, + txBufferedStorage, false); - // load tx set; - txset = LedgerRepositoryImpl.loadTransactionSet(snpht.transactionSetHash, this.cryptoSetting, - ledgerKeyPrefix, txBuffStorage, txBuffStorage, false); + // load txset; + txset = LedgerRepositoryImpl.loadTransactionSet(snpht.txsetHash, this.cryptoSetting, ledgerKeyPrefix, + txBufferedStorage, txBufferedStorage, false); + } else { + // Unreachable; + throw new IllegalStateException("Unreachable code was accidentally executed!"); } - lastTxCtx = new LedgerDataContext(txDataset, txset, txBuffStorage); } else { // Reuse previous object to optimize performance; - txBuffStorage = lastTxCtx.storage; - txDataset = lastTxCtx.dataset; - txset = lastTxCtx.txset; + // load dataset; + txDataset = LedgerRepositoryImpl.loadDataSet(previousTxSnapshot.dataSnapshot, ledgerKeyPrefix, + txBufferedStorage, txBufferedStorage, false); + + // load txset; + txset = LedgerRepositoryImpl.loadTransactionSet(previousTxSnapshot.txsetHash, this.cryptoSetting, + ledgerKeyPrefix, txBufferedStorage, txBufferedStorage, false); } - // newTxCtx = new LedgerTransactionContextImpl(newlyBlock.getHeight(), - // txRequest, txDataset, txset, txBuffStorage, - // this); - // return newTxCtx; + currentTxCtx = new LedgerTransactionContextImpl(txRequest, txDataset, txset, txBufferedStorage, this); - return new LedgerTransactionContextImpl(newlyBlock.getHeight(), txRequest, txDataset, txset, txBuffStorage, - this); + return currentTxCtx; } @Override public LedgerBlock prepare() { checkState(); - if (newTxCtx != null) { - throw new IllegalStateException("There is a opening transaction which isn't committed or rollbacked!"); + if (currentTxCtx != null) { + // 有进行中的交易尚未提交或回滚; + throw new IllegalStateException( + "There is an ongoing transaction that has been not committed or rolled back!"); } - if (lastTxCtx == null) { - // Genesis; - throw new IllegalStateException("No transaction to prepare!"); + if (previousTxSnapshot == null) { + // 当前区块没有加入过交易,不允许产生空区块; + throw new IllegalStateException( + "There is no transaction in the current block, and no empty blocks is allowed!"); } // do commit when transaction isolation level is BLOCK; - lastTxCtx.dataset.commit(); - lastTxCtx.txset.commit(); + currentBlock.setAdminAccountHash(previousTxSnapshot.getAdminAccountHash()); + currentBlock.setUserAccountSetHash(previousTxSnapshot.getUserAccountSetHash()); + currentBlock.setDataAccountSetHash(previousTxSnapshot.getDataAccountSetHash()); + currentBlock.setContractAccountSetHash(previousTxSnapshot.getContractAccountSetHash()); + currentBlock.setTransactionSetHash(previousTxSnapshot.getTransactionSetHash()); - newlyBlock.setAdminAccountHash(lastTxCtx.dataset.getAdminAccount().getHash()); - newlyBlock.setContractAccountSetHash(lastTxCtx.dataset.getContractAccountSet().getRootHash()); - newlyBlock.setDataAccountSetHash(lastTxCtx.dataset.getDataAccountSet().getRootHash()); - newlyBlock.setUserAccountSetHash(lastTxCtx.dataset.getUserAccountSet().getRootHash()); - newlyBlock.setTransactionSetHash(lastTxCtx.txset.getRootHash()); + // TODO: 根据所有交易的时间戳的平均值来生成区块的时间戳; +// long timestamp = +// currentBlock.setTimestamp(timestamp); // compute block hash; - byte[] blockBodyBytes = BinaryProtocol.encode(newlyBlock, BlockBody.class); + byte[] blockBodyBytes = BinaryProtocol.encode(currentBlock, BlockBody.class); HashDigest blockHash = Crypto.getHashFunction(cryptoSetting.getHashAlgorithm()).hash(blockBodyBytes); - newlyBlock.setHash(blockHash); - if (newlyBlock.getLedgerHash() == null) { - // init GenesisBlock's ledger hash; - newlyBlock.setLedgerHash(blockHash); - } + currentBlock.setHash(blockHash); + +// if (currentBlock.getLedgerHash() == null) { +// // init GenesisBlock's ledger hash; +// currentBlock.setLedgerHash(blockHash); +// } // persist block bytes; // only one version per block; - byte[] blockBytes = BinaryProtocol.encode(newlyBlock, LedgerBlock.class); - Bytes blockStorageKey = LedgerRepositoryImpl.encodeBlockStorageKey(newlyBlock.getHash()); - long v = bufferedStorage.set(blockStorageKey, blockBytes, -1); + byte[] blockBytes = BinaryProtocol.encode(currentBlock, LedgerBlock.class); + Bytes blockStorageKey = LedgerRepositoryImpl.encodeBlockStorageKey(currentBlock.getHash()); + long v = baseStorage.set(blockStorageKey, blockBytes, -1); if (v < 0) { throw new IllegalStateException( - "Block already exist! --[BlockHash=" + Base58Utils.encode(newlyBlock.getHash().toBytes()) + "]"); + "Block already exist! --[BlockHash=" + Base58Utils.encode(currentBlock.getHash().toBytes()) + "]"); } // persist block hash to ledger index; - HashDigest ledgerHash = newlyBlock.getLedgerHash(); + HashDigest ledgerHash = currentBlock.getLedgerHash(); + if (ledgerHash == null) { + ledgerHash = blockHash; + } Bytes ledgerIndexKey = LedgerRepositoryImpl.encodeLedgerIndexKey(ledgerHash); - long expectedVersion = newlyBlock.getHeight() - 1; - v = bufferedStorage.set(ledgerIndexKey, newlyBlock.getHash().toBytes(), expectedVersion); + long expectedVersion = currentBlock.getHeight() - 1; + v = baseStorage.set(ledgerIndexKey, currentBlock.getHash().toBytes(), expectedVersion); if (v < 0) { - throw new IllegalStateException("Index of BlockHash already exist! --[BlockHash=" - + Base58Utils.encode(newlyBlock.getHash().toBytes()) + "]"); + throw new IllegalStateException( + String.format("Index of BlockHash already exist! --[BlockHeight=%s][BlockHash=%s]", + currentBlock.getHeight(), currentBlock.getHash())); } prepared = true; - return newlyBlock; + return currentBlock; } @Override public void commit() { if (committed) { - throw new IllegalStateException("LedgerEditor had been committed!"); + throw new IllegalStateException("The current block has been committed!"); } if (canceled) { - throw new IllegalStateException("LedgerEditor had been canceled!"); + throw new IllegalStateException("The current block has been canceled!"); } if (!prepared) { // 未就绪; - throw new IllegalStateException("LedgerEditor has not prepared!"); + throw new IllegalStateException("The current block is not ready yet!"); } - bufferedStorage.flush(); + baseStorage.flush(); committed = true; } @@ -296,38 +380,47 @@ public class LedgerTransactionalEditor implements LedgerEditor { @Override public void cancel() { if (committed) { - throw new IllegalStateException("LedgerEditor had been committed!"); + throw new IllegalStateException("The current block has been committed!"); } if (canceled) { return; } canceled = true; - // if (newTxCtx != null) { - // newTxCtx.rollback(); - // newTxCtx = null; - // } - bufferedStorage.cancel(); + + baseStorage.cancel(); } private void checkState() { if (prepared) { - throw new IllegalStateException("LedgerEditor has been prepared!"); + throw new IllegalStateException("The current block is ready!"); } if (committed) { - throw new IllegalStateException("LedgerEditor has been committed!"); + throw new IllegalStateException("The current block has been committed!"); } if (canceled) { - throw new IllegalStateException("LedgerEditor has been canceled!"); + throw new IllegalStateException("The current block has been canceled!"); } } // --------------------------- inner type -------------------------- + /** + * 用于暂存交易上下文数据的快照对象; + * + * @author huanghaiquan + * + */ private static interface StagedSnapshot { } + /** + * 创世区块的快照对象; + * + * @author huanghaiquan + * + */ private static class GenesisSnapshot implements StagedSnapshot { private LedgerInitSetting initSetting; @@ -337,6 +430,12 @@ public class LedgerTransactionalEditor implements LedgerEditor { } } + /** + * 交易执行完毕后的快照对象; + * + * @author huanghaiquan + * + */ private static class TxSnapshot implements StagedSnapshot { /** @@ -347,58 +446,90 @@ public class LedgerTransactionalEditor implements LedgerEditor { /** * 交易集合的快照(根哈希); */ - private HashDigest transactionSetHash; + private HashDigest txsetHash; - public TxSnapshot(LedgerDataSnapshot dataSnapshot, HashDigest txSetHash) { - this.dataSnapshot = dataSnapshot; - this.transactionSetHash = txSetHash; + public HashDigest getAdminAccountHash() { + return dataSnapshot.getAdminAccountHash(); } - } - - private static class LedgerDataContext { + public HashDigest getUserAccountSetHash() { + return dataSnapshot.getUserAccountSetHash(); + } - protected LedgerDataSetImpl dataset; + public HashDigest getDataAccountSetHash() { + return dataSnapshot.getDataAccountSetHash(); + } - protected TransactionSet txset; + public HashDigest getContractAccountSetHash() { + return dataSnapshot.getContractAccountSetHash(); + } - protected BufferedKVStorage storage; + public HashDigest getTransactionSetHash() { + return txsetHash; + } - public LedgerDataContext(LedgerDataSetImpl dataset, TransactionSet txset, BufferedKVStorage storage) { - this.dataset = dataset; - this.txset = txset; - this.storage = storage; + public TxSnapshot(LedgerDataSnapshot dataSnapshot, HashDigest txsetHash) { + this.dataSnapshot = dataSnapshot; + this.txsetHash = txsetHash; } } - private static class LedgerTransactionContextImpl extends LedgerDataContext implements LedgerTransactionContext { +// /** +// * 账本的数据上下文; +// * +// * @author huanghaiquan +// * +// */ +// private static class LedgerDataContext { +// +// protected LedgerDataSetImpl dataset; +// +// protected TransactionSet txset; +// +// protected BufferedKVStorage storage; +// +// public LedgerDataContext(LedgerDataSetImpl dataset, TransactionSet txset, BufferedKVStorage storage) { +// this.dataset = dataset; +// this.txset = txset; +// this.storage = storage; +// } +// +// } - private long blockHeight; + /** + * 交易的上下文; + * + * @author huanghaiquan + * + */ + private static class LedgerTransactionContextImpl implements LedgerTransactionContext { - private LedgerTransactionalEditor editor; + private LedgerTransactionalEditor blockEditor; private TransactionRequest txRequest; - // private LedgerDataSetImpl dataset; - // - // private TransactionSet txset; - // - // private BufferedKVStorage storage; + private LedgerDataSetImpl dataset; + + private TransactionSet txset; + + private BufferedKVStorage storage; private boolean committed = false; private boolean rollbacked = false; - private LedgerTransactionContextImpl(long blockHeight, TransactionRequest txRequest, LedgerDataSetImpl dataset, + private LedgerTransaction transaction; + + private HashDigest txRootHash; + + private LedgerTransactionContextImpl(TransactionRequest txRequest, LedgerDataSetImpl dataset, TransactionSet txset, BufferedKVStorage storage, LedgerTransactionalEditor editor) { - super(dataset, txset, storage); this.txRequest = txRequest; - // this.dataset = dataset; - // this.txset = txset; - // this.storage = storage; - this.editor = editor; - this.blockHeight = blockHeight; + this.dataset = dataset; + this.txset = txset; + this.storage = storage; + this.blockEditor = editor; } @Override @@ -407,7 +538,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { } @Override - public TransactionRequest getRequestTX() { + public TransactionRequest getTransactionRequest() { return txRequest; } @@ -421,24 +552,28 @@ public class LedgerTransactionalEditor implements LedgerEditor { checkTxState(); // capture snapshot - // this.dataset.commit(); - // TransactionStagedSnapshot txDataSnapshot = takeSnapshot(); - - // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, - // txResult, txDataSnapshot); - - LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null, - operationResultArray(operationResults)); - this.txset.add(tx); - // this.txset.commit(); - - // this.storage.flush(); + this.dataset.commit(); + TransactionStagedSnapshot txDataSnapshot = takeDataSnapshot(); + + LedgerTransactionData tx; + try { + tx = new LedgerTransactionData(blockEditor.getBlockHeight(), txRequest, txResult, txDataSnapshot, + operationResultArray(operationResults)); + this.txset.add(tx); + this.txset.commit(); + } catch (Exception e) { + throw new TransactionRollbackException(e.getMessage(), e); + } - // TODO: 未处理出错时 dataset 和 txset 的内部状态恢复,有可能出现不一致的情况; + try { + this.storage.flush(); + } catch (Exception e) { + throw new BlockRollbackException(e.getMessage(), e); + } // put snapshot into stack; - // TxSnapshot snapshot = new TxSnapshot(txDataSnapshot, txset.getRootHash()); - // editor.commitTxSnapshot(snapshot); + TxSnapshot snapshot = new TxSnapshot(txDataSnapshot, txset.getRootHash()); + blockEditor.commitTxSnapshot(snapshot); committed = true; return tx; @@ -454,29 +589,35 @@ public class LedgerTransactionalEditor implements LedgerEditor { checkTxState(); // 未处理 - // dataset.cancel(); - - // TransactionStagedSnapshot txDataSnapshot = takeSnapshot(); - // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, - // txResult, txDataSnapshot); - LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null, - operationResultArray(operationResults)); - this.txset.add(tx); - // this.txset.commit(); - - // this.storage.flush(); + dataset.cancel(); + + TransactionStagedSnapshot txDataSnapshot = takeDataSnapshot(); + + LedgerTransactionData tx; + try { + tx = new LedgerTransactionData(blockEditor.getBlockHeight(), txRequest, txResult, txDataSnapshot, + operationResultArray(operationResults)); + this.txset.add(tx); + this.txset.commit(); + } catch (Exception e) { + throw new TransactionRollbackException(e.getMessage(), e); + } - // TODO: 未处理出错时 dataset 和 txset 的内部状态恢复,有可能出现不一致的情况; + try { + this.storage.flush(); + } catch (Exception e) { + throw new BlockRollbackException(e.getMessage(), e); + } // put snapshot into stack; - // TxSnapshot snapshot = new TxSnapshot(txDataSnapshot, txset.getRootHash()); - // editor.commitTxSnapshot(snapshot); + TxSnapshot snapshot = new TxSnapshot(txDataSnapshot, txset.getRootHash()); + blockEditor.commitTxSnapshot(snapshot); committed = true; return tx; } - private TransactionStagedSnapshot takeSnapshot() { + private TransactionStagedSnapshot takeDataSnapshot() { TransactionStagedSnapshot txDataSnapshot = new TransactionStagedSnapshot(); txDataSnapshot.setAdminAccountHash(dataset.getAdminAccount().getHash()); txDataSnapshot.setContractAccountSetHash(dataset.getContractAccountSet().getRootHash()); @@ -500,22 +641,22 @@ public class LedgerTransactionalEditor implements LedgerEditor { return; } if (this.committed) { - throw new IllegalStateException("Transaction had been committed!"); + throw new IllegalStateException("This transaction had been committed!"); } - // dataset.cancel(); - // storage.cancel(); + dataset.cancel(); + storage.cancel(); - // editor.rollbackNewTx(); + blockEditor.rollbackCurrentTx(); rollbacked = true; } private void checkTxState() { if (this.committed) { - throw new IllegalStateException("Transaction had been committed!"); + throw new IllegalStateException("This transaction had been committed!"); } if (this.rollbacked) { - throw new IllegalStateException("Transaction had been rollbacked!"); + throw new IllegalStateException("This transaction had been rollbacked!"); } } } 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 78b099ae..aa8fcf93 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 @@ -8,18 +8,19 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.jd.blockchain.crypto.HashDigest; +import com.jd.blockchain.ledger.BlockRollbackException; import com.jd.blockchain.ledger.BytesValue; import com.jd.blockchain.ledger.ContractDoesNotExistException; import com.jd.blockchain.ledger.DataAccountDoesNotExistException; -import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.ledger.IllegalTransactionException; 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.TransactionRollbackException; import com.jd.blockchain.ledger.TransactionState; import com.jd.blockchain.ledger.UserDoesNotExistException; import com.jd.blockchain.ledger.core.LedgerDataSet; @@ -31,8 +32,6 @@ 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; @@ -70,18 +69,6 @@ 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) * @@ -93,19 +80,6 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { public TransactionResponse schedule(TransactionRequest request) { 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; - } - LOGGER.debug("Start handling transaction... --[BlockHeight={}][RequestHash={}][TxHash={}]", newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash()); // 创建交易上下文; @@ -118,19 +92,34 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { LOGGER.debug("Complete handling transaction. --[BlockHeight={}][RequestHash={}][TxHash={}]", newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash()); - responseList.add(resp); - return resp; + } catch (IllegalTransactionException e) { + // 抛弃发生处理异常的交易请求; + resp = discard(request, e.getTxState()); + LOGGER.error(String.format( + "Ignore transaction caused by IllegalTransactionException! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", + newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), + e.getMessage()), e); + + } catch (BlockRollbackException e) { + // 抛弃发生处理异常的交易请求; +// resp = discard(request, TransactionState.IGNORED_BY_BLOCK_FULL_ROLLBACK); + LOGGER.error(String.format( + "Ignore transaction caused by BlockRollbackException! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", + newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), + e.getMessage()), e); + throw e; } 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", + "Ignore transaction 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; } + + responseList.add(resp); + return resp; } /** @@ -186,6 +175,23 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { // 提交交易(事务); result = TransactionState.SUCCESS; txCtx.commit(result, operationResults); + } catch (TransactionRollbackException e) { + result = TransactionState.IGNORED_BY_TX_FULL_ROLLBACK; + txCtx.rollback(); + LOGGER.error(String.format( + "Transaction was full rolled back! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", + newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), + e.getMessage()), e); + } catch (BlockRollbackException e) { + result = TransactionState.IGNORED_BY_BLOCK_FULL_ROLLBACK; + txCtx.rollback(); + LOGGER.error( + String.format("Transaction was rolled back! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", + newBlockEditor.getBlockHeight(), request.getHash(), + request.getTransactionContent().getHash(), e.getMessage()), + e); + // 重新抛出由上层错误处理; + throw e; } catch (LedgerException e) { // TODO: 识别更详细的异常类型以及执行对应的处理; result = TransactionState.LEDGER_ERROR; @@ -198,14 +204,14 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { } txCtx.discardAndCommit(result, operationResults); LOGGER.error(String.format( - "Transaction rollback caused by the ledger exception! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", + "Due to ledger exception, the data changes resulting from the transaction will be rolled back and the results of the transaction will be committed! --[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.error(String.format( - "Transaction rollback caused by the system exception! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", + "Due to system exception, the data changes resulting from the transaction will be rolled back and the results of the transaction will be committed! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), e.getMessage()), e); } @@ -218,32 +224,6 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { return 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; - } - /** * 直接丢弃交易; * diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditorTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditorTest.java index 7ce6908b..c75b624a 100644 --- a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditorTest.java +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditorTest.java @@ -138,7 +138,7 @@ public class LedgerEditorTest { LedgerTransaction tx = genisisTxCtx.commit(TransactionState.SUCCESS); - TransactionRequest genesisTxReq = genisisTxCtx.getRequestTX(); + TransactionRequest genesisTxReq = genisisTxCtx.getTransactionRequest(); assertEquals(genesisTxReq.getTransactionContent().getHash(), tx.getTransactionContent().getHash()); assertEquals(0, tx.getBlockHeight()); diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerManagerTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerManagerTest.java index 73e4a8c1..dfc17f24 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 @@ -116,7 +116,8 @@ public class LedgerManagerTest { assertEquals(0, genesisBlock.getHeight()); assertNotNull(genesisBlock.getHash()); assertNull(genesisBlock.getPreviousHash()); - assertEquals(ledgerHash, genesisBlock.getLedgerHash()); + // 创世区块的账本hash 为null;创世区块本身的哈希就代表了账本的哈希; + assertNull(genesisBlock.getLedgerHash()); // 提交数据,写入存储; ldgEdt.commit(); @@ -131,7 +132,8 @@ public class LedgerManagerTest { LedgerBlock latestBlock = reloadLedgerRepo.getLatestBlock(); assertEquals(0, latestBlock.getHeight()); assertEquals(ledgerHash, latestBlock.getHash()); - assertEquals(ledgerHash, latestBlock.getLedgerHash()); + // 创世区块的账本hash 为null;创世区块本身的哈希就代表了账本的哈希; + assertNull(latestBlock.getLedgerHash()); LedgerEditor editor1 = reloadLedgerRepo.createNextBlock(); 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 9fae875a..6101cdba 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 @@ -24,6 +24,7 @@ import com.jd.blockchain.transaction.ConsensusParticipantData; import com.jd.blockchain.transaction.LedgerInitSettingData; import com.jd.blockchain.transaction.TransactionService; import com.jd.blockchain.transaction.TxBuilder; +import com.jd.blockchain.utils.Bytes; import com.jd.blockchain.utils.io.BytesUtils; import com.jd.blockchain.utils.net.NetworkAddress; @@ -124,6 +125,44 @@ public class LedgerTestUtils { return txReqBuilder.buildRequest(); } + + public static TransactionRequest createTxRequest_DataAccountReg(BlockchainKeypair dataAccountID, HashDigest ledgerHash, + BlockchainKeypair nodeKeypair, BlockchainKeypair... signers) { + TxBuilder txBuilder = new TxBuilder(ledgerHash); + + txBuilder.dataAccounts().register(dataAccountID.getIdentity()); + + TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); + if (signers != null) { + for (BlockchainKeypair signer : signers) { + txReqBuilder.signAsEndpoint(signer); + } + } + if (nodeKeypair != null) { + txReqBuilder.signAsNode(nodeKeypair); + } + + return txReqBuilder.buildRequest(); + } + + public static TransactionRequest createTxRequest_DataAccountWrite(Bytes dataAccountAddress, String key, String value, long version, HashDigest ledgerHash, + BlockchainKeypair nodeKeypair, BlockchainKeypair... signers) { + TxBuilder txBuilder = new TxBuilder(ledgerHash); + + txBuilder.dataAccount(dataAccountAddress).setText(key, value, version); + + TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); + if (signers != null) { + for (BlockchainKeypair signer : signers) { + txReqBuilder.signAsEndpoint(signer); + } + } + if (nodeKeypair != null) { + txReqBuilder.signAsNode(nodeKeypair); + } + + return txReqBuilder.buildRequest(); + } /** * @param userKeypair 要注册的用户key; diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleDataSetTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleDataSetTest.java index fbb5eb09..ce571d71 100644 --- a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleDataSetTest.java +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleDataSetTest.java @@ -58,6 +58,7 @@ public class MerkleDataSetTest { mds.setValue("C", "C".getBytes(), -1); mds.commit(); + HashDigest root1 = mds.getRootHash(); // 1个KV项的存储KEY的数量= 1 + 1(保存SN) + Merkle节点数量; // 所以:3 项; @@ -68,6 +69,8 @@ public class MerkleDataSetTest { mds.setValue("B", "B".getBytes(), 0); mds.setValue("C", "C".getBytes(), 0); mds.commit(); + HashDigest root2 = mds.getRootHash(); + assertNotEquals(root1, root2); // Version changed only;仅仅增加 merkle 节点,此时 Merkle 树只有 1 层路径节点,因此只更新2个数据节点和 1 // 个路径节点;(注:版本值是在同一个 key 下按序列保存的); @@ -76,6 +79,9 @@ public class MerkleDataSetTest { mds.setValue("D", "DValue".getBytes(), -1); mds.commit(); + HashDigest root3 = mds.getRootHash(); + assertNotEquals(root2, root3); + assertNotEquals(root1, root3); // New key added, include 1 versioning kv, 1 sn key, 2 merkle nodes; // String[] keys = StringUtils.toStringArray(storage.keySet()); 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 ea20f8b1..f857a6ad 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 @@ -12,6 +12,8 @@ import com.jd.blockchain.binaryproto.DataContractRegistry; import com.jd.blockchain.crypto.HashDigest; import com.jd.blockchain.ledger.BlockchainKeyGenerator; import com.jd.blockchain.ledger.BlockchainKeypair; +import com.jd.blockchain.ledger.BytesValue; +import com.jd.blockchain.ledger.DataAccountRegisterOperation; import com.jd.blockchain.ledger.EndpointRequest; import com.jd.blockchain.ledger.LedgerBlock; import com.jd.blockchain.ledger.LedgerInitSetting; @@ -23,6 +25,7 @@ import com.jd.blockchain.ledger.TransactionRequest; import com.jd.blockchain.ledger.TransactionResponse; import com.jd.blockchain.ledger.TransactionState; import com.jd.blockchain.ledger.UserRegisterOperation; +import com.jd.blockchain.ledger.core.DataAccount; import com.jd.blockchain.ledger.core.LedgerDataSet; import com.jd.blockchain.ledger.core.LedgerEditor; import com.jd.blockchain.ledger.core.LedgerRepository; @@ -44,6 +47,7 @@ public class TransactionBatchProcessorTest { DataContractRegistry.register(EndpointRequest.class); DataContractRegistry.register(TransactionResponse.class); DataContractRegistry.register(UserRegisterOperation.class); + DataContractRegistry.register(DataAccountRegisterOperation.class); } private static final String LEDGER_KEY_PREFIX = "LDG://"; @@ -223,7 +227,7 @@ public class TransactionBatchProcessorTest { .get(transactionRequest2.getTransactionContent().getHash()); LedgerTransaction tx3 = ledgerRepo.getTransactionSet() .get(transactionRequest3.getTransactionContent().getHash()); - + assertNotNull(tx1); assertEquals(TransactionState.SUCCESS, tx1.getExecutionState()); assertNotNull(tx2); @@ -240,6 +244,131 @@ public class TransactionBatchProcessorTest { assertTrue(existUser3); } + @Test + public void testTxRollbackByVersionsConfliction() { + 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 dataAccountKeypair = BlockchainKeyGenerator.getInstance().generate(); + TransactionRequest transactionRequest1 = LedgerTestUtils.createTxRequest_DataAccountReg(dataAccountKeypair, + ledgerHash, parti0, parti0); + TransactionResponse txResp1 = txbatchProcessor.schedule(transactionRequest1); + LedgerBlock newBlock = newBlockEditor.prepare(); + newBlockEditor.commit(); + + assertEquals(TransactionState.SUCCESS, txResp1.getExecutionState()); + DataAccount dataAccount = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()); + assertNotNull(dataAccount); + + // 正确写入 KV 数据; + TransactionRequest txreq1 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(), + "K1", "V-1-1", -1, ledgerHash, parti0, parti0); + TransactionRequest txreq2 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(), + "K2", "V-2-1", -1, ledgerHash, parti0, parti0); + TransactionRequest txreq3 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(), + "K3", "V-3-1", -1, ledgerHash, parti0, parti0); + TransactionRequest txreq4 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(), + "K1", "V-1-2", 0, ledgerHash, parti0, parti0); + + newBlockEditor = ledgerRepo.createNextBlock(); + previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); + txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, opReg, ledgerManager); + + txbatchProcessor.schedule(txreq1); + txbatchProcessor.schedule(txreq2); + txbatchProcessor.schedule(txreq3); + txbatchProcessor.schedule(txreq4); + + newBlock = newBlockEditor.prepare(); + newBlockEditor.commit(); + + BytesValue v1_0 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K1", + 0); + BytesValue v1_1 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K1", + 1); + BytesValue v2 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K2", + 0); + BytesValue v3 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K3", + 0); + + assertNotNull(v1_0); + assertNotNull(v1_1); + assertNotNull(v2); + assertNotNull(v3); + + assertEquals("V-1-1", v1_0.getValue().toUTF8String()); + assertEquals("V-1-2", v1_1.getValue().toUTF8String()); + assertEquals("V-2-1", v2.getValue().toUTF8String()); + assertEquals("V-3-1", v3.getValue().toUTF8String()); + + // 提交多笔数据写入的交易,包含存在数据版本冲突的交易,验证交易是否正确回滚; + + TransactionRequest txreq5 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(), + "K3", "V-3-2", 0, ledgerHash, parti0, parti0); + // 指定冲突的版本号,正确的应该是版本1; + TransactionRequest txreq6 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(), + "K1", "V-1-3", 0, ledgerHash, parti0, parti0); + + newBlockEditor = ledgerRepo.createNextBlock(); + previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); + txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, opReg, ledgerManager); + + txbatchProcessor.schedule(txreq5); + txbatchProcessor.schedule(txreq6); + + newBlock = newBlockEditor.prepare(); + newBlockEditor.commit(); + + BytesValue v1 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K1"); + v3 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K3"); + + long k1_version = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getDataVersion("K1"); + assertEquals(1, k1_version); + long k3_version = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getDataVersion("K3"); + assertEquals(1, k3_version); + + assertNotNull(v1); + assertNotNull(v3); + assertEquals("V-1-2", v1.getValue().toUTF8String()); + assertEquals("V-3-2", v3.getValue().toUTF8String()); + +// // 验证正确性; +// ledgerManager = new LedgerManager(); +// ledgerRepo = ledgerManager.register(ledgerHash, STORAGE); +// +// LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); +// assertEquals(newBlock.getHash(), latestBlock.getHash()); +// assertEquals(1, newBlock.getHeight()); +// +// LedgerTransaction tx1 = ledgerRepo.getTransactionSet() +// .get(transactionRequest1.getTransactionContent().getHash()); +// +// assertNotNull(tx1); +// assertEquals(TransactionState.SUCCESS, tx1.getExecutionState()); + + } + private HashDigest initLedger(MemoryKVStorage storage, BlockchainKeypair... partiKeys) { // 创建初始化配置; LedgerInitSetting initSetting = LedgerTestUtils.createLedgerInitSetting(partiKeys); @@ -269,7 +398,9 @@ public class TransactionBatchProcessorTest { assertNotNull(block.getHash()); assertNull(block.getPreviousHash()); - assertEquals(block.getHash(), block.getLedgerHash()); + // 创世区块的账本哈希为 null; + assertNull(block.getLedgerHash()); + assertNotNull(block.getHash()); // 提交数据,写入存储; ldgEdt.commit(); diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockBody.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockBody.java index ff8f3705..7c82b214 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockBody.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockBody.java @@ -20,4 +20,7 @@ public interface BlockBody extends LedgerDataSnapshot{ @DataField(order=5, primitiveType = PrimitiveType.BYTES) HashDigest getTransactionSetHash(); + + @DataField(order=6, primitiveType = PrimitiveType.INT64) + long getTimestamp(); } diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockRollbackException.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockRollbackException.java new file mode 100644 index 00000000..c64f9e42 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockRollbackException.java @@ -0,0 +1,33 @@ +package com.jd.blockchain.ledger; + +public class BlockRollbackException extends LedgerException { + + private static final long serialVersionUID = 3583192000738807503L; + + private TransactionState state; + + public BlockRollbackException(String message) { + this(TransactionState.SYSTEM_ERROR, message); + } + + public BlockRollbackException(TransactionState state, String message) { + super(message); + assert TransactionState.SUCCESS != state; + this.state = state; + } + + public BlockRollbackException(String message, Throwable cause) { + this(TransactionState.SYSTEM_ERROR, message, cause); + } + + public BlockRollbackException(TransactionState state, String message, Throwable cause) { + super(message, cause); + assert TransactionState.SUCCESS != state; + this.state = state; + } + + public TransactionState getState() { + return state; + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/IllegalTransactionException.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/IllegalTransactionException.java new file mode 100644 index 00000000..5a2bb5a1 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/IllegalTransactionException.java @@ -0,0 +1,35 @@ +package com.jd.blockchain.ledger; + +public class IllegalTransactionException extends RuntimeException { + + private static final long serialVersionUID = 6348921847690512944L; + + private TransactionState txState; + + public IllegalTransactionException(String message) { + super(message); + this.txState = TransactionState.SYSTEM_ERROR; + } + + public IllegalTransactionException(String message, TransactionState txState) { + super(message); + assert TransactionState.SUCCESS != txState; + this.txState = txState; + } + + public IllegalTransactionException(String message, Throwable cause) { + super(message, cause); + this.txState = TransactionState.SYSTEM_ERROR; + } + + public IllegalTransactionException(String message, Throwable cause, TransactionState txState) { + super(message, cause); + assert TransactionState.SUCCESS != txState; + this.txState = txState; + } + + public TransactionState getTxState() { + return txState; + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionContentBody.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionContentBody.java index 5ffa8739..0c227ab5 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionContentBody.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionContentBody.java @@ -40,6 +40,6 @@ public interface TransactionContentBody { * @return */ @DataField(order = 3, primitiveType = PrimitiveType.INT64) - long getTime(); + long getTimestamp(); } diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionException.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionException.java deleted file mode 100644 index c88f0792..00000000 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionException.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.jd.blockchain.ledger; - -public class TransactionException extends Exception { - - private static final long serialVersionUID = 3583192000738807503L; - - private TransactionState state; - - public TransactionException(TransactionState state) { - this.state = state; - } - - public TransactionException(TransactionState state, String message) { - super(message); - this.state = state; - } - - public TransactionState getState() { - return state; - } - -} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRollbackException.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRollbackException.java new file mode 100644 index 00000000..7694d898 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRollbackException.java @@ -0,0 +1,16 @@ +package com.jd.blockchain.ledger; + +public class TransactionRollbackException extends RuntimeException { + + + private static final long serialVersionUID = -1223140447229570029L; + + public TransactionRollbackException(String message) { + super(message); + } + + public TransactionRollbackException(String message, Throwable cause) { + super(message, cause); + } + +} 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 8483f84d..6955eb94 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 @@ -20,39 +20,58 @@ public enum TransactionState { SUCCESS((byte) 0), /** - * 共识错误; + * 账本错误; */ - CONSENSUS_ERROR((byte) 1), + LEDGER_ERROR((byte) 0x01), /** - * 账本错误; + * 数据账户不存在; + */ + DATA_ACCOUNT_DOES_NOT_EXIST((byte) 0x02), + + /** + * 用户不存在; + */ + USER_DOES_NOT_EXIST((byte) 0x03), + + /** + * 合约不存在; */ - LEDGER_ERROR((byte) 2), + CONTRACT_DOES_NOT_EXIST((byte) 0x04), /** * 由于在错误的账本上执行交易而被丢弃; */ - DISCARD_BY_WRONG_LEDGER((byte) 3), + IGNORED_BY_WRONG_LEDGER((byte) 0x40), /** * 由于交易内容的验签失败而丢弃; */ - DISCARD_BY_WRONG_CONTENT_SIGNATURE((byte) 4), + IGNORED_BY_WRONG_CONTENT_SIGNATURE((byte) 0x41), /** - * 数据账户不存在; + * 由于交易内容的验签失败而丢弃; */ - DATA_ACCOUNT_DOES_NOT_EXIST((byte) 5), - + IGNORED_BY_CONFLICTING_STATE((byte) 0x42), + /** - * 用户不存在; + * 由于交易的整体回滚而丢弃; + *

+ * + * 注: “整体回滚”是指把交易引入的数据更改以及交易记录本身全部都回滚;
+ * “部分回滚”是指把交易引入的数据更改回滚了,但是交易记录本身以及相应的“交易结果({@link TransactionState})”都会提交;
*/ - USER_DOES_NOT_EXIST((byte) 6), - + IGNORED_BY_TX_FULL_ROLLBACK((byte) 0x43), + /** - * 合约不存在; + * 由于区块的整体回滚而丢弃; + *

+ * + * 注: “整体回滚”是指把交易引入的数据更改以及交易记录本身全部都回滚;
+ * + * “部分回滚”是指把交易引入的数据更改回滚了,但是交易记录本身以及相应的“交易结果({@link TransactionState})”都会提交;
*/ - CONTRACT_DOES_NOT_EXIST((byte) 6), + IGNORED_BY_BLOCK_FULL_ROLLBACK((byte) 0x44), /** * 系统错误; @@ -62,7 +81,12 @@ public enum TransactionState { /** * 超时; */ - TIMEOUT((byte) 0x81); + TIMEOUT((byte) 0x81), + + /** + * 共识错误; + */ + CONSENSUS_ERROR((byte) 0x82); @EnumField(type = PrimitiveType.INT8) public final byte CODE; diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxContentBlob.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxContentBlob.java index c03f1cc7..7413a5ff 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxContentBlob.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/TxContentBlob.java @@ -84,7 +84,7 @@ public class TxContentBlob implements TransactionContent { } @Override - public long getTime() { + public long getTimestamp() { return time; } diff --git a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/VersioningKVStorage.java b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/VersioningKVStorage.java index f9d859ff..711606d3 100644 --- a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/VersioningKVStorage.java +++ b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/VersioningKVStorage.java @@ -59,8 +59,8 @@ public interface VersioningKVStorage extends BatchStorageService { /** * Update the value of the key;
* - * If key exist, and the specified version equals to latest , then the value is - * updated and version is increased by 1;
+ * If key exist, and the specified version equals to it's latest version, then the value will be + * updated and version will be increased by 1;
* If key not exist, and the specified version is -1, then the value will be * created and initialized it's version by 0;
*