use of com.jd.blockchain.ledger.BlockRollbackException in project jdchain-core by blockchain-jd-com.
the class BftsmartNodeServer method preComputeAppHash.
/**
* From consensus outcomes, do nothing now The operation of executing the batch
* was moved to the consensus stage 2 and 3, in order to guaranteed ledger
* consistency
*/
// @Override
// public byte[][] appExecuteBatch(byte[][] commands, MessageContext[] msgCtxs, boolean fromConsensus) {
// if (replyList == null || replyList.size() == 0) {
// throw new IllegalArgumentException();
// }
// // todo 此部分需要重新改造
// /**
// * 默认BFTSmart接口提供的commands是一个或多个共识结果的顺序集合
// * 根据共识的规定,目前的做法是将其根据msgCtxs的内容进行分组,每组都作为一个结块标识来处理
// * 从msgCtxs可以获取对应commands的分组情况
// */
// int manageConsensusId = msgCtxs[0].getConsensusId();
// List<byte[]> manageConsensusCmds = new ArrayList<>();
// List<ReplyContextMessage> manageReplyMsgs = new ArrayList<>();
//
// int index = 0;
// for (MessageContext msgCtx : msgCtxs) {
// if (msgCtx.getConsensusId() == manageConsensusId) {
// manageConsensusCmds.add(commands[index]);
// manageReplyMsgs.add(replyList.get(index));
// } else {
// // 达到结块标准,需要进行结块并应答
// blockAndReply(manageConsensusCmds, manageReplyMsgs);
// // 重置链表和共识ID
// manageConsensusCmds = new ArrayList<>();
// manageReplyMsgs = new ArrayList<>();
// manageConsensusId = msgCtx.getConsensusId();
// manageConsensusCmds.add(commands[index]);
// manageReplyMsgs.add(replyList.get(index));
// }
// index++;
// }
// // 结束时,肯定有最后一个结块请求未处理
// if (!manageConsensusCmds.isEmpty()) {
// blockAndReply(manageConsensusCmds, manageReplyMsgs);
// }
// return null;
// }
/**
* Block and reply are moved to consensus completion stage
*/
// private void blockAndReply(List<byte[]> manageConsensusCmds, List<ReplyContextMessage> replyList) {
// consensusBatchId = messageHandle.beginBatch(realmName);
// List<AsyncFuture<byte[]>> asyncFutureLinkedList = new ArrayList<>(manageConsensusCmds.size());
// try {
// int msgId = 0;
// for (byte[] txContent : manageConsensusCmds) {
// AsyncFuture<byte[]> asyncFuture = messageHandle.processOrdered(msgId++, txContent, realmName, consensusBatchId);
// asyncFutureLinkedList.add(asyncFuture);
// }
// messageHandle.completeBatch(realmName, consensusBatchId);
// messageHandle.commitBatch(realmName, consensusBatchId);
// } catch (Exception e) {
// // todo 需要处理应答码 404
// LOGGER.error("Error occurred while processing ordered messages! --" + e.getMessage(), e);
// messageHandle.rollbackBatch(realmName, consensusBatchId, TransactionState.CONSENSUS_ERROR.CODE);
// }
//
// // 通知线程单独处理应答
// notifyReplyExecutors.execute(() -> {
// // 应答对应的结果
// int replyIndex = 0;
// for(ReplyContextMessage msg : replyList) {
// msg.setReply(asyncFutureLinkedList.get(replyIndex).get());
// TOMMessage request = msg.getTomMessage();
// ReplyContext replyContext = msg.getReplyContext();
// request.reply = new TOMMessage(replyContext.getId(), request.getSession(), request.getSequence(),
// request.getOperationId(), msg.getReply(), replyContext.getCurrentViewId(),
// request.getReqType());
//
// if (replyContext.getNumRepliers() > 0) {
// bftsmart.tom.util.Logger.println("(ServiceReplica.receiveMessages) sending reply to "
// + request.getSender() + " with sequence number " + request.getSequence()
// + " and operation ID " + request.getOperationId() + " via ReplyManager");
// replyContext.getRepMan().send(request);
// } else {
// bftsmart.tom.util.Logger.println("(ServiceReplica.receiveMessages) sending reply to "
// + request.getSender() + " with sequence number " + request.getSequence()
// + " and operation ID " + request.getOperationId());
// replyContext.getReplier().manageReply(request, msg.getMessageContext());
// }
// replyIndex++;
// }
// });
// }
/**
* Used by consensus write phase, pre compute new block hash
*
* @param cid 当前正在进行的共识ID;
* @param commands 请求列表
*/
@Override
public BatchAppResultImpl preComputeAppHash(int cid, byte[][] commands, long timestamp) {
List<AsyncFuture<byte[]>> asyncFutureLinkedList = new ArrayList<>(commands.length);
List<byte[]> responseLinkedList = new ArrayList<>();
StateSnapshot newStateSnapshot, preStateSnapshot, genisStateSnapshot;
BatchAppResultImpl result;
String batchId = "";
int msgId = 0;
byte[] cidBytes = BytesUtils.toBytes(cid);
batchHandleLock.lock();
try {
if (commands.length == 0) {
// 没有要做预计算的消息,直接组装结果返回
result = BatchAppResultImpl.createFailure(responseLinkedList, cidBytes, batchId, cidBytes);
} else {
BftsmartConsensusMessageContext context = BftsmartConsensusMessageContext.createInstance(realmName, timestamp);
batchId = messageHandle.beginBatch(context);
context.setBatchId(batchId);
contexts.put(batchId, context);
stateHolder.batchingID = batchId;
// 获取前置区块快照状态
preStateSnapshot = messageHandle.getLatestStateSnapshot(realmName);
if (preStateSnapshot instanceof BlockStateSnapshot) {
BlockStateSnapshot preBlockStateSnapshot = (BlockStateSnapshot) preStateSnapshot;
long preBlockTimestamp = preBlockStateSnapshot.getTimestamp();
if (timestamp < preBlockTimestamp && (preBlockTimestamp - timestamp) > timeTolerance) {
// 打印错误信息
LOGGER.warn("The time[{}] of the last block is mismatch with the current[{}] for time tolerance[{}] !!!", preBlockTimestamp, timestamp, timeTolerance);
// 设置返回的应答信息
for (byte[] command : commands) {
// 状态设置为共识错误
responseLinkedList.add(createAppResponse(command, TransactionState.CONSENSUS_TIMESTAMP_ERROR));
}
// 将该状态设置为未执行
stateHolder.setComputeStatus(PreComputeStatus.UN_EXECUTED);
// 回滚该操作
messageHandle.rollbackBatch(TransactionState.CONSENSUS_TIMESTAMP_ERROR.CODE, context);
// 返回成功,但需要设置当前的状态
return BatchAppResultImpl.createSuccess(responseLinkedList, cidBytes, batchId, cidBytes);
} else {
LOGGER.debug("Last block's timestamp = {}, current timestamp = {}, time tolerance = {} !", preBlockTimestamp, timestamp, timeTolerance);
}
}
// 创世区块的状态快照
genisStateSnapshot = messageHandle.getGenesisStateSnapshot(realmName);
for (byte[] txContent : commands) {
AsyncFuture<byte[]> asyncFuture = messageHandle.processOrdered(msgId++, txContent, context);
asyncFutureLinkedList.add(asyncFuture);
}
newStateSnapshot = messageHandle.completeBatch(context);
for (AsyncFuture<byte[]> asyncFuture : asyncFutureLinkedList) {
responseLinkedList.add(asyncFuture.get());
}
result = BatchAppResultImpl.createSuccess(responseLinkedList, newStateSnapshot.getSnapshot(), batchId, genisStateSnapshot.getSnapshot());
}
} catch (BlockRollbackException e) {
LOGGER.error("Error occurred while pre compute app! --" + e.getMessage(), e);
for (byte[] command : commands) {
responseLinkedList.add(createAppResponse(command, e.getState()));
}
result = BatchAppResultImpl.createFailure(responseLinkedList, cidBytes, batchId, cidBytes);
} catch (Exception e) {
LOGGER.error("Error occurred while pre compute app! --" + e.getMessage(), e);
for (byte[] command : commands) {
responseLinkedList.add(createAppResponse(command, TransactionState.IGNORED_BY_BLOCK_FULL_ROLLBACK));
}
result = BatchAppResultImpl.createFailure(responseLinkedList, cidBytes, batchId, cidBytes);
} finally {
batchHandleLock.unlock();
}
return result;
}
use of com.jd.blockchain.ledger.BlockRollbackException in project jdchain-core by blockchain-jd-com.
the class LedgerTransactionalEditor method prepare.
@Override
public LedgerBlock prepare() {
checkState();
if (currentTxCtx != null) {
// 有进行中的交易尚未提交或回滚;
throw new IllegalStateException("There is an ongoing transaction that has been not committed or rolled back!");
}
if (previousTxSnapshot == null) {
// 当前区块没有加入过交易,不允许产生空区块;
throw new BlockRollbackException(TransactionState.EMPTY_BLOCK_ERROR, "There is no transaction in the current block, and no empty blocks is allowed!");
}
// 生成交易集合根哈希;
txset.commit();
// 底层结构为KV时使用:记录参与方,各类账户,以及账户下的KV总数;
saveTotal();
currentBlock.setTransactionSetHash(txset.getRootHash());
// do commit when transaction isolation level is BLOCK;
currentBlock.setAdminAccountHash(previousTxSnapshot.getAdminAccountHash());
currentBlock.setUserAccountSetHash(previousTxSnapshot.getUserAccountSetHash());
currentBlock.setDataAccountSetHash(previousTxSnapshot.getDataAccountSetHash());
currentBlock.setContractAccountSetHash(previousTxSnapshot.getContractAccountSetHash());
currentBlock.setSystemEventSetHash(previousTxSnapshot.getSystemEventSetHash());
currentBlock.setUserEventSetHash(previousTxSnapshot.getUserEventSetHash());
// 根据ThreadLocal中的时间戳设置;
Long timestamp = TIMESTAMP_HOLDER.get();
if (timestamp != null) {
currentBlock.setTimestamp(timestamp);
}
// compute block hash;
byte[] blockBodyBytes = BinaryProtocol.encode(currentBlock, BlockBody.class);
HashDigest blockHash = Crypto.getHashFunction(cryptoSetting.getHashAlgorithm()).hash(blockBodyBytes);
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(currentBlock, LedgerBlock.class);
Bytes blockStorageKey = LedgerRepositoryImpl.encodeBlockStorageKey(currentBlock.getHash());
long v;
if (dataStructure.equals(LedgerDataStructure.MERKLE_TREE)) {
v = baseStorage.set(blockStorageKey, blockBytes, -1);
} else {
// key = BK/blockhash, value = blockheight, version = -1;
v = baseStorage.set(blockStorageKey, BytesUtils.toBytes(currentBlock.getHeight()), -1);
}
if (v < 0) {
throw new IllegalStateException("Block already exist! --[BlockHash=" + Base58Utils.encode(currentBlock.getHash().toBytes()) + "]");
}
// persist block hash to ledger index;
HashDigest ledgerHash = currentBlock.getLedgerHash();
if (ledgerHash == null) {
ledgerHash = blockHash;
}
Bytes ledgerIndexKey = LedgerRepositoryImpl.encodeLedgerIndexKey(ledgerHash);
long expectedVersion = currentBlock.getHeight() - 1;
if (dataStructure.equals(LedgerDataStructure.MERKLE_TREE)) {
v = baseStorage.set(ledgerIndexKey, currentBlock.getHash().toBytes(), expectedVersion);
} else {
// key = IX/, value = blockcontent, version = blockheight
v = baseStorage.set(ledgerIndexKey, blockBytes, expectedVersion);
}
if (v < 0) {
throw new IllegalStateException(String.format("Index of BlockHash already exist! --[BlockHeight=%s][BlockHash=%s]", currentBlock.getHeight(), currentBlock.getHash()));
}
prepared = true;
return currentBlock;
}
use of com.jd.blockchain.ledger.BlockRollbackException in project jdchain-core by blockchain-jd-com.
the class LedgerTransactionalEditor method commit.
@Override
public void commit() {
if (committed) {
throw new IllegalStateException("The current block has been committed!");
}
if (canceled) {
throw new IllegalStateException("The current block has been canceled!");
}
if (!prepared) {
// 未就绪;
throw new IllegalStateException("The current block is not ready yet!");
}
try {
baseStorage.flush();
} catch (Exception e) {
throw new BlockRollbackException(e.getMessage(), e);
}
clearCachedIndex(latestLedgerDataset, latestLedgerEventSet, txset);
updatePreBlockHeight(latestLedgerDataset, latestLedgerEventSet, txset);
committed = true;
}
use of com.jd.blockchain.ledger.BlockRollbackException in project jdchain-core by blockchain-jd-com.
the class BlockFullRollBackTest method testBlockFullkRollBack.
@Test
public void testBlockFullkRollBack() {
final MemoryKVStorage STORAGE = new MemoryKVStorage();
final MemoryKVStorage STORAGE_Mock = Mockito.spy(STORAGE);
// 初始化账本到指定的存储库;
ledgerHash = initLedger(STORAGE_Mock, parti0, parti1, parti2, parti3);
System.out.println("---------- Ledger init OK !!! ----------");
// 加载账本;
LedgerManager ledgerManager = new LedgerManager();
LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, STORAGE_Mock, LedgerDataStructure.MERKLE_TREE);
// 构造存储错误,并产生区块回滚
doThrow(BlockRollbackException.class).when(STORAGE_Mock).set(any(), any(), anyLong());
LedgerEditor newBlockEditor = ledgerRepo.createNextBlock();
OperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration();
LedgerSecurityManager securityManager = getSecurityManager();
TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(securityManager, newBlockEditor, ledgerRepo, opReg);
// 注册新用户;
BlockchainKeypair userKeypair = BlockchainKeyGenerator.getInstance().generate();
TransactionRequest transactionRequest = LedgerTestUtils.createTxRequest_UserReg(userKeypair, ledgerHash, parti0, parti0);
TransactionResponse txResp = txbatchProcessor.schedule(transactionRequest);
LedgerBlock newBlock = newBlockEditor.prepare();
try {
newBlockEditor.commit();
} catch (BlockRollbackException e) {
newBlockEditor.cancel();
}
// 验证正确性;
ledgerManager = new LedgerManager();
ledgerRepo = ledgerManager.register(ledgerHash, STORAGE_Mock, LedgerDataStructure.MERKLE_TREE);
LedgerBlock latestBlock = ledgerRepo.getLatestBlock();
assertEquals(ledgerRepo.getBlockHash(0), latestBlock.getHash());
assertEquals(0, latestBlock.getHeight());
LedgerDataSet ledgerDS = ledgerRepo.getLedgerDataSet(latestBlock);
boolean existUser = ledgerDS.getUserAccountSet().contains(userKeypair.getAddress());
assertFalse(existUser);
doCallRealMethod().when(STORAGE_Mock).set(any(), any(), anyLong());
// 区块正常提交
// 生成新区块;
LedgerEditor newBlockEditor1 = ledgerRepo.createNextBlock();
OperationHandleRegisteration opReg1 = new DefaultOperationHandleRegisteration();
LedgerSecurityManager securityManager1 = getSecurityManager();
TransactionBatchProcessor txbatchProcessor1 = new TransactionBatchProcessor(securityManager1, newBlockEditor1, ledgerRepo, opReg1);
// 注册新用户;
BlockchainKeypair userKeypair1 = BlockchainKeyGenerator.getInstance().generate();
TransactionRequest transactionRequest1 = LedgerTestUtils.createTxRequest_UserReg(userKeypair1, ledgerHash, parti0, parti0);
TransactionResponse txResp1 = txbatchProcessor1.schedule(transactionRequest1);
LedgerBlock newBlock1 = newBlockEditor1.prepare();
try {
newBlockEditor1.commit();
} catch (BlockRollbackException e) {
newBlockEditor1.cancel();
}
ledgerManager = new LedgerManager();
ledgerRepo = ledgerManager.register(ledgerHash, STORAGE_Mock, LedgerDataStructure.MERKLE_TREE);
LedgerBlock latestBlock1 = ledgerRepo.getLatestBlock();
assertEquals(newBlock1.getHash(), latestBlock1.getHash());
assertEquals(1, latestBlock1.getHeight());
LedgerDataSet ledgerDS1 = ledgerRepo.getLedgerDataSet(latestBlock1);
boolean existUser1 = ledgerDS1.getUserAccountSet().contains(userKeypair1.getAddress());
assertTrue(existUser1);
}
Aggregations