@@ -14,9 +14,9 @@ public class DataAccountSet implements MerkleProvable, Transactional { | |||
private AccountSet accountSet; | |||
public DataAccountSet(CryptoSetting cryptoSetting, String prefix,ExPolicyKVStorage exStorage, | |||
public DataAccountSet(CryptoSetting cryptoSetting, String prefix, ExPolicyKVStorage exStorage, | |||
VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) { | |||
accountSet = new AccountSet(cryptoSetting,prefix, exStorage, verStorage, accessPolicy); | |||
accountSet = new AccountSet(cryptoSetting, prefix, exStorage, verStorage, accessPolicy); | |||
} | |||
public DataAccountSet(HashDigest dataRootHash, CryptoSetting cryptoSetting, String prefix, | |||
@@ -26,7 +26,7 @@ public class DataAccountSet implements MerkleProvable, Transactional { | |||
} | |||
public AccountHeader[] getAccounts(int fromIndex, int count) { | |||
return accountSet.getAccounts(fromIndex,count); | |||
return accountSet.getAccounts(fromIndex, count); | |||
} | |||
public boolean isReadonly() { | |||
@@ -56,8 +56,18 @@ public class DataAccountSet implements MerkleProvable, Transactional { | |||
return new DataAccount(accBase); | |||
} | |||
/** | |||
* 返回数据账户; <br> | |||
* 如果不存在,则返回 null; | |||
* | |||
* @param address | |||
* @return | |||
*/ | |||
public DataAccount getDataAccount(Bytes address) { | |||
BaseAccount accBase = accountSet.getAccount(address); | |||
if (accBase == null) { | |||
return null; | |||
} | |||
return new DataAccount(accBase); | |||
} | |||
@@ -123,6 +123,12 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||
return ledgerHash; | |||
} | |||
/** | |||
* 检查当前账本是否是指定交易请求的账本; | |||
* | |||
* @param txRequest | |||
* @return | |||
*/ | |||
private boolean isRequestedLedger(TransactionRequest txRequest) { | |||
HashDigest reqLedgerHash = txRequest.getTransactionContent().getLedgerHash(); | |||
if (ledgerHash == reqLedgerHash) { | |||
@@ -142,8 +148,8 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||
+ txRequest.getTransactionContent().getHash() + "]!"); | |||
} | |||
// 检查状态是否允许创建新的交易请求;; | |||
checkState(); | |||
// TODO:验证交易签名; | |||
BufferedKVStorage txBuffStorage = null; | |||
LedgerDataSetImpl txDataset = null; | |||
@@ -9,6 +9,8 @@ import org.slf4j.LoggerFactory; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
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.LedgerBlock; | |||
import com.jd.blockchain.ledger.LedgerException; | |||
@@ -19,6 +21,7 @@ import com.jd.blockchain.ledger.TransactionContent; | |||
import com.jd.blockchain.ledger.TransactionRequest; | |||
import com.jd.blockchain.ledger.TransactionResponse; | |||
import com.jd.blockchain.ledger.TransactionState; | |||
import com.jd.blockchain.ledger.UserDoesNotExistException; | |||
import com.jd.blockchain.ledger.core.LedgerDataSet; | |||
import com.jd.blockchain.ledger.core.LedgerEditor; | |||
import com.jd.blockchain.ledger.core.LedgerService; | |||
@@ -111,10 +114,10 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
// 处理交易; | |||
resp = handleTx(request, txCtx); | |||
LOGGER.debug("Complete handling transaction. --[BlockHeight={}][RequestHash={}][TxHash={}]", | |||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash()); | |||
responseList.add(resp); | |||
return resp; | |||
} catch (Exception e) { | |||
@@ -124,7 +127,7 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
"Discard transaction rollback caused by the system exception! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||
e.getMessage()), e); | |||
responseList.add(resp); | |||
return resp; | |||
} | |||
@@ -186,7 +189,14 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
} catch (LedgerException e) { | |||
// TODO: 识别更详细的异常类型以及执行对应的处理; | |||
result = TransactionState.LEDGER_ERROR; | |||
txCtx.discardAndCommit(TransactionState.LEDGER_ERROR, operationResults); | |||
if (e instanceof DataAccountDoesNotExistException) { | |||
result = TransactionState.DATA_ACCOUNT_DOES_NOT_EXIST; | |||
} else if (e instanceof UserDoesNotExistException) { | |||
result = TransactionState.USER_DOES_NOT_EXIST; | |||
} else if (e instanceof ContractDoesNotExistException) { | |||
result = TransactionState.CONTRACT_DOES_NOT_EXIST; | |||
} | |||
txCtx.discardAndCommit(result, operationResults); | |||
LOGGER.error(String.format( | |||
"Transaction rollback caused by the ledger exception! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||
@@ -4,6 +4,7 @@ import org.springframework.stereotype.Service; | |||
import com.jd.blockchain.binaryproto.DataContractRegistry; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.DataAccountDoesNotExistException; | |||
import com.jd.blockchain.ledger.DataAccountKVSetOperation; | |||
import com.jd.blockchain.ledger.DataAccountKVSetOperation.KVWriteEntry; | |||
import com.jd.blockchain.ledger.Operation; | |||
@@ -26,9 +27,11 @@ public class DataAccountKVSetOperationHandle implements OperationHandle { | |||
LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||
DataAccountKVSetOperation kvWriteOp = (DataAccountKVSetOperation) op; | |||
DataAccount account = dataset.getDataAccountSet().getDataAccount(kvWriteOp.getAccountAddress()); | |||
if (account == null) { | |||
throw new DataAccountDoesNotExistException("DataAccount doesn't exist!"); | |||
} | |||
KVWriteEntry[] writeSet = kvWriteOp.getWriteSet(); | |||
for (KVWriteEntry kvw : writeSet) { | |||
// byte[] value = BinaryProtocol.encode(kvw.getValue(), BytesValue.class); | |||
account.setBytes(Bytes.fromString(kvw.getKey()), kvw.getValue(), kvw.getExpectedVersion()); | |||
} | |||
return null; | |||
@@ -125,12 +125,20 @@ public class LedgerTestUtils { | |||
return txReqBuilder.buildRequest(); | |||
} | |||
public static TransactionRequest createTxRequest_MultiOPs_WithError(BlockchainKeypair userKeypair, HashDigest ledgerHash, | |||
BlockchainKeypair nodeKeypair, BlockchainKeypair... signers) { | |||
/** | |||
* @param userKeypair 要注册的用户key; | |||
* @param ledgerHash 账本哈希; | |||
* @param nodeKeypair 节点key; | |||
* @param signers 签名者列表; | |||
* @return | |||
*/ | |||
public static TransactionRequest createTxRequest_MultiOPs_WithNotExistedDataAccount(BlockchainKeypair userKeypair, | |||
HashDigest ledgerHash, BlockchainKeypair nodeKeypair, BlockchainKeypair... signers) { | |||
TxBuilder txBuilder = new TxBuilder(ledgerHash); | |||
txBuilder.users().register(userKeypair.getIdentity()); | |||
// 故意构建一个错误的 | |||
BlockchainKeypair testKey = BlockchainKeyGenerator.getInstance().generate(); | |||
txBuilder.dataAccount(testKey.getAddress()).setBytes("AA", "Value".getBytes(), 1); | |||
@@ -57,8 +57,6 @@ public class TransactionBatchProcessorTest { | |||
private BlockchainKeypair[] participants = { parti0, parti1, parti2, parti3 }; | |||
private TransactionRequest transactionRequest; | |||
// TODO: 验证无效签名会被拒绝; | |||
@Test | |||
@@ -88,7 +86,8 @@ public class TransactionBatchProcessorTest { | |||
// 注册新用户; | |||
BlockchainKeypair userKeypair = BlockchainKeyGenerator.getInstance().generate(); | |||
transactionRequest = LedgerTestUtils.createTxRequest_UserReg(userKeypair, ledgerHash, parti0, parti0); | |||
TransactionRequest transactionRequest = LedgerTestUtils.createTxRequest_UserReg(userKeypair, ledgerHash, parti0, | |||
parti0); | |||
TransactionResponse txResp = txbatchProcessor.schedule(transactionRequest); | |||
LedgerBlock newBlock = newBlockEditor.prepare(); | |||
@@ -132,18 +131,20 @@ public class TransactionBatchProcessorTest { | |||
// 注册新用户; | |||
BlockchainKeypair userKeypair1 = BlockchainKeyGenerator.getInstance().generate(); | |||
transactionRequest = LedgerTestUtils.createTxRequest_UserReg(userKeypair1, ledgerHash, parti0, parti0); | |||
TransactionResponse txResp1 = txbatchProcessor.schedule(transactionRequest); | |||
TransactionRequest transactionRequest1 = LedgerTestUtils.createTxRequest_UserReg(userKeypair1, ledgerHash, | |||
parti0, parti0); | |||
TransactionResponse txResp1 = txbatchProcessor.schedule(transactionRequest1); | |||
// BlockchainKeypair userKeypair2 = BlockchainKeyGenerator.getInstance().generate(); | |||
// transactionRequest = LedgerTestUtils.createTxRequest_UserReg(ledgerHash, userKeypair2, parti0); | |||
// TransactionResponse txResp2 = txbatchProcessor.schedule(transactionRequest); | |||
BlockchainKeypair userKeypair2 = BlockchainKeyGenerator.getInstance().generate(); | |||
TransactionRequest transactionRequest2 = LedgerTestUtils.createTxRequest_UserReg(userKeypair2, ledgerHash, | |||
parti0, parti0); | |||
TransactionResponse txResp2 = txbatchProcessor.schedule(transactionRequest2); | |||
LedgerBlock newBlock = newBlockEditor.prepare(); | |||
newBlockEditor.commit(); | |||
assertEquals(TransactionState.SUCCESS, txResp1.getExecutionState()); | |||
// assertEquals(TransactionState.SUCCESS, txResp2.getExecutionState()); | |||
assertEquals(TransactionState.SUCCESS, txResp2.getExecutionState()); | |||
// 验证正确性; | |||
ledgerManager = new LedgerManager(); | |||
@@ -155,9 +156,9 @@ public class TransactionBatchProcessorTest { | |||
LedgerDataSet ledgerDS = ledgerRepo.getDataSet(latestBlock); | |||
boolean existUser1 = ledgerDS.getUserAccountSet().contains(userKeypair1.getAddress()); | |||
// boolean existUser2 = ledgerDS.getUserAccountSet().contains(userKeypair2.getAddress()); | |||
boolean existUser2 = ledgerDS.getUserAccountSet().contains(userKeypair2.getAddress()); | |||
assertTrue(existUser1); | |||
// assertTrue(existUser2); | |||
assertTrue(existUser2); | |||
} | |||
@Test | |||
@@ -187,22 +188,25 @@ public class TransactionBatchProcessorTest { | |||
// 注册新用户; | |||
BlockchainKeypair userKeypair1 = BlockchainKeyGenerator.getInstance().generate(); | |||
transactionRequest = LedgerTestUtils.createTxRequest_UserReg(userKeypair1, ledgerHash, parti0, parti0); | |||
TransactionResponse txResp1 = txbatchProcessor.schedule(transactionRequest); | |||
TransactionRequest transactionRequest1 = LedgerTestUtils.createTxRequest_UserReg(userKeypair1, ledgerHash, | |||
parti0, parti0); | |||
TransactionResponse txResp1 = txbatchProcessor.schedule(transactionRequest1); | |||
BlockchainKeypair userKeypair2 = BlockchainKeyGenerator.getInstance().generate(); | |||
transactionRequest = LedgerTestUtils.createTxRequest_MultiOPs_WithError(userKeypair2, ledgerHash, parti0, parti0); | |||
TransactionResponse txResp2 = txbatchProcessor.schedule(transactionRequest); | |||
TransactionRequest transactionRequest2 = LedgerTestUtils | |||
.createTxRequest_MultiOPs_WithNotExistedDataAccount(userKeypair2, ledgerHash, parti0, parti0); | |||
TransactionResponse txResp2 = txbatchProcessor.schedule(transactionRequest2); | |||
BlockchainKeypair userKeypair3 = BlockchainKeyGenerator.getInstance().generate(); | |||
transactionRequest = LedgerTestUtils.createTxRequest_UserReg(userKeypair3, ledgerHash, parti0, parti0); | |||
TransactionResponse txResp3 = txbatchProcessor.schedule(transactionRequest); | |||
TransactionRequest transactionRequest3 = LedgerTestUtils.createTxRequest_UserReg(userKeypair3, ledgerHash, | |||
parti0, parti0); | |||
TransactionResponse txResp3 = txbatchProcessor.schedule(transactionRequest3); | |||
LedgerBlock newBlock = newBlockEditor.prepare(); | |||
newBlockEditor.commit(); | |||
assertEquals(TransactionState.SUCCESS, txResp1.getExecutionState()); | |||
assertEquals(TransactionState.LEDGER_ERROR, txResp2.getExecutionState()); | |||
assertEquals(TransactionState.DATA_ACCOUNT_DOES_NOT_EXIST, txResp2.getExecutionState()); | |||
assertEquals(TransactionState.SUCCESS, txResp3.getExecutionState()); | |||
// 验证正确性; | |||
@@ -213,6 +217,20 @@ public class TransactionBatchProcessorTest { | |||
assertEquals(newBlock.getHash(), latestBlock.getHash()); | |||
assertEquals(1, newBlock.getHeight()); | |||
LedgerTransaction tx1 = ledgerRepo.getTransactionSet() | |||
.get(transactionRequest1.getTransactionContent().getHash()); | |||
LedgerTransaction tx2 = ledgerRepo.getTransactionSet() | |||
.get(transactionRequest2.getTransactionContent().getHash()); | |||
LedgerTransaction tx3 = ledgerRepo.getTransactionSet() | |||
.get(transactionRequest3.getTransactionContent().getHash()); | |||
assertNotNull(tx1); | |||
assertEquals(TransactionState.SUCCESS, tx1.getExecutionState()); | |||
assertNotNull(tx2); | |||
assertEquals(TransactionState.DATA_ACCOUNT_DOES_NOT_EXIST, tx2.getExecutionState()); | |||
assertNotNull(tx3); | |||
assertEquals(TransactionState.SUCCESS, tx3.getExecutionState()); | |||
LedgerDataSet ledgerDS = ledgerRepo.getDataSet(latestBlock); | |||
boolean existUser1 = ledgerDS.getUserAccountSet().contains(userKeypair1.getAddress()); | |||
boolean existUser2 = ledgerDS.getUserAccountSet().contains(userKeypair2.getAddress()); | |||
@@ -0,0 +1,16 @@ | |||
package com.jd.blockchain.ledger; | |||
public class ContractDoesNotExistException extends LedgerException { | |||
private static final long serialVersionUID = 8685914012112243771L; | |||
public ContractDoesNotExistException(String message) { | |||
super(message); | |||
} | |||
public ContractDoesNotExistException(String message, Throwable cause) { | |||
super(message, cause); | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.jd.blockchain.ledger; | |||
public class DataAccountDoesNotExistException extends LedgerException { | |||
private static final long serialVersionUID = -1889587937401974215L; | |||
public DataAccountDoesNotExistException(String message) { | |||
super(message); | |||
} | |||
public DataAccountDoesNotExistException(String message, Throwable cause) { | |||
super(message, cause); | |||
} | |||
} |
@@ -3,6 +3,8 @@ package com.jd.blockchain.ledger; | |||
public class LedgerException extends RuntimeException { | |||
private static final long serialVersionUID = -4090881296855827888L; | |||
public LedgerException(String message) { | |||
super(message); | |||
@@ -33,12 +33,27 @@ public enum TransactionState { | |||
* 由于在错误的账本上执行交易而被丢弃; | |||
*/ | |||
DISCARD_BY_WRONG_LEDGER((byte) 3), | |||
/** | |||
* 由于交易内容的验签失败而丢弃; | |||
*/ | |||
DISCARD_BY_WRONG_CONTENT_SIGNATURE((byte) 4), | |||
/** | |||
* 数据账户不存在; | |||
*/ | |||
DATA_ACCOUNT_DOES_NOT_EXIST((byte) 5), | |||
/** | |||
* 用户不存在; | |||
*/ | |||
USER_DOES_NOT_EXIST((byte) 6), | |||
/** | |||
* 合约不存在; | |||
*/ | |||
CONTRACT_DOES_NOT_EXIST((byte) 6), | |||
/** | |||
* 系统错误; | |||
*/ | |||
@@ -0,0 +1,15 @@ | |||
package com.jd.blockchain.ledger; | |||
public class UserDoesNotExistException extends LedgerException { | |||
private static final long serialVersionUID = 397450363050148898L; | |||
public UserDoesNotExistException(String message) { | |||
super(message); | |||
} | |||
public UserDoesNotExistException(String message, Throwable cause) { | |||
super(message, cause); | |||
} | |||
} |
@@ -1,9 +1,9 @@ | |||
package test.com.jd.blockchain.storage.service.utils; | |||
import static org.junit.Assert.*; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Matchers.anyLong; | |||
import static org.mockito.Matchers.anyString; | |||
import static org.mockito.Matchers.eq; | |||
import static org.mockito.Mockito.times; | |||
import static org.mockito.Mockito.verify; | |||
@@ -15,14 +15,11 @@ import org.junit.Test; | |||
import org.mockito.Mockito; | |||
import org.mockito.invocation.InvocationOnMock; | |||
import org.mockito.stubbing.Answer; | |||
import org.mockito.verification.VerificationMode; | |||
import com.jd.blockchain.storage.service.ExPolicyKVStorage; | |||
import com.jd.blockchain.storage.service.ExPolicyKVStorage.ExPolicy; | |||
import com.jd.blockchain.storage.service.VersioningKVStorage; | |||
import com.jd.blockchain.storage.service.utils.BufferedKVStorage; | |||
import com.jd.blockchain.storage.service.utils.ExistancePolicyKVStorageMap; | |||
import com.jd.blockchain.storage.service.utils.VersioningKVStorageMap; | |||
import com.jd.blockchain.utils.Bytes; | |||
public class BufferedKVStorageTest { | |||
@@ -77,82 +74,4 @@ public class BufferedKVStorageTest { | |||
verify(exStorage, times(1)).set(eq(Bytes.fromString("C")), any(), eq(ExPolicy.NOT_EXISTING)); | |||
verify(exStorage, times(1)).set(eq(Bytes.fromString("D")), any(), eq(ExPolicy.NOT_EXISTING)); | |||
} | |||
// 改变了存储结构,此测试用例不再适合; | |||
// @Test | |||
// public void testDataSet() { | |||
// | |||
// ExistancePolicyKVStorageMap memoryExStorage = new | |||
// ExistancePolicyKVStorageMap(); | |||
// | |||
// VersioningKVStorageMap memoryVerStorage = new VersioningKVStorageMap(); | |||
// | |||
// long v = memoryVerStorage.set("A", "A1".getBytes(), -1); | |||
// assertEquals(0, v); | |||
// v = memoryVerStorage.set("A", "A2".getBytes(), 0); | |||
// assertEquals(1, v); | |||
// v = memoryVerStorage.set("A", "A3".getBytes(), 1); | |||
// assertEquals(2, v); | |||
// v = memoryVerStorage.set("B", "B1".getBytes(), -1); | |||
// assertEquals(0, v); | |||
// | |||
// BufferedKVStorage bufferedStorage = new BufferedKVStorage(memoryExStorage, | |||
// memoryVerStorage, false); | |||
// | |||
// // test versioning get; | |||
// byte[] value = bufferedStorage.get("A", 0); | |||
// assertEquals("A1", new String(value)); | |||
// | |||
// value = bufferedStorage.get("A", -1); | |||
// assertEquals("A3", new String(value)); | |||
// | |||
// value = bufferedStorage.get("A", 2); | |||
// assertEquals("A3", new String(value)); | |||
// | |||
// value = bufferedStorage.get("B", 0); | |||
// assertEquals("B1", new String(value)); | |||
// | |||
// | |||
// // test versioning buffered set; | |||
// v = bufferedStorage.set("C", "C1".getBytes(), -1); | |||
// assertEquals(v, 0); | |||
// | |||
// v = bufferedStorage.set("C", "C2".getBytes(), 0); | |||
// assertEquals(v, 1); | |||
// | |||
// v = bufferedStorage.set("D", "D1".getBytes(), -1); | |||
// assertEquals(v, 0); | |||
// | |||
// v = bufferedStorage.set("E", "E1".getBytes(), 0); | |||
// assertEquals(v, -1); | |||
// | |||
// | |||
// value = bufferedStorage.get("C", 0); | |||
// assertEquals("C1", new String(value)); | |||
// value = bufferedStorage.get("C", 1); | |||
// assertEquals("C2", new String(value)); | |||
// value = bufferedStorage.get("D", 0); | |||
// assertEquals("D1", new String(value)); | |||
// value = bufferedStorage.get("E", 0); | |||
// assertNull(value); | |||
// | |||
// value = memoryVerStorage.get("C", 0); | |||
// assertNull(value); | |||
// value = memoryVerStorage.get("D", 0); | |||
// assertNull(value); | |||
// value = memoryVerStorage.get("E", 0); | |||
// assertNull(value); | |||
// | |||
// bufferedStorage.flush(); | |||
// | |||
// value = memoryVerStorage.get("C", 0); | |||
// assertEquals("C1", new String(value)); | |||
// value = memoryVerStorage.get("C", 1); | |||
// assertEquals("C2", new String(value)); | |||
// value = memoryVerStorage.get("D", 0); | |||
// assertEquals("D1", new String(value)); | |||
// value = memoryVerStorage.get("E", 0); | |||
// assertNull(value); | |||
// } | |||
} |