# Conflicts: # source/base/src/main/java/com/jd/blockchain/consts/DataCodes.java # source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java # source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionBatchProcessor.java # source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditerTest.java # source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.javatags/1.0.0
@@ -30,6 +30,8 @@ public interface DataCodes { | |||||
public static final int TX_CONTENT_BODY = 0x220; | public static final int TX_CONTENT_BODY = 0x220; | ||||
public static final int TX_RETURN_MESSAGE = 0x230; | |||||
public static final int TX_OP = 0x300; | public static final int TX_OP = 0x300; | ||||
public static final int TX_OP_LEDGER_INIT = 0x301; | public static final int TX_OP_LEDGER_INIT = 0x301; | ||||
@@ -79,6 +81,8 @@ public interface DataCodes { | |||||
public static final int DATA = 0x900; | public static final int DATA = 0x900; | ||||
public static final int CONTRACT_RETURN = 0xA22; | |||||
public static final int HASH = 0xB00; | public static final int HASH = 0xB00; | ||||
public static final int HASH_OBJECT = 0xB10; | public static final int HASH_OBJECT = 0xB10; | ||||
@@ -3,12 +3,14 @@ package com.jd.blockchain.contract.engine; | |||||
import com.jd.blockchain.contract.ContractEventContext; | import com.jd.blockchain.contract.ContractEventContext; | ||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
import java.util.concurrent.CompletableFuture; | |||||
import java.util.concurrent.Future; | |||||
public interface ContractCode { | public interface ContractCode { | ||||
Bytes getAddress(); | Bytes getAddress(); | ||||
long getVersion(); | long getVersion(); | ||||
void processEvent(ContractEventContext eventContext); | |||||
void processEvent(ContractEventContext eventContext, CompletableFuture<String> execReturn); | |||||
} | } |
@@ -14,6 +14,8 @@ import com.jd.blockchain.runtime.Module; | |||||
import com.jd.blockchain.transaction.ContractType; | import com.jd.blockchain.transaction.ContractType; | ||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
import com.jd.blockchain.utils.IllegalDataException; | import com.jd.blockchain.utils.IllegalDataException; | ||||
import java.util.concurrent.CompletableFuture; | |||||
import java.util.concurrent.Future; | |||||
/** | /** | ||||
* contract code based jvm | * contract code based jvm | ||||
@@ -46,9 +48,9 @@ public class JavaContractCode implements ContractCode { | |||||
} | } | ||||
@Override | @Override | ||||
public void processEvent(ContractEventContext eventContext) { | |||||
public void processEvent(ContractEventContext eventContext, CompletableFuture<String> execReturn) { | |||||
this.contractEventContext = eventContext; | this.contractEventContext = eventContext; | ||||
codeModule.execute(new ContractExecution()); | |||||
codeModule.execute(new ContractExecution(execReturn)); | |||||
} | } | ||||
private Object resolveArgs(byte[] args, List<DataContract> dataContractList) { | private Object resolveArgs(byte[] args, List<DataContract> dataContractList) { | ||||
@@ -61,6 +63,13 @@ public class JavaContractCode implements ContractCode { | |||||
} | } | ||||
class ContractExecution implements Runnable { | class ContractExecution implements Runnable { | ||||
private CompletableFuture<String> contractReturn; | |||||
public ContractExecution(CompletableFuture<String> contractReturn) { | |||||
this.contractReturn = contractReturn; | |||||
} | |||||
public void run() { | public void run() { | ||||
LOGGER.info("ContractThread execute()."); | LOGGER.info("ContractThread execute()."); | ||||
try { | try { | ||||
@@ -68,12 +77,16 @@ public class JavaContractCode implements ContractCode { | |||||
long startTime = System.currentTimeMillis(); | long startTime = System.currentTimeMillis(); | ||||
String contractClassName = codeModule.getMainClass(); | String contractClassName = codeModule.getMainClass(); | ||||
Class myClass = codeModule.loadClass(contractClassName); | Class myClass = codeModule.loadClass(contractClassName); | ||||
Object contractMainClassObj = myClass.newInstance();// 合约主类生成的类实例; | Object contractMainClassObj = myClass.newInstance();// 合约主类生成的类实例; | ||||
Method beforeMth_ = myClass.getMethod("beforeEvent", | Method beforeMth_ = myClass.getMethod("beforeEvent", | ||||
codeModule.loadClass(ContractEventContext.class.getName())); | codeModule.loadClass(ContractEventContext.class.getName())); | ||||
ReflectionUtils.invokeMethod(beforeMth_, contractMainClassObj, contractEventContext); | ReflectionUtils.invokeMethod(beforeMth_, contractMainClassObj, contractEventContext); | ||||
LOGGER.info("beforeEvent,耗时:" + (System.currentTimeMillis() - startTime)); | LOGGER.info("beforeEvent,耗时:" + (System.currentTimeMillis() - startTime)); | ||||
// Method eventMethod = this.getMethodByAnno(contractMainClassObj, contractEventContext.getEvent()); | // Method eventMethod = this.getMethodByAnno(contractMainClassObj, contractEventContext.getEvent()); | ||||
@@ -81,7 +94,9 @@ public class JavaContractCode implements ContractCode { | |||||
// 反序列化参数; | // 反序列化参数; | ||||
contractType = ContractType.resolve(myClass); | contractType = ContractType.resolve(myClass); | ||||
Method handleMethod = contractType.getHandleMethod(contractEventContext.getEvent()); | Method handleMethod = contractType.getHandleMethod(contractEventContext.getEvent()); | ||||
if (handleMethod == null){ | if (handleMethod == null){ | ||||
throw new IllegalDataException("don't get this method by it's @ContractEvent."); | throw new IllegalDataException("don't get this method by it's @ContractEvent."); | ||||
} | } | ||||
@@ -98,9 +113,17 @@ public class JavaContractCode implements ContractCode { | |||||
LOGGER.info("合约执行,耗时:" + (System.currentTimeMillis() - startTime)); | LOGGER.info("合约执行,耗时:" + (System.currentTimeMillis() - startTime)); | ||||
Method mth2 = myClass.getMethod("postEvent"); | Method mth2 = myClass.getMethod("postEvent"); | ||||
startTime = System.currentTimeMillis(); | startTime = System.currentTimeMillis(); | ||||
ReflectionUtils.invokeMethod(mth2, contractMainClassObj); | ReflectionUtils.invokeMethod(mth2, contractMainClassObj); | ||||
LOGGER.info("postEvent,耗时:" + (System.currentTimeMillis() - startTime)); | LOGGER.info("postEvent,耗时:" + (System.currentTimeMillis() - startTime)); | ||||
// 填充return结果 | |||||
if (this.contractReturn != null) { | |||||
this.contractReturn.complete(contractReturn); | |||||
} | |||||
} catch (NoSuchMethodException e) { | } catch (NoSuchMethodException e) { | ||||
throw new IllegalArgumentException(e.getMessage()); | throw new IllegalArgumentException(e.getMessage()); | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
@@ -0,0 +1,62 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<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"> | |||||
<parent> | |||||
<artifactId>contract</artifactId> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<version>0.9.0-SNAPSHOT</version> | |||||
</parent> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<artifactId>contract-samples</artifactId> | |||||
<name>contract-samples</name> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>ledger-model</artifactId> | |||||
<version>${project.version}</version> | |||||
<scope>provided</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>crypto-framework</artifactId> | |||||
<version>${project.version}</version> | |||||
<scope>provided</scope> | |||||
</dependency> | |||||
</dependencies> | |||||
<build> | |||||
<plugins> | |||||
<plugin> | |||||
<artifactId>maven-assembly-plugin</artifactId> | |||||
<configuration> | |||||
<finalName>contract</finalName> | |||||
<appendAssemblyId>false</appendAssemblyId> | |||||
<archive> | |||||
<manifest> | |||||
<mainClass>com.jd.blockchain.contract.ReadContractImpl</mainClass> | |||||
</manifest> | |||||
</archive> | |||||
<descriptorRefs> | |||||
<descriptorRef>jar-with-dependencies</descriptorRef> | |||||
</descriptorRefs> | |||||
</configuration> | |||||
<executions> | |||||
<execution> | |||||
<id>make-assembly</id> | |||||
<phase>package</phase> | |||||
<goals> | |||||
<goal>single</goal> | |||||
</goals> | |||||
</execution> | |||||
</executions> | |||||
</plugin> | |||||
</plugins> | |||||
</build> | |||||
</project> |
@@ -0,0 +1,13 @@ | |||||
package com.jd.blockchain.contract; | |||||
@Contract | |||||
public interface ReadContract { | |||||
@ContractEvent(name = "read-key") | |||||
String read(String address, String key); | |||||
@ContractEvent(name = "version-key") | |||||
Long readVersion(String address, String key); | |||||
} | |||||
@@ -0,0 +1,55 @@ | |||||
package com.jd.blockchain.contract; | |||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.ledger.KVDataEntry; | |||||
@Contract | |||||
public class ReadContractImpl implements EventProcessingAwire, ReadContract { | |||||
private ContractEventContext eventContext; | |||||
private HashDigest ledgerHash; | |||||
@Override | |||||
public void beforeEvent(ContractEventContext eventContext) { | |||||
this.eventContext = eventContext; | |||||
this.ledgerHash = eventContext.getCurrentLedgerHash(); | |||||
} | |||||
@Override | |||||
public void postEvent(ContractEventContext eventContext, ContractException error) { | |||||
} | |||||
@Override | |||||
public void postEvent(ContractException error) { | |||||
} | |||||
@Override | |||||
public void postEvent() { | |||||
} | |||||
@Override | |||||
@ContractEvent(name = "read-key") | |||||
public String read(String address, String key) { | |||||
KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, key); | |||||
if (kvDataEntries != null && kvDataEntries.length == 1) { | |||||
return kvDataEntries[0].getValue().toString(); | |||||
} | |||||
return null; | |||||
} | |||||
@Override | |||||
@ContractEvent(name = "version-key") | |||||
public Long readVersion(String address, String key) { | |||||
KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, key); | |||||
if (kvDataEntries != null && kvDataEntries.length == 1) { | |||||
return kvDataEntries[0].getVersion(); | |||||
} | |||||
return -1L; | |||||
} | |||||
} |
@@ -13,6 +13,7 @@ | |||||
<module>contract-framework</module> | <module>contract-framework</module> | ||||
<module>contract-jvm</module> | <module>contract-jvm</module> | ||||
<module>contract-maven-plugin</module> | <module>contract-maven-plugin</module> | ||||
</modules> | |||||
<module>contract-samples</module> | |||||
</modules> | |||||
</project> | </project> |
@@ -55,7 +55,7 @@ public class BlockBrowserController implements BlockchainExtendQueryService { | |||||
// @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/participants") | // @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/participants") | ||||
@Override | @Override | ||||
public ParticipantNode[] getConsensusParticipants(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { | |||||
public ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash) { | |||||
return peerService.getQueryService().getConsensusParticipants(ledgerHash); | return peerService.getQueryService().getConsensusParticipants(ledgerHash); | ||||
} | } | ||||
@@ -1,5 +1,6 @@ | |||||
package com.jd.blockchain.ledger.core; | package com.jd.blockchain.ledger.core; | ||||
import com.jd.blockchain.ledger.TransactionReturnMessage; | |||||
import com.jd.blockchain.ledger.TransactionState; | import com.jd.blockchain.ledger.TransactionState; | ||||
import com.jd.blockchain.ledger.LedgerTransaction; | import com.jd.blockchain.ledger.LedgerTransaction; | ||||
import com.jd.blockchain.ledger.TransactionRequest; | import com.jd.blockchain.ledger.TransactionRequest; | ||||
@@ -30,19 +31,21 @@ public interface LedgerTransactionContext { | |||||
* 提交对账本数据的修改,以指定的交易状态提交交易; | * 提交对账本数据的修改,以指定的交易状态提交交易; | ||||
* | * | ||||
* @param txResult | * @param txResult | ||||
* @param returnMessage | |||||
* | |||||
* @return | * @return | ||||
*/ | */ | ||||
LedgerTransaction commit(TransactionState txResult); | |||||
LedgerTransaction commit(TransactionState txResult, TransactionReturnMessage returnMessage); | |||||
/** | /** | ||||
* 抛弃对账本数据的修改,以指定的交易状态提交交易;<br> | * 抛弃对账本数据的修改,以指定的交易状态提交交易;<br> | ||||
* | * | ||||
* 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; | |||||
* 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState, TransactionReturnMessage)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; | |||||
* | * | ||||
* @param txResult | * @param txResult | ||||
* @return | * @return | ||||
*/ | */ | ||||
LedgerTransaction discardAndCommit(TransactionState txResult); | |||||
LedgerTransaction discardAndCommit(TransactionState txResult, TransactionReturnMessage returnMessage); | |||||
/** | /** | ||||
* 回滚事务,抛弃本次事务的所有数据更新; | * 回滚事务,抛弃本次事务的所有数据更新; | ||||
@@ -1,11 +1,7 @@ | |||||
package com.jd.blockchain.ledger.core.impl; | package com.jd.blockchain.ledger.core.impl; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.ledger.DigitalSignature; | |||||
import com.jd.blockchain.ledger.LedgerTransaction; | |||||
import com.jd.blockchain.ledger.TransactionContent; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | |||||
import com.jd.blockchain.ledger.TransactionState; | |||||
import com.jd.blockchain.ledger.*; | |||||
public class LedgerTransactionData implements LedgerTransaction { | public class LedgerTransactionData implements LedgerTransaction { | ||||
@@ -23,6 +19,8 @@ public class LedgerTransactionData implements LedgerTransaction { | |||||
private long blockHeight; | private long blockHeight; | ||||
private TransactionReturnMessage returnMessage; | |||||
// private HashDigest adminAccountHash; | // private HashDigest adminAccountHash; | ||||
// | // | ||||
// private HashDigest userAccountSetHash; | // private HashDigest userAccountSetHash; | ||||
@@ -49,7 +47,7 @@ public class LedgerTransactionData implements LedgerTransaction { | |||||
* 交易级的系统快照; | * 交易级的系统快照; | ||||
*/ | */ | ||||
public LedgerTransactionData(long blockHeight, TransactionRequest txReq, TransactionState execState, | public LedgerTransactionData(long blockHeight, TransactionRequest txReq, TransactionState execState, | ||||
TransactionStagedSnapshot txSnapshot) { | |||||
TransactionStagedSnapshot txSnapshot, TransactionReturnMessage returnMessage) { | |||||
this.blockHeight = blockHeight; | this.blockHeight = blockHeight; | ||||
// this.txSnapshot = txSnapshot == null ? new TransactionStagedSnapshot() : txSnapshot; | // this.txSnapshot = txSnapshot == null ? new TransactionStagedSnapshot() : txSnapshot; | ||||
this.txSnapshot = txSnapshot; | this.txSnapshot = txSnapshot; | ||||
@@ -57,6 +55,7 @@ public class LedgerTransactionData implements LedgerTransaction { | |||||
this.endpointSignatures = txReq.getEndpointSignatures(); | this.endpointSignatures = txReq.getEndpointSignatures(); | ||||
this.nodeSignatures = txReq.getNodeSignatures(); | this.nodeSignatures = txReq.getNodeSignatures(); | ||||
this.executionState = execState; | this.executionState = execState; | ||||
this.returnMessage = returnMessage; | |||||
} | } | ||||
@Override | @Override | ||||
@@ -74,6 +73,11 @@ public class LedgerTransactionData implements LedgerTransaction { | |||||
return executionState; | return executionState; | ||||
} | } | ||||
@Override | |||||
public TransactionReturnMessage getReturnMessage() { | |||||
return returnMessage; | |||||
} | |||||
@Override | @Override | ||||
public TransactionContent getTransactionContent() { | public TransactionContent getTransactionContent() { | ||||
return this.transactionContent; | return this.transactionContent; | ||||
@@ -350,7 +350,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
} | } | ||||
@Override | @Override | ||||
public LedgerTransaction commit(TransactionState txResult) { | |||||
public LedgerTransaction commit(TransactionState txResult, TransactionReturnMessage returnMessage) { | |||||
checkTxState(); | checkTxState(); | ||||
// capture snapshot | // capture snapshot | ||||
@@ -359,7 +359,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
// LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | ||||
// txResult, txDataSnapshot); | // txResult, txDataSnapshot); | ||||
LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null); | |||||
LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null, returnMessage); | |||||
this.txset.add(tx); | this.txset.add(tx); | ||||
// this.txset.commit(); | // this.txset.commit(); | ||||
@@ -376,7 +376,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
} | } | ||||
@Override | @Override | ||||
public LedgerTransaction discardAndCommit(TransactionState txResult) { | |||||
public LedgerTransaction discardAndCommit(TransactionState txResult, TransactionReturnMessage returnMessage) { | |||||
checkTxState(); | checkTxState(); | ||||
// 未处理 | // 未处理 | ||||
@@ -385,7 +385,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
// TransactionStagedSnapshot txDataSnapshot = takeSnapshot(); | // TransactionStagedSnapshot txDataSnapshot = takeSnapshot(); | ||||
// LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | ||||
// txResult, txDataSnapshot); | // txResult, txDataSnapshot); | ||||
LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null); | |||||
LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null, returnMessage); | |||||
this.txset.add(tx); | this.txset.add(tx); | ||||
// this.txset.commit(); | // this.txset.commit(); | ||||
@@ -3,17 +3,15 @@ package com.jd.blockchain.ledger.core.impl; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Iterator; | import java.util.Iterator; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.concurrent.CompletableFuture; | |||||
import com.jd.blockchain.ledger.*; | |||||
import com.jd.blockchain.ledger.core.impl.handles.ContractEventSendOperationHandle; | |||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.ledger.LedgerBlock; | |||||
import com.jd.blockchain.ledger.LedgerException; | import com.jd.blockchain.ledger.LedgerException; | ||||
import com.jd.blockchain.ledger.Operation; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | |||||
import com.jd.blockchain.ledger.TransactionResponse; | |||||
import com.jd.blockchain.ledger.TransactionState; | |||||
import com.jd.blockchain.ledger.core.LedgerDataSet; | import com.jd.blockchain.ledger.core.LedgerDataSet; | ||||
import com.jd.blockchain.ledger.core.LedgerEditor; | import com.jd.blockchain.ledger.core.LedgerEditor; | ||||
import com.jd.blockchain.ledger.core.LedgerService; | import com.jd.blockchain.ledger.core.LedgerService; | ||||
@@ -74,6 +72,8 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
// 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | // 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | ||||
LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | ||||
TransactionState result; | TransactionState result; | ||||
TransactionReturnMessageData returnMessageData = new TransactionReturnMessageData(); | |||||
try { | try { | ||||
LedgerDataSet dataset = txCtx.getDataSet(); | LedgerDataSet dataset = txCtx.getDataSet(); | ||||
TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | ||||
@@ -100,28 +100,53 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
} | } | ||||
}; | }; | ||||
OperationHandle opHandle; | OperationHandle opHandle; | ||||
int contractOpIndex = 0; | |||||
for (Operation op : ops) { | for (Operation op : ops) { | ||||
opHandle = opHandles.getHandle(op.getClass()); | opHandle = opHandles.getHandle(op.getClass()); | ||||
opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); | |||||
// 合约执行需要填充执行结果 | |||||
if (opHandle instanceof ContractEventSendOperationHandle) { | |||||
CompletableFuture<String> currContractReturn = new CompletableFuture<>(); | |||||
ContractReturnMessageData crmd = new ContractReturnMessageData(contractOpIndex++, currContractReturn); | |||||
returnMessageData.addContractReturnMessage(crmd); | |||||
((ContractEventSendOperationHandle) opHandle).process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService, currContractReturn); | |||||
} else { | |||||
opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); | |||||
} | |||||
} | } | ||||
// 提交交易(事务); | // 提交交易(事务); | ||||
result = TransactionState.SUCCESS; | result = TransactionState.SUCCESS; | ||||
txCtx.commit(result); | |||||
txCtx.commit(result, returnMessageData); | |||||
} catch (LedgerException e) { | } catch (LedgerException e) { | ||||
// TODO: 识别更详细的异常类型以及执行对应的处理; | // TODO: 识别更详细的异常类型以及执行对应的处理; | ||||
result = TransactionState.LEDGER_ERROR; | result = TransactionState.LEDGER_ERROR; | ||||
txCtx.discardAndCommit(TransactionState.LEDGER_ERROR); | |||||
txCtx.discardAndCommit(TransactionState.LEDGER_ERROR, returnMessageData); | |||||
LOGGER.warn(String.format("Transaction rollback caused by the ledger exception! --[TxHash=%s] --%s", | LOGGER.warn(String.format("Transaction rollback caused by the ledger exception! --[TxHash=%s] --%s", | ||||
request.getHash().toBase58(), e.getMessage()), e); | request.getHash().toBase58(), e.getMessage()), e); | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
result = TransactionState.SYSTEM_ERROR; | result = TransactionState.SYSTEM_ERROR; | ||||
txCtx.discardAndCommit(TransactionState.SYSTEM_ERROR); | |||||
txCtx.discardAndCommit(TransactionState.SYSTEM_ERROR, returnMessageData); | |||||
LOGGER.warn(String.format("Transaction rollback caused by the system exception! --[TxHash=%s] --%s", | LOGGER.warn(String.format("Transaction rollback caused by the system exception! --[TxHash=%s] --%s", | ||||
request.getHash().toBase58(), e.getMessage()), e); | request.getHash().toBase58(), e.getMessage()), e); | ||||
} | } | ||||
TxResponseHandle resp = new TxResponseHandle(request, result); | TxResponseHandle resp = new TxResponseHandle(request, result); | ||||
if (!returnMessageData.isContractReturnEmpty()) { | |||||
ContractReturnMessage[] contractReturnMessages = returnMessageData.getContractReturn(); | |||||
// 获取结果中的字符串 | |||||
String[] returnValue = new String[contractReturnMessages.length]; | |||||
try { | |||||
for (int i = 0; i < contractReturnMessages.length; i++) { | |||||
returnValue[i] = contractReturnMessages[i].getReturnMessage(); | |||||
} | |||||
resp.setContractReturn(returnValue); | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
responseList.add(resp); | responseList.add(resp); | ||||
return resp; | return resp; | ||||
@@ -201,6 +226,8 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
private TransactionState result; | private TransactionState result; | ||||
private String[] contractReturn; | |||||
public TxResponseHandle(TransactionRequest request, TransactionState result) { | public TxResponseHandle(TransactionRequest request, TransactionState result) { | ||||
this.request = request; | this.request = request; | ||||
this.result = result; | this.result = result; | ||||
@@ -231,6 +258,14 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
return globalResult == null ? result == TransactionState.SUCCESS : globalResult == TransactionState.SUCCESS; | return globalResult == null ? result == TransactionState.SUCCESS : globalResult == TransactionState.SUCCESS; | ||||
} | } | ||||
@Override | |||||
public String[] getContractReturn() { | |||||
return contractReturn; | |||||
} | |||||
public void setContractReturn(String[] contractReturn) { | |||||
this.contractReturn = contractReturn; | |||||
} | |||||
} | } | ||||
private class TransactionBatchResultImpl implements TransactionBatchResult { | private class TransactionBatchResultImpl implements TransactionBatchResult { | ||||
@@ -20,6 +20,9 @@ import com.jd.blockchain.ledger.core.TransactionRequestContext; | |||||
import com.jd.blockchain.ledger.core.impl.LedgerQueryService; | import com.jd.blockchain.ledger.core.impl.LedgerQueryService; | ||||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
import java.util.concurrent.CompletableFuture; | |||||
import java.util.concurrent.Future; | |||||
@Service | @Service | ||||
public class ContractEventSendOperationHandle implements OperationHandle { | public class ContractEventSendOperationHandle implements OperationHandle { | ||||
@@ -32,6 +35,18 @@ public class ContractEventSendOperationHandle implements OperationHandle { | |||||
@Override | @Override | ||||
public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | ||||
LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, LedgerService ledgerService) { | LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, LedgerService ledgerService) { | ||||
process(op, dataset, requestContext, previousBlockDataset, opHandleContext, ledgerService, null); | |||||
} | |||||
@Override | |||||
public boolean support(Class<?> operationType) { | |||||
return ContractEventSendOperation.class.isAssignableFrom(operationType); | |||||
} | |||||
public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||||
LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, | |||||
LedgerService ledgerService, CompletableFuture<String> contractReturn) { | |||||
ContractEventSendOperation contractOP = (ContractEventSendOperation) op; | ContractEventSendOperation contractOP = (ContractEventSendOperation) op; | ||||
// 先从账本校验合约的有效性; | // 先从账本校验合约的有效性; | ||||
// 注意:必须在前一个区块的数据集中进行校验,因为那是经过共识的数据;从当前新区块链数据集校验则会带来攻击风险:未经共识的合约得到执行; | // 注意:必须在前一个区块的数据集中进行校验,因为那是经过共识的数据;从当前新区块链数据集校验则会带来攻击风险:未经共识的合约得到执行; | ||||
@@ -66,12 +81,8 @@ public class ContractEventSendOperationHandle implements OperationHandle { | |||||
} | } | ||||
// 处理合约事件; | // 处理合约事件; | ||||
contractCode.processEvent(localContractEventContext); | |||||
contractCode.processEvent(localContractEventContext, contractReturn); | |||||
} | } | ||||
@Override | |||||
public boolean support(Class<?> operationType) { | |||||
return ContractEventSendOperation.class.isAssignableFrom(operationType); | |||||
} | |||||
} | } |
@@ -97,7 +97,7 @@ public class LedgerManagerTest { | |||||
System.out.println("UserAddress=" + userAccount.getAddress()); | System.out.println("UserAddress=" + userAccount.getAddress()); | ||||
// 提交交易结果; | // 提交交易结果; | ||||
LedgerTransaction tx = txCtx.commit(TransactionState.SUCCESS); | |||||
LedgerTransaction tx = txCtx.commit(TransactionState.SUCCESS, null); | |||||
assertEquals(genesisTxReq.getTransactionContent().getHash(), tx.getTransactionContent().getHash()); | assertEquals(genesisTxReq.getTransactionContent().getHash(), tx.getTransactionContent().getHash()); | ||||
assertEquals(0, tx.getBlockHeight()); | assertEquals(0, tx.getBlockHeight()); | ||||
@@ -137,7 +137,7 @@ public class LedgerManagerTest { | |||||
LedgerTransactionContext txCtx1 = editor1.newTransaction(txRequest); | LedgerTransactionContext txCtx1 = editor1.newTransaction(txRequest); | ||||
txCtx1.getDataSet().getDataAccountSet().register(dataKey.getAddress(), dataKey.getPubKey(), null); | txCtx1.getDataSet().getDataAccountSet().register(dataKey.getAddress(), dataKey.getPubKey(), null); | ||||
txCtx1.commit(TransactionState.SUCCESS); | |||||
txCtx1.commit(TransactionState.SUCCESS, null); | |||||
LedgerBlock block1 = editor1.prepare(); | LedgerBlock block1 = editor1.prepare(); | ||||
editor1.commit(); | editor1.commit(); | ||||
@@ -68,7 +68,7 @@ public class LedgerTransactionDataTest { | |||||
long blockHeight = 9986L; | long blockHeight = 9986L; | ||||
data = new LedgerTransactionData(blockHeight, txRequestMessage, TransactionState.SUCCESS, | data = new LedgerTransactionData(blockHeight, txRequestMessage, TransactionState.SUCCESS, | ||||
initTransactionStagedSnapshot()); | |||||
initTransactionStagedSnapshot(), null); | |||||
HashDigest hash = new HashDigest(ClassicAlgorithm.SHA256, "zhangsan".getBytes()); | HashDigest hash = new HashDigest(ClassicAlgorithm.SHA256, "zhangsan".getBytes()); | ||||
HashDigest adminAccountHash = new HashDigest(ClassicAlgorithm.SHA256, "lisi".getBytes()); | HashDigest adminAccountHash = new HashDigest(ClassicAlgorithm.SHA256, "lisi".getBytes()); | ||||
@@ -98,7 +98,7 @@ public class TransactionSetTest { | |||||
txSnapshot.setContractAccountSetHash(contractAccountSetHash); | txSnapshot.setContractAccountSetHash(contractAccountSetHash); | ||||
long blockHeight = 8922L; | long blockHeight = 8922L; | ||||
LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txReq, TransactionState.SUCCESS, txSnapshot); | |||||
LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txReq, TransactionState.SUCCESS, txSnapshot, null); | |||||
txset.add(tx); | txset.add(tx); | ||||
assertTrue(txset.isUpdated()); | assertTrue(txset.isUpdated()); | ||||
@@ -0,0 +1,17 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
@DataContract(code= DataCodes.CONTRACT_RETURN) | |||||
public interface ContractReturnMessage { | |||||
@DataField(order=1, primitiveType = PrimitiveType.INT32) | |||||
int getOperationIndex(); | |||||
@DataField(order=2, primitiveType = PrimitiveType.TEXT) | |||||
String getReturnMessage(); | |||||
} |
@@ -0,0 +1,41 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import java.util.concurrent.CompletableFuture; | |||||
public class ContractReturnMessageData implements ContractReturnMessage { | |||||
private int operationIndex; | |||||
private CompletableFuture<String> returnMsgFuture; | |||||
public ContractReturnMessageData() { | |||||
} | |||||
public ContractReturnMessageData(int operationIndex, CompletableFuture<String> returnMsgFuture) { | |||||
this.operationIndex = operationIndex; | |||||
this.returnMsgFuture = returnMsgFuture; | |||||
} | |||||
public void setOperationIndex(int operationIndex) { | |||||
this.operationIndex = operationIndex; | |||||
} | |||||
public void setReturnMsgFuture(CompletableFuture<String> returnMsgFuture) { | |||||
this.returnMsgFuture = returnMsgFuture; | |||||
} | |||||
@Override | |||||
public int getOperationIndex() { | |||||
return operationIndex; | |||||
} | |||||
@Override | |||||
public String getReturnMessage() { | |||||
try { | |||||
return returnMsgFuture.get(); | |||||
} catch (Exception e) { | |||||
e.printStackTrace(); | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
} |
@@ -45,4 +45,11 @@ public interface Transaction extends NodeRequest, HashObject { | |||||
@DataField(order=3, refEnum=true) | @DataField(order=3, refEnum=true) | ||||
TransactionState getExecutionState(); | TransactionState getExecutionState(); | ||||
/** | |||||
* 交易的返回结果 | |||||
* | |||||
* @return | |||||
*/ | |||||
@DataField(order=4, refContract=true) | |||||
TransactionReturnMessage getReturnMessage(); | |||||
} | } |
@@ -27,6 +27,8 @@ public class TransactionRespHandle implements TransactionResponse { | |||||
private TransactionState globalResult; | private TransactionState globalResult; | ||||
private String[] contractReturn; | |||||
public TransactionRespHandle(TransactionRequest request, TransactionState result, TransactionState globalResult) { | public TransactionRespHandle(TransactionRequest request, TransactionState result, TransactionState globalResult) { | ||||
this.request = request; | this.request = request; | ||||
this.result = result; | this.result = result; | ||||
@@ -49,6 +51,10 @@ public class TransactionRespHandle implements TransactionResponse { | |||||
this.result = result; | this.result = result; | ||||
} | } | ||||
public void setContractReturn(String[] contractReturn) { | |||||
this.contractReturn = contractReturn; | |||||
} | |||||
public LedgerBlock getBlock() { | public LedgerBlock getBlock() { | ||||
return block; | return block; | ||||
} | } | ||||
@@ -89,4 +95,9 @@ public class TransactionRespHandle implements TransactionResponse { | |||||
public boolean isSuccess() { | public boolean isSuccess() { | ||||
return globalResult == null ? result == TransactionState.SUCCESS : globalResult == TransactionState.SUCCESS; | return globalResult == null ? result == TransactionState.SUCCESS : globalResult == TransactionState.SUCCESS; | ||||
} | } | ||||
@Override | |||||
public String[] getContractReturn() { | |||||
return contractReturn; | |||||
} | |||||
} | } |
@@ -49,8 +49,20 @@ public interface TransactionResponse { | |||||
*/ | */ | ||||
@DataField(order=4, primitiveType=PrimitiveType.INT64) | @DataField(order=4, primitiveType=PrimitiveType.INT64) | ||||
long getBlockHeight(); | long getBlockHeight(); | ||||
/** | |||||
* 交易是否执行成功 | |||||
* | |||||
* @return | |||||
*/ | |||||
@DataField(order=5, primitiveType=PrimitiveType.BOOLEAN) | @DataField(order=5, primitiveType=PrimitiveType.BOOLEAN) | ||||
boolean isSuccess(); | boolean isSuccess(); | ||||
/** | |||||
* 合约返回值 | |||||
* | |||||
* @return | |||||
*/ | |||||
@DataField(order=6, list=true, primitiveType=PrimitiveType.TEXT) | |||||
String[] getContractReturn(); | |||||
} | } |
@@ -0,0 +1,17 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import com.jd.blockchain.binaryproto.DataContract; | |||||
import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.consts.DataCodes; | |||||
@DataContract(code= DataCodes.TX_RETURN_MESSAGE) | |||||
public interface TransactionReturnMessage { | |||||
/** | |||||
* 合约返回值列表 | |||||
* | |||||
* @return | |||||
*/ | |||||
@DataField(order=1, list = true, refContract=true) | |||||
ContractReturnMessage[] getContractReturn(); | |||||
} |
@@ -0,0 +1,26 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
public class TransactionReturnMessageData implements TransactionReturnMessage { | |||||
private List<ContractReturnMessage> contractReturnMessages = new ArrayList<>(); | |||||
public void addContractReturnMessage(ContractReturnMessage contractReturnMessage) { | |||||
contractReturnMessages.add(contractReturnMessage); | |||||
} | |||||
public boolean isContractReturnEmpty() { | |||||
return contractReturnMessages.isEmpty(); | |||||
} | |||||
@Override | |||||
public ContractReturnMessage[] getContractReturn() { | |||||
if (isContractReturnEmpty()) { | |||||
return null; | |||||
} | |||||
ContractReturnMessage[] crms = new ContractReturnMessage[contractReturnMessages.size()]; | |||||
return contractReturnMessages.toArray(crms); | |||||
} | |||||
} |
@@ -17,6 +17,8 @@ public class TxResponseMessage implements TransactionResponse { | |||||
private long blockHeight; | private long blockHeight; | ||||
private TransactionState executionState; | private TransactionState executionState; | ||||
private String[] contractReturn; | |||||
public TxResponseMessage() { | public TxResponseMessage() { | ||||
} | } | ||||
@@ -39,9 +41,6 @@ public class TxResponseMessage implements TransactionResponse { | |||||
this.executionState = executionState; | this.executionState = executionState; | ||||
} | } | ||||
/* (non-Javadoc) | |||||
* @see com.jd.blockchain.ledger.TransactionResponse#getBlockHash() | |||||
*/ | |||||
@Override | @Override | ||||
public HashDigest getBlockHash() { | public HashDigest getBlockHash() { | ||||
return blockHash; | return blockHash; | ||||
@@ -55,13 +54,23 @@ public class TxResponseMessage implements TransactionResponse { | |||||
public long getBlockHeight() { | public long getBlockHeight() { | ||||
return blockHeight; | return blockHeight; | ||||
} | } | ||||
public void setBlockHeight(long blockHeight) { | public void setBlockHeight(long blockHeight) { | ||||
this.blockHeight = blockHeight; | this.blockHeight = blockHeight; | ||||
} | } | ||||
public void setContractReturn(String[] contractReturn) { | |||||
this.contractReturn = contractReturn; | |||||
} | |||||
@Override | @Override | ||||
public boolean isSuccess() { | public boolean isSuccess() { | ||||
return blockHash != null & executionState == TransactionState.SUCCESS; | return blockHash != null & executionState == TransactionState.SUCCESS; | ||||
} | } | ||||
@Override | |||||
public String[] getContractReturn() { | |||||
return contractReturn; | |||||
} | |||||
} | } |
@@ -307,6 +307,11 @@ public class ConsensusMessageDispatcher implements MessageHandle { | |||||
public boolean isSuccess() { | public boolean isSuccess() { | ||||
return this.txResp.isSuccess(); | return this.txResp.isSuccess(); | ||||
} | } | ||||
@Override | |||||
public String[] getContractReturn() { | |||||
return txResp.getContractReturn(); | |||||
} | |||||
} | } | ||||
private final class BlockStateSnapshot implements StateSnapshot { | private final class BlockStateSnapshot implements StateSnapshot { | ||||
@@ -50,15 +50,12 @@ public abstract class AbstractModule implements Module { | |||||
if (origClassLoader != moduleClassLoader) { | if (origClassLoader != moduleClassLoader) { | ||||
Thread.currentThread().setContextClassLoader(moduleClassLoader); | Thread.currentThread().setContextClassLoader(moduleClassLoader); | ||||
} | } | ||||
return CompletableAsyncFuture.runAsync(new Runnable() { | |||||
@Override | |||||
public void run() { | |||||
try { | |||||
runnable.run(); | |||||
} finally { | |||||
if (origClassLoader != Thread.currentThread().getContextClassLoader()) { | |||||
Thread.currentThread().setContextClassLoader(origClassLoader); | |||||
} | |||||
return CompletableAsyncFuture.runAsync(() -> { | |||||
try { | |||||
runnable.run(); | |||||
} finally { | |||||
if (origClassLoader != Thread.currentThread().getContextClassLoader()) { | |||||
Thread.currentThread().setContextClassLoader(origClassLoader); | |||||
} | } | ||||
} | } | ||||
}); | }); | ||||
@@ -40,6 +40,13 @@ | |||||
<artifactId>sdk-client</artifactId> | <artifactId>sdk-client</artifactId> | ||||
<version>${project.version}</version> | <version>${project.version}</version> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>contract-samples</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | <dependency> | ||||
<groupId>io.nats</groupId> | <groupId>io.nats</groupId> | ||||
<artifactId>jnats</artifactId> | <artifactId>jnats</artifactId> | ||||
@@ -15,9 +15,11 @@ import com.jd.blockchain.tools.keygen.KeyGenCommand; | |||||
import com.jd.blockchain.utils.concurrent.ThreadInvoker; | import com.jd.blockchain.utils.concurrent.ThreadInvoker; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.springframework.core.io.ClassPathResource; | |||||
import test.com.jd.blockchain.intgr.initializer.LedgerInitializeTest; | import test.com.jd.blockchain.intgr.initializer.LedgerInitializeTest; | ||||
import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4Nodes; | import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4Nodes; | ||||
import java.io.File; | |||||
import java.util.concurrent.CountDownLatch; | import java.util.concurrent.CountDownLatch; | ||||
import java.util.concurrent.ExecutorService; | import java.util.concurrent.ExecutorService; | ||||
import java.util.concurrent.Executors; | import java.util.concurrent.Executors; | ||||
@@ -85,6 +87,20 @@ public class IntegrationTest4Contract { | |||||
BlockchainService blockchainService = gwsrvFact.getBlockchainService(); | BlockchainService blockchainService = gwsrvFact.getBlockchainService(); | ||||
if(isContractDeployAndExe){ | if(isContractDeployAndExe){ | ||||
// 合约测试需要runtime路径 | |||||
try { | |||||
ClassPathResource contractPath = new ClassPathResource(""); | |||||
File file = new File(contractPath.getURI()); | |||||
String runTimePath = file.getParentFile().getParent() + File.separator + "runtime"; | |||||
File runTime = new File(runTimePath); | |||||
if (!runTime.exists()) { | |||||
runTime.mkdir(); | |||||
} | |||||
} catch (Exception e) { | |||||
e.printStackTrace(); | |||||
} | |||||
IntegrationBase.testSDK_Contract(adminKey,ledgerHash,blockchainService,ledgerRepository); | IntegrationBase.testSDK_Contract(adminKey,ledgerHash,blockchainService,ledgerRepository); | ||||
} | } | ||||
@@ -425,7 +425,7 @@ public class LedgerInitializeWebController implements LedgerInitProcess, LedgerI | |||||
userRegOP.getUserID().getPubKey()); | userRegOP.getUserID().getPubKey()); | ||||
} | } | ||||
txCtx.commit(TransactionState.SUCCESS); | |||||
txCtx.commit(TransactionState.SUCCESS, null); | |||||
return ledgerEditor.prepare(); | return ledgerEditor.prepare(); | ||||
} | } | ||||
@@ -293,7 +293,7 @@ public class MockerLedgerInitializer implements LedgerInitProcess, LedgerInitCon | |||||
userRegOP.getUserID().getPubKey()); | userRegOP.getUserID().getPubKey()); | ||||
} | } | ||||
txCtx.commit(TransactionState.SUCCESS); | |||||
txCtx.commit(TransactionState.SUCCESS, null); | |||||
return ledgerEditor.prepare(); | return ledgerEditor.prepare(); | ||||
} | } | ||||