@@ -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; | |||||
} | |||||
} |
@@ -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); | ||||
@@ -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; | |||||
} | |||||
} | |||||
} | } |
@@ -1,27 +1,57 @@ | |||||
package test.com.jd.blockchain.ledger; | package test.com.jd.blockchain.ledger; | ||||
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.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.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.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 static com.jd.blockchain.transaction.ContractReturnValue.*; | |||||
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 +62,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 +76,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 +100,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 +152,86 @@ 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()); | |||||
} | |||||
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); | ||||
@@ -288,6 +288,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 +318,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; | ||||
@@ -343,11 +345,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()); | ||||
@@ -0,0 +1,18 @@ | |||||
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 = "prepareData") | |||||
// String[] prepareData(String address); | |||||
// | |||||
// @ContractEvent(name = "doVersionConflictedWritting") | |||||
// void doVersionConflictedWritting(String key, String value, long version); | |||||
} |
@@ -0,0 +1,79 @@ | |||||
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 String[] prepareData(String address) { | |||||
// // TODO Auto-generated method stub | |||||
// return null; | |||||
// } | |||||
// | |||||
// @Override | |||||
// public void doVersionConflictedWritting(String key, String value, long version) { | |||||
// // TODO Auto-generated method stub | |||||
// | |||||
// } | |||||
@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; | |||||
} | |||||
} |
@@ -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,14 @@ 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 serialize !!!", currParamType.getName())); | |||||
} | } | ||||
return true; | 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<>(); | ||||
@@ -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())); | |||||
} | |||||
} | |||||
} |
@@ -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); | |||||
} | } |
@@ -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> |
@@ -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() { | |||||
} | |||||
} |