diff --git a/source/peer/src/test/java/test/com/jd/blockchain/peer/web/ControllerTestConfiguration.java b/source/peer/src/test/java/test/com/jd/blockchain/peer/web/ControllerTestConfiguration.java index 612c97f4..46dad9bc 100644 --- a/source/peer/src/test/java/test/com/jd/blockchain/peer/web/ControllerTestConfiguration.java +++ b/source/peer/src/test/java/test/com/jd/blockchain/peer/web/ControllerTestConfiguration.java @@ -1,6 +1,6 @@ //package test.com.jd.blockchain.peer.web; // -//import org.springframework.boot.test.mock.mockito.MockBean; +//import org.springframework.boot.test.mocker.mockito.MockBean; //import org.springframework.context.annotation.Bean; //import org.springframework.context.annotation.Configuration; // diff --git a/source/peer/src/test/java/test/com/jd/blockchain/peer/web/LedgerInitializingTest.java b/source/peer/src/test/java/test/com/jd/blockchain/peer/web/LedgerInitializingTest.java index 6aa8cbdb..0595ced1 100644 --- a/source/peer/src/test/java/test/com/jd/blockchain/peer/web/LedgerInitializingTest.java +++ b/source/peer/src/test/java/test/com/jd/blockchain/peer/web/LedgerInitializingTest.java @@ -2,7 +2,7 @@ // //import static org.mockito.Matchers.any; //import static org.mockito.Matchers.anyInt; -//import static org.mockito.Mockito.mock; +//import static org.mockito.Mockito.mocker; //import static org.mockito.Mockito.spy; //import static org.mockito.Mockito.when; // @@ -211,7 +211,7 @@ // //when(keystoreService.getBlockchainKey(key.getAddress())).thenReturn(keyInfo); // //when(keystoreService.sign(any(), key.getAddress())).then(answerSignature(key)); // -// msgBroadcaster = mock(MessageBroadcaster.class); +// msgBroadcaster = mocker(MessageBroadcaster.class); // // LedgerInitializingController ctrl = new LedgerInitializingController(peerSettings, keystoreService, // ledgerService, msgBroadcaster); diff --git a/source/sdk/sdk-base/src/test/java/test/com/jd/blockchain/sdk/proxy/BlockchainServiceProxyTest.java b/source/sdk/sdk-base/src/test/java/test/com/jd/blockchain/sdk/proxy/BlockchainServiceProxyTest.java index b6235493..a4a5c45a 100644 --- a/source/sdk/sdk-base/src/test/java/test/com/jd/blockchain/sdk/proxy/BlockchainServiceProxyTest.java +++ b/source/sdk/sdk-base/src/test/java/test/com/jd/blockchain/sdk/proxy/BlockchainServiceProxyTest.java @@ -35,8 +35,8 @@ public class BlockchainServiceProxyTest { // // ArgCaptorMatcher txReqCaptor = new ArgCaptorMatcher<>(); // -// TransactionService consensusService = Mockito.mock(TransactionService.class); -// BlockchainQueryService queryService = Mockito.mock(BlockchainQueryService.class); +// TransactionService consensusService = Mockito.mocker(TransactionService.class); +// BlockchainQueryService queryService = Mockito.mocker(BlockchainQueryService.class); // // HashDigest txContentHash =CryptoUtils.hash(CryptoAlgorithm.SHA_256).hash(UUID.randomUUID().toString().getBytes("UTF-8")); // TxResponseMessage expectedResponse = new TxResponseMessage(txContentHash); diff --git a/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/ClientOperationUtil.java b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/ClientOperationUtil.java new file mode 100644 index 00000000..f5790c06 --- /dev/null +++ b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/ClientOperationUtil.java @@ -0,0 +1,280 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.client.ClientOperationUtil + * Author: shaozhuguang + * Department: Y事业部 + * Date: 2019/3/27 下午4:12 + * Description: + */ +package com.jd.blockchain.sdk.client; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.jd.blockchain.crypto.CryptoProvider; +import com.jd.blockchain.crypto.PubKey; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.transaction.*; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.codec.HexUtils; +import com.jd.blockchain.utils.io.BytesSlice; +import org.apache.commons.codec.binary.Base64; + +import java.lang.reflect.Field; + +/** + * + * @author shaozhuguang + * @create 2019/3/27 + * @since 1.0.0 + */ + +public class ClientOperationUtil { + + public static Operation read(Operation operation) { + + try { + // Class + Class clazz = operation.getClass(); + Field field = clazz.getSuperclass().getDeclaredField("h"); + field.setAccessible(true); + Object object = field.get(operation); + if (object instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) object; + if (jsonObject.containsKey("accountID")) { + return convertDataAccountRegisterOperation(jsonObject); + } else if (jsonObject.containsKey("userID")) { + return convertUserRegisterOperation(jsonObject); + } else if (jsonObject.containsKey("contractID")) { + return convertContractCodeDeployOperation(jsonObject); + } else if (jsonObject.containsKey("writeSet")) { + return convertDataAccountKVSetOperation(jsonObject); + } else if (jsonObject.containsKey("initSetting")) { + return convertLedgerInitOperation(jsonObject); + } else if (jsonObject.containsKey("contractAddress")) { + return convertContractEventSendOperation(jsonObject); + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + + return null; + } + + public static Object readValueByBytesValue(BytesValue bytesValue) { + BytesValueType dataType = bytesValue.getType(); + BytesSlice saveVal = bytesValue.getValue(); + Object showVal; + switch (dataType) { + case BYTES: + // return hex + showVal = HexUtils.encode(saveVal.getBytesCopy()); + break; + case TEXT: + case JSON: + showVal = saveVal.getString(); + break; + case INT64: + showVal = saveVal.getLong(); + break; + default: + showVal = HexUtils.encode(saveVal.getBytesCopy()); + break; + } + return showVal; + } + + public static DataAccountRegisterOperation convertDataAccountRegisterOperation(JSONObject jsonObject) { + JSONObject account = jsonObject.getJSONObject("accountID"); + return new DataAccountRegisterOpTemplate(blockchainIdentity(account)); + } + + public static DataAccountKVSetOperation convertDataAccountKVSetOperation(JSONObject jsonObject) { + // 写入集合处理 + JSONArray writeSetObj = jsonObject.getJSONArray("writeSet"); + JSONObject accountAddrObj = jsonObject.getJSONObject("accountAddress"); + String addressBase58 = accountAddrObj.getString("value"); + Bytes address = Bytes.fromBase58(addressBase58); + + DataAccountKVSetOpTemplate kvOperation = new DataAccountKVSetOpTemplate(address); + for (int i = 0; i tools-initializer tools-initializer-booter tools-capability - + tools-mocker + \ No newline at end of file diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitProperties.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitProperties.java index 2ee028b2..c0f5b5a0 100644 --- a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitProperties.java +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitProperties.java @@ -127,7 +127,7 @@ public class LedgerInitProperties { return resolve(props); } - private static LedgerInitProperties resolve(Properties props) { + public static LedgerInitProperties resolve(Properties props) { String hexLedgerSeed = PropertiesUtils.getRequiredProperty(props, LEDGER_SEED).replace("-", ""); byte[] ledgerSeed = HexUtils.decode(hexLedgerSeed); LedgerInitProperties initProps = new LedgerInitProperties(ledgerSeed); diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LocalConfig.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LocalConfig.java index c3ad268e..08b00bd7 100644 --- a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LocalConfig.java +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LocalConfig.java @@ -78,7 +78,7 @@ public class LocalConfig { return resolve(props, null); } - private static LocalConfig resolve(Properties props, String initSettingFile) { + public static LocalConfig resolve(Properties props, String initSettingFile) { LocalConfig conf = new LocalConfig(); diff --git a/source/tools/tools-mocker/pom.xml b/source/tools/tools-mocker/pom.xml new file mode 100644 index 00000000..65235636 --- /dev/null +++ b/source/tools/tools-mocker/pom.xml @@ -0,0 +1,44 @@ + + + + + tools + com.jd.blockchain + 0.9.0-SNAPSHOT + + 4.0.0 + + tools-mocker + + tools-mocker + + + + com.jd.blockchain + peer + ${project.version} + + + com.jd.blockchain + gateway + ${project.version} + + + com.jd.blockchain + tools-initializer + ${project.version} + + + com.jd.blockchain + sdk-client + ${project.version} + + + com.jd.blockchain + crypto-classic + ${project.version} + + + + diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/MockerLedgerInitializer.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/MockerLedgerInitializer.java new file mode 100644 index 00000000..b03d88af --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/MockerLedgerInitializer.java @@ -0,0 +1,342 @@ +package com.jd.blockchain.mocker; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.*; +import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; +import com.jd.blockchain.crypto.service.sm.SMCryptoService; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.core.*; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.tools.initializer.*; +import com.jd.blockchain.tools.initializer.LedgerInitProperties.ConsensusParticipantConfig; +import com.jd.blockchain.tools.initializer.web.LedgerInitConsensusService; +import com.jd.blockchain.tools.initializer.web.LedgerInitDecisionData; +import com.jd.blockchain.transaction.DigitalSignatureBlob; +import com.jd.blockchain.transaction.LedgerInitSettingData; +import com.jd.blockchain.transaction.TxBuilder; +import com.jd.blockchain.transaction.TxRequestBuilder; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.concurrent.InvocationResult; +import com.jd.blockchain.utils.io.BytesUtils; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.*; + +/** + * 账本初始化控制器; + * + * @author huanghaiquan + * + */ +public class MockerLedgerInitializer implements LedgerInitProcess, LedgerInitConsensusService { + + static { + DataContractRegistry.register(TransactionRequest.class); + } + + private static final String[] SUPPORTED_PROVIDERS = { + ClassicCryptoService.class.getName(), + SMCryptoService.class.getName() }; + + private static final String DEFAULT_SIGN_ALGORITHM = "ED25519"; + + private final SignatureFunction SIGN_FUNC; + + private volatile LedgerInitPermission localPermission; + + private TransactionContent initTxContent; + + private volatile int currentId = -1; + + private volatile LedgerInitSetting ledgerInitSetting; + +// private volatile LedgerInitPermission[] permissions; +// private volatile LedgerInitPermission permission; + + private volatile Prompter prompter; + + private volatile ConsensusProvider consensusProvider; + + private volatile LedgerBlock genesisBlock; + + private volatile LedgerInitDecision localDecision; + + private volatile DecisionResultHandle[] decisions; + + private volatile DbConnection dbConn; + + private volatile LedgerEditor ledgerEditor; + + private LedgerManager ledgerManager; + + private DbConnectionFactory dbConnFactory; + + public MockerLedgerInitializer() { + this.SIGN_FUNC = Crypto.getSignatureFunction(DEFAULT_SIGN_ALGORITHM); + } + + public MockerLedgerInitializer(DbConnectionFactory dbConnFactory, LedgerManager ledgerManager) { + this.SIGN_FUNC = Crypto.getSignatureFunction(DEFAULT_SIGN_ALGORITHM); + this.dbConnFactory = dbConnFactory; + this.ledgerManager = ledgerManager; + } + + public int getId() { + return currentId; + } + + public TransactionContent getInitTxContent() { + return initTxContent; + } + + public LedgerInitPermission getLocalPermission() { + return localPermission; + } + + public LedgerInitDecision getLocalDecision() { + return localDecision; + } + + public void setPrompter(Prompter prompter) { + this.prompter = prompter; + } + + private void setConsensusProvider(ConsensusProvider consensusProvider) { + this.consensusProvider = consensusProvider; + } + + @Override + public HashDigest initialize(int currentId, PrivKey privKey, LedgerInitProperties ledgerInitProps, + DBConnectionConfig dbConnConfig, Prompter prompter) { + return initialize(currentId, privKey, ledgerInitProps, dbConnConfig, prompter, createDefaultCryptoSetting()); + } + + @Override + public synchronized HashDigest initialize(int currentId, PrivKey privKey, LedgerInitProperties ledgerInitProps, + DBConnectionConfig dbConnConfig, Prompter prompter, CryptoSetting cryptoSetting) { + + if (this.ledgerInitSetting != null) { + throw new IllegalStateException("ledger init process has already started."); + } + + setPrompter(prompter); + + ConsensusProvider csProvider = ConsensusProviders.getProvider(ledgerInitProps.getConsensusProvider()); + setConsensusProvider(csProvider); + + prompter.info("Init settings and sign permision..."); + + prepareLocalPermission(currentId, privKey, ledgerInitProps, null, cryptoSetting); + + try { + // 连接数据库; + connectDb(dbConnConfig); + + // 生成账本; + makeLocalDecision(privKey); + + // 获取其它参与方的账本生成结果; + return consensusDecisions(); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } finally { + closeDb(); + } + } + + public DbConnection connectDb(DBConnectionConfig dbConnConfig) { + this.dbConn = dbConnFactory.connect(dbConnConfig.getUri(), dbConnConfig.getPassword()); + return dbConn; + } + + public LedgerInitDecision makeLocalDecision(PrivKey privKey) { + // 生成账本; + this.ledgerEditor = ledgerManager.newLedger(this.ledgerInitSetting, dbConn.getStorageService()); + this.genesisBlock = initLedgerDataset(ledgerEditor); + + // 生成签名决定; + this.localDecision = makeDecision(currentId, genesisBlock.getHash(), privKey); + this.decisions = new DecisionResultHandle[this.ledgerInitSetting.getConsensusParticipants().length]; + for (int i = 0; i < decisions.length; i++) { + // 参与者的 id 是依次递增的; + this.decisions[i] = new DecisionResultHandle(i); + } + // 预置当前参与方的“决定”到列表,避免向自己发起请求; + this.decisions[currentId].setValue(localDecision); + return localDecision; + } + + public HashDigest consensusDecisions() { + // 执行提交提交; + ledgerEditor.commit(); + return genesisBlock.getHash(); + } + + public void closeDb() { + if (dbConn != null) { + DbConnection connection = dbConn; + dbConn = null; + try { + connection.close(); + } catch (IOException e) { + prompter.error(e, "Error occurred on closing db connection! --" + e.getMessage()); + } + } + } + + public CryptoSetting createDefaultCryptoSetting() { + CryptoProvider[] supportedProviders = new CryptoProvider[SUPPORTED_PROVIDERS.length]; + for (int i = 0; i < SUPPORTED_PROVIDERS.length; i++) { + supportedProviders[i] = Crypto.getProvider(SUPPORTED_PROVIDERS[i]); + } + CryptoConfig defCryptoSetting = new CryptoConfig(); + defCryptoSetting.setSupportedProviders(supportedProviders); + defCryptoSetting.setAutoVerifyHash(true); + defCryptoSetting.setHashAlgorithm(Crypto.getAlgorithm("SHA256")); + + return defCryptoSetting; + } + + public LedgerInitPermission prepareLocalPermission(int currentId, PrivKey privKey, LedgerInitProperties ledgerProps, + ConsensusSettings csSettings, CryptoSetting cryptoSetting) { + // 创建初始化配置; + LedgerInitSettingData initSetting = new LedgerInitSettingData(); + initSetting.setLedgerSeed(ledgerProps.getLedgerSeed()); + initSetting.setCryptoSetting(cryptoSetting); + + List partiList = ledgerProps.getConsensusParticipants(); + ConsensusParticipantConfig[] parties = partiList.toArray(new ConsensusParticipantConfig[partiList.size()]); + ConsensusParticipantConfig[] orderedParties = sortAndVerify(parties); + initSetting.setConsensusParticipants(orderedParties); + + // 创建默认的共识配置; + try { + byte[] csSettingBytes = new byte[1024]; + new Random().nextBytes(csSettingBytes); + + initSetting.setConsensusProvider(consensusProvider.getName()); + initSetting.setConsensusSettings(new Bytes(csSettingBytes)); + } catch (Exception e) { + throw new LedgerInitException("Create default consensus config failed! --" + e.getMessage(), e); + } + + if (currentId < 0 || currentId >= orderedParties.length) { + throw new LedgerInitException("Your id is out of bound of participant list!"); + } + this.currentId = currentId; + this.ledgerInitSetting = initSetting; + + // 校验当前的公钥、私钥是否匹配; + byte[] testBytes = BytesUtils.toBytes(currentId); + SignatureDigest testSign = SIGN_FUNC.sign(privKey, testBytes); + PubKey myPubKey = orderedParties[currentId].getPubKey(); + if (!SIGN_FUNC.verify(testSign, myPubKey, testBytes)) { + throw new LedgerInitException("Your pub-key specified in the init-settings isn't match your priv-key!"); + } + // 生成初始化交易,并签署许可; + TransactionBuilder initTxBuilder = new TxBuilder(null);// 账本初始化交易的账本 hash 为 null; + initTxBuilder.ledgers().create(initSetting); + for (ParticipantNode p : initSetting.getConsensusParticipants()) { + // TODO:暂时只支持注册用户的初始化操作; + BlockchainIdentity superUserId = new BlockchainIdentityData(p.getPubKey()); + initTxBuilder.users().register(superUserId); + } + this.initTxContent = initTxBuilder.prepareContent(); + + // 对初始交易签名,生成当前参与者的账本初始化许可; + SignatureDigest permissionSign = TxRequestBuilder.sign(initTxContent, privKey); + localPermission = new LedgerInitPermissionData(currentId, permissionSign); + + this.currentId = currentId; + return localPermission; + } + + private LedgerInitDecision makeDecision(int participantId, HashDigest ledgerHash, PrivKey privKey) { + byte[] dataBytes = getDecisionBytes(participantId, ledgerHash); + SignatureFunction signFunc = Crypto.getSignatureFunction(privKey.getAlgorithm()); + SignatureDigest signature = signFunc.sign(privKey, dataBytes); + + LedgerInitDecisionData decision = new LedgerInitDecisionData(); + decision.setParticipantId(participantId); + decision.setLedgerHash(ledgerHash); + decision.setSignature(signature); + return decision; + } + + private LedgerBlock initLedgerDataset(LedgerEditor ledgerEditor) { + // 初始化时,自动将参与方注册为账本的用户; + TxRequestBuilder txReqBuilder = new TxRequestBuilder(this.initTxContent); +// ParticipantNode[] parties = this.ledgerInitSetting.getConsensusParticipants(); + ParticipantNode parti = this.ledgerInitSetting.getConsensusParticipants()[currentId]; + + PubKey pubKey = parti.getPubKey(); + SignatureDigest signDigest = this.localPermission.getTransactionSignature(); + DigitalSignatureBlob digitalSignature = new DigitalSignatureBlob(pubKey, signDigest); + txReqBuilder.addNodeSignature(digitalSignature); + + TransactionRequest txRequest = txReqBuilder.buildRequest(); + + LedgerTransactionContext txCtx = ledgerEditor.newTransaction(txRequest); + Operation[] ops = txRequest.getTransactionContent().getOperations(); + // 注册用户; 注:第一个操作是 LedgerInitOperation; + // TODO:暂时只支持注册用户的初始化操作; + for (int i = 1; i < ops.length; i++) { + UserRegisterOperation userRegOP = (UserRegisterOperation) ops[i]; + txCtx.getDataSet().getUserAccountSet().register(userRegOP.getUserID().getAddress(), + userRegOP.getUserID().getPubKey()); + } + + txCtx.commit(TransactionState.SUCCESS); + + return ledgerEditor.prepare(); + } + + private byte[] getDecisionBytes(int participantId, HashDigest ledgerHash) { + return BytesUtils.concat(BytesUtils.toBytes(participantId), ledgerHash.toBytes()); + } + + @Override + public LedgerInitPermission requestPermission(int requesterId, SignatureDigest signature) { + return localPermission; + } + + @Override + public LedgerInitDecision synchronizeDecision(@RequestBody LedgerInitDecision initDecision) { + return localDecision; + } + + /** + * 对参与者列表按照 id 进行升序排列,并校验id是否从 1 开始且没有跳跃; + * + * @param parties + * @return + */ + private ConsensusParticipantConfig[] sortAndVerify(ConsensusParticipantConfig[] parties) { + Arrays.sort(parties, (o1, o2) -> o1.getId() - o2.getId()); + for (int i = 0; i < parties.length; i++) { + if (parties[i].getId() != i) { + throw new LedgerInitException( + "The ids of participants are not match their positions in the participant-list!"); + } + } + return parties; + } + + private static class DecisionResultHandle extends InvocationResult { + + private final int PARTICIPANT_ID; + + public DecisionResultHandle(int participantId) { + this.PARTICIPANT_ID = participantId; + } + + } + +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/MockerNodeContext.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/MockerNodeContext.java new file mode 100644 index 00000000..08df2cc5 --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/MockerNodeContext.java @@ -0,0 +1,460 @@ +package com.jd.blockchain.mocker; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.consensus.ClientIdentification; +import com.jd.blockchain.consensus.ClientIdentifications; +import com.jd.blockchain.consensus.action.ActionRequest; +import com.jd.blockchain.consensus.action.ActionResponse; +import com.jd.blockchain.crypto.*; +import com.jd.blockchain.crypto.service.classic.ClassicAlgorithm; +import com.jd.blockchain.crypto.service.classic.ClassicCryptoService; +import com.jd.blockchain.crypto.service.sm.SMCryptoService; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.core.CryptoConfig; +import com.jd.blockchain.ledger.core.LedgerDataSet; +import com.jd.blockchain.ledger.core.LedgerEditor; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.ledger.core.impl.LedgerQueryService; +import com.jd.blockchain.ledger.core.impl.TransactionBatchProcessor; +import com.jd.blockchain.mocker.config.MockerConstant; +import com.jd.blockchain.mocker.config.PresetAnswerPrompter; +import com.jd.blockchain.mocker.handler.MockerContractExeHandle; +import com.jd.blockchain.mocker.handler.MockerOperationHandleRegister; +import com.jd.blockchain.mocker.proxy.ContractProxy; +import com.jd.blockchain.service.TransactionBatchResultHandle; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.storage.service.utils.MemoryDBConnFactory; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.transaction.*; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.web.serializes.ByteArrayObjectUtil; + +import java.util.*; + +import static java.lang.reflect.Proxy.newProxyInstance; + +public class MockerNodeContext implements BlockchainQueryService { + + private static final String[] SUPPORTED_PROVIDERS = { ClassicCryptoService.class.getName(), + SMCryptoService.class.getName() }; + + private static final DBConnectionConfig dbConnectionConfig = new DBConnectionConfig("memory://local/0"); + + private DbConnectionFactory dbConnFactory = new MemoryDBConnFactory(); + + private MockerOperationHandleRegister opHandler = new MockerOperationHandleRegister(); + + private MockerContractExeHandle contractExeHandle = new MockerContractExeHandle(); + + private Map participants = new HashMap<>(); + + private LedgerManager ledgerManager = new LedgerManager(); + + private BlockchainKeypair defaultKeypair; + + private LedgerRepository ledgerRepository; + + private LedgerQueryService queryService; + + private HashDigest ledgerHash; + + private String ledgerSeed; + + static { + DataContractRegistry.register(TransactionContent.class); + DataContractRegistry.register(TransactionContentBody.class); + DataContractRegistry.register(TransactionRequest.class); + DataContractRegistry.register(NodeRequest.class); + DataContractRegistry.register(EndpointRequest.class); + DataContractRegistry.register(TransactionResponse.class); + + DataContractRegistry.register(Operation.class); + DataContractRegistry.register(ContractCodeDeployOperation.class); + DataContractRegistry.register(ContractEventSendOperation.class); + DataContractRegistry.register(DataAccountRegisterOperation.class); + DataContractRegistry.register(UserRegisterOperation.class); + DataContractRegistry.register(DataAccountKVSetOperation.class); + DataContractRegistry.register(DataAccountKVSetOperation.KVWriteEntry.class); + + DataContractRegistry.register(ActionRequest.class); + DataContractRegistry.register(ActionResponse.class); + DataContractRegistry.register(ClientIdentifications.class); + DataContractRegistry.register(ClientIdentification.class); + + ByteArrayObjectUtil.init(); + } + + public MockerNodeContext() { + } + + public MockerNodeContext(String ledgerSeed) { + this.ledgerSeed = ledgerSeed; + } + + public MockerNodeContext participants(String name, BlockchainKeypair partiKey) { + participants.put(name, partiKey); + return this; + } + + public MockerNodeContext participants(BlockchainKeypair partiKey) { + participants.put(partiKey.getPubKey().toBase58(), partiKey); + return this; + } + + public MockerNodeContext build() { + if (ledgerSeed == null || ledgerSeed.length() == 0) { + ledgerSeed = MockerConstant.DEFAULT_LEDGER_SEED; + } + if (participants.size() < 4) { + // 缺少的需要补充,使用常量中的内容进行补充 + for (int i = 0; i < MockerConstant.PUBLIC_KEYS.length; i++) { + String pubKeyString = MockerConstant.PUBLIC_KEYS[i]; + boolean isExist = false; + // 通过公钥进行判断 + for (Map.Entry entry : participants.entrySet()) { + String existPubKey = KeyGenCommand.encodePubKey(entry.getValue().getPubKey()); + if (pubKeyString.equals(existPubKey)) { + isExist = true; + } + } + if (!isExist) { + // 加入系统中 + PrivKey privKey = KeyGenCommand.decodePrivKeyWithRawPassword(MockerConstant.PRIVATE_KEYS[i], MockerConstant.PASSWORD); + PubKey pubKey = KeyGenCommand.decodePubKey(MockerConstant.PUBLIC_KEYS[i]); + participants(new BlockchainKeypair(pubKey, privKey)); + } + if (participants.size() >= 4) { + break; + } + } + } + + LedgerInitProperties ledgerInitProperties = loadInitSetting(); + + MockerLedgerInitializer mockLedgerInitializer = new MockerLedgerInitializer(dbConnFactory, ledgerManager); + + ledgerHash = mockLedgerInitializer.initialize(0, defaultKeypair.getPrivKey(), + ledgerInitProperties, dbConnectionConfig, new PresetAnswerPrompter("N"), cryptoConfig()); + + ledgerRepository = registerLedger(ledgerHash, dbConnectionConfig); + + queryService = new LedgerQueryService(ledgerManager); + + contractExeHandle.initLedger(ledgerManager, ledgerHash); + + opHandler.registerHandler(contractExeHandle); + + return this; + } + + public String registerUser(BlockchainKeypair user) { + TxBuilder txBuilder = txBuilder(); + txBuilder.users().register(user.getIdentity()); + txProcess(txRequest(txBuilder)); + return user.getAddress().toBase58(); + } + + public String registerDataAccount(BlockchainKeypair dataAccount) { + TxBuilder txBuilder = txBuilder(); + txBuilder.dataAccounts().register(dataAccount.getIdentity()); + txProcess(txRequest(txBuilder)); + return dataAccount.getAddress().toBase58(); + } + + public String registerDataAccount() { + return registerDataAccount(BlockchainKeyGenerator.getInstance().generate()); + } + + public void writeKv(String address, String key, String value, long version) { + TxBuilder txBuilder = txBuilder(); + txBuilder.dataAccount(address).set(key, value, version); + txProcess(txRequest(txBuilder)); + } + + public void writeKv(String address, String key, long value, long version) { + TxBuilder txBuilder = txBuilder(); + txBuilder.dataAccount(address).set(key, value, version); + txProcess(txRequest(txBuilder)); + } + + public void writeKv(String address, String key, byte[] value, long version) { + TxBuilder txBuilder = txBuilder(); + txBuilder.dataAccount(address).set(key, value, version); + txProcess(txRequest(txBuilder)); + } + + public void writeKv(String address, String key, Bytes value, long version) { + TxBuilder txBuilder = txBuilder(); + txBuilder.dataAccount(address).set(key, value, version); + txProcess(txRequest(txBuilder)); + } + + public T deployContract(T contract) { + // 首先发布合约 + BlockchainIdentity identity = deployContract2Ledger(contract); + // 生成代理对象 + ContractProxy contractProxy = new ContractProxy<>(identity, this, + contract, contractExeHandle); + + T proxy = (T) newProxyInstance(contract.getClass().getClassLoader(), + contract.getClass().getInterfaces(), contractProxy); + + return proxy; + } + + private BlockchainIdentity deployContract2Ledger(Object contract) { + BlockchainIdentity contractIdentity = BlockchainKeyGenerator.getInstance().generate().getIdentity(); + // 合约发布 + // 注意此处只是将其放入内存中,而不需要真正编译为字节码 + TxBuilder txBuilder = txBuilder(); + txBuilder.contracts().deploy(contractIdentity, contract.getClass().getName().getBytes()); + // 执行 + txProcess(txRequest(txBuilder)); + return contractIdentity; + } + + public String getLedgerSeed() { + return ledgerSeed; + } + + public HashDigest getLedgerHash() { + return ledgerHash; + } + + @Override + public HashDigest[] getLedgerHashs() { + return queryService.getLedgerHashs(); + } + + @Override + public LedgerInfo getLedger(HashDigest ledgerHash) { + return queryService.getLedger(ledgerHash); + } + + @Override + public ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash) { + return queryService.getConsensusParticipants(ledgerHash); + } + + @Override + public LedgerMetadata getLedgerMetadata(HashDigest ledgerHash) { + return queryService.getLedgerMetadata(ledgerHash); + } + + @Override + public LedgerBlock getBlock(HashDigest ledgerHash, long height) { + return queryService.getBlock(ledgerHash, height); + } + + @Override + public LedgerBlock getBlock(HashDigest ledgerHash, HashDigest blockHash) { + return queryService.getBlock(ledgerHash, blockHash); + } + + @Override + public long getTransactionCount(HashDigest ledgerHash, long height) { + return queryService.getTransactionCount(ledgerHash, height); + } + + @Override + public long getTransactionCount(HashDigest ledgerHash, HashDigest blockHash) { + return queryService.getTransactionCount(ledgerHash, blockHash); + } + + @Override + public long getTransactionTotalCount(HashDigest ledgerHash) { + return queryService.getTransactionTotalCount(ledgerHash); + } + + @Override + public long getDataAccountCount(HashDigest ledgerHash, long height) { + return queryService.getDataAccountCount(ledgerHash, height); + } + + @Override + public long getDataAccountCount(HashDigest ledgerHash, HashDigest blockHash) { + return queryService.getDataAccountCount(ledgerHash, blockHash); + } + + @Override + public long getDataAccountTotalCount(HashDigest ledgerHash) { + return queryService.getDataAccountTotalCount(ledgerHash); + } + + @Override + public long getUserCount(HashDigest ledgerHash, long height) { + return queryService.getUserCount(ledgerHash, height); + } + + @Override + public long getUserCount(HashDigest ledgerHash, HashDigest blockHash) { + return queryService.getUserCount(ledgerHash, blockHash); + } + + @Override + public long getUserTotalCount(HashDigest ledgerHash) { + return queryService.getUserTotalCount(ledgerHash); + } + + @Override + public long getContractCount(HashDigest ledgerHash, long height) { + return queryService.getContractCount(ledgerHash, height); + } + + @Override + public long getContractCount(HashDigest ledgerHash, HashDigest blockHash) { + return queryService.getContractCount(ledgerHash, blockHash); + } + + @Override + public long getContractTotalCount(HashDigest ledgerHash) { + return queryService.getContractTotalCount(ledgerHash); + } + + @Override + public LedgerTransaction[] getTransactions(HashDigest ledgerHash, long height, int fromIndex, int count) { + return queryService.getTransactions(ledgerHash, height, fromIndex, count); + } + + @Override + public LedgerTransaction[] getTransactions(HashDigest ledgerHash, HashDigest blockHash, int fromIndex, int count) { + return queryService.getTransactions(ledgerHash, blockHash, fromIndex, count); + } + + @Override + public LedgerTransaction getTransactionByContentHash(HashDigest ledgerHash, HashDigest contentHash) { + return queryService.getTransactionByContentHash(ledgerHash, contentHash); + } + + @Override + public TransactionState getTransactionStateByContentHash(HashDigest ledgerHash, HashDigest contentHash) { + return queryService.getTransactionStateByContentHash(ledgerHash, contentHash); + } + + @Override + public UserInfo getUser(HashDigest ledgerHash, String address) { + return queryService.getUser(ledgerHash, address); + } + + @Override + public AccountHeader getDataAccount(HashDigest ledgerHash, String address) { + return queryService.getDataAccount(ledgerHash, address); + } + + @Override + public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { + return queryService.getDataEntries(ledgerHash, address, keys); + } + + @Override + public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, KVInfoVO kvInfoVO) { + return queryService.getDataEntries(ledgerHash, address, kvInfoVO); + } + + @Override + public long getDataEntriesTotalCount(HashDigest ledgerHash, String address) { + return queryService.getDataEntriesTotalCount(ledgerHash, address); + } + + @Override + public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count) { + return queryService.getDataEntries(ledgerHash, address, fromIndex, count); + } + + @Override + public AccountHeader getContract(HashDigest ledgerHash, String address) { + return queryService.getContract(ledgerHash, address); + } + + @Override + public AccountHeader[] getUsers(HashDigest ledgerHash, int fromIndex, int count) { + return queryService.getUsers(ledgerHash, fromIndex, count); + } + + @Override + public AccountHeader[] getDataAccounts(HashDigest ledgerHash, int fromIndex, int count) { + return queryService.getDataAccounts(ledgerHash, fromIndex, count); + } + + @Override + public AccountHeader[] getContractAccounts(HashDigest ledgerHash, int fromIndex, int count) { + return queryService.getContractAccounts(ledgerHash, fromIndex, count); + } + + public TxBuilder txBuilder() { + return new TxBuilder(ledgerHash); + } + + public TransactionRequest txRequest(TxBuilder txBuilder) { + TransactionRequestBuilder reqBuilder = txBuilder.prepareRequest(); + reqBuilder.signAsEndpoint(defaultKeypair); + return reqBuilder.buildRequest(); + } + + public void txProcess(TransactionRequest txRequest) { + LedgerEditor newEditor = ledgerRepository.createNextBlock(); + LedgerBlock latestBlock = ledgerRepository.getLatestBlock(); + LedgerDataSet previousDataSet = ledgerRepository.getDataSet(latestBlock); + TransactionBatchProcessor txProc = new TransactionBatchProcessor(newEditor, previousDataSet, opHandler, + ledgerManager); + txProc.schedule(txRequest); + TransactionBatchResultHandle handle = txProc.prepare(); + handle.commit(); + } + + private LedgerRepository registerLedger(HashDigest ledgerHash, DBConnectionConfig dbConnConf) { + return ledgerManager.register(ledgerHash, dbConnFactory.connect(dbConnConf.getUri()).getStorageService()); + } + + private CryptoConfig cryptoConfig() { + CryptoProvider[] supportedProviders = new CryptoProvider[SUPPORTED_PROVIDERS.length]; + for (int i = 0; i < SUPPORTED_PROVIDERS.length; i++) { + supportedProviders[i] = Crypto.getProvider(SUPPORTED_PROVIDERS[i]); + } + CryptoConfig cryptoSetting = new CryptoConfig(); + cryptoSetting.setSupportedProviders(supportedProviders); + cryptoSetting.setAutoVerifyHash(false); + cryptoSetting.setHashAlgorithm(ClassicAlgorithm.SHA256); + return cryptoSetting; + } + + private LedgerInitProperties loadInitSetting() { + + Properties ledgerProp = new Properties(); + + ledgerProp.put(LedgerInitProperties.LEDGER_SEED, ledgerSeed); + + ledgerProp.put("ledger.name", MockerConstant.LEDGER_NAME); + + ledgerProp.put(LedgerInitProperties.CONSENSUS_SERVICE_PROVIDER, "com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider"); + + ledgerProp.put(LedgerInitProperties.CONSENSUS_CONFIG, "classpath:bftsmart.config"); + + ledgerProp.put(LedgerInitProperties.CRYPTO_SERVICE_PROVIDERS, "com.jd.blockchain.crypto.service.classic.ClassicCryptoService," + + "com.jd.blockchain.crypto.service.sm.SMCryptoService"); + + ledgerProp.put(LedgerInitProperties.PART_COUNT, String.valueOf(participants.size())); + + int partiIndex = 0; + for (Map.Entry entry : participants.entrySet()) { + String name = entry.getKey(); + BlockchainKeypair keypair = entry.getValue(); + if (partiIndex == 0) { + defaultKeypair = keypair; + } + String partiPrefix = String.format(LedgerInitProperties.PART_ID_PATTERN, partiIndex) + "."; + ledgerProp.put(partiPrefix + LedgerInitProperties.PART_NAME, name); + ledgerProp.put(partiPrefix + LedgerInitProperties.PART_PUBKEY_PATH, ""); + ledgerProp.put(partiPrefix + LedgerInitProperties.PART_PUBKEY, KeyGenCommand.encodePubKey(keypair.getPubKey())); + ledgerProp.put(partiPrefix + LedgerInitProperties.PART_INITIALIZER_HOST, MockerConstant.LOCAL_ADDRESS); + ledgerProp.put(partiPrefix + LedgerInitProperties.PART_INITIALIZER_PORT, String.valueOf(MockerConstant.LEDGER_INIT_PORT_START + partiIndex * 10)); + ledgerProp.put(partiPrefix + LedgerInitProperties.PART_INITIALIZER_SECURE, String.valueOf(false)); + partiIndex++; + } + + return LedgerInitProperties.resolve(ledgerProp); + } +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/config/LedgerInitWebConfiguration.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/config/LedgerInitWebConfiguration.java new file mode 100644 index 00000000..11f6a762 --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/config/LedgerInitWebConfiguration.java @@ -0,0 +1,22 @@ +package com.jd.blockchain.mocker.config; + +import com.jd.blockchain.storage.service.impl.composite.CompositeConnectionFactory; +import com.jd.blockchain.tools.initializer.web.InitWebSecurityConfiguration; +import com.jd.blockchain.tools.initializer.web.InitWebServerConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@SpringBootApplication +@Configuration +@EnableConfigurationProperties +@Import(value = { InitWebServerConfiguration.class, InitWebSecurityConfiguration.class }) +public class LedgerInitWebConfiguration { + + @Bean + public CompositeConnectionFactory getCompositeConnectionFactory() { + return new CompositeConnectionFactory(); + } +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/config/MockerConstant.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/config/MockerConstant.java new file mode 100644 index 00000000..e52f02bd --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/config/MockerConstant.java @@ -0,0 +1,67 @@ +package com.jd.blockchain.mocker.config; + +public class MockerConstant { + + public static final String DEFAULT_LEDGER_SEED = "932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffa"; + + public static final int PEER_PORT_START = 12000; + + public static final int LEDGER_INIT_PORT_START = 1600; + + public static final int GATEWAY_PORT = 11000; + + public static final String LOCAL_ADDRESS = "127.0.0.1"; + + public static final String LEDGER_NAME = "JDChain"; + + public static final String LEDGER_INIT_FORMATTER = "ledger%s.init"; + + public static final String[] CONSENSUS_PROVIDERS = new String[] {"com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider"}; + + public static final String PASSWORD = "abc"; + + public static final String PASSWORD_ENCODE = "DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY"; + + public static final String[] PUBLIC_KEYS = { + "3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9", + "3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX", + "3snPdw7i7PZi6TStiyc6mzjprnNhgs2atSGNS8wPYzhbKaUWGFJt7x", + "3snPdw7i7PifPuRX7fu3jBjsb3rJRfDe9GtbDfvFJaJ4V4hHXQfhwk", + "3snPdw7i7PXPRMp3EAjsxJkHe7aZJRLNzdW8kEHBWeQsSgcPAiHP2J", + "3snPdw7i7PmmQoPgUpUmmAUj6nakHj8wMSQmiMi1RaiZp4YU1D4AXk", + "3snPdw7i7PiJKsa94q3EcLT1y6GRJ7LeFGe799hdzRRHmf6LNodyiM", + "3snPdw7i7Pm2wJwEnKn8kK8eGTkN36C2BZRRjVTr9FPB2rqtcgTq7h" + }; + + public static final String[] ADDRESS_ARRAY = { + "LdeP3fY7jJbNwL8CiL2wU21AF9unDWQjVEW5w", + "LdeNnz88dH6CA6PwkVdn3nFRibUKP3sFT2byG", + "LdeNmdpT4DiTwLUP9jRQhwdRBRiXeHno456vy", + "LdeNekdXMHqyz9Qxc2jDSBnkvvZLbty6pRDdP", + "LdeNryu2DK96tDvtLJfBz7ArWynAWPJAep38n", + "LdeNkoQpXffVF9qjsa4A7wZVgT9W2vnhpEEm5", + "LdeNzfFrsJT7Ni1L7k1EP3NuxUfK8QGAxMGpt", + "LdeNuLhR5AoyhQoVeS15haJvvGC5ByoPezrGq" + }; + + public static final String[] PRIVATE_KEYS = { + "177gjzHTznYdPgWqZrH43W3yp37onm74wYXT4v9FukpCHBrhRysBBZh7Pzdo5AMRyQGJD7x", + "177gju9p5zrNdHJVEQnEEKF4ZjDDYmAXyfG84V5RPGVc5xFfmtwnHA7j51nyNLUFffzz5UT", + "177gjtwLgmSx5v1hFb46ijh7L9kdbKUpJYqdKVf9afiEmAuLgo8Rck9yu5UuUcHknWJuWaF", + "177gk1pudweTq5zgJTh8y3ENCTwtSFsKyX7YnpuKPo7rKgCkCBXVXh5z2syaTCPEMbuWRns", + "177gjwyHzfmsD4g3MVB655seYWXua2KBdQEqTf9kHgeMc6gdRZADKb6cL13L5koqMsBtkGX", + "177gk2C9V7gwPhAGgawL53W8idDpSo63jnbg8finbZkk4zermr5aqgTeKspN45fbymey8t6", + "177gjtz29TXa2E3FFBpCNr5LpU5zYxkNPAgcAJZW7tCGUgWQr4gcVv8PHmoVVPeSVVnyZV5", + "177gjzpHnqGEuSKyi3pW69WhpEPmeFPxLNVmUfXQb4DDV2EfnMgY7T4NFsyRsThjJFsau7X"}; + + public static final String[] DB_MEMS = { + "memory://127.0.0.1/0", + "memory://127.0.0.1/1", + "memory://127.0.0.1/2", + "memory://127.0.0.1/3", + "memory://127.0.0.1/4", + "memory://127.0.0.1/5", + "memory://127.0.0.1/6", + "memory://127.0.0.1/7" + }; +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/config/PresetAnswerPrompter.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/config/PresetAnswerPrompter.java new file mode 100644 index 00000000..bb93d7e1 --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/config/PresetAnswerPrompter.java @@ -0,0 +1,32 @@ +package com.jd.blockchain.mocker.config; + +import com.jd.blockchain.tools.initializer.ConsolePrompter; + +import java.util.Properties; + +public class PresetAnswerPrompter extends ConsolePrompter { + + private Properties answers = new Properties(); + + private String defaultAnswer; + + public PresetAnswerPrompter(String defaultAnswer) { + this.defaultAnswer = defaultAnswer; + } + + public void setAnswer(String tag, String answer) { + answers.setProperty(tag, answer); + } + + public void setDefaultAnswer(String defaultAnswer) { + this.defaultAnswer = defaultAnswer; + } + + @Override + public String confirm(String tag, String format, Object... args) { + System.out.print(String.format(format, args)); + String answer = answers.getProperty(tag, defaultAnswer); + System.out.println(String.format("\r\n [Mocked answer:%s]", answer)); + return answer; + } +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/contracts/AccountContract.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/contracts/AccountContract.java new file mode 100644 index 00000000..d42d6458 --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/contracts/AccountContract.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.mocker.contracts; + +import com.jd.blockchain.contract.Contract; +import com.jd.blockchain.contract.ContractEvent; + +@Contract +public interface AccountContract { + + @ContractEvent(name = "create") + void create(String address, String account, long money); + + @ContractEvent(name = "transfer") + void transfer(String address, String from, String to, long money); + + @ContractEvent(name = "print") + void print(String address, String from, String to); +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/contracts/AccountContractImpl.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/contracts/AccountContractImpl.java new file mode 100644 index 00000000..15132de2 --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/contracts/AccountContractImpl.java @@ -0,0 +1,81 @@ +package com.jd.blockchain.mocker.contracts; + +import com.jd.blockchain.contract.ContractEventContext; +import com.jd.blockchain.contract.ContractException; +import com.jd.blockchain.contract.EventProcessingAwire; +import com.jd.blockchain.crypto.HashDigest; +import com.jd.blockchain.ledger.KVDataEntry; + +public class AccountContractImpl implements EventProcessingAwire, AccountContract { + + private ContractEventContext eventContext; + + private HashDigest ledgerHash; + + @Override + public void create(String address, String account, long money) { + // 暂不处理该账户已经存在的问题 + eventContext.getLedger().dataAccount(address).set(account, money, -1); + } + + @Override + public void transfer(String address, String from, String to, long money) { + // 首先分别查询from与to的结果 + KVDataEntry[] dataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, from, to); + long currentFromMoney = 0L, currentToMoney = 0L, currentFromVer = -1L, currentToVer = -1L; + if (dataEntries != null && dataEntries.length > 0) { + for (KVDataEntry dataEntry : dataEntries) { + String key = dataEntry.getKey(); + Object value = dataEntry.getValue(); + long version = dataEntry.getVersion(); + if (key.equals(from)) { + currentFromMoney = (long) value; + currentFromVer = version; + } + if (key.equals(to)) { + currentToMoney = (long) value; + currentToVer = version; + } + } + } + currentFromMoney -= money; + currentToMoney += money; + // 重新设置结果 + eventContext.getLedger().dataAccount(address).set(from, currentFromMoney, currentFromVer) + .set(to, currentToMoney, currentToVer); + } + + @Override + public void print(String address, String from, String to) { + KVDataEntry[] dataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, from, to); + if (dataEntries != null && dataEntries.length > 0) { + for (KVDataEntry dataEntry : dataEntries) { + String key = dataEntry.getKey(); + Object value = dataEntry.getValue(); + long version = dataEntry.getVersion(); + System.out.printf("Key = %s Value = %s Version = %s \r\n", key, value, version); + } + } + } + + @Override + public void beforeEvent(ContractEventContext eventContext) { + this.eventContext = eventContext; + this.ledgerHash = this.eventContext.getCurrentLedgerHash(); + } + + @Override + public void postEvent(ContractEventContext eventContext, ContractException error) { + + } + + @Override + public void postEvent(ContractException error) { + + } + + @Override + public void postEvent() { + + } +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/contracts/WriteContract.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/contracts/WriteContract.java new file mode 100644 index 00000000..2dbd8ae5 --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/contracts/WriteContract.java @@ -0,0 +1,14 @@ +package com.jd.blockchain.mocker.contracts; + +import com.jd.blockchain.contract.Contract; +import com.jd.blockchain.contract.ContractEvent; + +@Contract +public interface WriteContract { + + @ContractEvent(name = "print") + void print(String name); + + @ContractEvent(name = "writeKv") + void writeKv(String address, String key, String value); +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/contracts/WriteContractImpl.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/contracts/WriteContractImpl.java new file mode 100644 index 00000000..fd850d19 --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/contracts/WriteContractImpl.java @@ -0,0 +1,41 @@ +package com.jd.blockchain.mocker.contracts; + +import com.jd.blockchain.contract.ContractEventContext; +import com.jd.blockchain.contract.ContractException; +import com.jd.blockchain.contract.EventProcessingAwire; + +public class WriteContractImpl implements EventProcessingAwire, WriteContract { + + private ContractEventContext eventContext; + + @Override + public void print(String name) { + System.out.printf("My Name is %s \r\n", name); + System.out.printf("My Ledger Hash is %s \r\n", eventContext.getCurrentLedgerHash().toBase58()); + } + + @Override + public void writeKv(String address, String key, String value) { + eventContext.getLedger().dataAccount(address).set(key, value, -1); + } + + @Override + public void beforeEvent(ContractEventContext eventContext) { + this.eventContext = eventContext; + } + + @Override + public void postEvent(ContractEventContext eventContext, ContractException error) { + System.out.println("----- postEvent1 -----"); + } + + @Override + public void postEvent(ContractException error) { + System.out.println("----- postEvent2 -----"); + } + + @Override + public void postEvent() { + System.out.println("----- postEvent3 -----"); + } +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/data/KvData.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/data/KvData.java new file mode 100644 index 00000000..ec6cce3d --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/data/KvData.java @@ -0,0 +1,54 @@ +package com.jd.blockchain.mocker.data; + +public class KvData { + + private String dataAccount; + + private String key; + + private byte[] value; + + private long version; + + public KvData() { + } + + public KvData(String dataAccount, String key, byte[] value, long version) { + this.dataAccount = dataAccount; + this.key = key; + this.value = value; + this.version = version; + } + + public String getDataAccount() { + return dataAccount; + } + + public String getKey() { + return key; + } + + public byte[] getValue() { + return value; + } + + public long getVersion() { + return version; + } + + public void setDataAccount(String dataAccount) { + this.dataAccount = dataAccount; + } + + public void setKey(String key) { + this.key = key; + } + + public void setValue(byte[] value) { + this.value = value; + } + + public void setVersion(long version) { + this.version = version; + } +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/data/ResponseData.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/data/ResponseData.java new file mode 100644 index 00000000..61cf6893 --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/data/ResponseData.java @@ -0,0 +1,38 @@ +package com.jd.blockchain.mocker.data; + +import com.jd.blockchain.ledger.TransactionResponse; + +public class ResponseData { + + private TransactionResponse txResponse; + + private T data; + + public ResponseData() { + } + + public ResponseData(TransactionResponse txResponse) { + this.txResponse = txResponse; + } + + public ResponseData(TransactionResponse txResponse, T data) { + this.txResponse = txResponse; + this.data = data; + } + + public TransactionResponse getTxResponse() { + return txResponse; + } + + public T getData() { + return data; + } + + public void setTxResponse(TransactionResponse txResponse) { + this.txResponse = txResponse; + } + + public void setData(T data) { + this.data = data; + } +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/handler/MockerContractExeHandle.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/handler/MockerContractExeHandle.java new file mode 100644 index 00000000..53bc9417 --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/handler/MockerContractExeHandle.java @@ -0,0 +1,136 @@ +package com.jd.blockchain.mocker.handler; + +import com.jd.blockchain.contract.ContractEventContext; +import com.jd.blockchain.contract.ContractException; +import com.jd.blockchain.contract.EventProcessingAwire; +import com.jd.blockchain.contract.LedgerContext; +import com.jd.blockchain.crypto.HashDigest; +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.ContractEventSendOperation; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.core.*; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +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 java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + + +public class MockerContractExeHandle implements OperationHandle { + + private Map executorProxyMap = new ConcurrentHashMap<>(); + + private LedgerManager ledgerManager; + + private HashDigest ledgerHash; + + @Override + public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, + LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, LedgerService ledgerService) { + ContractEventSendOperation contractOP = (ContractEventSendOperation) op; + + HashDigest txHash = requestContext.getRequest().getTransactionContent().getHash(); + + ExecutorProxy executorProxy = executorProxyMap.get(txHash); + + if (executorProxy != null) { + LedgerQueryService queryService = new LedgerQueryService(ledgerManager); + ContractLedgerContext ledgerContext = new ContractLedgerContext(queryService, opHandleContext); + + MockerContractEventContext contractEventContext = new MockerContractEventContext( + ledgerHash, contractOP.getEvent(), requestContext.getRequest(), ledgerContext); + + EventProcessingAwire eventProcessingAwire = (EventProcessingAwire) executorProxy.getInstance(); + try { + // + // Before处理过程 + eventProcessingAwire.beforeEvent(contractEventContext); + executorProxy.invoke(); + + // After处理过程 + eventProcessingAwire.postEvent(); + } catch (Exception e) { + eventProcessingAwire.postEvent(new ContractException(e.getMessage())); + } finally { + removeExecutorProxy(txHash); + } + } + } + + @Override + public boolean support(Class operationType) { + return ContractEventSendOperation.class.isAssignableFrom(operationType); + } + + public void initLedger(LedgerManager ledgerManager, HashDigest ledgerHash) { + this.ledgerManager = ledgerManager; + this.ledgerHash = ledgerHash; + } + + public void registerExecutorProxy(HashDigest hashDigest, ExecutorProxy executorProxy) { + executorProxyMap.put(hashDigest, executorProxy); + } + + public ExecutorProxy removeExecutorProxy(HashDigest hashDigest) { + return executorProxyMap.remove(hashDigest); + } + + public static class MockerContractEventContext implements ContractEventContext { + + private HashDigest ledgeHash; + + private String event; + + private TransactionRequest transactionRequest; + + private LedgerContext ledgerContext; + + public MockerContractEventContext(HashDigest ledgeHash, String event, + TransactionRequest transactionRequest, LedgerContext ledgerContext) { + this.ledgeHash = ledgeHash; + this.event = event; + this.transactionRequest = transactionRequest; + this.ledgerContext = ledgerContext; + } + + @Override + public HashDigest getCurrentLedgerHash() { + return ledgeHash; + } + + @Override + public TransactionRequest getTransactionRequest() { + return transactionRequest; + } + + @Override + public Set getTxSigners() { + return null; + } + + @Override + public String getEvent() { + return event; + } + + @Override + public byte[] getArgs() { + return null; + } + + @Override + public LedgerContext getLedger() { + return ledgerContext; + } + + @Override + public Set getContracOwners() { + return null; + } + } +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/handler/MockerNodeHandler.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/handler/MockerNodeHandler.java new file mode 100644 index 00000000..bd1bea24 --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/handler/MockerNodeHandler.java @@ -0,0 +1,189 @@ +package com.jd.blockchain.mocker.handler; + +import com.jd.blockchain.crypto.HashDigest; +import com.jd.blockchain.crypto.PrivKey; +import com.jd.blockchain.gateway.GatewayConfigProperties; +import com.jd.blockchain.mocker.config.MockerConstant; +import com.jd.blockchain.mocker.config.PresetAnswerPrompter; +import com.jd.blockchain.mocker.node.GatewayNodeRunner; +import com.jd.blockchain.mocker.node.NodeWebContext; +import com.jd.blockchain.mocker.node.PeerNodeRunner; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.Prompter; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.net.NetworkAddress; +import org.springframework.util.ResourceUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static com.jd.blockchain.mocker.config.MockerConstant.*; + +public class MockerNodeHandler { + + private static final String LEDGER_BINDING_FORMAT = "binding.%s."; + + private static final String PARTI_FORMAT = LEDGER_BINDING_FORMAT + "parti."; + + private static final String BINDING_ID_FORMAT = PARTI_FORMAT + "id"; + + private static final String BINDING_PK_PATH_FORMAT = PARTI_FORMAT + "pk-path"; + + private static final String BINDING_PK_FORMAT = PARTI_FORMAT + "pk"; + + private static final String BINDING_PWD_FORMAT = PARTI_FORMAT + "pwd"; + + private static final String BINDING_ADDRESS_FORMAT = PARTI_FORMAT + "address"; + + private static final String DB_FORMAT = LEDGER_BINDING_FORMAT + "db."; + + private static final String BINDING_DB_URI_FORMAT = DB_FORMAT + "uri"; + + private static final String BINDING_DB_PWD_FORMAT = DB_FORMAT + "pwd"; + + private PeerNodeRunner[] peerNodes; + + private GatewayNodeRunner gatewayNodeRunner; + + public void start(int nodeSize) throws Exception { + + HashDigest ledgerHash = ledgerInit(nodeSize); + + // 启动Peer节点 + peerNodes = peerNodeStart(nodeSize, ledgerHash); + + // 启动网关节点 + gatewayNodeRunner = gatewayNodeStart(peerNodes[0].getServiceAddress()); + } + + public LedgerInitProperties initLedgerProperties(int nodeSize) throws Exception { + + File ledgerInitFile = ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX + + String.format(MockerConstant.LEDGER_INIT_FORMATTER, nodeSize)); + + final LedgerInitProperties ledgerInitProperties = LedgerInitProperties.resolve(new FileInputStream(ledgerInitFile)); + + return ledgerInitProperties; + } + + private HashDigest ledgerInit(int nodeSize) throws Exception { + + System.out.println("----------- is daemon=" + Thread.currentThread().isDaemon()); + + Prompter consolePrompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); + + LedgerInitProperties initSetting = initLedgerProperties(nodeSize); + + Set hashDigestSet = new HashSet<>(); + + CountDownLatch quitLatch = new CountDownLatch(nodeSize); + + ExecutorService peerThreads = Executors.newFixedThreadPool(nodeSize); + + for (int i = 0; i < nodeSize; i++) { + final int nodeIndex = i; + peerThreads.execute(() -> { + // 启动服务器; + NetworkAddress initAddr = initSetting.getConsensusParticipant(nodeIndex).getInitializerAddress(); + NodeWebContext node = new NodeWebContext(nodeIndex, initAddr); + PrivKey privkey = KeyGenCommand.decodePrivKeyWithRawPassword(PRIVATE_KEYS[nodeIndex], PASSWORD); + DBConnectionConfig dbConn = new DBConnectionConfig(); + dbConn.setConnectionUri(MockerConstant.DB_MEMS[nodeIndex]); + ThreadInvoker.AsyncCallback nodeCallback = node.startInit(privkey, initSetting, dbConn, consolePrompter, + quitLatch); + hashDigestSet.add(nodeCallback.waitReturn()); + }); + } + + quitLatch.await(); + + if (hashDigestSet.size() != 1) { + throw new IllegalStateException(String.format("%s Node Ledger Init Fail !!!", nodeSize)); + } + return hashDigestSet.toArray(new HashDigest[hashDigestSet.size()])[0]; + } + + + public PeerNodeRunner[] peerNodeStart(int nodeSize, HashDigest ledgerHash) { + + int portStart = PEER_PORT_START; + + List> threadInvokers = new ArrayList<>(); + + final PeerNodeRunner[] peerNodeRunners = new PeerNodeRunner[nodeSize]; + + for (int i = 0; i < nodeSize; i++) { + NetworkAddress peerSrvAddr = new NetworkAddress(LOCAL_ADDRESS, portStart); + LedgerBindingConfig bindingConfig = loadBindingConfig(i, ledgerHash); + PeerNodeRunner peerNodeRunner = new PeerNodeRunner(peerSrvAddr, bindingConfig); + peerNodeRunners[i] = peerNodeRunner; + portStart += 10; + threadInvokers.add(peerNodeRunner.start()); + } + + // 等待结果 + for (ThreadInvoker.AsyncCallback threadInvoker : threadInvokers) { + threadInvoker.waitReturn(); + } + + return peerNodeRunners; + } + + public GatewayNodeRunner gatewayNodeStart(NetworkAddress peerAddress) { + GatewayConfigProperties.KeyPairConfig gwKeyPair = new GatewayConfigProperties.KeyPairConfig(); + gwKeyPair.setPubKeyValue(PUBLIC_KEYS[0]); + gwKeyPair.setPrivKeyValue(PRIVATE_KEYS[0]); + gwKeyPair.setPrivKeyPassword(PASSWORD_ENCODE); + GatewayNodeRunner gateway = new GatewayNodeRunner(LOCAL_ADDRESS, GATEWAY_PORT, gwKeyPair, + peerAddress, CONSENSUS_PROVIDERS,null); + + ThreadInvoker.AsyncCallback gwStarting = gateway.start(); + + gwStarting.waitReturn(); + + return gateway; + } + + public void stop() { + if (peerNodes != null) { + for (PeerNodeRunner peerNodeRunner : peerNodes) { + peerNodeRunner.stop(); + } + } + if (gatewayNodeRunner != null) { + gatewayNodeRunner.stop(); + } + } + + private LedgerBindingConfig loadBindingConfig(int nodeIndex, HashDigest ledgerHash) { + + Properties properties = new Properties(); + + String ledgerHashBase58 = ledgerHash.toBase58(); + + properties.put("ledger.bindings", ledgerHashBase58); + + properties.put(String.format(BINDING_ID_FORMAT, ledgerHashBase58), nodeIndex); + + properties.put(String.format(BINDING_PK_PATH_FORMAT, ledgerHashBase58), ""); + + properties.put(String.format(BINDING_PK_FORMAT, ledgerHashBase58), PUBLIC_KEYS[nodeIndex]); + + properties.put(String.format(BINDING_PWD_FORMAT, ledgerHashBase58), PASSWORD_ENCODE); + + properties.put(String.format(BINDING_ADDRESS_FORMAT, ledgerHashBase58), ADDRESS_ARRAY[nodeIndex]); + + properties.put(String.format(BINDING_DB_URI_FORMAT, ledgerHashBase58), DB_MEMS[nodeIndex]); + + properties.put(String.format(BINDING_DB_PWD_FORMAT, ledgerHashBase58), ""); + + return LedgerBindingConfig.resolve(properties); + } +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/handler/MockerOperationHandleRegister.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/handler/MockerOperationHandleRegister.java new file mode 100644 index 00000000..fe902c1f --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/handler/MockerOperationHandleRegister.java @@ -0,0 +1,51 @@ +package com.jd.blockchain.mocker.handler; + +import com.jd.blockchain.ledger.core.LedgerException; +import com.jd.blockchain.ledger.core.OperationHandle; +import com.jd.blockchain.ledger.core.impl.OperationHandleRegisteration; +import com.jd.blockchain.ledger.core.impl.handles.*; + +import java.util.ArrayList; +import java.util.List; + +public class MockerOperationHandleRegister implements OperationHandleRegisteration { + + private List opHandles = new ArrayList<>(); + + public MockerOperationHandleRegister() { + initDefaultHandles(); + } + + /** + * 针对不采用bean依赖注入的方式来处理; + */ + private void initDefaultHandles(){ + opHandles.add(new DataAccountKVSetOperationHandle()); + opHandles.add(new DataAccountRegisterOperationHandle()); + opHandles.add(new UserRegisterOperationHandle()); + opHandles.add(new ContractCodeDeployOperationHandle()); +// opHandles.add(new ContractEventSendOperationHandle()); + } + + public List getOpHandles() { + return opHandles; + } + + public void registerHandler(OperationHandle operationHandle) { + opHandles.add(operationHandle); + } + + public void removeHandler(OperationHandle operationHandle) { + opHandles.remove(operationHandle); + } + + @Override + public OperationHandle getHandle(Class operationType) { + for (OperationHandle handle : opHandles) { + if (handle.support(operationType)) { + return handle; + } + } + throw new LedgerException("Unsupported operation type[" + operationType.getName() + "]!"); + } +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/handler/MockerServiceHandler.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/handler/MockerServiceHandler.java new file mode 100644 index 00000000..0334d914 --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/handler/MockerServiceHandler.java @@ -0,0 +1,216 @@ +package com.jd.blockchain.mocker.handler; + +import com.jd.blockchain.crypto.HashDigest; +import com.jd.blockchain.crypto.PrivKey; +import com.jd.blockchain.crypto.PubKey; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.mocker.config.MockerConstant; +import com.jd.blockchain.mocker.data.KvData; +import com.jd.blockchain.mocker.data.ResponseData; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.tools.keygen.KeyGenCommand; + +public class MockerServiceHandler { + + private BlockchainKeypair defaultParticipant; + + private BlockchainKeypair defaultUser; + + private BlockchainKeypair defaultDataAccount; + + private String gatewayHost; + + private int gatewayPort; + + private GatewayServiceFactory gatewayServiceFactory; + + private BlockchainService blockchainService; + + private HashDigest ledgerHash; + + public MockerServiceHandler(String gatewayHost, int gatewayPort) { + this.gatewayHost = gatewayHost; + this.gatewayPort = gatewayPort; + init(); + } + + public BlockchainKeypair getDefaultParticipant() { + return defaultParticipant; + } + + public BlockchainKeypair getDefaultUser() { + return defaultUser; + } + + public BlockchainKeypair getDefaultDataAccount() { + return defaultDataAccount; + } + + public String getDefaultDataAccountAddress() { + return defaultDataAccount.getAddress().toBase58(); + } + + public String getGatewayHost() { + return gatewayHost; + } + + public int getGatewayPort() { + return gatewayPort; + } + + public GatewayServiceFactory getGatewayServiceFactory() { + return gatewayServiceFactory; + } + + public BlockchainService getBlockchainService() { + return blockchainService; + } + + public HashDigest getLedgerHash() { + return ledgerHash; + } + + private void init() { + + defaultParticipant = defaultParticipant(); + + gatewayServiceFactory = GatewayServiceFactory.connect(gatewayHost, gatewayPort, + false, defaultParticipant); + + blockchainService = gatewayServiceFactory.getBlockchainService(); + + HashDigest[] ledgerHashs = blockchainService.getLedgerHashs(); + + ledgerHash = ledgerHashs[0]; + + // 默认注册部分内容 + // 注册一个用户和一个数据账户 + TransactionTemplate txTemplate = blockchainService.newTransaction(ledgerHash); + + defaultUser = newKeypair(); + + defaultDataAccount = newKeypair(); + + // 注册用户 + txTemplate.users().register(defaultUser.getIdentity()); + + // 注册数据账户 + txTemplate.dataAccounts().register(defaultDataAccount.getIdentity()); + + // TX 准备就绪; + PreparedTransaction prepTx = txTemplate.prepare(); + + // 使用私钥进行签名; + prepTx.sign(defaultParticipant); + + // 提交交易; + TransactionResponse txResponse = prepTx.commit(); + + System.out.printf("Commit Transaction Result = %s \r\n", txResponse.isSuccess()); + } + + public BlockchainKeypair newKeypair() { + return BlockchainKeyGenerator.getInstance().generate(); + } + + private BlockchainKeypair defaultParticipant() { + PrivKey privKey = KeyGenCommand.decodePrivKeyWithRawPassword(MockerConstant.PRIVATE_KEYS[0], MockerConstant.PASSWORD); + PubKey pubKey = KeyGenCommand.decodePubKey(MockerConstant.PUBLIC_KEYS[0]); + return new BlockchainKeypair(pubKey, privKey); + } + + public ResponseData writeKv(String key, byte[] value) { + return writeKv(key, value, -1); + } + + public ResponseData writeKv(String key, byte[] value, long version) { + return writeKv(getDefaultDataAccountAddress(), key, value, version); + } + + public ResponseData writeKv(String dataAccount, String key, byte[] value) { + return writeKv(dataAccount, key, value, -1); + } + + public ResponseData writeKv(String dataAccount, String key, byte[] value, long version) { + + TransactionTemplate txTemplate = newTxTemplate(); + + txTemplate.dataAccount(dataAccount).set(key, value, version); + + TransactionResponse txResponse = txPrepareAndCommit(txTemplate); + + long saveVersion = version; + + if (txResponse.isSuccess()) { + saveVersion = version + 1; + } + + KvData kvData = new KvData(dataAccount, key, value, saveVersion); + + return new ResponseData(txResponse, kvData); + } + + public ResponseData registerUser() { + + BlockchainKeypair newUser = BlockchainKeyGenerator.getInstance().generate(); + + return registerUser(newUser); + } + + public ResponseData registerUser(BlockchainKeypair user) { + + TransactionTemplate txTemplate = newTxTemplate(); + + // 注册 + txTemplate.users().register(user.getIdentity()); + + TransactionResponse txResponse = txPrepareAndCommit(txTemplate); + + return new ResponseData(txResponse, user); + } + + public ResponseData registerDataAccount() { + + BlockchainKeypair newDataAccount = BlockchainKeyGenerator.getInstance().generate(); + + return registerDataAccount(newDataAccount); + } + + public ResponseData registerDataAccount(BlockchainKeypair dataAccount) { + + TransactionTemplate txTemplate = newTxTemplate(); + + // 注册 + txTemplate.dataAccounts().register(dataAccount.getIdentity()); + + TransactionResponse txResponse = txPrepareAndCommit(txTemplate); + + return new ResponseData(txResponse, dataAccount); + } + + public ResponseData deployContract() { + return null; + } + + public ResponseData executeContract() { + return null; + } + + private TransactionTemplate newTxTemplate() { + return blockchainService.newTransaction(ledgerHash); + } + + private TransactionResponse txPrepareAndCommit(TransactionTemplate txTemplate) { + // TX 准备就绪; + PreparedTransaction prepTx = txTemplate.prepare(); + + // 使用私钥进行签名; + prepTx.sign(defaultParticipant); + + // 提交交易; + TransactionResponse txResponse = prepTx.commit(); + + return txResponse; + } +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/node/GatewayNodeRunner.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/node/GatewayNodeRunner.java new file mode 100644 index 00000000..61520c4b --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/node/GatewayNodeRunner.java @@ -0,0 +1,75 @@ +package com.jd.blockchain.mocker.node; + +import com.jd.blockchain.gateway.GatewayConfigProperties; +import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; +import com.jd.blockchain.gateway.GatewayServerBooter; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.net.NetworkAddress; +import org.springframework.core.io.ClassPathResource; +import org.springframework.util.CollectionUtils; + +import java.util.Map; + +public class GatewayNodeRunner { + + private NetworkAddress serviceAddress; + + private GatewayServerBooter gatewayServer; + + public GatewayNodeRunner(String host, int port, KeyPairConfig gatewayDefaultKey, NetworkAddress masterPeerAddres) { + this(host, port, gatewayDefaultKey, masterPeerAddres, null,null); + } + + public GatewayNodeRunner(String host, int port, KeyPairConfig gatewayDefaultKey, NetworkAddress masterPeerAddres, String[] providers, + Map otherMap) { + this.serviceAddress = new NetworkAddress(host, port); + GatewayConfigProperties config = new GatewayConfigProperties(); + + config.http().setHost(host); + config.http().setPort(port); + + if (providers != null) { + for (String provider : providers) { + config.providerConfig().add(provider); + } + } + + config.setMasterPeerAddress(masterPeerAddres); + + config.keys().getDefault().setPubKeyValue(gatewayDefaultKey.getPubKeyValue()); + config.keys().getDefault().setPrivKeyValue(gatewayDefaultKey.getPrivKeyValue()); + config.keys().getDefault().setPrivKeyPassword(gatewayDefaultKey.getPrivKeyPassword()); + + if(!CollectionUtils.isEmpty(otherMap)){ + config.setDataRetrievalUrl(otherMap.get("DATA_RETRIEVAL_URL").toString()); + } + + + //get the springConfigLocation; + ClassPathResource configResource = new ClassPathResource("application-gw.properties"); + String springConfigLocation = "classPath:"+configResource.getPath(); + + this.gatewayServer = new GatewayServerBooter(config,springConfigLocation); + } + + public AsyncCallback start() { + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected Object invoke() throws Exception { + gatewayServer.start(); + return null; + } + }; + + return invoker.start(); + } + + public void stop() { + gatewayServer.close(); + } + + public NetworkAddress getServiceAddress() { + return serviceAddress; + } +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/node/NodeWebContext.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/node/NodeWebContext.java new file mode 100644 index 00000000..93096613 --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/node/NodeWebContext.java @@ -0,0 +1,106 @@ +package com.jd.blockchain.mocker.node; + +import com.jd.blockchain.crypto.HashDigest; +import com.jd.blockchain.crypto.PrivKey; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.core.LedgerInitDecision; +import com.jd.blockchain.ledger.core.LedgerInitPermission; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.mocker.config.LedgerInitWebConfiguration; +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.impl.composite.CompositeConnectionFactory; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.LedgerInitProcess; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.Prompter; +import com.jd.blockchain.tools.initializer.web.LedgerInitializeWebController; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.net.NetworkAddress; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; + +import java.util.concurrent.CountDownLatch; + +public class NodeWebContext { + + private NetworkAddress serverAddress; + + private DBConnectionConfig dbConnConfig; + + private volatile ConfigurableApplicationContext ctx; + + private volatile LedgerInitProcess initProcess; + + private volatile LedgerInitializeWebController controller; + + private volatile LedgerManager ledgerManager; + + private volatile CompositeConnectionFactory db; + + private int id; + + public int getId() { + return controller.getId(); + } + + public TransactionContent getInitTxContent() { + return controller.getInitTxContent(); + } + + public LedgerInitPermission getLocalPermission() { + return controller.getLocalPermission(); + } + + public LedgerInitDecision getLocalDecision() { + return controller.getLocalDecision(); + } + + public NodeWebContext(int id, NetworkAddress serverAddress) { + this.id = id; + this.serverAddress = serverAddress; + } + + public LedgerRepository registLedger(HashDigest ledgerHash) { + DbConnection conn = db.connect(dbConnConfig.getUri()); + LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, conn.getStorageService()); + return ledgerRepo; + } + + public ThreadInvoker.AsyncCallback startInit(PrivKey privKey, LedgerInitProperties setting, + DBConnectionConfig dbConnConfig, Prompter prompter, CountDownLatch quitLatch) { + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + doStartServer(); + + NodeWebContext.this.dbConnConfig = dbConnConfig; + HashDigest ledgerHash = NodeWebContext.this.initProcess.initialize(id, privKey, setting, + dbConnConfig, prompter); + + System.out.printf("ledgerHash = %s \r\n", ledgerHash.toBase58()); + + quitLatch.countDown(); + return ledgerHash; + } + }; + + return invoker.start(); + } + + public void doStartServer() { + String argServerAddress = String.format("--server.address=%s", serverAddress.getHost()); + String argServerPort = String.format("--server.port=%s", serverAddress.getPort()); + String nodebug = "--debug=false"; + String[] innerArgs = { argServerAddress, argServerPort, nodebug }; + + ctx = SpringApplication.run(LedgerInitWebConfiguration.class, innerArgs); + + ctx.setId("Node-" + id); + controller = ctx.getBean(LedgerInitializeWebController.class); + ledgerManager = ctx.getBean(LedgerManager.class); + db = ctx.getBean(CompositeConnectionFactory.class); + initProcess = ctx.getBean(LedgerInitProcess.class); + } +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/node/PeerNodeRunner.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/node/PeerNodeRunner.java new file mode 100644 index 00000000..ba90d280 --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/node/PeerNodeRunner.java @@ -0,0 +1,62 @@ +package com.jd.blockchain.mocker.node; + +import com.jd.blockchain.peer.PeerServerBooter; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.net.NetworkAddress; + +public class PeerNodeRunner { + + private NetworkAddress serviceAddress; + + private volatile PeerServerBooter peerServer; + + private LedgerBindingConfig ledgerBindingConfig; + + public DbConnectionFactory getDBConnectionFactory() { + return peerServer.getDBConnectionFactory(); + } + + public NetworkAddress getServiceAddress() { + return serviceAddress; + } + + public LedgerBindingConfig getLedgerBindingConfig() { + return ledgerBindingConfig; + } + + public PeerNodeRunner(NetworkAddress serviceAddress, LedgerBindingConfig ledgerBindingConfig) { + this(serviceAddress, ledgerBindingConfig, null); + } + + public PeerNodeRunner(NetworkAddress serviceAddress, LedgerBindingConfig ledgerBindingConfig, + DbConnectionFactory dbConnectionFactory) { + this.serviceAddress = serviceAddress; + this.ledgerBindingConfig = ledgerBindingConfig; + if (dbConnectionFactory == null) { + this.peerServer = new PeerServerBooter(ledgerBindingConfig, serviceAddress.getHost(), serviceAddress.getPort(),null); + }else { + this.peerServer = new PeerServerBooter(ledgerBindingConfig, serviceAddress.getHost(), serviceAddress.getPort(),null, + dbConnectionFactory); + } + } + + public AsyncCallback start() { + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected Object invoke() throws Exception { + peerServer.start(); + + return null; + } + }; + + return invoker.start(); + } + + public void stop() { + peerServer.close(); + } +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/proxy/ContractProxy.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/proxy/ContractProxy.java new file mode 100644 index 00000000..69fc305c --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/proxy/ContractProxy.java @@ -0,0 +1,80 @@ +package com.jd.blockchain.mocker.proxy; + +import com.jd.blockchain.contract.Contract; +import com.jd.blockchain.contract.ContractEvent; +import com.jd.blockchain.crypto.HashDigest; +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.mocker.MockerNodeContext; +import com.jd.blockchain.mocker.handler.MockerContractExeHandle; +import com.jd.blockchain.transaction.TxBuilder; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +public class ContractProxy implements InvocationHandler { + + private BlockchainIdentity identity; + + private MockerNodeContext mockerNodeContext; + + private T instance; + + private MockerContractExeHandle operationHandle; + + public ContractProxy(BlockchainIdentity identity, MockerNodeContext mockerNodeContext, + T instance, MockerContractExeHandle operationHandle) { + this.identity = identity; + this.mockerNodeContext = mockerNodeContext; + this.instance = instance; + this.operationHandle = operationHandle; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + // 实际执行时,首先判断执行的是否是添加注解的方法 + if (!isExecuteContractMethod(method)) { + return method.invoke(instance, args); + } + + // 首先发送一次执行的请求 + TxBuilder txBuilder = mockerNodeContext.txBuilder(); + + Class contractInft = null; + + Class[] instanceInfts = instance.getClass().getInterfaces(); + + for (Class instanceInft : instanceInfts) { + if (instanceInft.isAnnotationPresent(Contract.class)) { + contractInft = instanceInft; + break; + } + } + + if (contractInft == null) { + throw new IllegalStateException("This object does not implement the interface for the @Contract annotation !!!"); + } + + // 生成代理类 + Object proxyInstance = txBuilder.contract(identity.getAddress().toBase58(), contractInft); + // 代理方式执行一次 + method.invoke(proxyInstance, args); + + TransactionRequest txRequest = mockerNodeContext.txRequest(txBuilder); + + // 放入到Map中 + HashDigest txHash = txRequest.getTransactionContent().getHash(); + operationHandle.registerExecutorProxy(txHash, new ExecutorProxy(instance, method, args)); + + // 提交该请求至整个区块链系统 + mockerNodeContext.txProcess(txRequest); + // 不处理返回值 + return null; + } + + private boolean isExecuteContractMethod(Method method) { + Annotation annotation = method.getAnnotation(ContractEvent.class); + return annotation != null; + } +} diff --git a/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/proxy/ExecutorProxy.java b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/proxy/ExecutorProxy.java new file mode 100644 index 00000000..04873be5 --- /dev/null +++ b/source/tools/tools-mocker/src/main/java/com/jd/blockchain/mocker/proxy/ExecutorProxy.java @@ -0,0 +1,34 @@ +package com.jd.blockchain.mocker.proxy; + +import java.lang.reflect.Method; + +public class ExecutorProxy { + + private Object instance; + + private Method method; + + private Object[] args; + + public ExecutorProxy(Object instance, Method method, Object[] args) { + this.instance = instance; + this.method = method; + this.args = args; + } + + public Object getInstance() { + return instance; + } + + public Method getMethod() { + return method; + } + + public Object[] getArgs() { + return args; + } + + public Object invoke() throws Exception { + return method.invoke(instance, args); + } +} diff --git a/source/tools/tools-mocker/src/main/resources/bftsmart4.config b/source/tools/tools-mocker/src/main/resources/bftsmart4.config new file mode 100644 index 00000000..870fcde2 --- /dev/null +++ b/source/tools/tools-mocker/src/main/resources/bftsmart4.config @@ -0,0 +1,167 @@ +# Copyright (c) 2007-2013 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +############################################ +###### Consensus Commit Block Parameters: transaction count ###### +############################################ +system.block.txsize=500 + +############################################ +###### Consensus Commit Block Parameters: delay time ###### +############################################ +system.block.maxdelay=5000 + +############################################ +###### #Consensus Participant0 ###### +############################################ + +system.server.0.pubkey=3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9 +system.server.0.network.host=127.0.0.1 +system.server.0.network.port=16000 +system.server.0.network.secure=false + +############################################ +###### #Consensus Participant1 ###### +############################################ + +system.server.1.pubkey=3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX +system.server.1.network.host=127.0.0.1 +system.server.1.network.port=16100 +system.server.1.network.secure=false + +############################################ +###### #Consensus Participant2 ###### +############################################ + +system.server.2.pubkey=3snPdw7i7PZi6TStiyc6mzjprnNhgs2atSGNS8wPYzhbKaUWGFJt7x +system.server.2.network.host=127.0.0.1 +system.server.2.network.port=16200 +system.server.2.network.secure=false + +############################################ +###### #Consensus Participant3 ###### +############################################ + +system.server.3.pubkey=3snPdw7i7PifPuRX7fu3jBjsb3rJRfDe9GtbDfvFJaJ4V4hHXQfhwk +system.server.3.network.host=127.0.0.1 +system.server.3.network.port=16300 +system.server.3.network.secure=false + +############################################ +####### Communication Configurations ####### +############################################ + +#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) +#This parameter is not currently being used +#system.authentication.hmacAlgorithm = HmacSHA1 + +#Specify if the communication system should use a thread to send data (true or false) +system.communication.useSenderThread = true + +#Force all processes to use the same public/private keys pair and secret key. This is useful when deploying experiments +#and benchmarks, but must not be used in production systems. +system.communication.defaultkeys = true + +############################################ +### Replication Algorithm Configurations ### +############################################ + +#Number of servers in the group +system.servers.num = 4 + +#Maximum number of faulty replicas +system.servers.f = 1 + +#Timeout to asking for a client request +system.totalordermulticast.timeout = 2000 + +#Maximum batch size (in number of messages) +system.totalordermulticast.maxbatchsize = 500 + +#Number of nonces (for non-determinism actions) generated +system.totalordermulticast.nonces = 10 + +#if verification of leader-generated timestamps are increasing +#it can only be used on systems in which the network clocks +#are synchronized +system.totalordermulticast.verifyTimestamps = false + +#Quantity of messages that can be stored in the receive queue of the communication system +system.communication.inQueueSize = 500000 + +# Quantity of messages that can be stored in the send queue of each replica +system.communication.outQueueSize = 500000 + +#Set to 1 if SMaRt should use signatures, set to 0 if otherwise +system.communication.useSignatures = 0 + +#Set to 1 if SMaRt should use MAC's, set to 0 if otherwise +system.communication.useMACs = 1 + +#Set to 1 if SMaRt should use the standard output to display debug messages, set to 0 if otherwise +system.debug = 0 + +#Print information about the replica when it is shutdown +system.shutdownhook = true + +############################################ +###### State Transfer Configurations ####### +############################################ + +#Activate the state transfer protocol ('true' to activate, 'false' to de-activate) +system.totalordermulticast.state_transfer = true + +#Maximum ahead-of-time message not discarded +system.totalordermulticast.highMark = 10000 + +#Maximum ahead-of-time message not discarded when the replica is still on EID 0 (after which the state transfer is triggered) +system.totalordermulticast.revival_highMark = 10 + +#Number of ahead-of-time messages necessary to trigger the state transfer after a request timeout occurs +system.totalordermulticast.timeout_highMark = 200 + +############################################ +###### Log and Checkpoint Configurations ### +############################################ + +system.totalordermulticast.log = true +system.totalordermulticast.log_parallel = false +system.totalordermulticast.log_to_disk = false +system.totalordermulticast.sync_log = false + +#Period at which BFT-SMaRt requests the state to the application (for the state transfer state protocol) +system.totalordermulticast.checkpoint_period = 1000 +system.totalordermulticast.global_checkpoint_period = 120000 + +system.totalordermulticast.checkpoint_to_disk = false +system.totalordermulticast.sync_ckp = false + + +############################################ +###### Reconfiguration Configurations ###### +############################################ + +#Replicas ID for the initial view, separated by a comma. +# The number of replicas in this parameter should be equal to that specified in 'system.servers.num' +system.initial.view = 0,1,2,3 + +#The ID of the trust third party (TTP) +system.ttp.id = 7002 + +#This sets if the system will function in Byzantine or crash-only mode. Set to "true" to support Byzantine faults +system.bft = true + +#Custom View Storage; +#view.storage.handler=bftsmart.reconfiguration.views.DefaultViewStorage \ No newline at end of file diff --git a/source/tools/tools-mocker/src/main/resources/bftsmart8.config b/source/tools/tools-mocker/src/main/resources/bftsmart8.config new file mode 100644 index 00000000..814a6c3d --- /dev/null +++ b/source/tools/tools-mocker/src/main/resources/bftsmart8.config @@ -0,0 +1,208 @@ +# Copyright (c) 2007-2013 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +############################################ +###### Consensus Commit Block Parameters: transaction count ###### +############################################ +system.block.txsize=500 + +############################################ +###### Consensus Commit Block Parameters: delay time ###### +############################################ +system.block.maxdelay=5000 + +############################################ +###### #Consensus Participant0 ###### +############################################ + +system.server.0.pubkey=3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9 +system.server.0.network.host=127.0.0.1 +system.server.0.network.port=16000 +system.server.0.network.secure=false + +############################################ +###### #Consensus Participant1 ###### +############################################ + +system.server.1.pubkey=3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX +system.server.1.network.host=127.0.0.1 +system.server.1.network.port=16100 +system.server.1.network.secure=false + +############################################ +###### #Consensus Participant2 ###### +############################################ + +system.server.2.pubkey=3snPdw7i7PZi6TStiyc6mzjprnNhgs2atSGNS8wPYzhbKaUWGFJt7x +system.server.2.network.host=127.0.0.1 +system.server.2.network.port=16200 +system.server.2.network.secure=false + +############################################ +###### #Consensus Participant3 ###### +############################################ + +system.server.3.pubkey=3snPdw7i7PifPuRX7fu3jBjsb3rJRfDe9GtbDfvFJaJ4V4hHXQfhwk +system.server.3.network.host=127.0.0.1 +system.server.3.network.port=16300 +system.server.3.network.secure=false + + +############################################ +###### #Consensus Participant4 ###### +############################################ + +system.server.4.pubkey=3snPdw7i7PXPRMp3EAjsxJkHe7aZJRLNzdW8kEHBWeQsSgcPAiHP2J +system.server.4.network.host=127.0.0.1 +system.server.4.network.port=16400 +system.server.4.network.secure=false + + +############################################ +###### #Consensus Participant5 ###### +############################################ + +system.server.5.pubkey=3snPdw7i7PmmQoPgUpUmmAUj6nakHj8wMSQmiMi1RaiZp4YU1D4AXk +system.server.5.network.host=127.0.0.1 +system.server.5.network.port=16500 +system.server.5.network.secure=false + + +############################################ +###### #Consensus Participant6 ###### +############################################ + +system.server.6.pubkey=3snPdw7i7PiJKsa94q3EcLT1y6GRJ7LeFGe799hdzRRHmf6LNodyiM +system.server.6.network.host=127.0.0.1 +system.server.6.network.port=16600 +system.server.6.network.secure=false + + +############################################ +###### #Consensus Participant7 ###### +############################################ + +system.server.7.pubkey=3snPdw7i7Pm2wJwEnKn8kK8eGTkN36C2BZRRjVTr9FPB2rqtcgTq7h +system.server.7.network.host=127.0.0.1 +system.server.7.network.port=16700 +system.server.7.network.secure=false + + +############################################ +####### Communication Configurations ####### +############################################ + +#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) +#This parameter is not currently being used +#system.authentication.hmacAlgorithm = HmacSHA1 + +#Specify if the communication system should use a thread to send data (true or false) +system.communication.useSenderThread = true + +#Force all processes to use the same public/private keys pair and secret key. This is useful when deploying experiments +#and benchmarks, but must not be used in production systems. +system.communication.defaultkeys = true + +############################################ +### Replication Algorithm Configurations ### +############################################ + +#Number of servers in the group +system.servers.num = 8 + +#Maximum number of faulty replicas +system.servers.f = 2 + +#Timeout to asking for a client request +system.totalordermulticast.timeout = 2000 + +#Maximum batch size (in number of messages) +system.totalordermulticast.maxbatchsize = 500 + +#Number of nonces (for non-determinism actions) generated +system.totalordermulticast.nonces = 10 + +#if verification of leader-generated timestamps are increasing +#it can only be used on systems in which the network clocks +#are synchronized +system.totalordermulticast.verifyTimestamps = false + +#Quantity of messages that can be stored in the receive queue of the communication system +system.communication.inQueueSize = 500000 + +# Quantity of messages that can be stored in the send queue of each replica +system.communication.outQueueSize = 500000 + +#Set to 1 if SMaRt should use signatures, set to 0 if otherwise +system.communication.useSignatures = 0 + +#Set to 1 if SMaRt should use MAC's, set to 0 if otherwise +system.communication.useMACs = 1 + +#Set to 1 if SMaRt should use the standard output to display debug messages, set to 0 if otherwise +system.debug = 0 + +#Print information about the replica when it is shutdown +system.shutdownhook = true + +############################################ +###### State Transfer Configurations ####### +############################################ + +#Activate the state transfer protocol ('true' to activate, 'false' to de-activate) +system.totalordermulticast.state_transfer = true + +#Maximum ahead-of-time message not discarded +system.totalordermulticast.highMark = 10000 + +#Maximum ahead-of-time message not discarded when the replica is still on EID 0 (after which the state transfer is triggered) +system.totalordermulticast.revival_highMark = 10 + +#Number of ahead-of-time messages necessary to trigger the state transfer after a request timeout occurs +system.totalordermulticast.timeout_highMark = 200 + +############################################ +###### Log and Checkpoint Configurations ### +############################################ + +system.totalordermulticast.log = true +system.totalordermulticast.log_parallel = false +system.totalordermulticast.log_to_disk = false +system.totalordermulticast.sync_log = false + +#Period at which BFT-SMaRt requests the state to the application (for the state transfer state protocol) +system.totalordermulticast.checkpoint_period = 1000 +system.totalordermulticast.global_checkpoint_period = 120000 + +system.totalordermulticast.checkpoint_to_disk = false +system.totalordermulticast.sync_ckp = false + + +############################################ +###### Reconfiguration Configurations ###### +############################################ + +#Replicas ID for the initial view, separated by a comma. +# The number of replicas in this parameter should be equal to that specified in 'system.servers.num' +system.initial.view = 0,1,2,3,4,5,6,7 + +#The ID of the trust third party (TTP) +system.ttp.id = 7002 + +#This sets if the system will function in Byzantine or crash-only mode. Set to "true" to support Byzantine faults +system.bft = true + +#Custom View Storage; +#view.storage.handler=bftsmart.reconfiguration.views.DefaultViewStorage \ No newline at end of file diff --git a/source/tools/tools-mocker/src/main/resources/ledger4.init b/source/tools/tools-mocker/src/main/resources/ledger4.init new file mode 100644 index 00000000..4ed4cd06 --- /dev/null +++ b/source/tools/tools-mocker/src/main/resources/ledger4.init @@ -0,0 +1,72 @@ +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffa + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +ledger.name=JDChain + + +#共识服务提供者;必须; +consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider + +#共识服务的参数配置;必须; +consensus.conf=classpath:bftsmart4.config + +#密码服务提供者列表,以英文逗点“,”分隔;必须; +crypto.service-providers=com.jd.blockchain.crypto.service.classic.ClassicCryptoService, \ +com.jd.blockchain.crypto.service.sm.SMCryptoService + + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=4 + +#第0个参与方的名称; +cons_parti.0.name=jd.com +#第0个参与方的公钥文件路径; +cons_parti.0.pubkey-path= +#第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.0.pubkey=3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9 +#第0个参与方的账本初始服务的主机; +cons_parti.0.initializer.host=127.0.0.1 +#第0个参与方的账本初始服务的端口; +cons_parti.0.initializer.port=1601 +#第0个参与方的账本初始服务是否开启安全连接; +cons_parti.0.initializer.secure=false + +#第1个参与方的名称; +cons_parti.1.name=at.com +#第1个参与方的公钥文件路径; +cons_parti.1.pubkey-path= +#第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.1.pubkey=3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX +#第1个参与方的账本初始服务的主机; +cons_parti.1.initializer.host=127.0.0.1 +#第1个参与方的账本初始服务的端口; +cons_parti.1.initializer.port=1611 +#第1个参与方的账本初始服务是否开启安全连接; +cons_parti.1.initializer.secure=false + +#第2个参与方的名称; +cons_parti.2.name=bt.com +#第2个参与方的公钥文件路径; +cons_parti.2.pubkey-path= +#第2个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.2.pubkey=3snPdw7i7PZi6TStiyc6mzjprnNhgs2atSGNS8wPYzhbKaUWGFJt7x +#第2个参与方的账本初始服务的主机; +cons_parti.2.initializer.host=127.0.0.1 +#第2个参与方的账本初始服务的端口; +cons_parti.2.initializer.port=1621 +#第2个参与方的账本初始服务是否开启安全连接; +cons_parti.2.initializer.secure=false + +#第3个参与方的名称; +cons_parti.3.name=xt.com +#第3个参与方的公钥文件路径; +cons_parti.3.pubkey-path= +#第3个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.3.pubkey=3snPdw7i7PifPuRX7fu3jBjsb3rJRfDe9GtbDfvFJaJ4V4hHXQfhwk +#第3个参与方的账本初始服务的主机; +cons_parti.3.initializer.host=127.0.0.1 +#第3个参与方的账本初始服务的端口; +cons_parti.3.initializer.port=1631 +#第3个参与方的账本初始服务是否开启安全连接; +cons_parti.3.initializer.secure=false \ No newline at end of file diff --git a/source/tools/tools-mocker/src/main/resources/ledger8.init b/source/tools/tools-mocker/src/main/resources/ledger8.init new file mode 100644 index 00000000..dacbfeca --- /dev/null +++ b/source/tools/tools-mocker/src/main/resources/ledger8.init @@ -0,0 +1,125 @@ +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +ledger.name=JDChain + + +#共识服务提供者;必须; +consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider + +#共识服务的参数配置;必须; +consensus.conf=classpath:bftsmart8.config + +#密码服务提供者列表,以英文逗点“,”分隔;必须; +crypto.service-providers=com.jd.blockchain.crypto.service.classic.ClassicCryptoService, \ +com.jd.blockchain.crypto.service.sm.SMCryptoService + + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=8 + +#第0个参与方的名称; +cons_parti.0.name=jd.com +#第0个参与方的公钥文件路径; +cons_parti.0.pubkey-path= +#第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.0.pubkey=3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9 +#第0个参与方的账本初始服务的主机; +cons_parti.0.initializer.host=127.0.0.1 +#第0个参与方的账本初始服务的端口; +cons_parti.0.initializer.port=1601 +#第0个参与方的账本初始服务是否开启安全连接; +cons_parti.0.initializer.secure=false + +#第1个参与方的名称; +cons_parti.1.name=at.com +#第1个参与方的公钥文件路径; +cons_parti.1.pubkey-path= +#第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.1.pubkey=3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX +#第1个参与方的账本初始服务的主机; +cons_parti.1.initializer.host=127.0.0.1 +#第1个参与方的账本初始服务的端口; +cons_parti.1.initializer.port=1611 +#第1个参与方的账本初始服务是否开启安全连接; +cons_parti.1.initializer.secure=false + +#第2个参与方的名称; +cons_parti.2.name=bt.com +#第2个参与方的公钥文件路径; +cons_parti.2.pubkey-path= +#第2个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.2.pubkey=3snPdw7i7PZi6TStiyc6mzjprnNhgs2atSGNS8wPYzhbKaUWGFJt7x +#第2个参与方的账本初始服务的主机; +cons_parti.2.initializer.host=127.0.0.1 +#第2个参与方的账本初始服务的端口; +cons_parti.2.initializer.port=1621 +#第2个参与方的账本初始服务是否开启安全连接; +cons_parti.2.initializer.secure=false + +#第3个参与方的名称; +cons_parti.3.name=xt.com +#第3个参与方的公钥文件路径; +cons_parti.3.pubkey-path= +#第3个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.3.pubkey=3snPdw7i7PifPuRX7fu3jBjsb3rJRfDe9GtbDfvFJaJ4V4hHXQfhwk +#第3个参与方的账本初始服务的主机; +cons_parti.3.initializer.host=127.0.0.1 +#第3个参与方的账本初始服务的端口; +cons_parti.3.initializer.port=1631 +#第3个参与方的账本初始服务是否开启安全连接; +cons_parti.3.initializer.secure=false + + +#第4个参与方的名称; +cons_parti.4.name=mt.com +#第4个参与方的公钥文件路径; +cons_parti.4.pubkey-path= +#第4个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.4.pubkey=3snPdw7i7PXPRMp3EAjsxJkHe7aZJRLNzdW8kEHBWeQsSgcPAiHP2J +#第4个参与方的账本初始服务的主机; +cons_parti.4.initializer.host=127.0.0.1 +#第4个参与方的账本初始服务的端口; +cons_parti.4.initializer.port=1641 +#第4个参与方的账本初始服务是否开启安全连接; +cons_parti.4.initializer.secure=false + +#第5个参与方的名称; +cons_parti.5.name=nt.com +#第5个参与方的公钥文件路径; +cons_parti.5.pubkey-path= +#第5个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.5.pubkey=3snPdw7i7PmmQoPgUpUmmAUj6nakHj8wMSQmiMi1RaiZp4YU1D4AXk +#第5个参与方的账本初始服务的主机; +cons_parti.5.initializer.host=127.0.0.1 +#第5个参与方的账本初始服务的端口; +cons_parti.5.initializer.port=1651 +#第5个参与方的账本初始服务是否开启安全连接; +cons_parti.5.initializer.secure=false + +#第6个参与方的名称; +cons_parti.6.name=yt.com +#第6个参与方的公钥文件路径; +cons_parti.6.pubkey-path= +#第6个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.6.pubkey=3snPdw7i7PiJKsa94q3EcLT1y6GRJ7LeFGe799hdzRRHmf6LNodyiM +#第6个参与方的账本初始服务的主机; +cons_parti.6.initializer.host=127.0.0.1 +#第6个参与方的账本初始服务的端口; +cons_parti.6.initializer.port=1661 +#第6个参与方的账本初始服务是否开启安全连接; +cons_parti.6.initializer.secure=false + +#第7个参与方的名称; +cons_parti.7.name=zt.com +#第7个参与方的公钥文件路径; +cons_parti.7.pubkey-path= +#第7个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.7.pubkey=3snPdw7i7Pm2wJwEnKn8kK8eGTkN36C2BZRRjVTr9FPB2rqtcgTq7h +#第7个参与方的账本初始服务的主机; +cons_parti.7.initializer.host=127.0.0.1 +#第7个参与方的账本初始服务的端口; +cons_parti.7.initializer.port=1671 +#第7个参与方的账本初始服务是否开启安全连接; +cons_parti.7.initializer.secure=false \ No newline at end of file diff --git a/source/tools/tools-mocker/src/test/java/com/jd/blockchain/test/AccountMockerTest.java b/source/tools/tools-mocker/src/test/java/com/jd/blockchain/test/AccountMockerTest.java new file mode 100644 index 00000000..55699a40 --- /dev/null +++ b/source/tools/tools-mocker/src/test/java/com/jd/blockchain/test/AccountMockerTest.java @@ -0,0 +1,50 @@ +package com.jd.blockchain.test; + +import com.jd.blockchain.crypto.HashDigest; +import com.jd.blockchain.mocker.MockerNodeContext; +import com.jd.blockchain.mocker.contracts.AccountContract; +import com.jd.blockchain.mocker.contracts.AccountContractImpl; +import org.junit.Before; +import org.junit.Test; + +public class AccountMockerTest { + + String accountFrom = "zhangsan"; + + String accountTo = "lisi"; + + MockerNodeContext mockerNodeContext = null; + + HashDigest ledgerHash = null; + + @Before + public void init() { + mockerNodeContext = new MockerNodeContext().build(); + ledgerHash = mockerNodeContext.getLedgerHash(); + } + + @Test + public void test() { + // 首先创建一个数据账户 + String address = mockerNodeContext.registerDataAccount(); + + // 处理合约 + AccountContract accountContract = new AccountContractImpl(); + + // 发布合约 + accountContract = mockerNodeContext.deployContract(accountContract); + + //首先创建账户 + accountContract.create(address, accountFrom, 1000L); + + accountContract.create(address, accountTo, 1000L); + + accountContract.print(address, accountFrom, accountTo); + + // 开始转账 + accountContract.transfer(address, accountFrom, accountTo, 500); + + // 打印转账后结果 + accountContract.print(address, accountFrom, accountTo); + } +} diff --git a/source/tools/tools-mocker/src/test/java/com/jd/blockchain/test/MockTest.java b/source/tools/tools-mocker/src/test/java/com/jd/blockchain/test/MockTest.java new file mode 100644 index 00000000..7cd85dbc --- /dev/null +++ b/source/tools/tools-mocker/src/test/java/com/jd/blockchain/test/MockTest.java @@ -0,0 +1,54 @@ +package com.jd.blockchain.test; + +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.mocker.MockerNodeContext; +import com.jd.blockchain.mocker.config.MockerConstant; +import com.jd.blockchain.mocker.contracts.WriteContract; +import com.jd.blockchain.mocker.contracts.WriteContractImpl; +import org.junit.Test; + +public class MockTest { + + @Test + public void test() { + // 准备环境 + BlockchainKeypair blockchainKeypair = BlockchainKeyGenerator.getInstance().generate(); + MockerNodeContext mockerNodeContext = + new MockerNodeContext(MockerConstant.DEFAULT_LEDGER_SEED) + .participants("zhangsan", blockchainKeypair) + .build(); + HashDigest ledgerHash = mockerNodeContext.getLedgerHash(); + + System.out.printf("LedgerHash = %s \r\n", ledgerHash.toBase58()); + System.out.printf("LedgerSeed = %s \r\n", mockerNodeContext.getLedgerSeed()); + + // 注册用户 + String userAddress = mockerNodeContext.registerUser(BlockchainKeyGenerator.getInstance().generate()); + System.out.printf("----- 注册用户地址 {%s} -----\r\n", userAddress); + + // 注册数据账户 + String dataAccountAddress = mockerNodeContext.registerDataAccount(BlockchainKeyGenerator.getInstance().generate()); + System.out.printf("----- 注册数据账户地址 {%s} -----\r\n", dataAccountAddress); + + WriteContract writeContract = new WriteContractImpl(); + + // 发布合约 + writeContract = mockerNodeContext.deployContract(writeContract); + + writeContract.print("张三"); + + String key = "Hello", value = "World"; + + writeContract.writeKv(dataAccountAddress, key, value); + + // 查询 + KVDataEntry[] kvDataEntries = mockerNodeContext.getDataEntries(ledgerHash, dataAccountAddress, key); + + for (KVDataEntry kvDataEntry : kvDataEntries) { + System.out.printf("Key = %s, Value = %s \r\n", kvDataEntry.getKey(), kvDataEntry.getValue()); + } + } +} diff --git a/source/tools/tools-mocker/src/test/java/com/jd/blockchain/test/SampleTest.java b/source/tools/tools-mocker/src/test/java/com/jd/blockchain/test/SampleTest.java new file mode 100644 index 00000000..e66d534e --- /dev/null +++ b/source/tools/tools-mocker/src/test/java/com/jd/blockchain/test/SampleTest.java @@ -0,0 +1,47 @@ +package com.jd.blockchain.test; + +import com.jd.blockchain.crypto.HashDigest; +import com.jd.blockchain.ledger.KVDataEntry; +import com.jd.blockchain.mocker.MockerNodeContext; +import com.jd.blockchain.mocker.contracts.WriteContract; +import com.jd.blockchain.mocker.contracts.WriteContractImpl; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class SampleTest { + + MockerNodeContext mockerNodeContext = null; + + HashDigest ledgerHash = null; + + @Before + public void init() { + mockerNodeContext = new MockerNodeContext().build(); + ledgerHash = mockerNodeContext.getLedgerHash(); + } + + @Test + public void writeTest() { + + String key = "MyKey-" + System.currentTimeMillis(), value = "JDChain"; + + WriteContract writeContract = new WriteContractImpl(); + + String dataAccountAddress = mockerNodeContext.registerDataAccount(); + + writeContract = mockerNodeContext.deployContract(writeContract); + + writeContract.writeKv(dataAccountAddress, key, value); + + // 查询结果 + KVDataEntry[] dataEntries = mockerNodeContext.getDataEntries(ledgerHash, dataAccountAddress, key); + + for (KVDataEntry kvDataEntry : dataEntries) { + assertEquals(key, kvDataEntry.getKey()); + assertEquals(value, kvDataEntry.getValue()); + System.out.printf("Key = %s, Value = %s \r\n", kvDataEntry.getKey(), kvDataEntry.getValue()); + } + } +}