Browse Source

Merge branch 'master' into develop

# Conflicts:
#	source/base/pom.xml
#	source/binary-proto/pom.xml
#	source/consensus/consensus-bftsmart/pom.xml
#	source/consensus/consensus-framework/pom.xml
#	source/consensus/consensus-mq/pom.xml
#	source/consensus/pom.xml
#	source/contract/contract-framework/pom.xml
#	source/contract/contract-jvm/pom.xml
#	source/contract/contract-maven-plugin/pom.xml
#	source/contract/contract-samples/pom.xml
#	source/contract/pom.xml
#	source/crypto/crypto-adv/pom.xml
#	source/crypto/crypto-classic/pom.xml
#	source/crypto/crypto-framework/pom.xml
#	source/crypto/crypto-pki/pom.xml
#	source/crypto/crypto-sm/pom.xml
#	source/crypto/pom.xml
#	source/deployment/deployment-gateway/pom.xml
#	source/deployment/deployment-peer/pom.xml
#	source/deployment/pom.xml
#	source/gateway/pom.xml
#	source/ledger/ledger-core/pom.xml
#	source/ledger/ledger-model/pom.xml
#	source/ledger/ledger-rpc/pom.xml
#	source/ledger/pom.xml
#	source/peer/pom.xml
#	source/pom.xml
#	source/runtime/pom.xml
#	source/runtime/runtime-context/pom.xml
#	source/runtime/runtime-modular-booter/pom.xml
#	source/runtime/runtime-modular/pom.xml
#	source/sdk/pom.xml
#	source/sdk/sdk-base/pom.xml
#	source/sdk/sdk-client/pom.xml
#	source/sdk/sdk-samples/pom.xml
#	source/storage/pom.xml
#	source/storage/storage-composite/pom.xml
#	source/storage/storage-redis/pom.xml
#	source/storage/storage-rocksdb/pom.xml
#	source/storage/storage-service/pom.xml
#	source/test/pom.xml
#	source/test/test-consensus-client/pom.xml
#	source/test/test-consensus-node/pom.xml
#	source/test/test-integration/pom.xml
#	source/test/test-ledger-core/pom.xml
#	source/tools/pom.xml
#	source/tools/tools-capability/pom.xml
#	source/tools/tools-initializer-booter/pom.xml
#	source/tools/tools-initializer/pom.xml
#	source/tools/tools-keygen-booter/pom.xml
#	source/tools/tools-keygen/pom.xml
#	source/tools/tools-mocker/pom.xml
#	source/utils/pom.xml
#	source/utils/utils-common/pom.xml
#	source/utils/utils-http/pom.xml
#	source/utils/utils-serialize/pom.xml
#	source/utils/utils-test/pom.xml
#	source/utils/utils-web-server/pom.xml
#	source/utils/utils-web/pom.xml
tags/1.1.0
huanghaiquan 5 years ago
parent
commit
8f09b044b4
21 changed files with 875 additions and 132 deletions
  1. +25
    -0
      source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/InstantiatedContractCode.java
  2. +94
    -3
      source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccount.java
  3. +1
    -1
      source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleDataSet.java
  4. +4
    -2
      source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerQueryService.java
  5. +6
    -1
      source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/DataAccountKVSetOperationHandle.java
  6. +2
    -24
      source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingHandle.java
  7. +288
    -15
      source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingTest.java
  8. +19
    -0
      source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/KeyValueEntry.java
  9. +47
    -0
      source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/KeyValueObject.java
  10. +33
    -0
      source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleDataSetTest.java
  11. +21
    -7
      source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionBatchProcessorTest.java
  12. +15
    -0
      source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TxTestContract.java
  13. +74
    -0
      source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TxTestContractImpl.java
  14. +41
    -20
      source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BytesValueEncoding.java
  15. +27
    -0
      source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataVersionConflictException.java
  16. +5
    -0
      source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionState.java
  17. +58
    -0
      source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/resolver/BooleanToBytesValueResolver.java
  18. +66
    -58
      source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/resolver/BytesValueResolver.java
  19. +1
    -1
      source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/RuntimeContext.java
  20. +34
    -0
      source/test/test-contract/pom.xml
  21. +14
    -0
      source/test/test-contract/src/test/java/test/com/jd/blockchain/contract/ContractTransactionRollbackTest.java

+ 25
- 0
source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/InstantiatedContractCode.java View File

@@ -0,0 +1,25 @@
package com.jd.blockchain.contract.jvm;

import com.jd.blockchain.contract.ContractType;
import com.jd.blockchain.utils.Bytes;

public class InstantiatedContractCode<T> extends AbstractContractCode {

private T instance;

public InstantiatedContractCode(Bytes address, long version, Class<T> delaredInterface, T instance) {
super(address, version, resolveContractDefinition(delaredInterface, instance.getClass()));
this.instance = instance;
}

private static ContractDefinition resolveContractDefinition(Class<?> declaredIntf, Class<?> implementedClass) {
ContractType contractType = ContractType.resolve(declaredIntf);
return new ContractDefinition(contractType, implementedClass);
}

@Override
protected T getContractInstance() {
return instance;
}

}

+ 94
- 3
source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccount.java View File

@@ -1,12 +1,11 @@
package com.jd.blockchain.ledger.core; package com.jd.blockchain.ledger.core;
import com.jd.blockchain.binaryproto.BinaryProtocol; import com.jd.blockchain.binaryproto.BinaryProtocol;
import com.jd.blockchain.binaryproto.PrimitiveType;
import com.jd.blockchain.crypto.HashDigest; import com.jd.blockchain.crypto.HashDigest;
import com.jd.blockchain.crypto.PubKey; import com.jd.blockchain.crypto.PubKey;
import com.jd.blockchain.ledger.AccountHeader; import com.jd.blockchain.ledger.AccountHeader;
import com.jd.blockchain.ledger.BytesValue;
import com.jd.blockchain.ledger.BytesData; import com.jd.blockchain.ledger.BytesData;
import com.jd.blockchain.ledger.BytesValue;
import com.jd.blockchain.ledger.KVDataEntry; import com.jd.blockchain.ledger.KVDataEntry;
import com.jd.blockchain.ledger.KVDataObject; import com.jd.blockchain.ledger.KVDataObject;
import com.jd.blockchain.utils.Bytes; import com.jd.blockchain.utils.Bytes;
@@ -43,16 +42,85 @@ public class DataAccount implements AccountHeader, MerkleProvable {
} }
/**
* Create or update the value associated the specified key if the version
* checking is passed.<br>
*
* The value of the key will be updated only if it's latest version equals the
* specified version argument. <br>
* If the key doesn't exist, the version checking will be ignored, and key will
* be created with a new sequence number as id. <br>
* It also could specify the version argument to -1 to ignore the version
* checking.
* <p>
* If updating is performed, the version of the key increase by 1. <br>
* If creating is performed, the version of the key initialize by 0. <br>
*
* @param key The key of data;
* @param value The value of data;
* @param version The expected version of the key.
* @return The new version of the key. <br>
* If the key is new created success, then return 0; <br>
* If the key is updated success, then return the new version;<br>
* If this operation fail by version checking or other reason, then
* return -1;
*/
public long setBytes(Bytes key, BytesValue value, long version) { public long setBytes(Bytes key, BytesValue value, long version) {
return baseAccount.setBytes(key, value, version); return baseAccount.setBytes(key, value, version);
} }
/**
* Create or update the value associated the specified key if the version
* checking is passed.<br>
*
* The value of the key will be updated only if it's latest version equals the
* specified version argument. <br>
* If the key doesn't exist, the version checking will be ignored, and key will
* be created with a new sequence number as id. <br>
* It also could specify the version argument to -1 to ignore the version
* checking.
* <p>
* If updating is performed, the version of the key increase by 1. <br>
* If creating is performed, the version of the key initialize by 0. <br>
*
* @param key The key of data;
* @param value The value of data;
* @param version The expected version of the key.
* @return The new version of the key. <br>
* If the key is new created success, then return 0; <br>
* If the key is updated success, then return the new version;<br>
* If this operation fail by version checking or other reason, then
* return -1;
*/
public long setBytes(Bytes key, String value, long version) { public long setBytes(Bytes key, String value, long version) {
BytesValue bytesValue = BytesData.fromText(value); BytesValue bytesValue = BytesData.fromText(value);
return baseAccount.setBytes(key, bytesValue, version); return baseAccount.setBytes(key, bytesValue, version);
} }
/**
* Create or update the value associated the specified key if the version
* checking is passed.<br>
*
* The value of the key will be updated only if it's latest version equals the
* specified version argument. <br>
* If the key doesn't exist, the version checking will be ignored, and key will
* be created with a new sequence number as id. <br>
* It also could specify the version argument to -1 to ignore the version
* checking.
* <p>
* If updating is performed, the version of the key increase by 1. <br>
* If creating is performed, the version of the key initialize by 0. <br>
*
* @param key The key of data;
* @param value The value of data;
* @param version The expected version of the key.
* @return The new version of the key. <br>
* If the key is new created success, then return 0; <br>
* If the key is updated success, then return the new version;<br>
* If this operation fail by version checking or other reason, then
* return -1;
*/
public long setBytes(Bytes key, byte[] value, long version) { public long setBytes(Bytes key, byte[] value, long version) {
BytesValue bytesValue = BytesData.fromBytes(value); BytesValue bytesValue = BytesData.fromBytes(value);
return baseAccount.setBytes(key, bytesValue, version); return baseAccount.setBytes(key, bytesValue, version);
@@ -121,6 +189,29 @@ public class DataAccount implements AccountHeader, MerkleProvable {
public BytesValue getBytes(Bytes key, long version) { public BytesValue getBytes(Bytes key, long version) {
return baseAccount.getBytes(key, version); return baseAccount.getBytes(key, version);
} }
/**
* @param key
* @param version
* @return
*/
public KVDataEntry getDataEntry(String key, long version) {
return getDataEntry(Bytes.fromString(key), version);
}
/**
* @param key
* @param version
* @return
*/
public KVDataEntry getDataEntry(Bytes key, long version) {
BytesValue value = baseAccount.getBytes(key, version);
if (value == null) {
return new KVDataObject(key.toUTF8String(), -1, null);
}else {
return new KVDataObject(key.toUTF8String(), version, value);
}
}
/** /**
* return the specified index's KVDataEntry; * return the specified index's KVDataEntry;
@@ -131,7 +222,7 @@ public class DataAccount implements AccountHeader, MerkleProvable {
*/ */
public KVDataEntry[] getDataEntries(int fromIndex, int count) { public KVDataEntry[] getDataEntries(int fromIndex, int count) {
if (getDataEntriesTotalCount() == 0 || count == 0) {
if (count == 0 || getDataEntriesTotalCount() == 0) {
return null; return null;
} }


+ 1
- 1
source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleDataSet.java View File

@@ -168,7 +168,7 @@ public class MerkleDataSet implements Transactional, MerkleProvable {
*/ */
public String getKeyAtIndex(int fromIndex) { public String getKeyAtIndex(int fromIndex) {
MerkleDataNode dataNode = merkleTree.getData(fromIndex); MerkleDataNode dataNode = merkleTree.getData(fromIndex);
return new String(dataNode.getKey().toBytes());
return dataNode.getKey().toUTF8String();
} }


+ 4
- 2
source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerQueryService.java View File

@@ -20,6 +20,8 @@ import com.jd.blockchain.utils.QueryUtil;


public class LedgerQueryService implements BlockchainQueryService { public class LedgerQueryService implements BlockchainQueryService {


private static final KVDataEntry[] EMPTY_ENTRIES = new KVDataEntry[0];

private LedgerService ledgerService; private LedgerService ledgerService;


public LedgerQueryService(LedgerService ledgerService) { public LedgerQueryService(LedgerService ledgerService) {
@@ -254,7 +256,7 @@ public class LedgerQueryService implements BlockchainQueryService {
@Override @Override
public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) {
if (keys == null || keys.length == 0) { if (keys == null || keys.length == 0) {
return null;
return EMPTY_ENTRIES;
} }
LedgerRepository ledger = ledgerService.getLedger(ledgerHash); LedgerRepository ledger = ledgerService.getLedger(ledgerHash);
LedgerBlock block = ledger.getLatestBlock(); LedgerBlock block = ledger.getLatestBlock();
@@ -266,7 +268,7 @@ public class LedgerQueryService implements BlockchainQueryService {
for (int i = 0; i < entries.length; i++) { for (int i = 0; i < entries.length; i++) {
final String currKey = keys[i]; final String currKey = keys[i];


ver = dataAccount.getDataVersion(Bytes.fromString(currKey));
ver = dataAccount == null ? -1 : dataAccount.getDataVersion(Bytes.fromString(currKey));


if (ver < 0) { if (ver < 0) {
entries[i] = new KVDataObject(currKey, -1, null); entries[i] = new KVDataObject(currKey, -1, null);


+ 6
- 1
source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/DataAccountKVSetOperationHandle.java View File

@@ -7,6 +7,7 @@ import com.jd.blockchain.ledger.BytesValue;
import com.jd.blockchain.ledger.DataAccountDoesNotExistException; import com.jd.blockchain.ledger.DataAccountDoesNotExistException;
import com.jd.blockchain.ledger.DataAccountKVSetOperation; import com.jd.blockchain.ledger.DataAccountKVSetOperation;
import com.jd.blockchain.ledger.DataAccountKVSetOperation.KVWriteEntry; import com.jd.blockchain.ledger.DataAccountKVSetOperation.KVWriteEntry;
import com.jd.blockchain.ledger.DataVersionConflictException;
import com.jd.blockchain.ledger.Operation; import com.jd.blockchain.ledger.Operation;
import com.jd.blockchain.ledger.core.DataAccount; import com.jd.blockchain.ledger.core.DataAccount;
import com.jd.blockchain.ledger.core.LedgerDataSet; import com.jd.blockchain.ledger.core.LedgerDataSet;
@@ -31,8 +32,12 @@ public class DataAccountKVSetOperationHandle implements OperationHandle {
throw new DataAccountDoesNotExistException("DataAccount doesn't exist!"); throw new DataAccountDoesNotExistException("DataAccount doesn't exist!");
} }
KVWriteEntry[] writeSet = kvWriteOp.getWriteSet(); KVWriteEntry[] writeSet = kvWriteOp.getWriteSet();
long v = -1;
for (KVWriteEntry kvw : writeSet) { for (KVWriteEntry kvw : writeSet) {
account.setBytes(Bytes.fromString(kvw.getKey()), kvw.getValue(), kvw.getExpectedVersion());
v = account.setBytes(Bytes.fromString(kvw.getKey()), kvw.getValue(), kvw.getExpectedVersion());
if (v < 0) {
throw new DataVersionConflictException();
}
} }
return null; return null;
} }


+ 2
- 24
source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingHandle.java View File

@@ -3,10 +3,8 @@ package test.com.jd.blockchain.ledger;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;


import com.jd.blockchain.contract.ContractType;
import com.jd.blockchain.contract.engine.ContractCode; import com.jd.blockchain.contract.engine.ContractCode;
import com.jd.blockchain.contract.jvm.AbstractContractCode;
import com.jd.blockchain.contract.jvm.ContractDefinition;
import com.jd.blockchain.contract.jvm.InstantiatedContractCode;
import com.jd.blockchain.ledger.core.ContractAccount; import com.jd.blockchain.ledger.core.ContractAccount;
import com.jd.blockchain.ledger.core.impl.handles.AbtractContractEventHandle; import com.jd.blockchain.ledger.core.impl.handles.AbtractContractEventHandle;
import com.jd.blockchain.utils.Bytes; import com.jd.blockchain.utils.Bytes;
@@ -21,30 +19,10 @@ public class ContractInvokingHandle extends AbtractContractEventHandle {
} }


public <T> ContractCode setup(Bytes address, Class<T> contractIntf, T instance) { public <T> ContractCode setup(Bytes address, Class<T> contractIntf, T instance) {
ContractCodeInstance<T> contract = new ContractCodeInstance<T>(address, 0, contractIntf, instance);
InstantiatedContractCode<T> contract = new InstantiatedContractCode<T>(address, 0, contractIntf, instance);
contractInstances.put(address, contract); contractInstances.put(address, contract);
return contract; return contract;
} }


private static class ContractCodeInstance<T> extends AbstractContractCode {

private T instance;

public ContractCodeInstance(Bytes address, long version, Class<T> delaredInterface, T instance) {
super(address, version, resolveContractDefinition(delaredInterface, instance.getClass()));
this.instance = instance;
}

private static ContractDefinition resolveContractDefinition(Class<?> declaredIntf, Class<?> implementedClass) {
ContractType contractType = ContractType.resolve(declaredIntf);
return new ContractDefinition(contractType, implementedClass);
}

@Override
protected T getContractInstance() {
return instance;
}

}


} }

+ 288
- 15
source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingTest.java View File

@@ -1,27 +1,60 @@
package test.com.jd.blockchain.ledger; package test.com.jd.blockchain.ledger;


import static com.jd.blockchain.transaction.ContractReturnValue.decode;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.Random;

import org.junit.Test;
import org.mockito.Mockito;

import com.jd.blockchain.binaryproto.BinaryProtocol; import com.jd.blockchain.binaryproto.BinaryProtocol;
import com.jd.blockchain.binaryproto.DataContractRegistry; import com.jd.blockchain.binaryproto.DataContractRegistry;
import com.jd.blockchain.crypto.HashDigest; import com.jd.blockchain.crypto.HashDigest;
import com.jd.blockchain.ledger.*;
import com.jd.blockchain.ledger.core.*;
import com.jd.blockchain.ledger.BlockchainKeyGenerator;
import com.jd.blockchain.ledger.BlockchainKeypair;
import com.jd.blockchain.ledger.BytesData;
import com.jd.blockchain.ledger.BytesValue;
import com.jd.blockchain.ledger.DataAccountRegisterOperation;
import com.jd.blockchain.ledger.EndpointRequest;
import com.jd.blockchain.ledger.KVDataEntry;
import com.jd.blockchain.ledger.LedgerBlock;
import com.jd.blockchain.ledger.LedgerInitSetting;
import com.jd.blockchain.ledger.LedgerTransaction;
import com.jd.blockchain.ledger.NodeRequest;
import com.jd.blockchain.ledger.OperationResult;
import com.jd.blockchain.ledger.TransactionContent;
import com.jd.blockchain.ledger.TransactionContentBody;
import com.jd.blockchain.ledger.TransactionRequest;
import com.jd.blockchain.ledger.TransactionRequestBuilder;
import com.jd.blockchain.ledger.TransactionResponse;
import com.jd.blockchain.ledger.TransactionState;
import com.jd.blockchain.ledger.UserRegisterOperation;
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.LedgerService;
import com.jd.blockchain.ledger.core.LedgerTransactionContext;
import com.jd.blockchain.ledger.core.UserAccount;
import com.jd.blockchain.ledger.core.impl.DefaultOperationHandleRegisteration; import com.jd.blockchain.ledger.core.impl.DefaultOperationHandleRegisteration;
import com.jd.blockchain.ledger.core.impl.LedgerManager; import com.jd.blockchain.ledger.core.impl.LedgerManager;
import com.jd.blockchain.ledger.core.impl.LedgerTransactionalEditor; import com.jd.blockchain.ledger.core.impl.LedgerTransactionalEditor;
import com.jd.blockchain.ledger.core.impl.OperationHandleRegisteration;
import com.jd.blockchain.ledger.core.impl.TransactionBatchProcessor; import com.jd.blockchain.ledger.core.impl.TransactionBatchProcessor;
import com.jd.blockchain.service.TransactionBatchResultHandle; import com.jd.blockchain.service.TransactionBatchResultHandle;
import com.jd.blockchain.storage.service.utils.MemoryKVStorage; import com.jd.blockchain.storage.service.utils.MemoryKVStorage;
import com.jd.blockchain.transaction.BooleanValueHolder;
import com.jd.blockchain.transaction.TxBuilder; import com.jd.blockchain.transaction.TxBuilder;
import com.jd.blockchain.utils.Bytes; import com.jd.blockchain.utils.Bytes;
import org.junit.Test;
import org.mockito.Mockito;

import java.util.Random;

import static org.junit.Assert.*;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;


public class ContractInvokingTest { public class ContractInvokingTest {
static { static {
@@ -32,6 +65,7 @@ public class ContractInvokingTest {
DataContractRegistry.register(EndpointRequest.class); DataContractRegistry.register(EndpointRequest.class);
DataContractRegistry.register(TransactionResponse.class); DataContractRegistry.register(TransactionResponse.class);
DataContractRegistry.register(UserRegisterOperation.class); DataContractRegistry.register(UserRegisterOperation.class);
DataContractRegistry.register(DataAccountRegisterOperation.class);
} }


private static final String LEDGER_KEY_PREFIX = "LDG://"; private static final String LEDGER_KEY_PREFIX = "LDG://";
@@ -45,7 +79,7 @@ public class ContractInvokingTest {
private MemoryKVStorage storage = new MemoryKVStorage(); private MemoryKVStorage storage = new MemoryKVStorage();


@Test @Test
public void test() {
public void testNormal() {
// 初始化账本到指定的存储库; // 初始化账本到指定的存储库;
HashDigest ledgerHash = initLedger(storage, parti0, parti1, parti2, parti3); HashDigest ledgerHash = initLedger(storage, parti0, parti1, parti2, parti3);


@@ -69,7 +103,6 @@ public class ContractInvokingTest {
// 发布指定地址合约 // 发布指定地址合约
deploy(ledgerRepo, ledgerManager, opReg, ledgerHash, contractKey); deploy(ledgerRepo, ledgerManager, opReg, ledgerHash, contractKey);



// 创建新区块的交易处理器; // 创建新区块的交易处理器;
LedgerBlock preBlock = ledgerRepo.getLatestBlock(); LedgerBlock preBlock = ledgerRepo.getLatestBlock();
LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(preBlock); LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(preBlock);
@@ -122,9 +155,243 @@ public class ContractInvokingTest {


} }


// @Test
public void testReadNewWritting() {
// 初始化账本到指定的存储库;
HashDigest ledgerHash = initLedger(storage, parti0, parti1, parti2, parti3);

// 重新加载账本;
LedgerManager ledgerManager = new LedgerManager();
LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, storage);

// 创建合约处理器;
ContractInvokingHandle contractInvokingHandle = new ContractInvokingHandle();

// 创建和加载合约实例;
BlockchainKeypair contractKey = BlockchainKeyGenerator.getInstance().generate();
Bytes contractAddress = contractKey.getAddress();
TxTestContractImpl contractInstance = new TxTestContractImpl();
contractInvokingHandle.setup(contractAddress, TxTestContract.class, contractInstance);

// 注册合约处理器;
DefaultOperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration();
opReg.insertAsTopPriority(contractInvokingHandle);

// 发布指定地址合约
deploy(ledgerRepo, ledgerManager, opReg, ledgerHash, contractKey);

// 创建新区块的交易处理器;
LedgerBlock preBlock = ledgerRepo.getLatestBlock();
LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(preBlock);

// 加载合约
LedgerEditor newBlockEditor = ledgerRepo.createNextBlock();
TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset,
opReg, ledgerManager);

String key = TxTestContractImpl.KEY;
String value = "VAL";

TxBuilder txBuilder = new TxBuilder(ledgerHash);
BlockchainKeypair kpDataAccount = BlockchainKeyGenerator.getInstance().generate();
contractInstance.setDataAddress(kpDataAccount.getAddress());

txBuilder.dataAccounts().register(kpDataAccount.getIdentity());
TransactionRequestBuilder txReqBuilder1 = txBuilder.prepareRequest();
txReqBuilder1.signAsEndpoint(parti0);
txReqBuilder1.signAsNode(parti0);
TransactionRequest txReq1 = txReqBuilder1.buildRequest();

// 构建基于接口调用合约的交易请求,用于测试合约调用;
txBuilder = new TxBuilder(ledgerHash);
TxTestContract contractProxy = txBuilder.contract(contractAddress, TxTestContract.class);
BooleanValueHolder readableHolder = decode(contractProxy.testReadable());

TransactionRequestBuilder txReqBuilder2 = txBuilder.prepareRequest();
txReqBuilder2.signAsEndpoint(parti0);
txReqBuilder2.signAsNode(parti0);
TransactionRequest txReq2 = txReqBuilder2.buildRequest();

TransactionResponse resp1 = txbatchProcessor.schedule(txReq1);
TransactionResponse resp2 = txbatchProcessor.schedule(txReq2);

// 提交区块;
TransactionBatchResultHandle txResultHandle = txbatchProcessor.prepare();
txResultHandle.commit();

BytesValue latestValue = ledgerRepo.getDataAccountSet().getDataAccount(kpDataAccount.getAddress()).getBytes(key,
-1);
System.out.printf("latest value=[%s] %s \r\n", latestValue.getType(), latestValue.getValue().toUTF8String());

boolean readable = readableHolder.get();
assertTrue(readable);

LedgerBlock latestBlock = ledgerRepo.getLatestBlock();
assertEquals(preBlock.getHeight() + 1, latestBlock.getHeight());
assertEquals(resp1.getBlockHeight(), latestBlock.getHeight());
assertEquals(resp1.getBlockHash(), latestBlock.getHash());
}

/**
* 验证在合约方法中写入数据账户时,如果版本校验失败是否会引发异常而导致回滚;<br>
* 期待正确的表现是引发异常而回滚当前交易;
*/
@Test
public void testRollbackWhileVersionConfliction() {
// 初始化账本到指定的存储库;
HashDigest ledgerHash = initLedger(storage, parti0, parti1, parti2, parti3);

// 重新加载账本;
LedgerManager ledgerManager = new LedgerManager();
LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, storage);

// 创建合约处理器;
ContractInvokingHandle contractInvokingHandle = new ContractInvokingHandle();

// 创建和加载合约实例;
BlockchainKeypair contractKey = BlockchainKeyGenerator.getInstance().generate();
Bytes contractAddress = contractKey.getAddress();
TxTestContractImpl contractInstance = new TxTestContractImpl();
contractInvokingHandle.setup(contractAddress, TxTestContract.class, contractInstance);

// 注册合约处理器;
DefaultOperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration();
opReg.insertAsTopPriority(contractInvokingHandle);

// 发布指定地址合约
deploy(ledgerRepo, ledgerManager, opReg, ledgerHash, contractKey);

// 注册数据账户;
BlockchainKeypair kpDataAccount = BlockchainKeyGenerator.getInstance().generate();
contractInstance.setDataAddress(kpDataAccount.getAddress());
registerDataAccount(ledgerRepo, ledgerManager, opReg, ledgerHash, kpDataAccount);

// 调用合约
// 构建基于接口调用合约的交易请求,用于测试合约调用;
buildBlock(ledgerRepo, ledgerManager, opReg, new TxDefinitor() {
@Override
public void buildTx(TxBuilder txBuilder) {
TxTestContract contractProxy = txBuilder.contract(contractAddress, TxTestContract.class);
contractProxy.testRollbackWhileVersionConfliction(kpDataAccount.getAddress().toBase58(), "K1", "V1-0",
-1);
contractProxy.testRollbackWhileVersionConfliction(kpDataAccount.getAddress().toBase58(), "K2", "V2-0",
-1);
}
});
// 预期数据都能够正常写入;
KVDataEntry kv1 = ledgerRepo.getDataAccountSet().getDataAccount(kpDataAccount.getAddress()).getDataEntry("K1",
0);
KVDataEntry kv2 = ledgerRepo.getDataAccountSet().getDataAccount(kpDataAccount.getAddress()).getDataEntry("K2",
0);
assertEquals(0, kv1.getVersion());
assertEquals(0, kv2.getVersion());
assertEquals("V1-0", kv1.getValue());
assertEquals("V2-0", kv2.getValue());

// 构建基于接口调用合约的交易请求,用于测试合约调用;
buildBlock(ledgerRepo, ledgerManager, opReg, new TxDefinitor() {
@Override
public void buildTx(TxBuilder txBuilder) {
TxTestContract contractProxy = txBuilder.contract(contractAddress, TxTestContract.class);
contractProxy.testRollbackWhileVersionConfliction(kpDataAccount.getAddress().toBase58(), "K1", "V1-1",
0);
contractProxy.testRollbackWhileVersionConfliction(kpDataAccount.getAddress().toBase58(), "K2", "V2-1",
0);
}
});
// 预期数据都能够正常写入;
kv1 = ledgerRepo.getDataAccountSet().getDataAccount(kpDataAccount.getAddress()).getDataEntry("K1", 1);
kv2 = ledgerRepo.getDataAccountSet().getDataAccount(kpDataAccount.getAddress()).getDataEntry("K2", 1);
assertEquals(1, kv1.getVersion());
assertEquals(1, kv2.getVersion());
assertEquals("V1-1", kv1.getValue());
assertEquals("V2-1", kv2.getValue());
// 构建基于接口调用合约的交易请求,用于测试合约调用;
buildBlock(ledgerRepo, ledgerManager, opReg, new TxDefinitor() {
@Override
public void buildTx(TxBuilder txBuilder) {
TxTestContract contractProxy = txBuilder.contract(contractAddress, TxTestContract.class);
contractProxy.testRollbackWhileVersionConfliction(kpDataAccount.getAddress().toBase58(), "K1", "V1-2",
1);
contractProxy.testRollbackWhileVersionConfliction(kpDataAccount.getAddress().toBase58(), "K2", "V2-2",
0);
}
});
// 预期数据都能够正常写入;
kv1 = ledgerRepo.getDataAccountSet().getDataAccount(kpDataAccount.getAddress()).getDataEntry("K1", 1);
assertEquals(1, kv1.getVersion());
assertEquals("V1-1", kv1.getValue());
kv1 = ledgerRepo.getDataAccountSet().getDataAccount(kpDataAccount.getAddress()).getDataEntry("K1", 2);
assertEquals(-1, kv1.getVersion());
assertEquals(null, kv1.getValue());

}

private LedgerBlock buildBlock(LedgerRepository ledgerRepo, LedgerService ledgerService,
OperationHandleRegisteration opReg, TxDefinitor txDefinitor) {
LedgerBlock preBlock = ledgerRepo.getLatestBlock();
LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(preBlock);
LedgerEditor newBlockEditor = ledgerRepo.createNextBlock();
TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset,
opReg, ledgerService);

TxBuilder txBuilder = new TxBuilder(ledgerRepo.getHash());
txDefinitor.buildTx(txBuilder);

TransactionRequest txReq = buildAndSignRequest(txBuilder, parti0, parti0);
TransactionResponse resp = txbatchProcessor.schedule(txReq);

// 提交区块;
TransactionBatchResultHandle txResultHandle = txbatchProcessor.prepare();
txResultHandle.commit();

LedgerBlock latestBlock = ledgerRepo.getLatestBlock();
assertNotNull(resp.getBlockHash());
assertEquals(preBlock.getHeight() + 1, resp.getBlockHeight());
return latestBlock;
}

private TransactionRequest buildAndSignRequest(TxBuilder txBuilder, BlockchainKeypair endpointKey,
BlockchainKeypair nodeKey) {
TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest();
txReqBuilder.signAsEndpoint(endpointKey);
txReqBuilder.signAsNode(nodeKey);
TransactionRequest txReq = txReqBuilder.buildRequest();
return txReq;
}

private void registerDataAccount(LedgerRepository ledgerRepo, LedgerManager ledgerManager,
DefaultOperationHandleRegisteration opReg, HashDigest ledgerHash, BlockchainKeypair kpDataAccount) {
LedgerBlock preBlock = ledgerRepo.getLatestBlock();
LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(preBlock);

// 加载合约
LedgerEditor newBlockEditor = ledgerRepo.createNextBlock();
TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset,
opReg, ledgerManager);

// 注册数据账户;
TxBuilder txBuilder = new TxBuilder(ledgerHash);

txBuilder.dataAccounts().register(kpDataAccount.getIdentity());
TransactionRequestBuilder txReqBuilder1 = txBuilder.prepareRequest();
txReqBuilder1.signAsEndpoint(parti0);
txReqBuilder1.signAsNode(parti0);
TransactionRequest txReq = txReqBuilder1.buildRequest();

TransactionResponse resp = txbatchProcessor.schedule(txReq);

TransactionBatchResultHandle txResultHandle = txbatchProcessor.prepare();
txResultHandle.commit();

assertNotNull(resp.getBlockHash());
assertEquals(TransactionState.SUCCESS, resp.getExecutionState());
assertEquals(preBlock.getHeight() + 1, resp.getBlockHeight());
}

private void deploy(LedgerRepository ledgerRepo, LedgerManager ledgerManager, private void deploy(LedgerRepository ledgerRepo, LedgerManager ledgerManager,
DefaultOperationHandleRegisteration opReg, HashDigest ledgerHash,
BlockchainKeypair contractKey) {
DefaultOperationHandleRegisteration opReg, HashDigest ledgerHash, BlockchainKeypair contractKey) {
// 创建新区块的交易处理器; // 创建新区块的交易处理器;
LedgerBlock preBlock = ledgerRepo.getLatestBlock(); LedgerBlock preBlock = ledgerRepo.getLatestBlock();
LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(preBlock); LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(preBlock);
@@ -193,4 +460,10 @@ public class ContractInvokingTest {
new Random().nextBytes(chainCode); new Random().nextBytes(chainCode);
return chainCode; return chainCode;
} }

public static interface TxDefinitor {

void buildTx(TxBuilder txBuilder);

}
} }

+ 19
- 0
source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/KeyValueEntry.java View File

@@ -0,0 +1,19 @@
package test.com.jd.blockchain.ledger;

import com.jd.blockchain.binaryproto.DataContract;
import com.jd.blockchain.binaryproto.DataField;
import com.jd.blockchain.binaryproto.PrimitiveType;

@DataContract(code = 0x4010)
public interface KeyValueEntry {

@DataField(order = 1, primitiveType = PrimitiveType.TEXT)
String getKey();

@DataField(order = 2, primitiveType = PrimitiveType.TEXT)
String getValue();

@DataField(order = 3, primitiveType = PrimitiveType.INT64)
long getVersion();

}

+ 47
- 0
source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/KeyValueObject.java View File

@@ -0,0 +1,47 @@
package test.com.jd.blockchain.ledger;

public class KeyValueObject implements KeyValueEntry {

private String key;

private String value;

private long version;

public KeyValueObject() {
}

public KeyValueObject(String key, String value, long version) {
this.key = key;
this.value = value;
this.version = version;
}

@Override
public String getKey() {
return key;
}

@Override
public String getValue() {
return value;
}

@Override
public long getVersion() {
return version;
}

public void setKey(String key) {
this.key = key;
}

public void setValue(String value) {
this.value = value;
}

public void setVersion(long version) {
this.version = version;
}

}

+ 33
- 0
source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleDataSetTest.java View File

@@ -32,6 +32,38 @@ public class MerkleDataSetTest {


private static final String[] SUPPORTED_PROVIDERS = { ClassicCryptoService.class.getName(), private static final String[] SUPPORTED_PROVIDERS = { ClassicCryptoService.class.getName(),
SMCryptoService.class.getName() }; SMCryptoService.class.getName() };
/**
* 测试存储的增长;
*/
@Test
public void testKeyIndex() {
CryptoProvider[] supportedProviders = new CryptoProvider[SUPPORTED_PROVIDERS.length];
for (int i = 0; i < SUPPORTED_PROVIDERS.length; i++) {
supportedProviders[i] = Crypto.getProvider(SUPPORTED_PROVIDERS[i]);
}
String keyPrefix = "";
CryptoConfig cryptoConfig = new CryptoConfig();
cryptoConfig.setSupportedProviders(supportedProviders);
cryptoConfig.setHashAlgorithm(ClassicAlgorithm.SHA256);
cryptoConfig.setAutoVerifyHash(true);
MemoryKVStorage storage = new MemoryKVStorage();
MerkleDataSet mds = new MerkleDataSet(cryptoConfig, keyPrefix, storage, storage);
mds.setValue("A", "A".getBytes(), -1);
mds.setValue("B", "B".getBytes(), -1);
mds.setValue("C", "C".getBytes(), -1);
mds.commit();
//校验 Key 的正确性;
assertEquals("A", mds.getKeyAtIndex(0));
assertEquals("B", mds.getKeyAtIndex(1));
assertEquals("C", mds.getKeyAtIndex(2));
}


/** /**
* 测试存储的增长; * 测试存储的增长;
@@ -59,6 +91,7 @@ public class MerkleDataSetTest {


mds.commit(); mds.commit();
HashDigest root1 = mds.getRootHash(); HashDigest root1 = mds.getRootHash();


// 1个KV项的存储KEY的数量= 1 + 1(保存SN) + Merkle节点数量; // 1个KV项的存储KEY的数量= 1 + 1(保存SN) + Merkle节点数量;
// 所以:3 项; // 所以:3 项;


+ 21
- 7
source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionBatchProcessorTest.java View File

@@ -14,6 +14,7 @@ import com.jd.blockchain.ledger.BlockchainKeyGenerator;
import com.jd.blockchain.ledger.BlockchainKeypair; import com.jd.blockchain.ledger.BlockchainKeypair;
import com.jd.blockchain.ledger.BytesValue; import com.jd.blockchain.ledger.BytesValue;
import com.jd.blockchain.ledger.DataAccountRegisterOperation; import com.jd.blockchain.ledger.DataAccountRegisterOperation;
import com.jd.blockchain.ledger.DataVersionConflictException;
import com.jd.blockchain.ledger.EndpointRequest; import com.jd.blockchain.ledger.EndpointRequest;
import com.jd.blockchain.ledger.LedgerBlock; import com.jd.blockchain.ledger.LedgerBlock;
import com.jd.blockchain.ledger.LedgerInitSetting; import com.jd.blockchain.ledger.LedgerInitSetting;
@@ -245,7 +246,7 @@ public class TransactionBatchProcessorTest {
} }


@Test @Test
public void testTxRollbackByVersionsConfliction() {
public void testTxRollbackByVersionsConflict() {
final MemoryKVStorage STORAGE = new MemoryKVStorage(); final MemoryKVStorage STORAGE = new MemoryKVStorage();


// 初始化账本到指定的存储库; // 初始化账本到指定的存储库;
@@ -288,6 +289,8 @@ public class TransactionBatchProcessorTest {
"K2", "V-2-1", -1, ledgerHash, parti0, parti0); "K2", "V-2-1", -1, ledgerHash, parti0, parti0);
TransactionRequest txreq3 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(), TransactionRequest txreq3 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(),
"K3", "V-3-1", -1, ledgerHash, parti0, parti0); "K3", "V-3-1", -1, ledgerHash, parti0, parti0);

// 连续写 K1,K1的版本将变为1;
TransactionRequest txreq4 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(), TransactionRequest txreq4 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(),
"K1", "V-1-2", 0, ledgerHash, parti0, parti0); "K1", "V-1-2", 0, ledgerHash, parti0, parti0);


@@ -316,14 +319,14 @@ public class TransactionBatchProcessorTest {
assertNotNull(v1_1); assertNotNull(v1_1);
assertNotNull(v2); assertNotNull(v2);
assertNotNull(v3); assertNotNull(v3);
assertEquals("V-1-1", v1_0.getValue().toUTF8String()); assertEquals("V-1-1", v1_0.getValue().toUTF8String());
assertEquals("V-1-2", v1_1.getValue().toUTF8String()); assertEquals("V-1-2", v1_1.getValue().toUTF8String());
assertEquals("V-2-1", v2.getValue().toUTF8String()); assertEquals("V-2-1", v2.getValue().toUTF8String());
assertEquals("V-3-1", v3.getValue().toUTF8String()); assertEquals("V-3-1", v3.getValue().toUTF8String());


// 提交多笔数据写入的交易,包含存在数据版本冲突的交易,验证交易是否正确回滚; // 提交多笔数据写入的交易,包含存在数据版本冲突的交易,验证交易是否正确回滚;
// 先写一笔正确的交易; k3 的版本将变为 1 ;
TransactionRequest txreq5 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(), TransactionRequest txreq5 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(),
"K3", "V-3-2", 0, ledgerHash, parti0, parti0); "K3", "V-3-2", 0, ledgerHash, parti0, parti0);
// 指定冲突的版本号,正确的应该是版本1; // 指定冲突的版本号,正确的应该是版本1;
@@ -335,7 +338,14 @@ public class TransactionBatchProcessorTest {
txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, opReg, ledgerManager); txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, opReg, ledgerManager);


txbatchProcessor.schedule(txreq5); txbatchProcessor.schedule(txreq5);
txbatchProcessor.schedule(txreq6);
// 预期会产生版本冲突异常; DataVersionConflictionException;
DataVersionConflictException versionConflictionException = null;
try {
txbatchProcessor.schedule(txreq6);
} catch (DataVersionConflictException e) {
versionConflictionException = e;
}
assertNotNull(versionConflictionException);


newBlock = newBlockEditor.prepare(); newBlock = newBlockEditor.prepare();
newBlockEditor.commit(); newBlockEditor.commit();
@@ -343,11 +353,15 @@ public class TransactionBatchProcessorTest {
BytesValue v1 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K1"); BytesValue v1 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K1");
v3 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K3"); v3 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K3");


long k1_version = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getDataVersion("K1");
// k1 的版本仍然为1,没有更新;
long k1_version = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress())
.getDataVersion("K1");
assertEquals(1, k1_version); assertEquals(1, k1_version);
long k3_version = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getDataVersion("K3");

long k3_version = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress())
.getDataVersion("K3");
assertEquals(1, k3_version); assertEquals(1, k3_version);
assertNotNull(v1); assertNotNull(v1);
assertNotNull(v3); assertNotNull(v3);
assertEquals("V-1-2", v1.getValue().toUTF8String()); assertEquals("V-1-2", v1.getValue().toUTF8String());


+ 15
- 0
source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TxTestContract.java View File

@@ -0,0 +1,15 @@
package test.com.jd.blockchain.ledger;

import com.jd.blockchain.contract.Contract;
import com.jd.blockchain.contract.ContractEvent;

@Contract
public interface TxTestContract {

@ContractEvent(name = "testReadable")
boolean testReadable();

@ContractEvent(name = "testRollbackWhileVersionConfliction")
void testRollbackWhileVersionConfliction(String address, String key, String value, long version);

}

+ 74
- 0
source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TxTestContractImpl.java View File

@@ -0,0 +1,74 @@
package test.com.jd.blockchain.ledger;

import com.jd.blockchain.contract.ContractEventContext;
import com.jd.blockchain.contract.ContractLifecycleAware;
import com.jd.blockchain.contract.EventProcessingAware;
import com.jd.blockchain.ledger.KVDataEntry;
import com.jd.blockchain.utils.Bytes;

public class TxTestContractImpl implements TxTestContract, ContractLifecycleAware, EventProcessingAware {

private ContractEventContext eventContext;

private Bytes dataAddress;

public static String KEY = "k1";

@Override
public boolean testReadable() {
KVDataEntry v1 = eventContext.getLedger().getDataEntries(eventContext.getCurrentLedgerHash(),
dataAddress.toBase58(), KEY)[0];
String text1 = (String) v1.getValue();
System.out.printf("k1=%s, version=%s \r\n", text1, v1.getVersion());

text1 = null == text1 ? "v" : text1;
String newValue = text1 + "-" + (v1.getVersion() + 1);
System.out.printf("new value = %s\r\n", newValue);
eventContext.getLedger().dataAccount(dataAddress).setText(KEY, newValue, v1.getVersion());

KVDataEntry v2 = eventContext.getLedger().getDataEntries(eventContext.getCurrentLedgerHash(),
dataAddress.toBase58(), KEY)[0];
System.out.printf("---- read new value ----\r\nk1=%s, version=%s \r\n", v2.getValue(), v2.getVersion());

String text2 = (String) v2.getValue();
return text1.equals(text2);
}
@Override
public void testRollbackWhileVersionConfliction(String address, String key, String value, long version) {
eventContext.getLedger().dataAccount(address).setText(key, value, version);
}


@Override
public void postConstruct() {
// TODO Auto-generated method stub

}

@Override
public void beforeDestroy() {
// TODO Auto-generated method stub

}

@Override
public void beforeEvent(ContractEventContext eventContext) {
this.eventContext = eventContext;
}

@Override
public void postEvent(ContractEventContext eventContext, Exception error) {
this.eventContext = null;
}

public Bytes getDataAddress() {
return dataAddress;
}

public void setDataAddress(Bytes dataAddress) {
this.dataAddress = dataAddress;
}


}

+ 41
- 20
source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BytesValueEncoding.java View File

@@ -1,32 +1,36 @@
package com.jd.blockchain.ledger; package com.jd.blockchain.ledger;


import com.jd.blockchain.binaryproto.BinaryProtocol;
import com.jd.blockchain.binaryproto.DataContract;
import com.jd.blockchain.ledger.resolver.*;

import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;


import com.jd.blockchain.binaryproto.BinaryProtocol;
import com.jd.blockchain.binaryproto.DataContract;
import com.jd.blockchain.ledger.resolver.BooleanToBytesValueResolver;
import com.jd.blockchain.ledger.resolver.BytesToBytesValueResolver;
import com.jd.blockchain.ledger.resolver.BytesValueResolver;
import com.jd.blockchain.ledger.resolver.IntegerToBytesValueResolver;
import com.jd.blockchain.ledger.resolver.LongToBytesValueResolver;
import com.jd.blockchain.ledger.resolver.ShortToBytesValueResolver;
import com.jd.blockchain.ledger.resolver.StringToBytesValueResolver;

public class BytesValueEncoding { public class BytesValueEncoding {


private static final Map<Class<?>, BytesValueResolver> CLASS_RESOLVER_MAP = new ConcurrentHashMap<>(); private static final Map<Class<?>, BytesValueResolver> CLASS_RESOLVER_MAP = new ConcurrentHashMap<>();


private static final Map<DataType, BytesValueResolver> DATA_TYPE_RESOLVER_MAP = new ConcurrentHashMap<>(); private static final Map<DataType, BytesValueResolver> DATA_TYPE_RESOLVER_MAP = new ConcurrentHashMap<>();


private static final Object[] EMPTY_OBJECTS = {};

static { static {
init(); init();
} }


private static void init() { private static void init() {
BytesValueResolver[] resolvers = new BytesValueResolver[]{
new BytesToBytesValueResolver(),
new IntegerToBytesValueResolver(),
new LongToBytesValueResolver(),
new ShortToBytesValueResolver(),
new StringToBytesValueResolver()
};
BytesValueResolver[] resolvers = new BytesValueResolver[] { new BooleanToBytesValueResolver(),
new BytesToBytesValueResolver(), new IntegerToBytesValueResolver(), new LongToBytesValueResolver(),
new ShortToBytesValueResolver(), new StringToBytesValueResolver() };


for (BytesValueResolver currResolver : resolvers) { for (BytesValueResolver currResolver : resolvers) {
// 填充classMAP // 填充classMAP
@@ -47,7 +51,6 @@ public class BytesValueEncoding {
} }
} }



public static BytesValue encodeSingle(Object value, Class<?> type) { public static BytesValue encodeSingle(Object value, Class<?> type) {
if (value == null) { if (value == null) {
return null; return null;
@@ -60,7 +63,8 @@ public class BytesValueEncoding {
if (type.isInterface()) { if (type.isInterface()) {
// 判断是否含有DataContract注解 // 判断是否含有DataContract注解
if (!type.isAnnotationPresent(DataContract.class)) { if (!type.isAnnotationPresent(DataContract.class)) {
throw new IllegalStateException(String.format("Interface[%s] can not be serialize !!!", type.getName()));
throw new IllegalStateException(
String.format("Interface[%s] can not be serialize !!!", type.getName()));
} }
// 将对象序列化 // 将对象序列化
byte[] serialBytes = BinaryProtocol.encode(value, type); byte[] serialBytes = BinaryProtocol.encode(value, type);
@@ -72,7 +76,7 @@ public class BytesValueEncoding {
} }
return bytesValueResolver.encode(value, type); return bytesValueResolver.encode(value, type);
} }
public static BytesValueList encodeArray(Object[] values, Class<?>[] types) { public static BytesValueList encodeArray(Object[] values, Class<?>[] types) {
if (values == null || values.length == 0) { if (values == null || values.length == 0) {
return null; return null;
@@ -101,11 +105,14 @@ public class BytesValueEncoding {
} }
return type == null ? valueResolver.decode(value) : valueResolver.decode(value, type); return type == null ? valueResolver.decode(value) : valueResolver.decode(value, type);
} }
public static Object[] decode(BytesValueList values, Class<?>[] types) { public static Object[] decode(BytesValueList values, Class<?>[] types) {
if (values == null) {
return EMPTY_OBJECTS;
}
BytesValue[] bytesValues = values.getValues(); BytesValue[] bytesValues = values.getValues();
if (bytesValues == null || bytesValues.length == 0) { if (bytesValues == null || bytesValues.length == 0) {
return null;
return EMPTY_OBJECTS;
} }
// 允许types为null,此时每个BytesValue按照当前的对象来处理 // 允许types为null,此时每个BytesValue按照当前的对象来处理
// 若types不为null,则types's长度必须和bytesValues一致 // 若types不为null,则types's长度必须和bytesValues一致
@@ -120,7 +127,8 @@ public class BytesValueEncoding {
DataType dataType = bytesValue.getType(); DataType dataType = bytesValue.getType();
BytesValueResolver valueResolver = DATA_TYPE_RESOLVER_MAP.get(dataType); BytesValueResolver valueResolver = DATA_TYPE_RESOLVER_MAP.get(dataType);
if (valueResolver == null) { if (valueResolver == null) {
throw new IllegalStateException(String.format("DataType[%s] can not find encoder !!!", dataType.name()));
throw new IllegalStateException(
String.format("DataType[%s] can not find encoder !!!", dataType.name()));
} }
resolveObjs[i] = valueResolver.decode(bytesValue); resolveObjs[i] = valueResolver.decode(bytesValue);
} }
@@ -132,7 +140,7 @@ public class BytesValueEncoding {
} }
return resolveObjs; return resolveObjs;
} }
public static Object getDefaultValue(Class<?> type) { public static Object getDefaultValue(Class<?> type) {
if (type == void.class || type == Void.class) { if (type == void.class || type == Void.class) {
return null; return null;
@@ -174,14 +182,27 @@ public class BytesValueEncoding {
if (currParamType.isInterface()) { if (currParamType.isInterface()) {
// 接口序列化必须实现DataContract注解 // 接口序列化必须实现DataContract注解
if (!currParamType.isAnnotationPresent(DataContract.class)) { if (!currParamType.isAnnotationPresent(DataContract.class)) {
throw new IllegalStateException(String.format("Interface[%s] can not be serialize !!!", currParamType.getName()));
throw new IllegalStateException(
String.format("Interface[%s] can not be annotated as a DataContract!!!", currParamType.getName()));
} }
return true; return true;
} }
if (currParamType.isArray() ) {
Class<?> componentType = currParamType.getComponentType();
if (componentType.isInterface()) {
// 接口序列化必须实现DataContract注解
if (!componentType.isAnnotationPresent(DataContract.class)) {
throw new IllegalStateException(
String.format("Interface[%s] can not be annotated as a DataContract!!!", currParamType.getName()));
}
return true;
}
}
return CLASS_RESOLVER_MAP.containsKey(currParamType); return CLASS_RESOLVER_MAP.containsKey(currParamType);
} }



public static class BytesValueListData implements BytesValueList { public static class BytesValueListData implements BytesValueList {


private List<BytesValue> bytesValues = new ArrayList<>(); private List<BytesValue> bytesValues = new ArrayList<>();


+ 27
- 0
source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataVersionConflictException.java View File

@@ -0,0 +1,27 @@
package com.jd.blockchain.ledger;
public class DataVersionConflictException extends BlockRollbackException {
private static final long serialVersionUID = 3583192000738807503L;
private TransactionState state;
public DataVersionConflictException() {
this(TransactionState.DATA_VERSION_CONFLICT, null);
}
public DataVersionConflictException(String message) {
this(TransactionState.DATA_VERSION_CONFLICT, message);
}
private DataVersionConflictException(TransactionState state, String message) {
super(message);
assert TransactionState.SUCCESS != state;
this.state = state;
}
public TransactionState getState() {
return state;
}
}

+ 5
- 0
source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionState.java View File

@@ -38,6 +38,11 @@ public enum TransactionState {
* 合约不存在; * 合约不存在;
*/ */
CONTRACT_DOES_NOT_EXIST((byte) 0x04), CONTRACT_DOES_NOT_EXIST((byte) 0x04),
/**
* 数据写入时版本冲突;
*/
DATA_VERSION_CONFLICT((byte) 0x05),
/** /**
* 由于在错误的账本上执行交易而被丢弃; * 由于在错误的账本上执行交易而被丢弃;


+ 58
- 0
source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/resolver/BooleanToBytesValueResolver.java View File

@@ -0,0 +1,58 @@
package com.jd.blockchain.ledger.resolver;

import com.jd.blockchain.ledger.BytesData;
import com.jd.blockchain.ledger.BytesValue;
import com.jd.blockchain.ledger.DataType;
import com.jd.blockchain.utils.Bytes;
import com.jd.blockchain.utils.io.BytesUtils;

import java.util.Set;

public class BooleanToBytesValueResolver extends AbstractBytesValueResolver {

private final Class<?>[] supportClasses = { Boolean.class, boolean.class };

private final DataType[] supportDataTypes = { DataType.BOOLEAN };

private final Set<Class<?>> convertClasses = initBooleanConvertSet();

@Override
public BytesValue encode(Object value, Class<?> type) {
if (!isSupport(type)) {
throw new IllegalStateException(String.format("Un-support encode Class[%s] Object !!!", type.getName()));
}
return BytesData.fromBoolean((boolean) value);
}

@Override
public Class<?>[] supportClasses() {
return supportClasses;
}

@Override
public DataType[] supportDataTypes() {
return supportDataTypes;
}

@Override
protected Object decode(Bytes value) {
return BytesUtils.toInt(value.toBytes());
}

@Override
public Object decode(BytesValue value, Class<?> clazz) {
// 支持转换为short、int、long
int intVal = (int) decode(value);
if (convertClasses.contains(clazz)) {
// 对于short和Short需要强制类型转换
if (clazz.equals(short.class) || clazz.equals(Short.class)) {
return (short) intVal;
} else if (clazz.equals(long.class) || clazz.equals(Long.class)) {
return (long) intVal;
}
return intVal;
} else {
throw new IllegalStateException(String.format("Un-Support decode value to class[%s] !!!", clazz.getName()));
}
}
}

+ 66
- 58
source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/resolver/BytesValueResolver.java View File

@@ -10,71 +10,79 @@ import java.util.Set;


public interface BytesValueResolver { public interface BytesValueResolver {


/**
* Int相关的可转换Class集合
*/
Class<?>[] supportIntConvertClasses = {
short.class, Short.class, int.class, Integer.class, long.class, Long.class};
/**
* Boolean相关的可转换Class集合
*/
Class<?>[] supportBooleanConvertClasses = { boolean.class, Boolean.class };


/**
* 字节数组(字符串)相关可转换的Class集合
*/
Class<?>[] supportByteConvertClasses = {
String.class, Bytes.class, byte[].class};
/**
* Int相关的可转换Class集合
*/
Class<?>[] supportIntConvertClasses = { short.class, Short.class, int.class, Integer.class, long.class,
Long.class };


default Set<Class<?>> initIntConvertSet() {
return new HashSet<>(Arrays.asList(supportIntConvertClasses));
}
/**
* 字节数组(字符串)相关可转换的Class集合
*/
Class<?>[] supportByteConvertClasses = { String.class, Bytes.class, byte[].class };
default Set<Class<?>> initBooleanConvertSet() {
return new HashSet<>(Arrays.asList(supportBooleanConvertClasses));
}


default Set<Class<?>> initByteConvertSet() {
return new HashSet<>(Arrays.asList(supportByteConvertClasses));
}
default Set<Class<?>> initIntConvertSet() {
return new HashSet<>(Arrays.asList(supportIntConvertClasses));
}


/**
* 将对象转换为BytesValue
*
* @param value
* @return
*/
BytesValue encode(Object value);
default Set<Class<?>> initByteConvertSet() {
return new HashSet<>(Arrays.asList(supportByteConvertClasses));
}


/**
* 将对象转换为BytesValue
*
* @param value
* @param type
* @return
*/
BytesValue encode(Object value, Class<?> type);
/**
* 将对象转换为BytesValue
*
* @param value
* @return
*/
BytesValue encode(Object value);


/**
* 当前解析器支持的Class列表
*
* @return
*/
Class<?>[] supportClasses();
/**
* 将对象转换为BytesValue
*
* @param value
* @param type
* @return
*/
BytesValue encode(Object value, Class<?> type);


/**
* 当前解析器支持的DataType列表
*
* @return
*/
DataType[] supportDataTypes();
/**
* 当前解析器支持的Class列表
*
* @return
*/
Class<?>[] supportClasses();


/**
* 将BytesValue解析为对应的Object
*
* @param value
* @return
*/
Object decode(BytesValue value);
/**
* 当前解析器支持的DataType列表
*
* @return
*/
DataType[] supportDataTypes();


/**
* 将BytesValue转换为指定Class的Object
*
* @param value
* @param clazz
* @return
*/
Object decode(BytesValue value, Class<?> clazz);
/**
* 将BytesValue解析为对应的Object
*
* @param value
* @return
*/
Object decode(BytesValue value);

/**
* 将BytesValue转换为指定Class的Object
*
* @param value
* @param clazz
* @return
*/
Object decode(BytesValue value, Class<?> clazz);
} }

+ 1
- 1
source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/RuntimeContext.java View File

@@ -81,7 +81,7 @@ public abstract class RuntimeContext {
if (jarFile.isFile()) { if (jarFile.isFile()) {
FileUtils.deleteFile(jarFile); FileUtils.deleteFile(jarFile);
} else { } else {
throw new IllegalStateException("Code storage confliction! --" + jarFile.getAbsolutePath());
throw new IllegalStateException("Code storage conflict! --" + jarFile.getAbsolutePath());
} }
} }
FileUtils.writeBytes(jarBytes, jarFile); FileUtils.writeBytes(jarBytes, jarFile);


+ 34
- 0
source/test/test-contract/pom.xml View File

@@ -0,0 +1,34 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.jd.blockchain</groupId>
<artifactId>test</artifactId>
<version>1.0.1.RELEASE</version>
</parent>
<artifactId>test-contract</artifactId>

<dependencies>
<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>contract-jvm</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>ledger-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>storage-rocksdb</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>crypto-classic</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

+ 14
- 0
source/test/test-contract/src/test/java/test/com/jd/blockchain/contract/ContractTransactionRollbackTest.java View File

@@ -0,0 +1,14 @@
package test.com.jd.blockchain.contract;

import static org.junit.Assert.*;

import org.junit.Test;

public class ContractTransactionRollbackTest {

@Test
public void test() {
}

}

Loading…
Cancel
Save