# Conflicts: # 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/handles/AbtractContractEventHandle.java # source/ledger/ledger-model/src/main/java/com/jd/blockchain/contract/ContractType.java # source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxy.java # source/ledger/ledger-model/src/main/java/com/jd/blockchain/transaction/ContractInvocationProxyBuilder.java # source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/handler/MockerContractExeHandle.javatags/1.0.0
@@ -51,6 +51,8 @@ public interface DataCodes { | |||||
public static final int TX_RESPONSE = 0x350; | public static final int TX_RESPONSE = 0x350; | ||||
public static final int TX_OP_RESULT = 0x360; | |||||
public static final int METADATA = 0x600; | public static final int METADATA = 0x600; | ||||
public static final int METADATA_INIT_SETTING = 0x610; | public static final int METADATA_INIT_SETTING = 0x610; | ||||
@@ -81,6 +83,26 @@ public interface DataCodes { | |||||
public static final int DATA = 0x900; | public static final int DATA = 0x900; | ||||
//contract related; | |||||
public static final int CONTRACT = 0xA00; | |||||
public static final int CONTRACT_BYTE = 0xA01; | |||||
public static final int CONTRACT_SHORT = 0xA02; | |||||
public static final int CONTRACT_INT = 0xA03; | |||||
public static final int CONTRACT_LONG = 0xA04; | |||||
public static final int CONTRACT_STRING = 0xA05; | |||||
public static final int CONTRACT_BYTES = 0xA06; | |||||
public static final int CONTRACT_BIG_INT = 0xA07; | |||||
//...0xA19 | |||||
public static final int CONTRACT_BIZ_CONTENT = 0xA20; | |||||
public static final int CONTRACT_ARGS = 0xA21; | |||||
public static final int CONTRACT_RETURN = 0xA22; | public static final int CONTRACT_RETURN = 0xA22; | ||||
public static final int HASH = 0xB00; | public static final int HASH = 0xB00; | ||||
@@ -3,6 +3,9 @@ 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(); | ||||
@@ -1,6 +1,5 @@ | |||||
package com.jd.blockchain.contract; | package com.jd.blockchain.contract; | ||||
import com.jd.blockchain.ledger.ContractBizContent; | |||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
/** | /** | ||||
@@ -1,6 +1,5 @@ | |||||
package com.jd.blockchain.contract; | package com.jd.blockchain.contract; | ||||
import com.jd.blockchain.ledger.ContractBizContent; | |||||
import com.jd.blockchain.ledger.KVDataEntry; | import com.jd.blockchain.ledger.KVDataEntry; | ||||
import com.jd.blockchain.ledger.KVDataObject; | import com.jd.blockchain.ledger.KVDataObject; | ||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
@@ -9,5 +9,7 @@ public interface ReadContract { | |||||
@ContractEvent(name = "version-key") | @ContractEvent(name = "version-key") | ||||
Long readVersion(String address, String key); | Long readVersion(String address, String key); | ||||
int test(); | |||||
} | } | ||||
@@ -42,4 +42,9 @@ public class ReadContractImpl implements EventProcessingAware, ReadContract { | |||||
} | } | ||||
return -1L; | return -1L; | ||||
} | } | ||||
@Override | |||||
public int test() { | |||||
return 0; | |||||
} | |||||
} | } |
@@ -162,8 +162,10 @@ public class AccountSet implements Transactional, MerkleProvable { | |||||
* | * | ||||
* 只有最新版本的账户才能可写的,其它都是只读; | * 只有最新版本的账户才能可写的,其它都是只读; | ||||
* | * | ||||
* @param address 账户地址; | |||||
* @param version 账户版本;如果指定为 -1,则返回最新版本; | |||||
* @param address | |||||
* 账户地址; | |||||
* @param version | |||||
* 账户版本;如果指定为 -1,则返回最新版本; | |||||
* @return | * @return | ||||
*/ | */ | ||||
public BaseAccount getAccount(Bytes address, long version) { | public BaseAccount getAccount(Bytes address, long version) { | ||||
@@ -57,7 +57,7 @@ public class DataAccount implements AccountHeader, MerkleProvable { | |||||
BytesValue bytesValue = BytesValueEntry.fromBytes(value); | BytesValue bytesValue = BytesValueEntry.fromBytes(value); | ||||
return baseAccount.setBytes(key, bytesValue, version); | return baseAccount.setBytes(key, bytesValue, version); | ||||
} | } | ||||
/** | /** | ||||
* Return the latest version entry associated the specified key; If the key | * Return the latest version entry associated the specified key; If the key | ||||
* doesn't exist, then return -1; | * doesn't exist, then return -1; | ||||
@@ -1,10 +1,8 @@ | |||||
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.OperationResult; | |||||
import com.jd.blockchain.ledger.LedgerTransaction; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | |||||
import com.jd.blockchain.ledger.*; | |||||
import java.util.List; | |||||
/** | /** | ||||
* 事务上下文; | * 事务上下文; | ||||
@@ -28,27 +26,45 @@ public interface LedgerTransactionContext { | |||||
*/ | */ | ||||
TransactionRequest getRequestTX(); | TransactionRequest getRequestTX(); | ||||
/** | |||||
* 提交对账本数据的修改,以指定的交易状态提交交易; | |||||
* | |||||
* @param txResult | |||||
* | |||||
* @return | |||||
*/ | |||||
LedgerTransaction commit(TransactionState txResult); | |||||
/** | /** | ||||
* 提交对账本数据的修改,以指定的交易状态提交交易; | * 提交对账本数据的修改,以指定的交易状态提交交易; | ||||
* | * | ||||
* @param txResult | * @param txResult | ||||
* @param returnMessage | |||||
* @param operationResults | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
LedgerTransaction commit(TransactionState txResult, OperationResult... opResults); | |||||
LedgerTransaction commit(TransactionState txResult, List<OperationResult> operationResults); | |||||
/** | /** | ||||
* 抛弃对账本数据的修改,以指定的交易状态提交交易;<br> | * 抛弃对账本数据的修改,以指定的交易状态提交交易;<br> | ||||
* | |||||
* 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState, TransactionReturnMessage)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; | |||||
* | |||||
* | |||||
* 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; | |||||
* | |||||
* @param txResult | * @param txResult | ||||
* @return | * @return | ||||
*/ | */ | ||||
LedgerTransaction discardAndCommit(TransactionState txResult); | LedgerTransaction discardAndCommit(TransactionState txResult); | ||||
/** | |||||
* 抛弃对账本数据的修改,以指定的交易状态提交交易;<br> | |||||
* | |||||
* 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState, List)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; | |||||
* | |||||
* @param txResult | |||||
* @return | |||||
*/ | |||||
LedgerTransaction discardAndCommit(TransactionState txResult, List<OperationResult> operationResults); | |||||
/** | /** | ||||
* 回滚事务,抛弃本次事务的所有数据更新; | * 回滚事务,抛弃本次事务的所有数据更新; | ||||
*/ | */ | ||||
@@ -2,6 +2,8 @@ package com.jd.blockchain.ledger.core; | |||||
import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
public interface OperationHandle { | public interface OperationHandle { | ||||
@@ -14,8 +16,9 @@ public interface OperationHandle { | |||||
boolean support(Class<?> operationType); | boolean support(Class<?> operationType); | ||||
/** | /** | ||||
* 解析和执行操作; | |||||
* | |||||
* 同步解析和执行操作; | |||||
* | |||||
* | |||||
* @param op | * @param op | ||||
* 操作实例; | * 操作实例; | ||||
* @param newBlockDataset | * @param newBlockDataset | ||||
@@ -24,8 +27,28 @@ public interface OperationHandle { | |||||
* 交易请求上下文; | * 交易请求上下文; | ||||
* @param previousBlockDataset | * @param previousBlockDataset | ||||
* 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | * 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | ||||
* | |||||
* @return 操作执行结果 | |||||
*/ | */ | ||||
byte[] process(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, | byte[] process(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, | ||||
LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService); | LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService); | ||||
// /** | |||||
// * 异步解析和执行操作; | |||||
// * TODO 未来规划实现 | |||||
// * | |||||
// * | |||||
// * @param op | |||||
// * 操作实例; | |||||
// * @param newBlockDataset | |||||
// * 需要修改的新区块的数据集; | |||||
// * @param requestContext | |||||
// * 交易请求上下文; | |||||
// * @param previousBlockDataset | |||||
// * 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | |||||
// * | |||||
// * @return 操作执行结果 | |||||
// */ | |||||
// AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, | |||||
// LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService); | |||||
} | } |
@@ -276,15 +276,15 @@ public class LedgerQueryService implements BlockchainQueryService { | |||||
KVDataEntry[] entries = new KVDataEntry[keys.length]; | KVDataEntry[] entries = new KVDataEntry[keys.length]; | ||||
long ver; | long ver; | ||||
for (int i = 0; i < entries.length; i++) { | for (int i = 0; i < entries.length; i++) { | ||||
ver = dataAccount.getDataVersion(Bytes.fromString(keys[i])); | |||||
final String currKey = keys[i]; | |||||
dataAccount.getBytes(Bytes.fromString(keys[i]), 1); | |||||
ver = dataAccount.getDataVersion(Bytes.fromString(currKey)); | |||||
if (ver < 0) { | if (ver < 0) { | ||||
entries[i] = new KVDataObject(keys[i], -1, null); | |||||
entries[i] = new KVDataObject(currKey, -1, null); | |||||
} else { | } else { | ||||
BytesValue value = dataAccount.getBytes(Bytes.fromString(keys[i]), ver); | |||||
entries[i] = new KVDataObject(keys[i], ver, value); | |||||
BytesValue value = dataAccount.getBytes(Bytes.fromString(currKey), ver); | |||||
entries[i] = new KVDataObject(currKey, ver, value); | |||||
} | } | ||||
} | } | ||||
@@ -1,5 +1,6 @@ | |||||
package com.jd.blockchain.ledger.core.impl; | package com.jd.blockchain.ledger.core.impl; | ||||
import java.util.List; | |||||
import java.util.Stack; | import java.util.Stack; | ||||
import com.jd.blockchain.binaryproto.BinaryProtocol; | import com.jd.blockchain.binaryproto.BinaryProtocol; | ||||
@@ -348,9 +349,14 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
public TransactionRequest getRequestTX() { | public TransactionRequest getRequestTX() { | ||||
return txRequest; | return txRequest; | ||||
} | } | ||||
@Override | |||||
public LedgerTransaction commit(TransactionState txResult) { | |||||
return commit(txResult, null); | |||||
} | |||||
@Override | @Override | ||||
public LedgerTransaction commit(TransactionState txResult, OperationResult... opResults) { | |||||
public LedgerTransaction commit(TransactionState txResult, List<OperationResult> operationResults) { | |||||
checkTxState(); | checkTxState(); | ||||
// capture snapshot | // capture snapshot | ||||
@@ -359,7 +365,8 @@ 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, opResults); | |||||
LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null, operationResultArray(operationResults)); | |||||
this.txset.add(tx); | this.txset.add(tx); | ||||
// this.txset.commit(); | // this.txset.commit(); | ||||
@@ -374,9 +381,14 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
committed = true; | committed = true; | ||||
return tx; | return tx; | ||||
} | } | ||||
@Override | @Override | ||||
public LedgerTransaction discardAndCommit(TransactionState txResult) { | public LedgerTransaction discardAndCommit(TransactionState txResult) { | ||||
return discardAndCommit(txResult, null); | |||||
} | |||||
@Override | |||||
public LedgerTransaction discardAndCommit(TransactionState txResult, List<OperationResult> operationResults) { | |||||
checkTxState(); | checkTxState(); | ||||
// 未处理 | // 未处理 | ||||
@@ -385,7 +397,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, operationResultArray(operationResults)); | |||||
this.txset.add(tx); | this.txset.add(tx); | ||||
// this.txset.commit(); | // this.txset.commit(); | ||||
@@ -410,6 +422,15 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||||
return txDataSnapshot; | return txDataSnapshot; | ||||
} | } | ||||
private OperationResult[] operationResultArray(List<OperationResult> operationResults) { | |||||
OperationResult[] operationResultArray = null; | |||||
if (operationResults != null && !operationResults.isEmpty()) { | |||||
operationResultArray = new OperationResult[operationResults.size()]; | |||||
operationResults.toArray(operationResultArray); | |||||
} | |||||
return operationResultArray; | |||||
} | |||||
@Override | @Override | ||||
public void rollback() { | public void rollback() { | ||||
if (this.rollbacked) { | if (this.rollbacked) { | ||||
@@ -50,9 +50,12 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
private TransactionBatchResult batchResult; | private TransactionBatchResult batchResult; | ||||
/** | /** | ||||
* @param newBlockEditor 新区块的数据编辑器; | |||||
* @param previousBlockDataset 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | |||||
* @param opHandles 操作处理对象注册表; | |||||
* @param newBlockEditor | |||||
* 新区块的数据编辑器; | |||||
* @param previousBlockDataset | |||||
* 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | |||||
* @param opHandles | |||||
* 操作处理对象注册表; | |||||
*/ | */ | ||||
public TransactionBatchProcessor(LedgerEditor newBlockEditor, LedgerDataSet previousBlockDataset, | public TransactionBatchProcessor(LedgerEditor newBlockEditor, LedgerDataSet previousBlockDataset, | ||||
OperationHandleRegisteration opHandles, LedgerService ledgerService) { | OperationHandleRegisteration opHandles, LedgerService ledgerService) { | ||||
@@ -73,9 +76,12 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
public TransactionResponse schedule(TransactionRequest request) { | public TransactionResponse schedule(TransactionRequest request) { | ||||
// 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | // 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | ||||
LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | ||||
TransactionState txResult; | |||||
OperationResult[] opResults = null; | |||||
TransactionState result; | |||||
List<OperationResult> operationResults = new ArrayList<>(); | |||||
try { | try { | ||||
LedgerDataSet dataset = txCtx.getDataSet(); | LedgerDataSet dataset = txCtx.getDataSet(); | ||||
TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | ||||
// TODO: 验证签名者的有效性; | // TODO: 验证签名者的有效性; | ||||
@@ -95,44 +101,44 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
OperationHandleContext handleContext = new OperationHandleContext() { | OperationHandleContext handleContext = new OperationHandleContext() { | ||||
@Override | @Override | ||||
public void handle(Operation operation) { | public void handle(Operation operation) { | ||||
// assert; Instance of operation are one of User related operations or | |||||
// DataAccount related operations; | |||||
//assert; Instance of operation are one of User related operations or DataAccount related operations; | |||||
OperationHandle hdl = opHandles.getHandle(operation.getClass()); | OperationHandle hdl = opHandles.getHandle(operation.getClass()); | ||||
hdl.process(operation, dataset, reqCtx, previousBlockDataset, this, ledgerService); | hdl.process(operation, dataset, reqCtx, previousBlockDataset, this, ledgerService); | ||||
} | } | ||||
}; | }; | ||||
OperationHandle opHandle; | OperationHandle opHandle; | ||||
int contractOpIndex = 0; | |||||
List<OperationResult> opResultList = new ArrayList<OperationResult>(); | |||||
int opIdx = 0; | |||||
int opIndex = 0; | |||||
for (Operation op : ops) { | for (Operation op : ops) { | ||||
opHandle = opHandles.getHandle(op.getClass()); | opHandle = opHandles.getHandle(op.getClass()); | ||||
byte[] retn = opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); | |||||
if (op instanceof ContractEventSendOperation) { | |||||
// 支持返回值的操作; | |||||
opResultList.add(new OperationResultData(opIdx, retn)); | |||||
byte[] opResult = opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); | |||||
if (opResult != null) { | |||||
operationResults.add(new OperationResultData(opIndex, opResult)); | |||||
} | } | ||||
opIdx++; | |||||
opIndex++; | |||||
} | } | ||||
// 提交交易(事务); | // 提交交易(事务); | ||||
txResult = TransactionState.SUCCESS; | |||||
opResults = opResultList.toArray(new OperationResult[opResultList.size()]); | |||||
txCtx.commit(txResult, opResults); | |||||
result = TransactionState.SUCCESS; | |||||
txCtx.commit(result, operationResults); | |||||
} catch (LedgerException e) { | } catch (LedgerException e) { | ||||
// TODO: 识别更详细的异常类型以及执行对应的处理; | // TODO: 识别更详细的异常类型以及执行对应的处理; | ||||
txResult = TransactionState.LEDGER_ERROR; | |||||
txCtx.discardAndCommit(TransactionState.LEDGER_ERROR); | |||||
result = TransactionState.LEDGER_ERROR; | |||||
txCtx.discardAndCommit(TransactionState.LEDGER_ERROR, operationResults); | |||||
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) { | ||||
txResult = TransactionState.SYSTEM_ERROR; | |||||
txCtx.discardAndCommit(TransactionState.SYSTEM_ERROR); | |||||
result = TransactionState.SYSTEM_ERROR; | |||||
txCtx.discardAndCommit(TransactionState.SYSTEM_ERROR, operationResults); | |||||
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, txResult, opResults); | |||||
TxResponseHandle resp = new TxResponseHandle(request, result); | |||||
if (!operationResults.isEmpty()) { | |||||
OperationResult[] operationResultArray = new OperationResult[operationResults.size()]; | |||||
resp.setOperationResults(operationResults.toArray(operationResultArray)); | |||||
} | |||||
responseList.add(resp); | responseList.add(resp); | ||||
@@ -213,12 +219,11 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
private TransactionState result; | private TransactionState result; | ||||
private OperationResult[] opResults; | |||||
private OperationResult[] operationResults; | |||||
public TxResponseHandle(TransactionRequest request, TransactionState result, OperationResult... opResults) { | |||||
public TxResponseHandle(TransactionRequest request, TransactionState result) { | |||||
this.request = request; | this.request = request; | ||||
this.result = result; | this.result = result; | ||||
this.opResults = opResults; | |||||
} | } | ||||
@Override | @Override | ||||
@@ -247,8 +252,12 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||||
} | } | ||||
@Override | @Override | ||||
public OperationResult[] getContractReturn() { | |||||
return opResults; | |||||
public OperationResult[] getOperationResults() { | |||||
return operationResults; | |||||
} | |||||
public void setOperationResults(OperationResult[] operationResults) { | |||||
this.operationResults = operationResults; | |||||
} | } | ||||
} | } | ||||
@@ -1,5 +1,6 @@ | |||||
package com.jd.blockchain.ledger.core.impl.handles; | package com.jd.blockchain.ledger.core.impl.handles; | ||||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||||
import com.jd.blockchain.ledger.ContractCodeDeployOperation; | import com.jd.blockchain.ledger.ContractCodeDeployOperation; | ||||
import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
import com.jd.blockchain.ledger.core.LedgerDataSet; | import com.jd.blockchain.ledger.core.LedgerDataSet; | ||||
@@ -8,8 +9,12 @@ import com.jd.blockchain.ledger.core.OperationHandle; | |||||
import com.jd.blockchain.ledger.core.TransactionRequestContext; | import com.jd.blockchain.ledger.core.TransactionRequestContext; | ||||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
import com.jd.blockchain.utils.Bytes; | |||||
import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
import java.util.concurrent.Future; | |||||
@Service | @Service | ||||
public class ContractCodeDeployOperationHandle implements OperationHandle { | public class ContractCodeDeployOperationHandle implements OperationHandle { | ||||
@@ -20,12 +25,18 @@ public class ContractCodeDeployOperationHandle implements OperationHandle { | |||||
// TODO: 校验合约代码的正确性; | // TODO: 校验合约代码的正确性; | ||||
// TODO: 请求者应该提供合约账户的公钥签名,已确定注册的地址的唯一性; | // TODO: 请求者应该提供合约账户的公钥签名,已确定注册的地址的唯一性; | ||||
dataset.getContractAccountSet().deploy(contractOP.getContractID().getAddress(), | dataset.getContractAccountSet().deploy(contractOP.getContractID().getAddress(), | ||||
contractOP.getContractID().getPubKey(), contractOP.getAddressSignature(), contractOP.getChainCode()); | contractOP.getContractID().getPubKey(), contractOP.getAddressSignature(), contractOP.getChainCode()); | ||||
//No result; | |||||
return null; | return null; | ||||
} | } | ||||
// @Override | |||||
// public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||||
// return null; | |||||
// } | |||||
@Override | @Override | ||||
public boolean support(Class<?> operationType) { | public boolean support(Class<?> operationType) { | ||||
return ContractCodeDeployOperation.class.isAssignableFrom(operationType); | return ContractCodeDeployOperation.class.isAssignableFrom(operationType); | ||||
@@ -1,5 +1,7 @@ | |||||
package com.jd.blockchain.ledger.core.impl.handles; | package com.jd.blockchain.ledger.core.impl.handles; | ||||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||||
import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
import com.jd.blockchain.binaryproto.DataContractRegistry; | import com.jd.blockchain.binaryproto.DataContractRegistry; | ||||
@@ -26,15 +28,19 @@ public class DataAccountKVSetOperationHandle implements OperationHandle { | |||||
LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | ||||
DataAccountKVSetOperation kvWriteOp = (DataAccountKVSetOperation) op; | DataAccountKVSetOperation kvWriteOp = (DataAccountKVSetOperation) op; | ||||
DataAccount account = dataset.getDataAccountSet().getDataAccount(kvWriteOp.getAccountAddress()); | DataAccount account = dataset.getDataAccountSet().getDataAccount(kvWriteOp.getAccountAddress()); | ||||
KVWriteEntry[] writeset = kvWriteOp.getWriteSet(); | |||||
for (KVWriteEntry kvw : writeset) { | |||||
KVWriteEntry[] writeSet = kvWriteOp.getWriteSet(); | |||||
for (KVWriteEntry kvw : writeSet) { | |||||
// byte[] value = BinaryProtocol.encode(kvw.getValue(), BytesValue.class); | |||||
account.setBytes(Bytes.fromString(kvw.getKey()), kvw.getValue(), kvw.getExpectedVersion()); | account.setBytes(Bytes.fromString(kvw.getKey()), kvw.getValue(), kvw.getExpectedVersion()); | ||||
} | } | ||||
// No return value; | |||||
return null; | return null; | ||||
} | } | ||||
// @Override | |||||
// public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||||
// return null; | |||||
// } | |||||
@Override | @Override | ||||
public boolean support(Class<?> operationType) { | public boolean support(Class<?> operationType) { | ||||
return DataAccountKVSetOperation.class.isAssignableFrom(operationType); | return DataAccountKVSetOperation.class.isAssignableFrom(operationType); | ||||
@@ -9,6 +9,7 @@ import com.jd.blockchain.ledger.core.OperationHandle; | |||||
import com.jd.blockchain.ledger.core.TransactionRequestContext; | import com.jd.blockchain.ledger.core.TransactionRequestContext; | ||||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
@Service | @Service | ||||
@@ -20,15 +21,19 @@ public class DataAccountRegisterOperationHandle implements OperationHandle { | |||||
DataAccountRegisterOperation dataAccountRegOp = (DataAccountRegisterOperation) op; | DataAccountRegisterOperation dataAccountRegOp = (DataAccountRegisterOperation) op; | ||||
BlockchainIdentity bid = dataAccountRegOp.getAccountID(); | BlockchainIdentity bid = dataAccountRegOp.getAccountID(); | ||||
// TODO: 校验用户身份; | |||||
//TODO: 校验用户身份; | |||||
// TODO: 请求者应该提供数据账户的公钥签名,已确定注册的地址的唯一性; | |||||
//TODO: 请求者应该提供数据账户的公钥签名,已确定注册的地址的唯一性; | |||||
dataset.getDataAccountSet().register(bid.getAddress(), bid.getPubKey(), null); | dataset.getDataAccountSet().register(bid.getAddress(), bid.getPubKey(), null); | ||||
// No return value; | |||||
return null; | return null; | ||||
} | } | ||||
// @Override | |||||
// public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||||
// return null; | |||||
// } | |||||
@Override | @Override | ||||
public boolean support(Class<?> operationType) { | public boolean support(Class<?> operationType) { | ||||
return DataAccountRegisterOperation.class.isAssignableFrom(operationType); | return DataAccountRegisterOperation.class.isAssignableFrom(operationType); | ||||
@@ -5,7 +5,13 @@ import static com.jd.blockchain.utils.BaseConstant.CONTRACT_SERVICE_PROVIDER; | |||||
import com.jd.blockchain.contract.engine.ContractCode; | import com.jd.blockchain.contract.engine.ContractCode; | ||||
import com.jd.blockchain.contract.engine.ContractEngine; | import com.jd.blockchain.contract.engine.ContractEngine; | ||||
import com.jd.blockchain.contract.engine.ContractServiceProviders; | import com.jd.blockchain.contract.engine.ContractServiceProviders; | ||||
import com.jd.blockchain.ledger.Operation; | |||||
import com.jd.blockchain.ledger.core.ContractAccount; | import com.jd.blockchain.ledger.core.ContractAccount; | ||||
import com.jd.blockchain.ledger.core.LedgerDataSet; | |||||
import com.jd.blockchain.ledger.core.LedgerService; | |||||
import com.jd.blockchain.ledger.core.TransactionRequestContext; | |||||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | |||||
import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
public class JVMContractEventSendOperationHandle extends AbtractContractEventHandle { | public class JVMContractEventSendOperationHandle extends AbtractContractEventHandle { | ||||
@@ -26,4 +32,12 @@ public class JVMContractEventSendOperationHandle extends AbtractContractEventHan | |||||
return contractCode; | return contractCode; | ||||
} | } | ||||
// @Override | |||||
// public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, | |||||
// TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, | |||||
// OperationHandleContext handleContext, LedgerService ledgerService) { | |||||
// // TODO Auto-generated method stub | |||||
// return null; | |||||
// } | |||||
} | } |
@@ -8,20 +8,32 @@ import com.jd.blockchain.ledger.core.LedgerService; | |||||
import com.jd.blockchain.ledger.core.OperationHandle; | import com.jd.blockchain.ledger.core.OperationHandle; | ||||
import com.jd.blockchain.ledger.core.TransactionRequestContext; | import com.jd.blockchain.ledger.core.TransactionRequestContext; | ||||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | ||||
import com.jd.blockchain.utils.Bytes; | |||||
import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
public class UserRegisterOperationHandle implements OperationHandle { | public class UserRegisterOperationHandle implements OperationHandle { | ||||
@Override | @Override | ||||
public byte[] process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | public byte[] process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | ||||
LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | ||||
UserRegisterOperation userRegOp = (UserRegisterOperation) op; | UserRegisterOperation userRegOp = (UserRegisterOperation) op; | ||||
BlockchainIdentity bid = userRegOp.getUserID(); | BlockchainIdentity bid = userRegOp.getUserID(); | ||||
dataset.getUserAccountSet().register(bid.getAddress(), bid.getPubKey()); | |||||
// No return value; | |||||
return null; | |||||
Bytes userAddress = bid.getAddress(); | |||||
dataset.getUserAccountSet().register(userAddress, bid.getPubKey()); | |||||
return userAddress.toBytes(); | |||||
} | } | ||||
// @Override | |||||
// public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||||
// return null; | |||||
// } | |||||
@Override | @Override | ||||
public boolean support(Class<?> operationType) { | public boolean support(Class<?> operationType) { | ||||
return UserRegisterOperation.class.isAssignableFrom(operationType); | return UserRegisterOperation.class.isAssignableFrom(operationType); | ||||
@@ -116,7 +116,7 @@ public class ContractInvokingTest { | |||||
TransactionResponse resp = txbatchProcessor.schedule(txReq); | TransactionResponse resp = txbatchProcessor.schedule(txReq); | ||||
verify(contractInstance, times(1)).issue(asset, issueAmount); | verify(contractInstance, times(1)).issue(asset, issueAmount); | ||||
OperationResult[] opResults = resp.getContractReturn(); | |||||
OperationResult[] opResults = resp.getOperationResults(); | |||||
assertEquals(1, opResults.length); | assertEquals(1, opResults.length); | ||||
assertEquals(0, opResults[0].getIndex()); | assertEquals(0, opResults[0].getIndex()); | ||||
@@ -0,0 +1,157 @@ | |||||
package com.jd.blockchain.contract; | |||||
import com.jd.blockchain.binaryproto.*; | |||||
import com.jd.blockchain.contract.param.*; | |||||
import com.jd.blockchain.utils.io.BytesUtils; | |||||
import java.util.ArrayList; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
public class ContractSerializeUtils { | |||||
final static int INT_LENGTH = 4; | |||||
static Map<Class<?>, Class<?>> MAP = new HashMap<>(); | |||||
static { | |||||
MAP.put(byte[].class, WRAP_BYTES.class); | |||||
MAP.put(Short.class, WRAP_SHORT.class); | |||||
MAP.put(short.class, WRAP_SHORT.class); | |||||
MAP.put(Integer.class, WRAP_INT.class); | |||||
MAP.put(int.class, WRAP_INT.class); | |||||
MAP.put(Long.class, WRAP_LONG.class); | |||||
MAP.put(long.class, WRAP_LONG.class); | |||||
MAP.put(String.class, WRAP_STRING.class); | |||||
DataContractRegistry.register(WRAP_BYTES.class); | |||||
DataContractRegistry.register(WRAP_SHORT.class); | |||||
DataContractRegistry.register(WRAP_INT.class); | |||||
DataContractRegistry.register(WRAP_LONG.class); | |||||
DataContractRegistry.register(WRAP_STRING.class); | |||||
} | |||||
public static boolean support(Class<?> clazz) { | |||||
if (clazz.isAnnotationPresent(DataContract.class) || MAP.containsKey(clazz)) { | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
public static byte[] serialize(Object data) { | |||||
if (data == null) { | |||||
return null; | |||||
} | |||||
Class<?> clazz = data.getClass(); | |||||
Class<?> serialClass; | |||||
Object wrapData = data; | |||||
if (clazz.isAnnotationPresent(DataContract.class)) { | |||||
serialClass = clazz; | |||||
} else { | |||||
// 判断类型是否可以序列化 | |||||
Class<?> wrapClass = MAP.get(clazz); | |||||
if (wrapClass == null) { | |||||
throw new IllegalArgumentException("There are Un-Support Type !!!"); | |||||
} | |||||
serialClass = wrapClass; | |||||
if (wrapClass.equals(WRAP_BYTES.class)) { | |||||
wrapData = (WRAP_BYTES) () -> (byte[])data; | |||||
} else if (wrapClass.equals(WRAP_INT.class)) { | |||||
wrapData = (WRAP_INT) () -> (int)data; | |||||
} else if (wrapClass.equals(WRAP_LONG.class)) { | |||||
wrapData = (WRAP_LONG) () -> (long)data; | |||||
} else if (wrapClass.equals(WRAP_SHORT.class)) { | |||||
wrapData = (WRAP_SHORT) () -> (short)data; | |||||
} else if (wrapClass.equals(WRAP_STRING.class)) { | |||||
wrapData = (WRAP_STRING) () -> (String)data; | |||||
} | |||||
} | |||||
// 按照对应接口进行序列化 | |||||
// 生成该接口对应的对象 | |||||
return BinaryProtocol.encode(wrapData, serialClass); | |||||
} | |||||
public static byte[] serializeArray(Object[] datas) { | |||||
if (datas == null || datas.length == 0) { | |||||
return null; | |||||
} | |||||
int contentBytesSize = 0; | |||||
byte[] header = new byte[(datas.length + 1) * 4]; | |||||
System.arraycopy(BytesUtils.toBytes(datas.length), 0, header, 0, INT_LENGTH); | |||||
int offset = INT_LENGTH; | |||||
List<byte[]> serialBytesList = new ArrayList<>(); | |||||
for (Object data : datas) { | |||||
// 按照对应接口进行序列化 | |||||
byte[] currBytes = serialize(data); | |||||
// 长度写入 | |||||
System.arraycopy(BytesUtils.toBytes(currBytes.length), 0, header, offset, INT_LENGTH); | |||||
serialBytesList.add(currBytes); | |||||
contentBytesSize += currBytes.length; | |||||
offset += INT_LENGTH; | |||||
} | |||||
// 填充content | |||||
byte[] content = new byte[contentBytesSize]; | |||||
offset = 0; | |||||
for (byte[] bytes : serialBytesList) { | |||||
System.arraycopy(bytes, 0, content, offset, bytes.length); | |||||
offset += bytes.length; | |||||
} | |||||
// 将header和content组装 | |||||
return BytesUtils.concat(header, content); | |||||
} | |||||
public static Object[] resolveArray(byte[] bytes) { | |||||
if (bytes == null || bytes.length == 0) { | |||||
return null; | |||||
} | |||||
byte[] lengthBytes = new byte[INT_LENGTH]; | |||||
System.arraycopy(bytes, 0, lengthBytes, 0, INT_LENGTH); | |||||
int length = BytesUtils.toInt(lengthBytes); | |||||
Object[] datas = new Object[length]; | |||||
int headerOffset = INT_LENGTH; | |||||
int contentStart = (length + 1) * INT_LENGTH; | |||||
for (int i = 0 ; i < length; i++) { | |||||
byte[] currDataLengthBytes = new byte[INT_LENGTH]; | |||||
System.arraycopy(bytes, headerOffset, currDataLengthBytes, 0, INT_LENGTH); | |||||
int currDataLength = BytesUtils.toInt(currDataLengthBytes); | |||||
// 读取 | |||||
byte[] dataBytes = new byte[currDataLength]; | |||||
System.arraycopy(bytes, contentStart, dataBytes, 0, currDataLength); | |||||
datas[i] = resolve(dataBytes); | |||||
contentStart += currDataLength; | |||||
headerOffset += INT_LENGTH; | |||||
} | |||||
return datas; | |||||
} | |||||
public static Object resolve(byte[] bytes) { | |||||
// 反序列化该接口 | |||||
Object currData = BinaryProtocol.decode(bytes); | |||||
// 代理对象类型不能使用class判断,只能使用instanceof | |||||
if (currData instanceof WRAP_STRING) { | |||||
return ((WRAP_STRING) currData).getValue(); | |||||
} else if (currData instanceof WRAP_INT) { | |||||
return ((WRAP_INT) currData).getValue(); | |||||
} else if (currData instanceof WRAP_LONG) { | |||||
return ((WRAP_LONG) currData).getValue(); | |||||
} else if (currData instanceof WRAP_BYTES) { | |||||
return ((WRAP_BYTES) currData).getValue(); | |||||
} else if (currData instanceof WRAP_SHORT) { | |||||
return ((WRAP_SHORT) currData).getValue(); | |||||
} else { | |||||
return currData; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,195 @@ | |||||
//package com.jd.blockchain.contract; | |||||
// | |||||
//import com.jd.blockchain.binaryproto.BinaryProtocol; | |||||
//import com.jd.blockchain.binaryproto.DataContract; | |||||
//import com.jd.blockchain.consts.DataCodes; | |||||
//import com.jd.blockchain.ledger.*; | |||||
//import com.jd.blockchain.utils.Bytes; | |||||
//import com.jd.blockchain.utils.IllegalDataException; | |||||
//import org.springframework.util.ReflectionUtils; | |||||
// | |||||
//import java.nio.ByteBuffer; | |||||
//import java.util.Arrays; | |||||
//import java.util.HashMap; | |||||
//import java.util.List; | |||||
//import java.util.Map; | |||||
// | |||||
///** | |||||
// * @author zhaogw | |||||
// * date 2019/5/16 18:05 | |||||
// */ | |||||
//public class ContractSerializeUtils { | |||||
// public static Map<Integer, Class<?>> DATA_CONTRACT_MAP = new HashMap<>(); | |||||
// public static final Integer[] PRIMITIVE_DATA_CODES = {DataCodes.CONTRACT_INT8, DataCodes.CONTRACT_INT16, DataCodes.CONTRACT_INT32, | |||||
// DataCodes.CONTRACT_INT64, DataCodes.CONTRACT_BIG_INT,DataCodes.CONTRACT_TEXT, DataCodes.CONTRACT_BINARY }; | |||||
// | |||||
// /** | |||||
// * serialize the Object[] by List<DataContract> list; | |||||
// * @param objArr | |||||
// * @param dataContractList | |||||
// * @return | |||||
// */ | |||||
// public static byte[] serializeMethodParam(Object[] objArr,List<DataContract> dataContractList) { | |||||
// byte[][] result = new byte[objArr.length][]; | |||||
// //将method中形参转换为实体对象,每个形参都必须为@DataContract类型;每个形参使用系统的BinaryProtocol来进行序列化,如果有5个参数,则使用5次序列化; | |||||
// int sum = 0; | |||||
// | |||||
// for(int i=0;i<objArr.length;i++){ | |||||
// DataContract dataContract = dataContractList.get(i); | |||||
// objArr[i] = regenObj(dataContract,objArr[i]); | |||||
// //get data interface; | |||||
// result[i] = BinaryProtocol.encode(objArr[i],getDataIntf().get(dataContract.code())); | |||||
// sum += result[i].length; | |||||
// } | |||||
// /** | |||||
// * return byte[] format: | |||||
// return is byte[], but now is byte[][], so we should reduct dimension by adding the header info to the rtnBytes[]; | |||||
// rtnBytes[]=classTypes.length/first length/second length/third length/result[0]/result[1]/result[2]; | |||||
// rtnBytes[0]: 4 bytes(classTypes.length); | |||||
// rtnBytes[1]: 4 bytes(1 param's length); | |||||
// rtnBytes[2]: 4 bytes(2 param's length); | |||||
// rtnBytes[3]: 4 bytes(3 param's length); | |||||
// rtnBytes[...]: result[0][] bytes(1 param's length); | |||||
// rtnBytes[...]: result[1][] bytes(2 param's length); | |||||
// rtnBytes[...]: result[2][] bytes(3 param's length); | |||||
// */ | |||||
// int bodyFirstPosition = 4 + 4 * (objArr.length); | |||||
// ByteBuffer byteBuffer = ByteBuffer.allocate(bodyFirstPosition + sum); | |||||
// byteBuffer.putInt(objArr.length); | |||||
// for(int j=0; j<result.length; j++) { | |||||
// byte[] curResult = result[j]; | |||||
// byteBuffer.putInt(curResult.length); | |||||
// } | |||||
// for(int k=0; k<result.length; k++){ | |||||
// byteBuffer.put(result[k]); | |||||
// } | |||||
// return byteBuffer.array(); | |||||
// } | |||||
// | |||||
// /** | |||||
// * deserialize the params bytes[]; | |||||
// * params format: nums|first length| second length| third length| ... |bytes[0]| byte[1] | bytes[2]| ... | |||||
// * @param params | |||||
// * @param dataContractList | |||||
// * @return | |||||
// */ | |||||
// public static Object[] deserializeMethodParam(byte[] params, List<DataContract> dataContractList) { | |||||
// Object result[] = new Object[dataContractList.size()]; | |||||
// ByteBuffer byteBuffer = ByteBuffer.allocate(params.length); | |||||
// byteBuffer.put(params); | |||||
// int paramNums = byteBuffer.getInt(0); | |||||
// | |||||
// if(paramNums != dataContractList.size()){ | |||||
// throw new IllegalArgumentException("deserialize Method param. params'length in byte[] != method's param length"); | |||||
// } | |||||
// | |||||
// int offsetPosition = (1 + dataContractList.size())*4; //start position of real data; | |||||
// for(int i=0; i<dataContractList.size(); i++){ | |||||
// DataContract dataContract = dataContractList.get(i); | |||||
// int curParamLength = byteBuffer.getInt((i+1)*4); | |||||
// ByteBuffer byteBuffer1 = ByteBuffer.allocate(curParamLength); | |||||
// byteBuffer1.put(params,offsetPosition,curParamLength); | |||||
// offsetPosition += curParamLength; | |||||
// //if dataContract=primitive type(byte/short/int/long/String),only use its getValues(); | |||||
// Object object = BinaryProtocol.decodeAs(byteBuffer1.array(), | |||||
// getDataIntf().get(dataContract.code())); | |||||
// if(isPrimitiveType(dataContract.code())){ | |||||
// Class<?> classObj = getDataIntf().get(dataContract.code()); | |||||
// try { | |||||
// result[i] = ReflectionUtils.invokeMethod(classObj.getMethod("getValue"),object); | |||||
// } catch (NoSuchMethodException e) { | |||||
// throw new IllegalStateException("no getValue(). detail="+e.getMessage()); | |||||
// } | |||||
// }else { | |||||
// result[i] = object; | |||||
// } | |||||
// byteBuffer1.clear(); | |||||
// } | |||||
// | |||||
// return result; | |||||
// } | |||||
// | |||||
// | |||||
// /** | |||||
// * the param types that we can support; | |||||
// * @param <T> | |||||
// * @return | |||||
// */ | |||||
// public static <T> Map<Integer, Class<?> > getDataIntf(){ | |||||
// DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT8, CONTRACT_INT8.class); | |||||
// DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT16, CONTRACT_INT16.class); | |||||
// DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT32, CONTRACT_INT32.class); | |||||
// DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_INT64, CONTRACT_INT64.class); | |||||
// DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_TEXT, CONTRACT_TEXT.class); | |||||
// DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_BINARY, CONTRACT_BINARY.class); | |||||
// DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_BIZ_CONTENT, ContractBizContent.class); | |||||
// return DATA_CONTRACT_MAP; | |||||
// } | |||||
// | |||||
// public static boolean isPrimitiveType(int dataContractCode){ | |||||
// return Arrays.asList(PRIMITIVE_DATA_CODES).contains(dataContractCode); | |||||
// } | |||||
// | |||||
// private static Object regenObj(DataContract dataContract, Object object){ | |||||
// if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT8.class)){ | |||||
// | |||||
// return (CONTRACT_INT8) () -> Byte.parseByte(object.toString()); | |||||
// }else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT16.class)){ | |||||
// return (CONTRACT_INT16) () -> Short.parseShort(object.toString()); | |||||
// }else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT32.class)){ | |||||
// return (CONTRACT_INT32) () -> Integer.parseInt(object.toString()); | |||||
// }else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_INT64.class)){ | |||||
// return (CONTRACT_INT64) () -> Long.parseLong(object.toString()); | |||||
// }else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_TEXT.class)){ | |||||
// return (CONTRACT_TEXT) () -> object.toString(); | |||||
// }else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_BINARY.class)){ | |||||
// return (CONTRACT_BINARY) () -> (Bytes) object; | |||||
// }else if(getDataIntf().get(dataContract.code()).equals(ContractBizContent.class)){ | |||||
// ContractBizContent contractBizContent = (ContractBizContent)object; | |||||
// return contractBizContent; | |||||
// }else { | |||||
// throw new IllegalDataException("cann't get new Object by dataContract and object."); | |||||
// } | |||||
// } | |||||
// | |||||
// /** | |||||
// * get contractType(contain @DataContract) by primitive class type; | |||||
// * some class type can be supported default (byte/char/int/long/String/Bytes, and so on). | |||||
// * in other words, need not contain the @DataContract in its class for contract param's serialization or deserialization. | |||||
// * @param classType | |||||
// * @return | |||||
// */ | |||||
// private static Class<?> getContractTypeByPrimitiveType(Class<?> classType) { | |||||
// if(classType.equals(byte.class) || classType.equals(Byte.class)){ | |||||
// return CONTRACT_INT8.class; | |||||
// }else if(classType.equals(char.class) || classType.equals(Character.class)){ | |||||
// return CONTRACT_INT16.class; | |||||
// }else if(classType.equals(int.class) || classType.equals(Integer.class)){ | |||||
// return CONTRACT_INT32.class; | |||||
// }else if(classType.equals(long.class) || classType.equals(Long.class)){ | |||||
// return CONTRACT_INT64.class; | |||||
// }else if(classType.equals(String.class)){ | |||||
// return CONTRACT_TEXT.class; | |||||
// }else if(classType.equals(Bytes.class)){ | |||||
// return CONTRACT_BINARY.class; | |||||
// }else { | |||||
// throw new IllegalDataException(String.format("no support the classType=%s, please check @DataContract.",classType.toString())); | |||||
// } | |||||
// } | |||||
// | |||||
// public static DataContract parseDataContract(Class<?> classType){ | |||||
// DataContract dataContract = classType.getAnnotation(DataContract.class); | |||||
// //if the param's class Type don't contain @DataContract, then check parameterAnnotations of this method. | |||||
// if(dataContract == null){ | |||||
// boolean canPass = false; | |||||
// //if parameterAnnotations don't contain @DataContract, is it primitive type? | |||||
// Class<?> contractType = getContractTypeByPrimitiveType(classType); | |||||
// dataContract = contractType.getAnnotation(DataContract.class); | |||||
// } | |||||
// if(!getDataIntf().containsKey(dataContract.code())){ | |||||
// throw new IllegalArgumentException(String.format( | |||||
// "for now, this @dataContract(code=%s) is forbidden in the param list.",dataContract.code())); | |||||
// } | |||||
// return dataContract; | |||||
// } | |||||
//} |
@@ -1,13 +1,11 @@ | |||||
package com.jd.blockchain.contract; | package com.jd.blockchain.contract; | ||||
import java.lang.annotation.Annotation; | |||||
import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
import java.util.ArrayList; | |||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.List; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Set; | import java.util.Set; | ||||
import com.jd.blockchain.contract.ContractSerializeUtils; | |||||
import com.jd.blockchain.utils.IllegalDataException; | import com.jd.blockchain.utils.IllegalDataException; | ||||
public class ContractType { | public class ContractType { | ||||
@@ -68,45 +66,59 @@ public class ContractType { | |||||
* @param delaredInterface 声明合约的接口类型; | * @param delaredInterface 声明合约的接口类型; | ||||
* @return | * @return | ||||
*/ | */ | ||||
public static ContractType resolve(Class<?> delaredInterface) { | |||||
ContractType contractType = new ContractType(); | |||||
public static ContractType resolve(Class<?> contractIntf) { | |||||
// 接口上必须有注解 | |||||
if (!contractIntf.isAnnotationPresent(Contract.class)) { | |||||
throw new IllegalDataException("It is not a Contract Type, because there is not @Contract !"); | |||||
} | |||||
Annotation annotation = delaredInterface.getDeclaredAnnotation(Contract.class); | |||||
Method[] classMethods = contractIntf.getDeclaredMethods(); | |||||
// contains: @Contract? | |||||
boolean isContractType = annotation != null ? true : false; | |||||
if (!isContractType) { | |||||
throw new IllegalDataException("The specified type is not annotated by @Contract!"); | |||||
if (classMethods.length == 0) { | |||||
throw new IllegalDataException("This interface have not any methods !"); | |||||
} | } | ||||
// contractIntf contains @Contract and @ContractEvent; | |||||
Method[] classMethods = delaredInterface.getDeclaredMethods(); | |||||
ContractType contractType = new ContractType(); | |||||
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); | ||||
if (contractEvent != null) { | if (contractEvent != null) { | ||||
String eventName_ = contractEvent.name(); | |||||
// if annoMethodMap has contained the eventName, too many same eventNames exists | |||||
// probably, say NO! | |||||
if (contractType.events.containsKey(eventName_)) { | |||||
String eventName = contractEvent.name(); | |||||
//if annoMethodMap has contained the eventName, too many same eventNames exists probably, say NO! | |||||
if(contractType.events.containsKey(eventName)){ | |||||
throw new ContractException("there is repeat definition of contractEvent to @ContractEvent."); | throw new ContractException("there is repeat definition of contractEvent to @ContractEvent."); | ||||
} | } | ||||
// check param's type is fit for need. | |||||
//check param's type is fit for need. | |||||
Class<?>[] paramTypes = method.getParameterTypes(); | Class<?>[] paramTypes = method.getParameterTypes(); | ||||
List dataContractList = new ArrayList(); | |||||
for (Class<?> curParamType : paramTypes) { | |||||
throw new IllegalStateException("Not implemented!"); | |||||
// DataContract dataContract = ContractSerializeUtils.parseDataContract(curParamType); | |||||
// dataContractList.add(dataContract); | |||||
for(Class<?> currParamType : paramTypes) { | |||||
if (!ContractSerializeUtils.support(currParamType)) { | |||||
throw new IllegalStateException(String.format("Param Type = %s can not support !!!", currParamType.getName())); | |||||
} | |||||
} | |||||
// 判断返回值是否可序列化 | |||||
Class<?> returnType = method.getReturnType(); | |||||
if (!ContractSerializeUtils.support(returnType)) { | |||||
throw new IllegalStateException(String.format("Return Type = %s can not support !!!", returnType.getName())); | |||||
} | } | ||||
// if(dataContractList.size()>0){ | |||||
// contractType.dataContractMap.put(method,dataContractList); | |||||
// } | |||||
contractType.events.put(eventName_, method); | |||||
contractType.handleMethods.put(method, eventName_); | |||||
contractType.events.put(eventName, method); | |||||
contractType.handleMethods.put(method, eventName); | |||||
} | } | ||||
} | } | ||||
return contractType; | return contractType; | ||||
} | } | ||||
@Override | |||||
public String toString() { | |||||
return "ContractType{" + | |||||
"name='" + name + '\'' + | |||||
", events=" + events + | |||||
", handleMethods=" + handleMethods + | |||||
'}'; | |||||
} | |||||
} | } |
@@ -0,0 +1,39 @@ | |||||
package com.jd.blockchain.contract; | |||||
import com.jd.blockchain.utils.IllegalDataException; | |||||
import java.util.concurrent.CompletableFuture; | |||||
import java.util.concurrent.TimeUnit; | |||||
public class EventResult<T> { | |||||
private static final long MAX_SECONDS = 30; | |||||
private CompletableFuture<T> data = new CompletableFuture<>(); | |||||
private int opIndex; | |||||
public EventResult() { | |||||
} | |||||
public EventResult(int opIndex) { | |||||
this.opIndex = opIndex; | |||||
} | |||||
public void done(T value) { | |||||
data.complete(value); | |||||
} | |||||
public int opIndex() { | |||||
return this.opIndex; | |||||
} | |||||
public T get() { | |||||
try { | |||||
// 防止长时间阻塞 | |||||
return data.get(MAX_SECONDS, TimeUnit.SECONDS); | |||||
} catch (Exception e) { | |||||
throw new IllegalDataException(e.getMessage()); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,13 @@ | |||||
package com.jd.blockchain.contract.param; | |||||
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_BYTES) | |||||
public interface WRAP_BYTES { | |||||
@DataField(order = 1, primitiveType = PrimitiveType.BYTES) | |||||
byte[] getValue(); | |||||
} |
@@ -0,0 +1,13 @@ | |||||
package com.jd.blockchain.contract.param; | |||||
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_INT) | |||||
public interface WRAP_INT { | |||||
@DataField(order = 1, primitiveType = PrimitiveType.INT32) | |||||
int getValue(); | |||||
} |
@@ -0,0 +1,13 @@ | |||||
package com.jd.blockchain.contract.param; | |||||
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_LONG) | |||||
public interface WRAP_LONG { | |||||
@DataField(order = 1, primitiveType = PrimitiveType.INT64) | |||||
long getValue(); | |||||
} |
@@ -0,0 +1,13 @@ | |||||
package com.jd.blockchain.contract.param; | |||||
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_SHORT) | |||||
public interface WRAP_SHORT { | |||||
@DataField(order = 1, primitiveType = PrimitiveType.INT16) | |||||
short getValue(); | |||||
} |
@@ -0,0 +1,13 @@ | |||||
package com.jd.blockchain.contract.param; | |||||
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_STRING) | |||||
public interface WRAP_STRING { | |||||
@DataField(order = 1, primitiveType = PrimitiveType.TEXT) | |||||
String getValue(); | |||||
} |
@@ -44,5 +44,4 @@ public interface ContractEventSendOperation extends Operation { | |||||
*/ | */ | ||||
@DataField(order = 5, primitiveType = PrimitiveType.INT64) | @DataField(order = 5, primitiveType = PrimitiveType.INT64) | ||||
long getTxOpTime(); | long getTxOpTime(); | ||||
} | } |
@@ -5,13 +5,12 @@ import com.jd.blockchain.binaryproto.DataField; | |||||
import com.jd.blockchain.binaryproto.PrimitiveType; | import com.jd.blockchain.binaryproto.PrimitiveType; | ||||
import com.jd.blockchain.consts.DataCodes; | import com.jd.blockchain.consts.DataCodes; | ||||
@DataContract(code = DataCodes.CONTRACT_RETURN) | |||||
@DataContract(code = DataCodes.TX_OP_RESULT) | |||||
public interface OperationResult { | public interface OperationResult { | ||||
@DataField(order = 1, primitiveType = PrimitiveType.INT32) | |||||
int getIndex(); | |||||
@DataField(order = 2, primitiveType = PrimitiveType.BYTES) | |||||
byte[] getResult(); | |||||
@DataField(order=1, primitiveType = PrimitiveType.INT32) | |||||
int getIndex(); | |||||
@DataField(order=2, primitiveType = PrimitiveType.BYTES) | |||||
byte[] getResult(); | |||||
} | } |
@@ -1,26 +1,35 @@ | |||||
package com.jd.blockchain.ledger; | package com.jd.blockchain.ledger; | ||||
public class OperationResultData implements OperationResult { | public class OperationResultData implements OperationResult { | ||||
private int index; | |||||
private int index; | |||||
private byte[] result; | |||||
public OperationResultData() { | |||||
} | |||||
private byte[] result; | |||||
public OperationResultData(int index, byte[] result) { | |||||
this.index = index; | |||||
this.result = result; | |||||
} | |||||
public OperationResultData() { | |||||
} | |||||
@Override | |||||
public int getIndex() { | |||||
return index; | |||||
} | |||||
public OperationResultData(int operationIndex, byte[] result) { | |||||
this.index = operationIndex; | |||||
this.result = result; | |||||
} | |||||
@Override | |||||
public byte[] getResult() { | |||||
return result; | |||||
} | |||||
@Override | |||||
public int getIndex() { | |||||
return index; | |||||
} | |||||
public void setIndex(int index) { | |||||
this.index = index; | |||||
} | |||||
@Override | |||||
public byte[] getResult() { | |||||
return result; | |||||
} | |||||
public void setResult(byte[] result) { | |||||
this.result = result; | |||||
} | |||||
} | } |
@@ -49,6 +49,6 @@ public interface Transaction extends NodeRequest, HashObject { | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
@DataField(order = 4, refContract = true, list = true) | |||||
@DataField(order=4, list = true, refContract=true) | |||||
OperationResult[] getOperationResults(); | OperationResult[] getOperationResults(); | ||||
} | } |
@@ -27,7 +27,7 @@ public class TransactionRespHandle implements TransactionResponse { | |||||
private TransactionState globalResult; | private TransactionState globalResult; | ||||
private OperationResult[] contractReturn; | |||||
private OperationResult[] operationResults; | |||||
public TransactionRespHandle(TransactionRequest request, TransactionState result, TransactionState globalResult) { | public TransactionRespHandle(TransactionRequest request, TransactionState result, TransactionState globalResult) { | ||||
this.request = request; | this.request = request; | ||||
@@ -51,8 +51,8 @@ public class TransactionRespHandle implements TransactionResponse { | |||||
this.result = result; | this.result = result; | ||||
} | } | ||||
public void setContractReturn(OperationResult[] contractReturn) { | |||||
this.contractReturn = contractReturn; | |||||
public void setOperationResults(OperationResult[] operationResults) { | |||||
this.operationResults = operationResults; | |||||
} | } | ||||
public LedgerBlock getBlock() { | public LedgerBlock getBlock() { | ||||
@@ -97,7 +97,7 @@ public class TransactionRespHandle implements TransactionResponse { | |||||
} | } | ||||
@Override | @Override | ||||
public OperationResult[] getContractReturn() { | |||||
return contractReturn; | |||||
public OperationResult[] getOperationResults() { | |||||
return operationResults; | |||||
} | } | ||||
} | } |
@@ -63,6 +63,6 @@ public interface TransactionResponse { | |||||
* | * | ||||
* @return | * @return | ||||
*/ | */ | ||||
@DataField(order = 6, list = true, refContract = true) | |||||
OperationResult[] getContractReturn(); | |||||
@DataField(order=6, list=true, refContract = true) | |||||
OperationResult[] getOperationResults(); | |||||
} | } |
@@ -1,17 +0,0 @@ | |||||
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) | |||||
OperationResult[] getContractReturn(); | |||||
} |
@@ -1,26 +0,0 @@ | |||||
package com.jd.blockchain.ledger; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
public class TransactionReturnMessageData implements TransactionReturnMessage { | |||||
private List<OperationResult> contractReturnMessages = new ArrayList<>(); | |||||
public void addContractReturnMessage(OperationResult contractReturnMessage) { | |||||
contractReturnMessages.add(contractReturnMessage); | |||||
} | |||||
public boolean isContractReturnEmpty() { | |||||
return contractReturnMessages.isEmpty(); | |||||
} | |||||
@Override | |||||
public OperationResult[] getContractReturn() { | |||||
if (isContractReturnEmpty()) { | |||||
return null; | |||||
} | |||||
OperationResult[] crms = new OperationResult[contractReturnMessages.size()]; | |||||
return contractReturnMessages.toArray(crms); | |||||
} | |||||
} |
@@ -4,6 +4,7 @@ import java.util.ArrayList; | |||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.List; | import java.util.List; | ||||
import com.jd.blockchain.contract.EventResult; | |||||
import com.jd.blockchain.ledger.BlockchainIdentity; | import com.jd.blockchain.ledger.BlockchainIdentity; | ||||
import com.jd.blockchain.ledger.ContractCodeDeployOperation; | import com.jd.blockchain.ledger.ContractCodeDeployOperation; | ||||
import com.jd.blockchain.ledger.ContractEventSendOperation; | import com.jd.blockchain.ledger.ContractEventSendOperation; | ||||
@@ -29,7 +30,7 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||||
private static final ContractCodeDeployOperationBuilderImpl CONTRACT_CODE_DEPLOY_OP_BUILDER = new ContractCodeDeployOperationBuilderImpl(); | private static final ContractCodeDeployOperationBuilderImpl CONTRACT_CODE_DEPLOY_OP_BUILDER = new ContractCodeDeployOperationBuilderImpl(); | ||||
private static final ContractEventSendOperationBuilderImpl CONTRACT_EVENT_SEND_OP_BUILDER = new ContractEventSendOperationBuilderImpl(); | |||||
// private static final ContractEventSendOperationBuilderImpl CONTRACT_EVENT_SEND_OP_BUILDER = new ContractEventSendOperationBuilderImpl(); | |||||
private LedgerInitOperationBuilder ledgerInitOpBuilder = new LedgerInitOperationBuilderFilter(); | private LedgerInitOperationBuilder ledgerInitOpBuilder = new LedgerInitOperationBuilderFilter(); | ||||
@@ -89,6 +90,11 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||||
return contractInvoProxyBuilder.create(address, contractIntf, contractEventSendOpBuilder); | return contractInvoProxyBuilder.create(address, contractIntf, contractEventSendOpBuilder); | ||||
} | } | ||||
@Override | |||||
public <T> EventResult<T> result(ContractEventExecutor execute) { | |||||
return contractInvoProxyBuilder.execute(execute); | |||||
} | |||||
public Collection<Operation> getOperations() { | public Collection<Operation> getOperations() { | ||||
// TODO: 合并操作列表中可能的重复操作; | // TODO: 合并操作列表中可能的重复操作; | ||||
return operationList; | return operationList; | ||||
@@ -130,7 +136,6 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||||
operationList.add(op); | operationList.add(op); | ||||
return op; | return op; | ||||
} | } | ||||
} | } | ||||
private class DataAccountKVSetOperationBuilderFilter implements DataAccountKVSetOperationBuilder { | private class DataAccountKVSetOperationBuilderFilter implements DataAccountKVSetOperationBuilder { | ||||
@@ -235,25 +240,35 @@ public class BlockchainOperationFactory implements ClientOperator, LedgerInitOpe | |||||
operationList.add(op); | operationList.add(op); | ||||
return op; | return op; | ||||
} | } | ||||
} | } | ||||
private class ContractEventSendOperationBuilderFilter implements ContractEventSendOperationBuilder { | private class ContractEventSendOperationBuilderFilter implements ContractEventSendOperationBuilder { | ||||
@Override | @Override | ||||
public ContractEventSendOperation send(String address, String event, byte[] args) { | public ContractEventSendOperation send(String address, String event, byte[] args) { | ||||
ContractEventSendOperation op = CONTRACT_EVENT_SEND_OP_BUILDER.send(address, event, args); | |||||
operationList.add(op); | |||||
return op; | |||||
return send(Bytes.fromBase58(address), event, args); | |||||
} | } | ||||
@Override | @Override | ||||
public ContractEventSendOperation send(Bytes address, String event, byte[] args) { | public ContractEventSendOperation send(Bytes address, String event, byte[] args) { | ||||
ContractEventSendOperation op = CONTRACT_EVENT_SEND_OP_BUILDER.send(address, event, args); | |||||
int opIndex = operationList.size(); | |||||
ContractEventSendOpTemplate op = new ContractEventSendOpTemplate(address, event, args, opIndex); | |||||
operationList.add(op); | operationList.add(op); | ||||
return op; | return op; | ||||
} | } | ||||
@Override | |||||
public ContractEventSendOperation send(String address) { | |||||
return send(Bytes.fromBase58(address)); | |||||
} | |||||
@Override | |||||
public ContractEventSendOperation send(Bytes address) { | |||||
int opIndex = operationList.size(); | |||||
ContractEventSendOpTemplate op = new ContractEventSendOpTemplate(address, opIndex); | |||||
operationList.add(op); | |||||
return op; | |||||
} | |||||
} | } | ||||
} | } |
@@ -0,0 +1,6 @@ | |||||
package com.jd.blockchain.transaction; | |||||
public interface ContractEventExecutor<T> { | |||||
T execute(); | |||||
} |
@@ -5,6 +5,7 @@ import com.jd.blockchain.ledger.ContractEventSendOperation; | |||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
public class ContractEventSendOpTemplate implements ContractEventSendOperation { | public class ContractEventSendOpTemplate implements ContractEventSendOperation { | ||||
static { | static { | ||||
DataContractRegistry.register(ContractEventSendOperation.class); | DataContractRegistry.register(ContractEventSendOperation.class); | ||||
} | } | ||||
@@ -14,17 +15,47 @@ public class ContractEventSendOpTemplate implements ContractEventSendOperation { | |||||
private String event; | private String event; | ||||
//交易操作时间; | //交易操作时间; | ||||
private long txOpTime; | private long txOpTime; | ||||
// 所属操作Index | |||||
private int opIndex; | |||||
public ContractEventSendOpTemplate() { | public ContractEventSendOpTemplate() { | ||||
} | } | ||||
public ContractEventSendOpTemplate(Bytes contractAddress) { | |||||
this(contractAddress, -1); | |||||
} | |||||
public ContractEventSendOpTemplate(Bytes contractAddress, int opIndex) { | |||||
this.contractAddress = contractAddress; | |||||
this.opIndex = opIndex; | |||||
this.txOpTime = System.currentTimeMillis(); | |||||
} | |||||
public ContractEventSendOpTemplate(Bytes contractAddress, String event, byte[] args) { | public ContractEventSendOpTemplate(Bytes contractAddress, String event, byte[] args) { | ||||
this(contractAddress, event, args, -1); | |||||
} | |||||
public ContractEventSendOpTemplate(Bytes contractAddress, String event, byte[] args, int opIndex) { | |||||
this.contractAddress = contractAddress; | this.contractAddress = contractAddress; | ||||
this.event = event; | this.event = event; | ||||
this.args = args; | this.args = args; | ||||
this.opIndex = opIndex; | |||||
this.txOpTime = System.currentTimeMillis(); | this.txOpTime = System.currentTimeMillis(); | ||||
} | } | ||||
public void setArgs(byte[] args) { | |||||
this.args = args; | |||||
} | |||||
public void setEvent(String event) { | |||||
this.event = event; | |||||
} | |||||
public void setEventAndArgs(String event, byte[] args) { | |||||
this.event = event; | |||||
this.args = args; | |||||
} | |||||
@Override | @Override | ||||
public Bytes getContractAddress() { | public Bytes getContractAddress() { | ||||
return contractAddress; | return contractAddress; | ||||
@@ -44,4 +75,13 @@ public class ContractEventSendOpTemplate implements ContractEventSendOperation { | |||||
public long getTxOpTime() { | public long getTxOpTime() { | ||||
return txOpTime; | return txOpTime; | ||||
} | } | ||||
/** | |||||
* 获取所属交易中的序号,该值不需要序列化 | |||||
* | |||||
* @return | |||||
*/ | |||||
public int getOpIndex() { | |||||
return opIndex; | |||||
} | |||||
} | } |
@@ -23,4 +23,17 @@ public interface ContractEventSendOperationBuilder { | |||||
@Deprecated | @Deprecated | ||||
ContractEventSendOperation send(Bytes address, String event, byte[] args); | ContractEventSendOperation send(Bytes address, String event, byte[] args); | ||||
/** | |||||
* | |||||
* @param address | |||||
* @return | |||||
*/ | |||||
ContractEventSendOperation send(String address); | |||||
/** | |||||
* | |||||
* @param address | |||||
* @return | |||||
*/ | |||||
ContractEventSendOperation send(Bytes address); | |||||
} | } |
@@ -1,21 +1,21 @@ | |||||
package com.jd.blockchain.transaction; | |||||
import com.jd.blockchain.ledger.ContractEventSendOperation; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
@Deprecated | |||||
class ContractEventSendOperationBuilderImpl implements ContractEventSendOperationBuilder { | |||||
@Override | |||||
public ContractEventSendOperation send(String address, String event, byte[] args) { | |||||
ContractEventSendOpTemplate op = new ContractEventSendOpTemplate(Bytes.fromBase58(address), event, args); | |||||
return op; | |||||
} | |||||
@Override | |||||
public ContractEventSendOperation send(Bytes address, String event, byte[] args) { | |||||
ContractEventSendOpTemplate op = new ContractEventSendOpTemplate(address, event, args); | |||||
return op; | |||||
} | |||||
} | |||||
//package com.jd.blockchain.transaction; | |||||
// | |||||
//import com.jd.blockchain.ledger.ContractEventSendOperation; | |||||
//import com.jd.blockchain.utils.Bytes; | |||||
// | |||||
//@Deprecated | |||||
//class ContractEventSendOperationBuilderImpl implements ContractEventSendOperationBuilder { | |||||
// | |||||
// @Override | |||||
// public ContractEventSendOperation send(String address, String event, byte[] args) { | |||||
// ContractEventSendOpTemplate op = new ContractEventSendOpTemplate(Bytes.fromBase58(address), event, args); | |||||
// return op; | |||||
// } | |||||
// | |||||
// @Override | |||||
// public ContractEventSendOperation send(Bytes address, String event, byte[] args) { | |||||
// ContractEventSendOpTemplate op = new ContractEventSendOpTemplate(address, event, args); | |||||
// return op; | |||||
// } | |||||
// | |||||
//} |
@@ -3,7 +3,9 @@ package com.jd.blockchain.transaction; | |||||
import java.lang.reflect.InvocationHandler; | import java.lang.reflect.InvocationHandler; | ||||
import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
import com.jd.blockchain.contract.ContractSerializeUtils; | |||||
import com.jd.blockchain.contract.ContractType; | import com.jd.blockchain.contract.ContractType; | ||||
import com.jd.blockchain.ledger.ContractEventSendOperation; | |||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
import com.jd.blockchain.utils.IllegalDataException; | import com.jd.blockchain.utils.IllegalDataException; | ||||
@@ -11,45 +13,66 @@ public class ContractInvocationProxy implements InvocationHandler { | |||||
// private String contractMessage; | // private String contractMessage; | ||||
private Bytes contractAddress; | |||||
// private Bytes contractAddress; | |||||
private ContractType contractType; | private ContractType contractType; | ||||
private ContractEventSendOperationBuilder sendOpBuilder; | |||||
// private ContractEventSendOperationBuilder sendOpBuilder; | |||||
private ContractEventSendOperation sendOperation; | |||||
public ContractInvocationProxy(Bytes contractAddress, ContractType contractType, | public ContractInvocationProxy(Bytes contractAddress, ContractType contractType, | ||||
ContractEventSendOperationBuilder sendOpBuilder) { | ContractEventSendOperationBuilder sendOpBuilder) { | ||||
this.contractAddress = contractAddress; | |||||
// this.contractAddress = contractAddress; | |||||
if(contractType == null){ | if(contractType == null){ | ||||
throw new IllegalDataException("contractType == null, no invoke really."); | throw new IllegalDataException("contractType == null, no invoke really."); | ||||
} | } | ||||
this.contractType = contractType; | this.contractType = contractType; | ||||
this.sendOpBuilder = sendOpBuilder; | |||||
// this.sendOpBuilder = sendOpBuilder; | |||||
// Send一个地址,但不涉及Event | |||||
this.sendOperation = sendOpBuilder.send(contractAddress); | |||||
} | } | ||||
@Override | @Override | ||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | ||||
// 判断是否是常规方法调用 | |||||
if (method.getName().equals("hashCode")) { | |||||
// 该处需要使用当前代理类的HashCode | |||||
return this.hashCode(); | |||||
} | |||||
if (method.getName().equals("toString")) { | |||||
// 该处使用当前代理类的toString | |||||
return this.toString(); | |||||
} | |||||
if (sendOperation.getEvent() != null) { | |||||
throw new IllegalStateException("Contract Object can only execute method one time !!!"); | |||||
} | |||||
String event = contractType.getEvent(method); | String event = contractType.getEvent(method); | ||||
if (event == null) { | if (event == null) { | ||||
// 适配 Object 对象的方法; | |||||
// toString 方法; | |||||
return String.format("[%s]-%s", contractAddress, contractType.toString()); | |||||
// hashCode 方法; | |||||
// 该方法不是合约可执行的方法 | |||||
throw new IllegalAccessException(String.format("This Method [%s] is not Contract Event Method !!!", | |||||
method.getName())); | |||||
} | } | ||||
// 合约方法; | // 合约方法; | ||||
byte[] argBytes = serializeArgs(args,method); | |||||
sendOpBuilder.send(contractAddress, event, argBytes); | |||||
// TODO: 暂时未考虑有返回值的情况; | |||||
byte[] argBytes = serializeArgs(args); | |||||
if (sendOperation instanceof ContractEventSendOpTemplate) { | |||||
((ContractEventSendOpTemplate) sendOperation).setEventAndArgs(event, argBytes); | |||||
} | |||||
// 代理操作,返回值类型无法创建 | |||||
return null; | return null; | ||||
} | } | ||||
private byte[] serializeArgs(Object[] args, Method method) { | |||||
if(args == null || args.length==0){ | |||||
return null; | |||||
private byte[] serializeArgs(Object[] args) { | |||||
return ContractSerializeUtils.serializeArray(args); | |||||
} | |||||
public int opIndex() { | |||||
if (sendOperation instanceof ContractEventSendOpTemplate) { | |||||
return ((ContractEventSendOpTemplate) sendOperation).getOpIndex(); | |||||
} | } | ||||
throw new IllegalStateException("Not implemented!"); | |||||
//return ContractSerializeUtils.serializeMethodParam(args,contractType.getDataContractMap().get(method)); | |||||
return -1; | |||||
} | } | ||||
} | } |
@@ -1,18 +1,20 @@ | |||||
package com.jd.blockchain.transaction; | package com.jd.blockchain.transaction; | ||||
import java.lang.annotation.Annotation; | |||||
import java.lang.reflect.Proxy; | import java.lang.reflect.Proxy; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
import com.jd.blockchain.contract.Contract; | |||||
import com.jd.blockchain.contract.EventResult; | |||||
import com.jd.blockchain.contract.ContractType; | import com.jd.blockchain.contract.ContractType; | ||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
import com.jd.blockchain.utils.IllegalDataException; | |||||
public class ContractInvocationProxyBuilder { | public class ContractInvocationProxyBuilder { | ||||
private Map<Class<?>, ContractType> contractTypes = new ConcurrentHashMap<>(); | private Map<Class<?>, ContractType> contractTypes = new ConcurrentHashMap<>(); | ||||
private Map<Object, Integer> contractOperations = new ConcurrentHashMap<>(); | |||||
public <T> T create(String address, Class<T> contractIntf, ContractEventSendOperationBuilder contractEventBuilder) { | public <T> T create(String address, Class<T> contractIntf, ContractEventSendOperationBuilder contractEventBuilder) { | ||||
return create(Bytes.fromBase58(address), contractIntf, contractEventBuilder); | return create(Bytes.fromBase58(address), contractIntf, contractEventBuilder); | ||||
} | } | ||||
@@ -23,10 +25,31 @@ public class ContractInvocationProxyBuilder { | |||||
ContractInvocationProxy proxyHandler = new ContractInvocationProxy(address, contractType, | ContractInvocationProxy proxyHandler = new ContractInvocationProxy(address, contractType, | ||||
contractEventBuilder); | contractEventBuilder); | ||||
T proxy = (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), | T proxy = (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), | ||||
new Class<?>[] { contractIntf }, proxyHandler); | new Class<?>[] { contractIntf }, proxyHandler); | ||||
// 创建关联关系 | |||||
contractOperations.put(proxy, proxyHandler.opIndex()); | |||||
return proxy; | |||||
} | |||||
public <T> EventResult<T> execute(ContractEventExecutor execute) { | |||||
Object contractProxy = execute.execute(); | |||||
if (contractProxy == null) { | |||||
// 该方法执行必须要有返回值 | |||||
throw new IllegalStateException( | |||||
String.format("ContractEventExecutor [%s] 's return must be not empty !!!", execute.toString())); | |||||
} | |||||
if (!(contractProxy instanceof Proxy)) { | |||||
throw new IllegalDataException( | |||||
String.format("ContractEventExecutor [%s] 's return must from TxTemplate.contract()'s result !!!", execute.toString())); | |||||
} | |||||
return (T) proxy; | |||||
Integer opIndex = contractOperations.get(contractProxy); | |||||
if (opIndex != null && opIndex > -1) { | |||||
return new EventResult<>(opIndex); | |||||
} | |||||
return null; | |||||
} | } | ||||
private ContractType resolveContractType(Class<?> contractIntf) { | private ContractType resolveContractType(Class<?> contractIntf) { | ||||
@@ -34,21 +57,8 @@ public class ContractInvocationProxyBuilder { | |||||
if (contractType != null) { | if (contractType != null) { | ||||
return contractType; | return contractType; | ||||
} | } | ||||
// TODO 检查返回值类型; | |||||
ContractType contractType1 = ContractType.resolve(contractIntf); | |||||
contractTypes.put(contractIntf,contractType1); | |||||
return contractType1; | |||||
} | |||||
/** | |||||
* is contractType really? identified by @Contract; | |||||
* @param contractIntf | |||||
* @return | |||||
*/ | |||||
private boolean isContractType(Class<?> contractIntf) { | |||||
Annotation annotation = contractIntf.getDeclaredAnnotation(Contract.class); | |||||
return annotation != null ? true : false; | |||||
ContractType ct = ContractType.resolve(contractIntf); | |||||
contractTypes.put(contractIntf, ct); | |||||
return ct; | |||||
} | } | ||||
} | } |
@@ -1,5 +1,6 @@ | |||||
package com.jd.blockchain.transaction; | package com.jd.blockchain.transaction; | ||||
import com.jd.blockchain.contract.EventResult; | |||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
public interface EventOperator { | public interface EventOperator { | ||||
@@ -30,4 +31,11 @@ public interface EventOperator { | |||||
*/ | */ | ||||
<T> T contract(Bytes address, Class<T> contractIntf); | <T> T contract(Bytes address, Class<T> contractIntf); | ||||
/** | |||||
* 执行合约异步等待应答结果 | |||||
* | |||||
* @param execute | |||||
* @return | |||||
*/ | |||||
<T> EventResult<T> result(ContractEventExecutor execute); | |||||
} | } |
@@ -1,18 +1,17 @@ | |||||
package com.jd.blockchain.transaction; | package com.jd.blockchain.transaction; | ||||
import com.jd.blockchain.binaryproto.BinaryProtocol; | import com.jd.blockchain.binaryproto.BinaryProtocol; | ||||
import com.jd.blockchain.contract.ContractSerializeUtils; | |||||
import com.jd.blockchain.contract.EventResult; | |||||
import com.jd.blockchain.crypto.AsymmetricKeypair; | import com.jd.blockchain.crypto.AsymmetricKeypair; | ||||
import com.jd.blockchain.crypto.Crypto; | import com.jd.blockchain.crypto.Crypto; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.crypto.PrivKey; | import com.jd.blockchain.crypto.PrivKey; | ||||
import com.jd.blockchain.crypto.SignatureDigest; | import com.jd.blockchain.crypto.SignatureDigest; | ||||
import com.jd.blockchain.crypto.SignatureFunction; | import com.jd.blockchain.crypto.SignatureFunction; | ||||
import com.jd.blockchain.ledger.DigitalSignature; | |||||
import com.jd.blockchain.ledger.PreparedTransaction; | |||||
import com.jd.blockchain.ledger.TransactionContent; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | |||||
import com.jd.blockchain.ledger.TransactionRequestBuilder; | |||||
import com.jd.blockchain.ledger.TransactionResponse; | |||||
import com.jd.blockchain.ledger.*; | |||||
import java.util.Map; | |||||
public class PreparedTx implements PreparedTransaction { | public class PreparedTx implements PreparedTransaction { | ||||
@@ -20,9 +19,16 @@ public class PreparedTx implements PreparedTransaction { | |||||
private TransactionService txProcessor; | private TransactionService txProcessor; | ||||
private Map<Integer, EventResult> eventResults; | |||||
public PreparedTx(TransactionRequestBuilder txReqBuilder, TransactionService txProcessor) { | public PreparedTx(TransactionRequestBuilder txReqBuilder, TransactionService txProcessor) { | ||||
this(txReqBuilder, txProcessor, null); | |||||
} | |||||
public PreparedTx(TransactionRequestBuilder txReqBuilder, TransactionService txProcessor, Map<Integer, EventResult> eventResults) { | |||||
this.txReqBuilder = txReqBuilder; | this.txReqBuilder = txReqBuilder; | ||||
this.txProcessor = txProcessor; | this.txProcessor = txProcessor; | ||||
this.eventResults = eventResults; | |||||
} | } | ||||
@Override | @Override | ||||
@@ -56,6 +62,20 @@ public class PreparedTx implements PreparedTransaction { | |||||
TransactionRequest txReq = txReqBuilder.buildRequest(); | TransactionRequest txReq = txReqBuilder.buildRequest(); | ||||
// 发起交易请求; | // 发起交易请求; | ||||
TransactionResponse txResponse = txProcessor.process(txReq); | TransactionResponse txResponse = txProcessor.process(txReq); | ||||
// 重新包装操作集合 | |||||
OperationResult[] operationResults = txResponse.getOperationResults(); | |||||
if (operationResults != null && operationResults.length > 0 && | |||||
eventResults != null && !eventResults.isEmpty()) { | |||||
for (OperationResult operationResult : operationResults) { | |||||
int opIndex = operationResult.getIndex(); | |||||
EventResult eventResult = eventResults.get(opIndex); | |||||
if (eventResult != null) { | |||||
eventResult.done(ContractSerializeUtils.resolve(operationResult.getResult())); | |||||
} | |||||
} | |||||
} | |||||
return txResponse; | return txResponse; | ||||
} | } | ||||
} | } |
@@ -2,6 +2,7 @@ package com.jd.blockchain.transaction; | |||||
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.contract.EventResult; | |||||
import com.jd.blockchain.crypto.Crypto; | import com.jd.blockchain.crypto.Crypto; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.ledger.TransactionBuilder; | import com.jd.blockchain.ledger.TransactionBuilder; | ||||
@@ -10,6 +11,9 @@ import com.jd.blockchain.ledger.TransactionContentBody; | |||||
import com.jd.blockchain.ledger.TransactionRequestBuilder; | import com.jd.blockchain.ledger.TransactionRequestBuilder; | ||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
public class TxBuilder implements TransactionBuilder { | public class TxBuilder implements TransactionBuilder { | ||||
static { | static { | ||||
@@ -18,6 +22,8 @@ public class TxBuilder implements TransactionBuilder { | |||||
private BlockchainOperationFactory opFactory = new BlockchainOperationFactory(); | private BlockchainOperationFactory opFactory = new BlockchainOperationFactory(); | ||||
private Map<Object, Integer> contractProxyMap = new HashMap<>(); | |||||
private static final String DEFAULT_HASH_ALGORITHM = "SHA256"; | private static final String DEFAULT_HASH_ALGORITHM = "SHA256"; | ||||
private HashDigest ledgerHash; | private HashDigest ledgerHash; | ||||
@@ -88,6 +94,11 @@ public class TxBuilder implements TransactionBuilder { | |||||
return opFactory.contract(address, contractIntf); | return opFactory.contract(address, contractIntf); | ||||
} | } | ||||
@Override | |||||
public <T> EventResult<T> result(ContractEventExecutor execute) { | |||||
return opFactory.result(execute); | |||||
} | |||||
@Override | @Override | ||||
public <T> T contract(String address, Class<T> contractIntf) { | public <T> T contract(String address, Class<T> contractIntf) { | ||||
return opFactory.contract(address, contractIntf); | return opFactory.contract(address, contractIntf); | ||||
@@ -19,10 +19,19 @@ public class TxResponseMessage implements TransactionResponse { | |||||
private TransactionState executionState; | private TransactionState executionState; | ||||
private OperationResult[] contractReturn; | |||||
private OperationResult[] operationResults; | |||||
public TxResponseMessage() { | public TxResponseMessage() { | ||||
} | } | ||||
// 重新包装operationResults | |||||
public TxResponseMessage(TransactionResponse transactionResponse, OperationResult[] operationResults) { | |||||
this.contentHash = transactionResponse.getContentHash(); | |||||
this.blockHash = transactionResponse.getBlockHash(); | |||||
this.blockHeight = transactionResponse.getBlockHeight(); | |||||
this.executionState = transactionResponse.getExecutionState(); | |||||
this.operationResults = operationResults; | |||||
} | |||||
public TxResponseMessage(HashDigest contentHash) { | public TxResponseMessage(HashDigest contentHash) { | ||||
this.contentHash = contentHash; | this.contentHash = contentHash; | ||||
@@ -60,8 +69,8 @@ public class TxResponseMessage implements TransactionResponse { | |||||
this.blockHeight = blockHeight; | this.blockHeight = blockHeight; | ||||
} | } | ||||
public void setContractReturn(OperationResult[] contractReturn) { | |||||
this.contractReturn = contractReturn; | |||||
public void setOperationResults(OperationResult[] operationResults) { | |||||
this.operationResults = operationResults; | |||||
} | } | ||||
@Override | @Override | ||||
@@ -70,8 +79,8 @@ public class TxResponseMessage implements TransactionResponse { | |||||
} | } | ||||
@Override | @Override | ||||
public OperationResult[] getContractReturn() { | |||||
return contractReturn; | |||||
public OperationResult[] getOperationResults() { | |||||
return operationResults; | |||||
} | } | ||||
} | } |
@@ -1,20 +1,27 @@ | |||||
package com.jd.blockchain.transaction; | package com.jd.blockchain.transaction; | ||||
import com.jd.blockchain.contract.EventResult; | |||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.ledger.PreparedTransaction; | import com.jd.blockchain.ledger.PreparedTransaction; | ||||
import com.jd.blockchain.ledger.TransactionRequestBuilder; | import com.jd.blockchain.ledger.TransactionRequestBuilder; | ||||
import com.jd.blockchain.ledger.TransactionTemplate; | import com.jd.blockchain.ledger.TransactionTemplate; | ||||
import com.jd.blockchain.utils.Bytes; | import com.jd.blockchain.utils.Bytes; | ||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
public class TxTemplate implements TransactionTemplate { | public class TxTemplate implements TransactionTemplate { | ||||
private TxBuilder txBuilder; | private TxBuilder txBuilder; | ||||
private TransactionService txService; | private TransactionService txService; | ||||
private Map<Integer, EventResult> eventResults; | |||||
public TxTemplate(HashDigest ledgerHash, TransactionService txService) { | public TxTemplate(HashDigest ledgerHash, TransactionService txService) { | ||||
this.txBuilder = new TxBuilder(ledgerHash); | this.txBuilder = new TxBuilder(ledgerHash); | ||||
this.txService = txService; | this.txService = txService; | ||||
this.eventResults = new HashMap<>(); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -25,7 +32,7 @@ public class TxTemplate implements TransactionTemplate { | |||||
@Override | @Override | ||||
public PreparedTransaction prepare() { | public PreparedTransaction prepare() { | ||||
TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); | ||||
return new PreparedTx(txReqBuilder, txService); | |||||
return new PreparedTx(txReqBuilder, txService, eventResults); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -62,7 +69,16 @@ public class TxTemplate implements TransactionTemplate { | |||||
public <T> T contract(Bytes address, Class<T> contractIntf) { | public <T> T contract(Bytes address, Class<T> contractIntf) { | ||||
return txBuilder.contract(address, contractIntf); | return txBuilder.contract(address, contractIntf); | ||||
} | } | ||||
@Override | |||||
public <T> EventResult<T> result(ContractEventExecutor execute) { | |||||
EventResult<T> eventResult = txBuilder.result(execute); | |||||
if (eventResult != null) { | |||||
eventResults.put(eventResult.opIndex(), eventResult); | |||||
} | |||||
return eventResult; | |||||
} | |||||
@Override | @Override | ||||
public <T> T contract(String address, Class<T> contractIntf) { | public <T> T contract(String address, Class<T> contractIntf) { | ||||
return txBuilder.contract(address, contractIntf); | return txBuilder.contract(address, contractIntf); | ||||
@@ -310,8 +310,8 @@ public class ConsensusMessageDispatcher implements MessageHandle { | |||||
} | } | ||||
@Override | @Override | ||||
public OperationResult[] getContractReturn() { | |||||
return txResp.getContractReturn(); | |||||
public OperationResult[] getOperationResults() { | |||||
return txResp.getOperationResults(); | |||||
} | } | ||||
} | } | ||||
@@ -24,6 +24,10 @@ import java.util.Random; | |||||
import java.util.concurrent.CountDownLatch; | import java.util.concurrent.CountDownLatch; | ||||
import java.util.concurrent.atomic.AtomicLong; | import java.util.concurrent.atomic.AtomicLong; | ||||
import com.jd.blockchain.contract.EventResult; | |||||
import com.jd.blockchain.contract.ReadContract; | |||||
import com.jd.blockchain.ledger.*; | |||||
import com.jd.blockchain.transaction.ContractEventExecutor; | |||||
import org.apache.commons.io.FileUtils; | import org.apache.commons.io.FileUtils; | ||||
import org.springframework.core.io.ClassPathResource; | import org.springframework.core.io.ClassPathResource; | ||||
@@ -31,16 +35,6 @@ import com.jd.blockchain.binaryproto.DataContractRegistry; | |||||
import com.jd.blockchain.crypto.AddressEncoding; | import com.jd.blockchain.crypto.AddressEncoding; | ||||
import com.jd.blockchain.crypto.AsymmetricKeypair; | import com.jd.blockchain.crypto.AsymmetricKeypair; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||||
import com.jd.blockchain.ledger.KVDataEntry; | |||||
import com.jd.blockchain.ledger.LedgerBlock; | |||||
import com.jd.blockchain.ledger.LedgerInitOperation; | |||||
import com.jd.blockchain.ledger.PreparedTransaction; | |||||
import com.jd.blockchain.ledger.TransactionResponse; | |||||
import com.jd.blockchain.ledger.TransactionState; | |||||
import com.jd.blockchain.ledger.TransactionTemplate; | |||||
import com.jd.blockchain.ledger.UserRegisterOperation; | |||||
import com.jd.blockchain.ledger.core.LedgerRepository; | import com.jd.blockchain.ledger.core.LedgerRepository; | ||||
import com.jd.blockchain.ledger.core.impl.LedgerManager; | import com.jd.blockchain.ledger.core.impl.LedgerManager; | ||||
import com.jd.blockchain.sdk.BlockchainService; | import com.jd.blockchain.sdk.BlockchainService; | ||||
@@ -446,9 +440,9 @@ public class IntegrationBase { | |||||
// 合约测试使用的初始化数据; | // 合约测试使用的初始化数据; | ||||
static BlockchainKeypair contractDataKey = BlockchainKeyGenerator.getInstance().generate(); | static BlockchainKeypair contractDataKey = BlockchainKeyGenerator.getInstance().generate(); | ||||
static BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | static BlockchainKeypair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); | ||||
// 保存资产总数的键; | |||||
// 第二个参数; | |||||
private static String contractZipName = "contract.jar"; | |||||
// 保存资产总数的键; | |||||
// 第二个参数; | |||||
private static String contractZipName = "contract-read.jar"; | |||||
static HashDigest txContentHash; | static HashDigest txContentHash; | ||||
public static LedgerBlock testSDK_Contract(AsymmetricKeypair adminKey, HashDigest ledgerHash, | public static LedgerBlock testSDK_Contract(AsymmetricKeypair adminKey, HashDigest ledgerHash, | ||||
@@ -484,7 +478,7 @@ public class IntegrationBase { | |||||
// execute the contract; | // execute the contract; | ||||
// testContractExe(adminKey, ledgerHash, keyPairResponse.keyPair, blockchainService, ledgerRepository); | // testContractExe(adminKey, ledgerHash, keyPairResponse.keyPair, blockchainService, ledgerRepository); | ||||
// testContractExe1(adminKey, ledgerHash, keyPairResponse.keyPair, blockchainService, ledgerRepository); | // testContractExe1(adminKey, ledgerHash, keyPairResponse.keyPair, blockchainService, ledgerRepository); | ||||
testExeReadContract(adminKey, ledgerHash, blockchainService); | |||||
return block; | return block; | ||||
} | } | ||||
@@ -545,6 +539,91 @@ public class IntegrationBase { | |||||
// assertEquals(888L,kvDataEntries[1].getValue()); | // assertEquals(888L,kvDataEntries[1].getValue()); | ||||
// } | // } | ||||
private static void testExeReadContract(AsymmetricKeypair adminKey, HashDigest ledgerHash, BlockchainService blockchainService) { | |||||
// 首先注册一个数据账户 | |||||
BlockchainKeypair newDataAccount = BlockchainKeyGenerator.getInstance().generate(); | |||||
TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||||
txTpl.dataAccounts().register(newDataAccount.getIdentity()); | |||||
PreparedTransaction ptx = txTpl.prepare(); | |||||
ptx.sign(adminKey); | |||||
// 提交并等待共识返回; | |||||
ptx.commit(); | |||||
// 再提交一个KV写入 | |||||
String key1 = "JingDong", value1 = "www.jd.com"; | |||||
String key2 = "JD", value2 = "JingDong"; | |||||
TransactionTemplate txKv = blockchainService.newTransaction(ledgerHash); | |||||
txKv.dataAccount(newDataAccount.getAddress()).setText(key1, value1, -1).setText(key2, value2, -1); | |||||
PreparedTransaction kvPtx = txKv.prepare(); | |||||
kvPtx.sign(adminKey); | |||||
// 提交并等待共识返回; | |||||
kvPtx.commit(); | |||||
// 下面才是执行Read交易 | |||||
// 定义交易; | |||||
TransactionTemplate txContract = blockchainService.newTransaction(ledgerHash); | |||||
ReadContract readContract1 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | |||||
EventResult<String> read1 = txContract.result((ContractEventExecutor<ReadContract>) () -> { | |||||
readContract1.read(newDataAccount.getAddress().toBase58(), key1); | |||||
return readContract1; | |||||
}); | |||||
ReadContract readContract2 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | |||||
EventResult<String> read2 = txContract.result((ContractEventExecutor<ReadContract>) () -> { | |||||
readContract2.read(newDataAccount.getAddress().toBase58(), key2); | |||||
return readContract2; | |||||
}); | |||||
ReadContract readContract3 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | |||||
EventResult<Long> read3 = txContract.result((ContractEventExecutor<ReadContract>) () -> { | |||||
readContract3.readVersion(newDataAccount.getAddress().toBase58(), key2); | |||||
return readContract3; | |||||
}); | |||||
// 签名; | |||||
PreparedTransaction contractPtx = txContract.prepare(); | |||||
contractPtx.sign(adminKey); | |||||
// 提交并等待共识返回; | |||||
TransactionResponse readTxResp = contractPtx.commit(); | |||||
OperationResult[] operationResults = readTxResp.getOperationResults(); | |||||
// 通过EventResult获取结果 | |||||
System.out.printf("readContract1.result = %s \r\n", read1.get()); | |||||
System.out.printf("readContract2.result = %s \r\n", read2.get()); | |||||
System.out.printf("readContract3.result = %s \r\n", read3.get()); | |||||
// 打印结果 | |||||
// for (OperationResult or : operationResults) { | |||||
// System.out.printf("操作[%s].Result = %s \r\n", or.getIndex(), or.getResult()); | |||||
// } | |||||
// | |||||
// // 验证结果 | |||||
// assertNotNull(contractReturn); | |||||
// assertEquals(contractReturn.length, 3); | |||||
// | |||||
// String returnVal1 = contractReturn[0]; | |||||
// assertEquals(value1, returnVal1); | |||||
// | |||||
// String returnVal2 = contractReturn[1]; | |||||
// assertEquals(value2, returnVal2); | |||||
// | |||||
// String returnVal3 = contractReturn[2]; | |||||
// assertEquals("0", returnVal3); | |||||
} | |||||
/** | /** | ||||
* 根据合约构建字节数组; | * 根据合约构建字节数组; | ||||
* | * | ||||
@@ -19,32 +19,7 @@ import com.jd.blockchain.crypto.PubKey; | |||||
import com.jd.blockchain.crypto.service.classic.ClassicAlgorithm; | import com.jd.blockchain.crypto.service.classic.ClassicAlgorithm; | ||||
import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; | import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; | ||||
import com.jd.blockchain.crypto.service.sm.SMCryptoService; | import com.jd.blockchain.crypto.service.sm.SMCryptoService; | ||||
import com.jd.blockchain.ledger.AccountHeader; | |||||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||||
import com.jd.blockchain.ledger.ContractCodeDeployOperation; | |||||
import com.jd.blockchain.ledger.ContractEventSendOperation; | |||||
import com.jd.blockchain.ledger.DataAccountKVSetOperation; | |||||
import com.jd.blockchain.ledger.DataAccountRegisterOperation; | |||||
import com.jd.blockchain.ledger.EndpointRequest; | |||||
import com.jd.blockchain.ledger.KVDataEntry; | |||||
import com.jd.blockchain.ledger.KVInfoVO; | |||||
import com.jd.blockchain.ledger.LedgerBlock; | |||||
import com.jd.blockchain.ledger.LedgerInfo; | |||||
import com.jd.blockchain.ledger.LedgerMetadata; | |||||
import com.jd.blockchain.ledger.LedgerTransaction; | |||||
import com.jd.blockchain.ledger.NodeRequest; | |||||
import com.jd.blockchain.ledger.Operation; | |||||
import com.jd.blockchain.ledger.ParticipantNode; | |||||
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.UserInfo; | |||||
import com.jd.blockchain.ledger.UserRegisterOperation; | |||||
import com.jd.blockchain.ledger.*; | |||||
import com.jd.blockchain.ledger.core.CryptoConfig; | import com.jd.blockchain.ledger.core.CryptoConfig; | ||||
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; | ||||
@@ -427,15 +402,16 @@ public class MockerNodeContext implements BlockchainQueryService { | |||||
return reqBuilder.buildRequest(); | return reqBuilder.buildRequest(); | ||||
} | } | ||||
public void txProcess(TransactionRequest txRequest) { | |||||
public OperationResult[] txProcess(TransactionRequest txRequest) { | |||||
LedgerEditor newEditor = ledgerRepository.createNextBlock(); | LedgerEditor newEditor = ledgerRepository.createNextBlock(); | ||||
LedgerBlock latestBlock = ledgerRepository.getLatestBlock(); | LedgerBlock latestBlock = ledgerRepository.getLatestBlock(); | ||||
LedgerDataSet previousDataSet = ledgerRepository.getDataSet(latestBlock); | LedgerDataSet previousDataSet = ledgerRepository.getDataSet(latestBlock); | ||||
TransactionBatchProcessor txProc = new TransactionBatchProcessor(newEditor, previousDataSet, opHandler, | TransactionBatchProcessor txProc = new TransactionBatchProcessor(newEditor, previousDataSet, opHandler, | ||||
ledgerManager); | ledgerManager); | ||||
txProc.schedule(txRequest); | |||||
TransactionResponse txResp = txProc.schedule(txRequest); | |||||
TransactionBatchResultHandle handle = txProc.prepare(); | TransactionBatchResultHandle handle = txProc.prepare(); | ||||
handle.commit(); | handle.commit(); | ||||
return txResp.getOperationResults(); | |||||
} | } | ||||
private LedgerRepository registerLedger(HashDigest ledgerHash, DBConnectionConfig dbConnConf) { | private LedgerRepository registerLedger(HashDigest ledgerHash, DBConnectionConfig dbConnConf) { | ||||
@@ -10,5 +10,5 @@ public interface WriteContract { | |||||
void print(String name); | void print(String name); | ||||
@ContractEvent(name = "writeKv") | @ContractEvent(name = "writeKv") | ||||
void writeKv(String address, String key, String value); | |||||
String writeKv(String address, String key, String value); | |||||
} | } |
@@ -15,8 +15,9 @@ public class WriteContractImpl implements EventProcessingAware, WriteContract { | |||||
} | } | ||||
@Override | @Override | ||||
public void writeKv(String address, String key, String value) { | |||||
public String writeKv(String address, String key, String value) { | |||||
eventContext.getLedger().dataAccount(address).setText(key, value, -1); | eventContext.getLedger().dataAccount(address).setText(key, value, -1); | ||||
return String.format("address = %s, key = %s, value = %s, version = %s", address, key, value, 0); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -1,7 +1,12 @@ | |||||
package com.jd.blockchain.mocker.handler; | package com.jd.blockchain.mocker.handler; | ||||
import java.util.Map; | |||||
import java.util.Set; | |||||
import java.util.concurrent.ConcurrentHashMap; | |||||
import com.jd.blockchain.contract.ContractEventContext; | import com.jd.blockchain.contract.ContractEventContext; | ||||
import com.jd.blockchain.contract.ContractException; | import com.jd.blockchain.contract.ContractException; | ||||
import com.jd.blockchain.contract.ContractSerializeUtils; | |||||
import com.jd.blockchain.contract.EventProcessingAware; | import com.jd.blockchain.contract.EventProcessingAware; | ||||
import com.jd.blockchain.contract.LedgerContext; | import com.jd.blockchain.contract.LedgerContext; | ||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
@@ -9,17 +14,16 @@ import com.jd.blockchain.ledger.BlockchainIdentity; | |||||
import com.jd.blockchain.ledger.ContractEventSendOperation; | import com.jd.blockchain.ledger.ContractEventSendOperation; | ||||
import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
import com.jd.blockchain.ledger.TransactionRequest; | import com.jd.blockchain.ledger.TransactionRequest; | ||||
import com.jd.blockchain.ledger.core.*; | |||||
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.LedgerManager; | import com.jd.blockchain.ledger.core.impl.LedgerManager; | ||||
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 com.jd.blockchain.ledger.core.impl.handles.ContractLedgerContext; | import com.jd.blockchain.ledger.core.impl.handles.ContractLedgerContext; | ||||
import com.jd.blockchain.mocker.proxy.ExecutorProxy; | import com.jd.blockchain.mocker.proxy.ExecutorProxy; | ||||
import java.util.Map; | |||||
import java.util.Set; | |||||
import java.util.concurrent.ConcurrentHashMap; | |||||
public class MockerContractExeHandle implements OperationHandle { | public class MockerContractExeHandle implements OperationHandle { | ||||
private Map<HashDigest, ExecutorProxy> executorProxyMap = new ConcurrentHashMap<>(); | private Map<HashDigest, ExecutorProxy> executorProxyMap = new ConcurrentHashMap<>(); | ||||
@@ -37,6 +41,7 @@ public class MockerContractExeHandle implements OperationHandle { | |||||
ExecutorProxy executorProxy = executorProxyMap.get(txHash); | ExecutorProxy executorProxy = executorProxyMap.get(txHash); | ||||
Object result = null; | |||||
if (executorProxy != null) { | if (executorProxy != null) { | ||||
LedgerQueryService queryService = new LedgerQueryService(ledgerManager); | LedgerQueryService queryService = new LedgerQueryService(ledgerManager); | ||||
ContractLedgerContext ledgerContext = new ContractLedgerContext(queryService, opHandleContext); | ContractLedgerContext ledgerContext = new ContractLedgerContext(queryService, opHandleContext); | ||||
@@ -44,26 +49,38 @@ public class MockerContractExeHandle implements OperationHandle { | |||||
MockerContractEventContext contractEventContext = new MockerContractEventContext(ledgerHash, | MockerContractEventContext contractEventContext = new MockerContractEventContext(ledgerHash, | ||||
contractOP.getEvent(), requestContext.getRequest(), ledgerContext); | contractOP.getEvent(), requestContext.getRequest(), ledgerContext); | ||||
EventProcessingAware eventProcessingAwire = (EventProcessingAware) executorProxy.getInstance(); | |||||
try { | |||||
// | |||||
// Before处理过程 | |||||
eventProcessingAwire.beforeEvent(contractEventContext); | |||||
executorProxy.invoke(); | |||||
Object instance = executorProxy.getInstance(); | |||||
EventProcessingAware awire = null; | |||||
if (instance instanceof EventProcessingAware) { | |||||
awire = (EventProcessingAware) instance; | |||||
awire.beforeEvent(contractEventContext); | |||||
} | |||||
// After处理过程 | |||||
eventProcessingAwire.postEvent(contractEventContext, null); | |||||
try { | |||||
result = executorProxy.invoke(); | |||||
if (awire != null) { | |||||
// After处理过程 | |||||
awire.postEvent(contractEventContext, null); | |||||
} | |||||
} catch (Exception e) { | } catch (Exception e) { | ||||
eventProcessingAwire.postEvent(contractEventContext, new ContractException(e.getMessage())); | |||||
if (awire != null) { | |||||
awire.postEvent(contractEventContext, new ContractException(e.getMessage())); | |||||
} | |||||
} finally { | } finally { | ||||
removeExecutorProxy(txHash); | removeExecutorProxy(txHash); | ||||
} | } | ||||
} | } | ||||
// No return value; | // No return value; | ||||
return null; | |||||
return ContractSerializeUtils.serialize(result); | |||||
} | } | ||||
// @Override | |||||
// public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||||
// return null; | |||||
// } | |||||
@Override | @Override | ||||
public boolean support(Class<?> operationType) { | public boolean support(Class<?> operationType) { | ||||
return ContractEventSendOperation.class.isAssignableFrom(operationType); | return ContractEventSendOperation.class.isAssignableFrom(operationType); | ||||
@@ -2,8 +2,11 @@ package com.jd.blockchain.mocker.proxy; | |||||
import com.jd.blockchain.contract.Contract; | import com.jd.blockchain.contract.Contract; | ||||
import com.jd.blockchain.contract.ContractEvent; | import com.jd.blockchain.contract.ContractEvent; | ||||
import com.jd.blockchain.contract.ContractSerializeUtils; | |||||
import com.jd.blockchain.crypto.HashDigest; | import com.jd.blockchain.crypto.HashDigest; | ||||
import com.jd.blockchain.ledger.BlockchainIdentity; | import com.jd.blockchain.ledger.BlockchainIdentity; | ||||
import com.jd.blockchain.ledger.OperationResult; | |||||
import com.jd.blockchain.ledger.OperationResultData; | |||||
import com.jd.blockchain.ledger.TransactionRequest; | import com.jd.blockchain.ledger.TransactionRequest; | ||||
import com.jd.blockchain.mocker.MockerNodeContext; | import com.jd.blockchain.mocker.MockerNodeContext; | ||||
import com.jd.blockchain.mocker.handler.MockerContractExeHandle; | import com.jd.blockchain.mocker.handler.MockerContractExeHandle; | ||||
@@ -68,9 +71,14 @@ public class ContractProxy<T> implements InvocationHandler { | |||||
operationHandle.registerExecutorProxy(txHash, new ExecutorProxy(instance, method, args)); | operationHandle.registerExecutorProxy(txHash, new ExecutorProxy(instance, method, args)); | ||||
// 提交该请求至整个区块链系统 | // 提交该请求至整个区块链系统 | ||||
mockerNodeContext.txProcess(txRequest); | |||||
// 不处理返回值 | |||||
return null; | |||||
OperationResult[] operationResults = mockerNodeContext.txProcess(txRequest); | |||||
if (operationResults == null || operationResults.length == 0) { | |||||
return null; | |||||
} | |||||
OperationResult opResult = operationResults[0]; | |||||
// 处理返回值 | |||||
return ContractSerializeUtils.resolve(opResult.getResult()); | |||||
} | } | ||||
private boolean isExecuteContractMethod(Method method) { | private boolean isExecuteContractMethod(Method method) { | ||||
@@ -10,7 +10,7 @@ import org.junit.Test; | |||||
import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||
public class SampleTest_ { | |||||
public class SampleTest { | |||||
MockerNodeContext mockerNodeContext = null; | MockerNodeContext mockerNodeContext = null; | ||||
@@ -33,7 +33,9 @@ public class SampleTest_ { | |||||
writeContract = mockerNodeContext.deployContract(writeContract); | writeContract = mockerNodeContext.deployContract(writeContract); | ||||
writeContract.writeKv(dataAccountAddress, key, value); | |||||
String result = writeContract.writeKv(dataAccountAddress, key, value); | |||||
System.out.println(result); | |||||
// 查询结果 | // 查询结果 | ||||
KVDataEntry[] dataEntries = mockerNodeContext.getDataEntries(ledgerHash, dataAccountAddress, key); | KVDataEntry[] dataEntries = mockerNodeContext.getDataEntries(ledgerHash, dataAccountAddress, key); |