| @@ -20,7 +20,6 @@ import com.jd.blockchain.utils.Bytes; | |||||
| */ | */ | ||||
| public class JavaContractCode extends AbstractContractCode { | public class JavaContractCode extends AbstractContractCode { | ||||
| private static final Logger LOGGER = LoggerFactory.getLogger(JavaContractCode.class); | private static final Logger LOGGER = LoggerFactory.getLogger(JavaContractCode.class); | ||||
| private Module codeModule; | private Module codeModule; | ||||
| private Bytes address; | private Bytes address; | ||||
| private long version; | private long version; | ||||
| @@ -42,8 +41,9 @@ public class JavaContractCode extends AbstractContractCode { | |||||
| if (annoContract != null) { | if (annoContract != null) { | ||||
| if (contractInterface == null) { | if (contractInterface == null) { | ||||
| contractInterface = itf; | contractInterface = itf; | ||||
| }else { | |||||
| throw new ContractException("One contract definition is only allowed to implement one contract type!"); | |||||
| } else { | |||||
| throw new ContractException( | |||||
| "One contract definition is only allowed to implement one contract type!"); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -66,7 +66,20 @@ public class JavaContractCode extends AbstractContractCode { | |||||
| @Override | @Override | ||||
| public byte[] processEvent(ContractEventContext eventContext) { | public byte[] processEvent(ContractEventContext eventContext) { | ||||
| return codeModule.call(new ContractExecution(eventContext)); | |||||
| if (LOGGER.isDebugEnabled()) { | |||||
| LOGGER.debug("Start processing event[%s] of contract[%s]...", eventContext.getEvent(), address.toString()); | |||||
| } | |||||
| try { | |||||
| return codeModule.call(new ContractExecution(eventContext)); | |||||
| } catch (Exception ex) { | |||||
| LOGGER.error(String.format("Error occurred while processing event[%s] of contract[%s]! --%s", | |||||
| eventContext.getEvent(), address.toString(), ex.getMessage()), ex); | |||||
| throw ex; | |||||
| } finally { | |||||
| if (LOGGER.isDebugEnabled()) { | |||||
| LOGGER.debug("End processing event[%s] of contract[%s]. ", eventContext.getEvent(), address.toString()); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| protected Object getContractInstance() { | protected Object getContractInstance() { | ||||
| @@ -8,7 +8,7 @@ import org.springframework.stereotype.Component; | |||||
| import com.jd.blockchain.ledger.LedgerException; | import com.jd.blockchain.ledger.LedgerException; | ||||
| import com.jd.blockchain.ledger.core.OperationHandle; | import com.jd.blockchain.ledger.core.OperationHandle; | ||||
| import com.jd.blockchain.ledger.core.impl.handles.ContractCodeDeployOperationHandle; | import com.jd.blockchain.ledger.core.impl.handles.ContractCodeDeployOperationHandle; | ||||
| import com.jd.blockchain.ledger.core.impl.handles.ContractEventSendOperationHandle; | |||||
| import com.jd.blockchain.ledger.core.impl.handles.JVMContractEventSendOperationHandle; | |||||
| import com.jd.blockchain.ledger.core.impl.handles.DataAccountKVSetOperationHandle; | import com.jd.blockchain.ledger.core.impl.handles.DataAccountKVSetOperationHandle; | ||||
| import com.jd.blockchain.ledger.core.impl.handles.DataAccountRegisterOperationHandle; | import com.jd.blockchain.ledger.core.impl.handles.DataAccountRegisterOperationHandle; | ||||
| import com.jd.blockchain.ledger.core.impl.handles.UserRegisterOperationHandle; | import com.jd.blockchain.ledger.core.impl.handles.UserRegisterOperationHandle; | ||||
| @@ -30,7 +30,7 @@ public class DefaultOperationHandleRegisteration implements OperationHandleRegis | |||||
| opHandles.add(new DataAccountRegisterOperationHandle()); | opHandles.add(new DataAccountRegisterOperationHandle()); | ||||
| opHandles.add(new UserRegisterOperationHandle()); | opHandles.add(new UserRegisterOperationHandle()); | ||||
| opHandles.add(new ContractCodeDeployOperationHandle()); | opHandles.add(new ContractCodeDeployOperationHandle()); | ||||
| opHandles.add(new ContractEventSendOperationHandle()); | |||||
| opHandles.add(new JVMContractEventSendOperationHandle()); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -21,13 +21,7 @@ import com.jd.blockchain.ledger.core.impl.LedgerQueryService; | |||||
| import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
| @Service | @Service | ||||
| public class ContractEventSendOperationHandle implements OperationHandle { | |||||
| private static final ContractEngine JVM_ENGINE; | |||||
| static { | |||||
| JVM_ENGINE = ContractServiceProviders.getProvider(CONTRACT_SERVICE_PROVIDER).getEngine(); | |||||
| } | |||||
| public abstract class AbtractContractEventHandle implements OperationHandle { | |||||
| @Override | @Override | ||||
| public boolean support(Class<?> operationType) { | public boolean support(Class<?> operationType) { | ||||
| @@ -63,15 +57,15 @@ public class ContractEventSendOperationHandle implements OperationHandle { | |||||
| localContractEventContext.setArgs(contractOP.getArgs()).setTransactionRequest(requestContext.getRequest()) | localContractEventContext.setArgs(contractOP.getArgs()).setTransactionRequest(requestContext.getRequest()) | ||||
| .setLedgerContext(ledgerContext); | .setLedgerContext(ledgerContext); | ||||
| ContractCode contractCode = JVM_ENGINE.getContract(contract.getAddress(), contract.getChaincodeVersion()); | |||||
| if (contractCode == null) { | |||||
| // 装载合约; | |||||
| contractCode = JVM_ENGINE.setupContract(contract.getAddress(), contract.getChaincodeVersion(), | |||||
| contract.getChainCode()); | |||||
| } | |||||
| // 装载合约; | |||||
| ContractCode contractCode = loadContractCode(contract); | |||||
| // 处理合约事件; | // 处理合约事件; | ||||
| return contractCode.processEvent(localContractEventContext); | return contractCode.processEvent(localContractEventContext); | ||||
| } | } | ||||
| protected abstract ContractCode loadContractCode(ContractAccount contract); | |||||
| } | } | ||||
| @@ -0,0 +1,29 @@ | |||||
| package com.jd.blockchain.ledger.core.impl.handles; | |||||
| import static com.jd.blockchain.utils.BaseConstant.CONTRACT_SERVICE_PROVIDER; | |||||
| import com.jd.blockchain.contract.engine.ContractCode; | |||||
| import com.jd.blockchain.contract.engine.ContractEngine; | |||||
| import com.jd.blockchain.contract.engine.ContractServiceProviders; | |||||
| import com.jd.blockchain.ledger.core.ContractAccount; | |||||
| public class JVMContractEventSendOperationHandle extends AbtractContractEventHandle { | |||||
| private static final ContractEngine JVM_ENGINE; | |||||
| static { | |||||
| JVM_ENGINE = ContractServiceProviders.getProvider(CONTRACT_SERVICE_PROVIDER).getEngine(); | |||||
| } | |||||
| @Override | |||||
| protected ContractCode loadContractCode(ContractAccount contract) { | |||||
| ContractCode contractCode = JVM_ENGINE.getContract(contract.getAddress(), contract.getChaincodeVersion()); | |||||
| if (contractCode == null) { | |||||
| // 装载合约; | |||||
| contractCode = JVM_ENGINE.setupContract(contract.getAddress(), contract.getChaincodeVersion(), | |||||
| contract.getChainCode()); | |||||
| } | |||||
| return contractCode; | |||||
| } | |||||
| } | |||||
| @@ -1,82 +1,50 @@ | |||||
| package test.com.jd.blockchain.ledger; | package test.com.jd.blockchain.ledger; | ||||
| import java.util.Map; | import java.util.Map; | ||||
| import java.util.concurrent.CompletableFuture; | |||||
| import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
| import com.jd.blockchain.contract.LocalContractEventContext; | |||||
| import com.jd.blockchain.contract.ContractType; | |||||
| import com.jd.blockchain.contract.engine.ContractCode; | import com.jd.blockchain.contract.engine.ContractCode; | ||||
| import com.jd.blockchain.ledger.ContractEventSendOperation; | |||||
| import com.jd.blockchain.ledger.LedgerException; | |||||
| import com.jd.blockchain.ledger.Operation; | |||||
| import com.jd.blockchain.contract.jvm.AbstractContractCode; | |||||
| import com.jd.blockchain.contract.jvm.ContractDefinition; | |||||
| import com.jd.blockchain.ledger.core.ContractAccount; | import com.jd.blockchain.ledger.core.ContractAccount; | ||||
| import com.jd.blockchain.ledger.core.ContractAccountSet; | |||||
| import com.jd.blockchain.ledger.core.LedgerDataSet; | |||||
| import com.jd.blockchain.ledger.core.LedgerService; | |||||
| import com.jd.blockchain.ledger.core.OperationHandle; | |||||
| import com.jd.blockchain.ledger.core.TransactionRequestContext; | |||||
| import com.jd.blockchain.ledger.core.impl.LedgerQueryService; | |||||
| import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | |||||
| import com.jd.blockchain.ledger.core.impl.handles.ContractLedgerContext; | |||||
| import com.jd.blockchain.ledger.core.impl.handles.AbtractContractEventHandle; | |||||
| import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
| public class ContractInvokingHandle implements OperationHandle { | |||||
| public class ContractInvokingHandle extends AbtractContractEventHandle { | |||||
| private Map<Bytes, Object> contractInstances = new ConcurrentHashMap<Bytes, Object>(); | |||||
| private Map<Bytes, ContractCode> contractInstances = new ConcurrentHashMap<Bytes, ContractCode>(); | |||||
| @Override | @Override | ||||
| public byte[] process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
| LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, LedgerService ledgerService) { | |||||
| process(op, dataset, requestContext, previousBlockDataset, opHandleContext, ledgerService, null); | |||||
| // TODO: return value; | |||||
| return null; | |||||
| protected ContractCode loadContractCode(ContractAccount contract) { | |||||
| return contractInstances.get(contract.getAddress()); | |||||
| } | } | ||||
| @Override | |||||
| public boolean support(Class<?> operationType) { | |||||
| return ContractEventSendOperation.class.isAssignableFrom(operationType); | |||||
| public <T> ContractCode setup(Bytes address, Class<T> contractIntf, T instance) { | |||||
| ContractCodeInstance<T> contract = new ContractCodeInstance<T>(address, 0, contractIntf, instance); | |||||
| contractInstances.put(address, contract); | |||||
| return contract; | |||||
| } | } | ||||
| public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
| LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, LedgerService ledgerService, | |||||
| CompletableFuture<String> contractReturn) { | |||||
| private static class ContractCodeInstance<T> extends AbstractContractCode { | |||||
| ContractEventSendOperation contractOP = (ContractEventSendOperation) op; | |||||
| // 先从账本校验合约的有效性; | |||||
| // 注意:必须在前一个区块的数据集中进行校验,因为那是经过共识的数据;从当前新区块链数据集校验则会带来攻击风险:未经共识的合约得到执行; | |||||
| ContractAccountSet contractSet = previousBlockDataset.getContractAccountSet(); | |||||
| if (!contractSet.contains(contractOP.getContractAddress())) { | |||||
| throw new LedgerException(String.format("Contract was not registered! --[ContractAddress=%s]", | |||||
| contractOP.getContractAddress())); | |||||
| } | |||||
| private T instance; | |||||
| // 创建合约的账本上下文实例; | |||||
| LedgerQueryService queryService = new LedgerQueryService(ledgerService); | |||||
| ContractLedgerContext ledgerContext = new ContractLedgerContext(queryService, opHandleContext); | |||||
| // 先检查合约引擎是否已经加载合约;如果未加载,再从账本中读取合约代码并装载到引擎中执行; | |||||
| ContractAccount contract = contractSet.getContract(contractOP.getContractAddress()); | |||||
| if (contract == null) { | |||||
| throw new LedgerException(String.format("Contract was not registered! --[ContractAddress=%s]", | |||||
| contractOP.getContractAddress())); | |||||
| public ContractCodeInstance(Bytes address, long version, Class<T> delaredInterface, T instance) { | |||||
| super(address, version, resolveContractDefinition(delaredInterface, instance.getClass())); | |||||
| this.instance = instance; | |||||
| } | } | ||||
| // 创建合约上下文; | |||||
| LocalContractEventContext localContractEventContext = new LocalContractEventContext( | |||||
| requestContext.getRequest().getTransactionContent().getLedgerHash(), contractOP.getEvent()); | |||||
| localContractEventContext.setArgs(contractOP.getArgs()).setTransactionRequest(requestContext.getRequest()) | |||||
| .setLedgerContext(ledgerContext); | |||||
| private static ContractDefinition resolveContractDefinition(Class<?> declaredIntf, Class<?> implementedClass) { | |||||
| ContractType contractType = ContractType.resolve(declaredIntf); | |||||
| return new ContractDefinition(contractType, implementedClass); | |||||
| } | |||||
| ContractCode contractCode = JVM_ENGINE.getContract(contract.getAddress(), contract.getChaincodeVersion()); | |||||
| if (contractCode == null) { | |||||
| // 装载合约; | |||||
| contractCode = JVM_ENGINE.setupContract(contract.getAddress(), contract.getChaincodeVersion(), | |||||
| contract.getChainCode()); | |||||
| @Override | |||||
| protected T getContractInstance() { | |||||
| return instance; | |||||
| } | } | ||||
| // 处理合约事件; | |||||
| contractCode.processEvent(localContractEventContext, contractReturn); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,21 +1,37 @@ | |||||
| package test.com.jd.blockchain.ledger; | package test.com.jd.blockchain.ledger; | ||||
| import static org.junit.Assert.*; | |||||
| 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.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.junit.Test; | ||||
| import org.mockito.Mockito; | |||||
| 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.BlockchainKeyGenerator; | 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.BytesValueEntry; | |||||
| 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; | ||||
| import com.jd.blockchain.ledger.LedgerTransaction; | import com.jd.blockchain.ledger.LedgerTransaction; | ||||
| import com.jd.blockchain.ledger.NodeRequest; | import com.jd.blockchain.ledger.NodeRequest; | ||||
| import com.jd.blockchain.ledger.OperationResult; | |||||
| import com.jd.blockchain.ledger.TransactionContent; | import com.jd.blockchain.ledger.TransactionContent; | ||||
| import com.jd.blockchain.ledger.TransactionContentBody; | import com.jd.blockchain.ledger.TransactionContentBody; | ||||
| import com.jd.blockchain.ledger.TransactionRequest; | import com.jd.blockchain.ledger.TransactionRequest; | ||||
| import com.jd.blockchain.ledger.TransactionRequestBuilder; | |||||
| import com.jd.blockchain.ledger.TransactionResponse; | import com.jd.blockchain.ledger.TransactionResponse; | ||||
| import com.jd.blockchain.ledger.TransactionState; | import com.jd.blockchain.ledger.TransactionState; | ||||
| import com.jd.blockchain.ledger.UserRegisterOperation; | import com.jd.blockchain.ledger.UserRegisterOperation; | ||||
| @@ -27,9 +43,11 @@ 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.service.TransactionBatchResultHandle; | |||||
| import com.jd.blockchain.storage.service.utils.MemoryKVStorage; | import com.jd.blockchain.storage.service.utils.MemoryKVStorage; | ||||
| import com.jd.blockchain.transaction.TxBuilder; | import com.jd.blockchain.transaction.TxBuilder; | ||||
| import com.jd.blockchain.utils.Bytes; | |||||
| public class ContractInvokingTest { | public class ContractInvokingTest { | ||||
| static { | static { | ||||
| @@ -44,7 +62,6 @@ public class ContractInvokingTest { | |||||
| private static final String LEDGER_KEY_PREFIX = "LDG://"; | private static final String LEDGER_KEY_PREFIX = "LDG://"; | ||||
| private BlockchainKeypair parti0 = BlockchainKeyGenerator.getInstance().generate(); | private BlockchainKeypair parti0 = BlockchainKeyGenerator.getInstance().generate(); | ||||
| private BlockchainKeypair parti1 = BlockchainKeyGenerator.getInstance().generate(); | private BlockchainKeypair parti1 = BlockchainKeyGenerator.getInstance().generate(); | ||||
| private BlockchainKeypair parti2 = BlockchainKeyGenerator.getInstance().generate(); | private BlockchainKeypair parti2 = BlockchainKeyGenerator.getInstance().generate(); | ||||
| @@ -56,18 +73,69 @@ public class ContractInvokingTest { | |||||
| @Test | @Test | ||||
| public void test() { | public void test() { | ||||
| // 初始化账本到指定的存储库; | // 初始化账本到指定的存储库; | ||||
| HashDigest ledgerHash = initLedger(storage, parti0, parti1, parti2, parti3); | |||||
| HashDigest ledgerHash = initLedger(storage, parti0, parti1, parti2, parti3); | |||||
| // 重新加载账本; | // 重新加载账本; | ||||
| LedgerManager ledgerManager = new LedgerManager(); | LedgerManager ledgerManager = new LedgerManager(); | ||||
| LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, storage); | LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, storage); | ||||
| OperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration(); | |||||
| //构建基于接口调用合约的交易请求; | |||||
| // 创建合约处理器; | |||||
| ContractInvokingHandle contractInvokingHandle = new ContractInvokingHandle(); | |||||
| // 创建和加载合约实例; | |||||
| BlockchainKeypair contractKey = BlockchainKeyGenerator.getInstance().generate(); | |||||
| Bytes contractAddress = contractKey.getAddress(); | |||||
| TestContract contractInstance = Mockito.mock(TestContract.class); | |||||
| contractInvokingHandle.setup(contractAddress, TestContract.class, contractInstance); | |||||
| // 注册合约处理器; | |||||
| DefaultOperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration(); | |||||
| opReg.insertAsTopPriority(contractInvokingHandle); | |||||
| // 创建新区块的交易处理器; | |||||
| LedgerBlock preBlock = ledgerRepo.getLatestBlock(); | |||||
| LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerRepo.getLatestBlock()); | |||||
| LedgerEditor newBlockEditor = ledgerRepo.createNextBlock(); | |||||
| TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, | |||||
| opReg, ledgerManager); | |||||
| // 构建基于接口调用合约的交易请求,用于测试合约调用; | |||||
| Random rand = new Random(); | |||||
| TxBuilder txBuilder = new TxBuilder(ledgerHash); | TxBuilder txBuilder = new TxBuilder(ledgerHash); | ||||
| // txBuilder.contract(address, contractIntf) | |||||
| TestContract contractProxy = txBuilder.contract(contractAddress, TestContract.class); | |||||
| String asset = "AK"; | |||||
| long issueAmount = rand.nextLong(); | |||||
| when(contractInstance.issue(anyString(), anyLong())).thenReturn(issueAmount); | |||||
| contractProxy.issue(asset, issueAmount); | |||||
| TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | |||||
| txReqBuilder.signAsEndpoint(parti0); | |||||
| txReqBuilder.signAsNode(parti0); | |||||
| TransactionRequest txReq = txReqBuilder.buildRequest(); | |||||
| TransactionResponse resp = txbatchProcessor.schedule(txReq); | |||||
| verify(contractInstance, times(1)).issue(asset, issueAmount); | |||||
| OperationResult[] opResults = resp.getContractReturn(); | |||||
| assertEquals(1, opResults.length); | |||||
| assertEquals(0, opResults[0].getIndex()); | |||||
| byte[] retnBytes = BinaryProtocol.encode(BytesValueEntry.fromInt64(issueAmount), BytesValue.class); | |||||
| assertArrayEquals(retnBytes, opResults[0].getResult()); | |||||
| // 提交区块; | |||||
| TransactionBatchResultHandle txResultHandle = txbatchProcessor.prepare(); | |||||
| txResultHandle.commit(); | |||||
| LedgerBlock latestBlock = ledgerRepo.getLatestBlock(); | |||||
| assertEquals(preBlock.getHeight() + 1, latestBlock.getHeight()); | |||||
| assertEquals(resp.getBlockHeight(), latestBlock.getHeight()); | |||||
| assertEquals(resp.getBlockHash(), latestBlock.getHash()); | |||||
| // 再验证一次结果; | |||||
| assertEquals(1, opResults.length); | |||||
| assertEquals(0, opResults[0].getIndex()); | |||||
| assertArrayEquals(retnBytes, opResults[0].getResult()); | |||||
| } | } | ||||
| private HashDigest initLedger(MemoryKVStorage storage, BlockchainKeypair... partiKeys) { | private HashDigest initLedger(MemoryKVStorage storage, BlockchainKeypair... partiKeys) { | ||||
| @@ -1,5 +1,9 @@ | |||||
| package test.com.jd.blockchain.ledger; | package test.com.jd.blockchain.ledger; | ||||
| import com.jd.blockchain.contract.Contract; | |||||
| import com.jd.blockchain.contract.ContractEvent; | |||||
| @Contract | |||||
| public interface TestContract { | public interface TestContract { | ||||
| /** | /** | ||||
| @@ -9,6 +13,7 @@ public interface TestContract { | |||||
| * @param amount 本次发行的资产数量; | * @param amount 本次发行的资产数量; | ||||
| * @return 资产总量; | * @return 资产总量; | ||||
| */ | */ | ||||
| @ContractEvent(name = "issue") | |||||
| long issue(String asset, long amount); | long issue(String asset, long amount); | ||||
| /** | /** | ||||
| @@ -17,6 +22,7 @@ public interface TestContract { | |||||
| * @param asset | * @param asset | ||||
| * @return | * @return | ||||
| */ | */ | ||||
| @ContractEvent(name = "get-amount") | |||||
| long getAmount(String asset); | long getAmount(String asset); | ||||
| /** | /** | ||||
| @@ -26,6 +32,7 @@ public interface TestContract { | |||||
| * @param asset | * @param asset | ||||
| * @return | * @return | ||||
| */ | */ | ||||
| @ContractEvent(name = "get-balance") | |||||
| long getBalance(String address, String asset); | long getBalance(String address, String asset); | ||||
| /** | /** | ||||
| @@ -35,6 +42,7 @@ public interface TestContract { | |||||
| * @param asset | * @param asset | ||||
| * @param amount | * @param amount | ||||
| */ | */ | ||||
| @ContractEvent(name = "assign") | |||||
| void assign(String address, String asset, int amount); | void assign(String address, String asset, int amount); | ||||
| /** | /** | ||||
| @@ -45,5 +53,6 @@ public interface TestContract { | |||||
| * @param asset | * @param asset | ||||
| * @param amount | * @param amount | ||||
| */ | */ | ||||
| @ContractEvent(name = "transfer") | |||||
| void transfer(String fromAddress, String toAddress, String asset, long amount); | void transfer(String fromAddress, String toAddress, String asset, long amount); | ||||
| } | } | ||||
| @@ -65,22 +65,22 @@ public class ContractType { | |||||
| /** | /** | ||||
| * 解析合约的声明; | * 解析合约的声明; | ||||
| * | * | ||||
| * @param contractDelaredInterface 声明合约的接口类型; | |||||
| * @param delaredInterface 声明合约的接口类型; | |||||
| * @return | * @return | ||||
| */ | */ | ||||
| public static ContractType resolve(Class<?> contractDelaredInterface) { | |||||
| public static ContractType resolve(Class<?> delaredInterface) { | |||||
| ContractType contractType = new ContractType(); | ContractType contractType = new ContractType(); | ||||
| Annotation annotation = contractDelaredInterface.getDeclaredAnnotation(Contract.class); | |||||
| Annotation annotation = delaredInterface.getDeclaredAnnotation(Contract.class); | |||||
| // contains: @Contract? | // contains: @Contract? | ||||
| boolean isContractType = annotation != null ? true : false; | boolean isContractType = annotation != null ? true : false; | ||||
| if (!isContractType) { | if (!isContractType) { | ||||
| throw new IllegalDataException("is not Contract Type, becaust there is not @Contract."); | |||||
| throw new IllegalDataException("The specified type is not annotated by @Contract!"); | |||||
| } | } | ||||
| // contractIntf contains @Contract and @ContractEvent; | // contractIntf contains @Contract and @ContractEvent; | ||||
| Method[] classMethods = contractDelaredInterface.getDeclaredMethods(); | |||||
| Method[] classMethods = delaredInterface.getDeclaredMethods(); | |||||
| for (Method method : classMethods) { | for (Method method : classMethods) { | ||||
| // if current method contains @ContractEvent,then put it in this map; | // if current method contains @ContractEvent,then put it in this map; | ||||
| ContractEvent contractEvent = method.getAnnotation(ContractEvent.class); | ContractEvent contractEvent = method.getAnnotation(ContractEvent.class); | ||||
| @@ -10,6 +10,7 @@ import java.util.Set; | |||||
| import org.junit.Test; | import org.junit.Test; | ||||
| import com.jd.blockchain.contract.ContractException; | |||||
| import com.jd.blockchain.contract.ContractType; | import com.jd.blockchain.contract.ContractType; | ||||
| public class ContractTypeTest { | public class ContractTypeTest { | ||||
| @@ -54,6 +55,15 @@ public class ContractTypeTest { | |||||
| Method toStringMethod = NormalContractImpl.class.getMethod("toString"); | Method toStringMethod = NormalContractImpl.class.getMethod("toString"); | ||||
| assertNull(contractType.getEvent(toStringMethod)); | assertNull(contractType.getEvent(toStringMethod)); | ||||
| assertNull(contractType.getHandleMethod("NotExist")); | assertNull(contractType.getHandleMethod("NotExist")); | ||||
| //解析非合约声明接口类型时,应该引发异常 ContractException; | |||||
| ContractException ex = null; | |||||
| try { | |||||
| ContractType.resolve(NormalContractImpl.class); | |||||
| } catch (ContractException e) { | |||||
| ex = e; | |||||
| } | |||||
| assertNotNull(ex); | |||||
| } | } | ||||
| } | } | ||||