@@ -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); | |||
} | |||
@@ -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()) { | |||
@@ -50,9 +50,11 @@ public interface LedgerEditor { | |||
* 每一次事务性的账本写入操作在提交后,都会记录该事务相关的系统全局快照,以交易对象 {@link LedgerTransaction} 进行保存; | |||
* <p> | |||
* | |||
* 注:方法不解析、不执行交易中的操作; | |||
* | |||
* @param txRequest | |||
* | |||
* 注:方法不解析、不执行交易中的操作;<p> | |||
* | |||
* @param txRequest 交易请求; | |||
* @return | |||
*/ | |||
LedgerTransactionContext newTransaction(TransactionRequest txRequest); | |||
@@ -24,7 +24,7 @@ public interface LedgerTransactionContext { | |||
* | |||
* @return | |||
*/ | |||
TransactionRequest getRequestTX(); | |||
TransactionRequest getTransactionRequest(); | |||
/** | |||
* 提交对账本数据的修改,以指定的交易状态提交交易; | |||
@@ -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; | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
@@ -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<OperationResult>() { | |||
@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) { | |||
@@ -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<StagedSnapshot> stagedSnapshots = new Stack<>(); | |||
// private Stack<StagedSnapshot> 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!"); | |||
} | |||
} | |||
} | |||
@@ -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; | |||
} | |||
/** | |||
* 直接丢弃交易; | |||
* | |||
@@ -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()); | |||
@@ -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(); | |||
@@ -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; | |||
@@ -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()); | |||
@@ -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(); | |||
@@ -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(); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -40,6 +40,6 @@ public interface TransactionContentBody { | |||
* @return | |||
*/ | |||
@DataField(order = 3, primitiveType = PrimitiveType.INT64) | |||
long getTime(); | |||
long getTimestamp(); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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), | |||
/** | |||
* 用户不存在; | |||
* 由于交易的整体回滚而丢弃; | |||
* <p> | |||
* | |||
* 注: “整体回滚”是指把交易引入的数据更改以及交易记录本身全部都回滚;<br> | |||
* “部分回滚”是指把交易引入的数据更改回滚了,但是交易记录本身以及相应的“交易结果({@link TransactionState})”都会提交;<br> | |||
*/ | |||
USER_DOES_NOT_EXIST((byte) 6), | |||
IGNORED_BY_TX_FULL_ROLLBACK((byte) 0x43), | |||
/** | |||
* 合约不存在; | |||
* 由于区块的整体回滚而丢弃; | |||
* <p> | |||
* | |||
* 注: “整体回滚”是指把交易引入的数据更改以及交易记录本身全部都回滚;<br> | |||
* | |||
* “部分回滚”是指把交易引入的数据更改回滚了,但是交易记录本身以及相应的“交易结果({@link TransactionState})”都会提交;<br> | |||
*/ | |||
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; | |||
@@ -84,7 +84,7 @@ public class TxContentBlob implements TransactionContent { | |||
} | |||
@Override | |||
public long getTime() { | |||
public long getTimestamp() { | |||
return time; | |||
} | |||
@@ -59,8 +59,8 @@ public interface VersioningKVStorage extends BatchStorageService { | |||
/** | |||
* Update the value of the key;<br> | |||
* | |||
* If key exist, and the specified version equals to latest , then the value is | |||
* updated and version is increased by 1;<br> | |||
* 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;<br> | |||
* If key not exist, and the specified version is -1, then the value will be | |||
* created and initialized it's version by 0; <br> | |||
* | |||