@@ -51,6 +51,8 @@ public interface DataCodes { | |||
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_INIT_SETTING = 0x610; | |||
@@ -81,6 +83,26 @@ public interface DataCodes { | |||
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 HASH = 0xB00; | |||
@@ -3,6 +3,9 @@ package com.jd.blockchain.contract.engine; | |||
import com.jd.blockchain.contract.ContractEventContext; | |||
import com.jd.blockchain.utils.Bytes; | |||
import java.util.concurrent.CompletableFuture; | |||
import java.util.concurrent.Future; | |||
public interface ContractCode { | |||
Bytes getAddress(); | |||
@@ -2,7 +2,6 @@ package com.jd.blockchain.contract.jvm; | |||
import com.jd.blockchain.contract.engine.ContractCode; | |||
import com.jd.blockchain.contract.engine.ContractEngine; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.runtime.Module; | |||
import com.jd.blockchain.runtime.RuntimeContext; | |||
import com.jd.blockchain.utils.Bytes; | |||
@@ -3,6 +3,7 @@ package com.jd.blockchain.contract.jvm; | |||
import java.lang.reflect.Method; | |||
import java.util.concurrent.Callable; | |||
import com.jd.blockchain.contract.ContractSerializeUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.util.ReflectionUtils; | |||
@@ -91,9 +92,8 @@ public class JavaContractCode implements ContractCode { | |||
address.toString(), contractClass.getName(), eventContext.getEvent())); | |||
} | |||
Object[] args = resolveArgs(); | |||
Object[] args = resolveArgs(eventContext.getArgs()); | |||
retn = ReflectionUtils.invokeMethod(handleMethod, contractInstance, args); | |||
} catch (Exception e) { | |||
error = e; | |||
} | |||
@@ -115,16 +115,11 @@ public class JavaContractCode implements ContractCode { | |||
} | |||
private byte[] resolveResult(Object retn) { | |||
if (retn == null) { | |||
return null; | |||
} | |||
// TODO: resolve result in bytes; | |||
return null; | |||
return ContractSerializeUtils.serialize(retn); | |||
} | |||
private Object[] resolveArgs() { | |||
// TODO Auto-generated method stub | |||
throw new IllegalStateException("Not implemented!"); | |||
private Object[] resolveArgs(byte[] argBytes) { | |||
return ContractSerializeUtils.resolveArray(argBytes); | |||
} | |||
} | |||
@@ -1,6 +1,5 @@ | |||
package com.jd.blockchain.contract; | |||
import com.jd.blockchain.ledger.ContractBizContent; | |||
import com.jd.blockchain.utils.Bytes; | |||
/** | |||
@@ -1,6 +1,5 @@ | |||
package com.jd.blockchain.contract; | |||
import com.jd.blockchain.ledger.ContractBizContent; | |||
import com.jd.blockchain.ledger.KVDataEntry; | |||
import com.jd.blockchain.ledger.KVDataObject; | |||
import com.jd.blockchain.utils.Bytes; | |||
@@ -162,8 +162,10 @@ public class AccountSet implements Transactional, MerkleProvable { | |||
* | |||
* 只有最新版本的账户才能可写的,其它都是只读; | |||
* | |||
* @param address 账户地址; | |||
* @param version 账户版本;如果指定为 -1,则返回最新版本; | |||
* @param address | |||
* 账户地址; | |||
* @param version | |||
* 账户版本;如果指定为 -1,则返回最新版本; | |||
* @return | |||
*/ | |||
public BaseAccount getAccount(Bytes address, long version) { | |||
@@ -57,7 +57,7 @@ public class DataAccount implements AccountHeader, MerkleProvable { | |||
BytesValue bytesValue = BytesValueEntry.fromBytes(value); | |||
return baseAccount.setBytes(key, bytesValue, version); | |||
} | |||
/** | |||
* Return the latest version entry associated the specified key; If the key | |||
* doesn't exist, then return -1; | |||
@@ -1,10 +1,8 @@ | |||
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(); | |||
/** | |||
* 提交对账本数据的修改,以指定的交易状态提交交易; | |||
* | |||
* @param txResult | |||
* | |||
* @return | |||
*/ | |||
LedgerTransaction commit(TransactionState txResult); | |||
/** | |||
* 提交对账本数据的修改,以指定的交易状态提交交易; | |||
* | |||
* @param txResult | |||
* @param returnMessage | |||
* @param operationResults | |||
* | |||
* @return | |||
*/ | |||
LedgerTransaction commit(TransactionState txResult, OperationResult... opResults); | |||
LedgerTransaction commit(TransactionState txResult, List<OperationResult> operationResults); | |||
/** | |||
* 抛弃对账本数据的修改,以指定的交易状态提交交易;<br> | |||
* | |||
* 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState, TransactionReturnMessage)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; | |||
* | |||
* | |||
* 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; | |||
* | |||
* @param txResult | |||
* @return | |||
*/ | |||
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.core.impl.OperationHandleContext; | |||
import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||
public interface OperationHandle { | |||
@@ -14,8 +16,9 @@ public interface OperationHandle { | |||
boolean support(Class<?> operationType); | |||
/** | |||
* 解析和执行操作; | |||
* | |||
* 同步解析和执行操作; | |||
* | |||
* | |||
* @param op | |||
* 操作实例; | |||
* @param newBlockDataset | |||
@@ -24,8 +27,28 @@ public interface OperationHandle { | |||
* 交易请求上下文; | |||
* @param previousBlockDataset | |||
* 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | |||
* | |||
* @return 操作执行结果 | |||
*/ | |||
byte[] process(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, | |||
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]; | |||
long ver; | |||
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) { | |||
entries[i] = new KVDataObject(keys[i], -1, null); | |||
entries[i] = new KVDataObject(currKey, -1, null); | |||
} 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; | |||
import java.util.List; | |||
import java.util.Stack; | |||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
@@ -348,9 +349,14 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||
public TransactionRequest getRequestTX() { | |||
return txRequest; | |||
} | |||
@Override | |||
public LedgerTransaction commit(TransactionState txResult) { | |||
return commit(txResult, null); | |||
} | |||
@Override | |||
public LedgerTransaction commit(TransactionState txResult, OperationResult... opResults) { | |||
public LedgerTransaction commit(TransactionState txResult, List<OperationResult> operationResults) { | |||
checkTxState(); | |||
// capture snapshot | |||
@@ -359,7 +365,8 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||
// LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | |||
// 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.commit(); | |||
@@ -374,9 +381,14 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||
committed = true; | |||
return tx; | |||
} | |||
@Override | |||
public LedgerTransaction discardAndCommit(TransactionState txResult) { | |||
return discardAndCommit(txResult, null); | |||
} | |||
@Override | |||
public LedgerTransaction discardAndCommit(TransactionState txResult, List<OperationResult> operationResults) { | |||
checkTxState(); | |||
// 未处理 | |||
@@ -385,7 +397,7 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||
// TransactionStagedSnapshot txDataSnapshot = takeSnapshot(); | |||
// LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, | |||
// 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.commit(); | |||
@@ -410,6 +422,15 @@ public class LedgerTransactionalEditor implements LedgerEditor { | |||
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 | |||
public void rollback() { | |||
if (this.rollbacked) { | |||
@@ -50,9 +50,12 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
private TransactionBatchResult batchResult; | |||
/** | |||
* @param newBlockEditor 新区块的数据编辑器; | |||
* @param previousBlockDataset 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | |||
* @param opHandles 操作处理对象注册表; | |||
* @param newBlockEditor | |||
* 新区块的数据编辑器; | |||
* @param previousBlockDataset | |||
* 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | |||
* @param opHandles | |||
* 操作处理对象注册表; | |||
*/ | |||
public TransactionBatchProcessor(LedgerEditor newBlockEditor, LedgerDataSet previousBlockDataset, | |||
OperationHandleRegisteration opHandles, LedgerService ledgerService) { | |||
@@ -73,9 +76,12 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
public TransactionResponse schedule(TransactionRequest request) { | |||
// 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | |||
LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | |||
TransactionState txResult; | |||
OperationResult[] opResults = null; | |||
TransactionState result; | |||
List<OperationResult> operationResults = new ArrayList<>(); | |||
try { | |||
LedgerDataSet dataset = txCtx.getDataSet(); | |||
TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); | |||
// TODO: 验证签名者的有效性; | |||
@@ -95,44 +101,44 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
OperationHandleContext handleContext = new OperationHandleContext() { | |||
@Override | |||
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()); | |||
hdl.process(operation, dataset, reqCtx, previousBlockDataset, this, ledgerService); | |||
} | |||
}; | |||
OperationHandle opHandle; | |||
int contractOpIndex = 0; | |||
List<OperationResult> opResultList = new ArrayList<OperationResult>(); | |||
int opIdx = 0; | |||
int opIndex = 0; | |||
for (Operation op : ops) { | |||
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) { | |||
// 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", | |||
request.getHash().toBase58(), e.getMessage()), 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", | |||
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); | |||
@@ -213,12 +219,11 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
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.result = result; | |||
this.opResults = opResults; | |||
} | |||
@Override | |||
@@ -247,8 +252,12 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
} | |||
@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; | |||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
import com.jd.blockchain.ledger.ContractCodeDeployOperation; | |||
import com.jd.blockchain.ledger.Operation; | |||
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.impl.OperationHandleContext; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||
import org.springframework.stereotype.Service; | |||
import java.util.concurrent.Future; | |||
@Service | |||
public class ContractCodeDeployOperationHandle implements OperationHandle { | |||
@@ -20,9 +25,15 @@ public class ContractCodeDeployOperationHandle implements OperationHandle { | |||
// TODO: 校验合约代码的正确性; | |||
// TODO: 请求者应该提供合约账户的公钥签名,已确定注册的地址的唯一性; | |||
dataset.getContractAccountSet().deploy(contractOP.getContractID().getAddress(), | |||
contractOP.getContractID().getPubKey(), contractOP.getAddressSignature(), contractOP.getChainCode()); | |||
//No result; | |||
return null; | |||
} | |||
@Override | |||
public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||
return null; | |||
} | |||
@@ -2,8 +2,8 @@ package com.jd.blockchain.ledger.core.impl.handles; | |||
import static com.jd.blockchain.utils.BaseConstant.CONTRACT_SERVICE_PROVIDER; | |||
import java.util.concurrent.CompletableFuture; | |||
import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||
import org.springframework.stereotype.Service; | |||
import com.jd.blockchain.contract.LocalContractEventContext; | |||
@@ -76,4 +76,9 @@ public class ContractEventSendOperationHandle implements OperationHandle { | |||
return contractCode.processEvent(localContractEventContext); | |||
} | |||
@Override | |||
public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||
return null; | |||
} | |||
} |
@@ -1,5 +1,7 @@ | |||
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 com.jd.blockchain.binaryproto.DataContractRegistry; | |||
@@ -26,12 +28,16 @@ public class DataAccountKVSetOperationHandle implements OperationHandle { | |||
LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||
DataAccountKVSetOperation kvWriteOp = (DataAccountKVSetOperation) op; | |||
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()); | |||
} | |||
return null; | |||
} | |||
// No return value; | |||
@Override | |||
public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||
return null; | |||
} | |||
@@ -9,6 +9,7 @@ import com.jd.blockchain.ledger.core.OperationHandle; | |||
import com.jd.blockchain.ledger.core.TransactionRequestContext; | |||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | |||
import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||
import org.springframework.stereotype.Service; | |||
@Service | |||
@@ -20,12 +21,16 @@ public class DataAccountRegisterOperationHandle implements OperationHandle { | |||
DataAccountRegisterOperation dataAccountRegOp = (DataAccountRegisterOperation) op; | |||
BlockchainIdentity bid = dataAccountRegOp.getAccountID(); | |||
// TODO: 校验用户身份; | |||
//TODO: 校验用户身份; | |||
// TODO: 请求者应该提供数据账户的公钥签名,已确定注册的地址的唯一性; | |||
//TODO: 请求者应该提供数据账户的公钥签名,已确定注册的地址的唯一性; | |||
dataset.getDataAccountSet().register(bid.getAddress(), bid.getPubKey(), null); | |||
// No return value; | |||
return null; | |||
} | |||
@Override | |||
public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||
return null; | |||
} | |||
@@ -8,17 +8,29 @@ 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.OperationHandleContext; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||
public class UserRegisterOperationHandle implements OperationHandle { | |||
@Override | |||
public byte[] process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, | |||
LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||
UserRegisterOperation userRegOp = (UserRegisterOperation) op; | |||
BlockchainIdentity bid = userRegOp.getUserID(); | |||
dataset.getUserAccountSet().register(bid.getAddress(), bid.getPubKey()); | |||
// No return value; | |||
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; | |||
} | |||
@@ -0,0 +1,149 @@ | |||
package com.jd.blockchain.contract; | |||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
import com.jd.blockchain.binaryproto.DataContract; | |||
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(Integer.class, WRAP_INT.class); | |||
MAP.put(Long.class, WRAP_LONG.class); | |||
MAP.put(String.class, 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; | |||
// } | |||
//} |
@@ -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(); | |||
} |
@@ -5,13 +5,14 @@ import com.jd.blockchain.binaryproto.DataField; | |||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||
import com.jd.blockchain.consts.DataCodes; | |||
@DataContract(code = DataCodes.CONTRACT_RETURN) | |||
@DataContract(code = DataCodes.TX_OP_RESULT) | |||
public interface OperationResult { | |||
@DataField(order = 1, primitiveType = PrimitiveType.INT32) | |||
int getIndex(); | |||
@DataField(order=1, primitiveType = PrimitiveType.INT32) | |||
int getIndex(); | |||
@DataField(order = 2, primitiveType = PrimitiveType.BYTES) | |||
byte[] getResult(); | |||
@DataField(order=2, primitiveType = PrimitiveType.BYTES) | |||
byte[] getResult(); | |||
<T> T getResultData(); | |||
} |
@@ -1,26 +1,45 @@ | |||
package com.jd.blockchain.ledger; | |||
import com.jd.blockchain.contract.ContractSerializeUtils; | |||
public class OperationResultData implements OperationResult { | |||
private int index; | |||
private int index; | |||
private byte[] result; | |||
public OperationResultData() { | |||
} | |||
public OperationResultData(OperationResult operationResult) { | |||
this(operationResult.getIndex(), operationResult.getResult()); | |||
} | |||
public OperationResultData(int index, byte[] result) { | |||
this.index = index; | |||
this.result = result; | |||
} | |||
private byte[] result; | |||
@Override | |||
public int getIndex() { | |||
return index; | |||
} | |||
public OperationResultData() { | |||
} | |||
@Override | |||
public byte[] getResult() { | |||
return result; | |||
} | |||
public OperationResultData(int operationIndex, byte[] result) { | |||
this.index = operationIndex; | |||
this.result = result; | |||
} | |||
@Override | |||
public <T> T getResultData() { | |||
return (T) ContractSerializeUtils.resolve(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 | |||
*/ | |||
@DataField(order = 4, refContract = true, list = true) | |||
@DataField(order=4, list = true, refContract=true) | |||
OperationResult[] getOperationResults(); | |||
} |
@@ -27,7 +27,7 @@ public class TransactionRespHandle implements TransactionResponse { | |||
private TransactionState globalResult; | |||
private OperationResult[] contractReturn; | |||
private OperationResult[] operationResults; | |||
public TransactionRespHandle(TransactionRequest request, TransactionState result, TransactionState globalResult) { | |||
this.request = request; | |||
@@ -51,8 +51,8 @@ public class TransactionRespHandle implements TransactionResponse { | |||
this.result = result; | |||
} | |||
public void setContractReturn(OperationResult[] contractReturn) { | |||
this.contractReturn = contractReturn; | |||
public void setOperationResults(OperationResult[] operationResults) { | |||
this.operationResults = operationResults; | |||
} | |||
public LedgerBlock getBlock() { | |||
@@ -97,7 +97,7 @@ public class TransactionRespHandle implements TransactionResponse { | |||
} | |||
@Override | |||
public OperationResult[] getContractReturn() { | |||
return contractReturn; | |||
public OperationResult[] getOperationResults() { | |||
return operationResults; | |||
} | |||
} |
@@ -63,6 +63,6 @@ public interface TransactionResponse { | |||
* | |||
* @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); | |||
} | |||
} |
@@ -3,6 +3,7 @@ package com.jd.blockchain.transaction; | |||
import java.lang.reflect.InvocationHandler; | |||
import java.lang.reflect.Method; | |||
import com.jd.blockchain.contract.ContractSerializeUtils; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.IllegalDataException; | |||
@@ -37,18 +38,14 @@ public class ContractInvocationProxy implements InvocationHandler { | |||
// hashCode 方法; | |||
} | |||
// 合约方法; | |||
byte[] argBytes = serializeArgs(args,method); | |||
byte[] argBytes = serializeArgs(args); | |||
sendOpBuilder.send(contractAddress, event, argBytes); | |||
// TODO: 暂时未考虑有返回值的情况; | |||
return null; | |||
} | |||
private byte[] serializeArgs(Object[] args, Method method) { | |||
if(args == null || args.length==0){ | |||
return null; | |||
} | |||
throw new IllegalStateException("Not implemented!"); | |||
//return ContractSerializeUtils.serializeMethodParam(args,contractType.getDataContractMap().get(method)); | |||
private byte[] serializeArgs(Object[] args) { | |||
return ContractSerializeUtils.serializeArray(args); | |||
} | |||
} |
@@ -35,9 +35,9 @@ public class ContractInvocationProxyBuilder { | |||
} | |||
// TODO 检查返回值类型; | |||
ContractType contractType1 = ContractType.resolve(contractIntf); | |||
contractTypes.put(contractIntf,contractType1); | |||
return contractType1; | |||
ContractType ct = ContractType.resolve(contractIntf); | |||
contractTypes.put(contractIntf, ct); | |||
return ct; | |||
} | |||
@@ -1,16 +1,14 @@ | |||
package com.jd.blockchain.transaction; | |||
import java.lang.annotation.Annotation; | |||
import java.lang.reflect.Method; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import com.jd.blockchain.contract.Contract; | |||
import com.jd.blockchain.contract.ContractEvent; | |||
import com.jd.blockchain.contract.ContractException; | |||
import com.jd.blockchain.contract.ContractSerializeUtils; | |||
import com.jd.blockchain.utils.IllegalDataException; | |||
public class ContractType { | |||
@@ -60,42 +58,41 @@ public class ContractType { | |||
private ContractType() { | |||
} | |||
public static ContractType resolve(Class<?> contractIntf){ | |||
ContractType contractType = new ContractType(); | |||
Annotation annotation = contractIntf.getDeclaredAnnotation(Contract.class); | |||
public static ContractType resolve(Class<?> contractIntf) { | |||
//contains: @Contract? | |||
boolean isContractType = annotation != null ? true : false; | |||
if(!isContractType){ | |||
throw new IllegalDataException("is not Contract Type, becaust there is not @Contract."); | |||
// 接口上必须有注解 | |||
if (!contractIntf.isAnnotationPresent(Contract.class)) { | |||
throw new IllegalDataException("It is not a Contract Type, because there is not @Contract !"); | |||
} | |||
//contractIntf contains @Contract and @ContractEvent; | |||
Method[] classMethods = contractIntf.getDeclaredMethods(); | |||
if (classMethods.length == 0) { | |||
throw new IllegalDataException("This interface have not any methods !"); | |||
} | |||
ContractType contractType = new ContractType(); | |||
for (Method method : classMethods) { | |||
// if current method contains @ContractEvent,then put it in this map; | |||
ContractEvent contractEvent = method.getAnnotation(ContractEvent.class); | |||
if (contractEvent != null) { | |||
String eventName_ = contractEvent.name(); | |||
String eventName = contractEvent.name(); | |||
//if annoMethodMap has contained the eventName, too many same eventNames exists probably, say NO! | |||
if(contractType.events.containsKey(eventName_)){ | |||
if(contractType.events.containsKey(eventName)){ | |||
throw new ContractException("there is repeat definition of contractEvent to @ContractEvent."); | |||
} | |||
//check param's type is fit for need. | |||
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())); | |||
} | |||
} | |||
// 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; | |||
@@ -7,12 +7,7 @@ import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.crypto.PrivKey; | |||
import com.jd.blockchain.crypto.SignatureDigest; | |||
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.*; | |||
public class PreparedTx implements PreparedTransaction { | |||
@@ -56,6 +51,17 @@ public class PreparedTx implements PreparedTransaction { | |||
TransactionRequest txReq = txReqBuilder.buildRequest(); | |||
// 发起交易请求; | |||
TransactionResponse txResponse = txProcessor.process(txReq); | |||
// 重新包装操作集合 | |||
OperationResult[] operationResults = txResponse.getOperationResults(); | |||
if (operationResults != null && operationResults.length > 0) { | |||
OperationResult[] wrapOpResults = new OperationResult[operationResults.length]; | |||
for (int i = 0; i < operationResults.length; i++) { | |||
wrapOpResults[i] = new OperationResultData(operationResults[i]); | |||
} | |||
return new TxResponseMessage(txResponse, wrapOpResults); | |||
} | |||
return txResponse; | |||
} | |||
} |
@@ -19,10 +19,19 @@ public class TxResponseMessage implements TransactionResponse { | |||
private TransactionState executionState; | |||
private OperationResult[] contractReturn; | |||
private OperationResult[] operationResults; | |||
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) { | |||
this.contentHash = contentHash; | |||
@@ -60,8 +69,8 @@ public class TxResponseMessage implements TransactionResponse { | |||
this.blockHeight = blockHeight; | |||
} | |||
public void setContractReturn(OperationResult[] contractReturn) { | |||
this.contractReturn = contractReturn; | |||
public void setOperationResults(OperationResult[] operationResults) { | |||
this.operationResults = operationResults; | |||
} | |||
@Override | |||
@@ -70,8 +79,8 @@ public class TxResponseMessage implements TransactionResponse { | |||
} | |||
@Override | |||
public OperationResult[] getContractReturn() { | |||
return contractReturn; | |||
public OperationResult[] getOperationResults() { | |||
return operationResults; | |||
} | |||
} |
@@ -310,8 +310,8 @@ public class ConsensusMessageDispatcher implements MessageHandle { | |||
} | |||
@Override | |||
public OperationResult[] getContractReturn() { | |||
return txResp.getContractReturn(); | |||
public OperationResult[] getOperationResults() { | |||
return txResp.getOperationResults(); | |||
} | |||
} | |||
@@ -24,6 +24,8 @@ import java.util.Random; | |||
import java.util.concurrent.CountDownLatch; | |||
import java.util.concurrent.atomic.AtomicLong; | |||
import com.jd.blockchain.contract.ReadContract; | |||
import com.jd.blockchain.ledger.*; | |||
import org.apache.commons.io.FileUtils; | |||
import org.springframework.core.io.ClassPathResource; | |||
@@ -31,16 +33,6 @@ import com.jd.blockchain.binaryproto.DataContractRegistry; | |||
import com.jd.blockchain.crypto.AddressEncoding; | |||
import com.jd.blockchain.crypto.AsymmetricKeypair; | |||
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.impl.LedgerManager; | |||
import com.jd.blockchain.sdk.BlockchainService; | |||
@@ -446,9 +438,9 @@ public class IntegrationBase { | |||
// 合约测试使用的初始化数据; | |||
static BlockchainKeypair contractDataKey = 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; | |||
public static LedgerBlock testSDK_Contract(AsymmetricKeypair adminKey, HashDigest ledgerHash, | |||
@@ -484,7 +476,7 @@ public class IntegrationBase { | |||
// execute the contract; | |||
// testContractExe(adminKey, ledgerHash, keyPairResponse.keyPair, blockchainService, ledgerRepository); | |||
// testContractExe1(adminKey, ledgerHash, keyPairResponse.keyPair, blockchainService, ledgerRepository); | |||
testExeReadContract(adminKey, ledgerHash, blockchainService); | |||
return block; | |||
} | |||
@@ -545,6 +537,73 @@ public class IntegrationBase { | |||
// 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); | |||
readContract1.read(newDataAccount.getAddress().toBase58(), key1); | |||
ReadContract readContract2 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | |||
readContract2.read(newDataAccount.getAddress().toBase58(), key2); | |||
ReadContract readContract3 = txContract.contract(contractDeployKey.getAddress(), ReadContract.class); | |||
readContract3.readVersion(newDataAccount.getAddress().toBase58(), key2); | |||
// 签名; | |||
PreparedTransaction contractPtx = txContract.prepare(); | |||
contractPtx.sign(adminKey); | |||
// 提交并等待共识返回; | |||
TransactionResponse readTxResp = contractPtx.commit(); | |||
OperationResult[] operationResults = readTxResp.getOperationResults(); | |||
// 打印结果 | |||
for (OperationResult or : operationResults) { | |||
System.out.printf("操作[%s].Result = %s \r\n", or.getIndex(), or.getResultData()); | |||
} | |||
// | |||
// // 验证结果 | |||
// 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); | |||
} | |||
/** | |||
* 根据合约构建字节数组; | |||
* | |||
@@ -15,6 +15,7 @@ import com.jd.blockchain.ledger.core.impl.LedgerQueryService; | |||
import com.jd.blockchain.ledger.core.impl.OperationHandleContext; | |||
import com.jd.blockchain.ledger.core.impl.handles.ContractLedgerContext; | |||
import com.jd.blockchain.mocker.proxy.ExecutorProxy; | |||
import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||
import java.util.Map; | |||
import java.util.Set; | |||
@@ -64,6 +65,11 @@ public class MockerContractExeHandle implements OperationHandle { | |||
return null; | |||
} | |||
@Override | |||
public AsyncFuture<byte[]> asyncProcess(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { | |||
return null; | |||
} | |||
@Override | |||
public boolean support(Class<?> operationType) { | |||
return ContractEventSendOperation.class.isAssignableFrom(operationType); | |||