| @@ -1,55 +0,0 @@ | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>consensus-core</artifactId> | |||||
| <version>1.2.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>consensus-bftsmart</artifactId> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>bft-smart</artifactId> | |||||
| <version>${bftsmart.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>base</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>consensus-framework</artifactId> | |||||
| <version>${framework.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>utils-common</artifactId> | |||||
| <version>${framework.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.apache.commons</groupId> | |||||
| <artifactId>commons-pool2</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>ledger-model</artifactId> | |||||
| <version>${framework.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>tools-keygen</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <!-- <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>ledger-core</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> --> | |||||
| </dependencies> | |||||
| </project> | |||||
| @@ -1,67 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| public class BftsmartClientIncomingConfig implements BftsmartClientIncomingSettings { | |||||
| private BftsmartConsensusSettings consensusSettings; | |||||
| private byte[] topology; | |||||
| private byte[] tomConfig; | |||||
| private int clientId; | |||||
| private PubKey pubKey; | |||||
| @Override | |||||
| public BftsmartConsensusSettings getConsensusSettings() { | |||||
| return consensusSettings; | |||||
| } | |||||
| public void setConsensusSettings(BftsmartConsensusSettings consensusSettings) { | |||||
| this.consensusSettings = consensusSettings; | |||||
| } | |||||
| @Override | |||||
| public byte[] getTopology() { | |||||
| return topology; | |||||
| } | |||||
| public void setTopology(byte[] topology) { | |||||
| this.topology = topology; | |||||
| } | |||||
| @Override | |||||
| public int getClientId() { | |||||
| return clientId; | |||||
| } | |||||
| @Override | |||||
| public String getProviderName() { | |||||
| return BftsmartConsensusProvider.NAME; | |||||
| } | |||||
| public void setClientId(int clientId) { | |||||
| this.clientId = clientId; | |||||
| } | |||||
| @Override | |||||
| public byte[] getTomConfig() { | |||||
| return tomConfig; | |||||
| } | |||||
| public void setTomConfig(byte[] tomConfig) { | |||||
| this.tomConfig = tomConfig; | |||||
| } | |||||
| @Override | |||||
| public PubKey getPubKey() { | |||||
| return pubKey; | |||||
| } | |||||
| public void setPubKey(PubKey pubKey) { | |||||
| this.pubKey = pubKey; | |||||
| } | |||||
| } | |||||
| @@ -1,22 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart; | |||||
| import com.jd.blockchain.binaryproto.DataContract; | |||||
| import com.jd.blockchain.binaryproto.DataField; | |||||
| import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
| import com.jd.blockchain.consensus.ClientIncomingSettings; | |||||
| import com.jd.blockchain.consts.DataCodes; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| @DataContract(code = DataCodes.CONSENSUS_BFTSMART_CLI_INCOMING_SETTINGS) | |||||
| public interface BftsmartClientIncomingSettings extends ClientIncomingSettings { | |||||
| @DataField(order = 1, primitiveType = PrimitiveType.BYTES) | |||||
| byte[] getTopology(); | |||||
| @DataField(order = 2, primitiveType = PrimitiveType.BYTES) | |||||
| byte[] getTomConfig(); | |||||
| @DataField(order = 3, primitiveType=PrimitiveType.BYTES) | |||||
| PubKey getPubKey(); | |||||
| } | |||||
| @@ -1,35 +0,0 @@ | |||||
| //package com.jd.blockchain.consensus.bftsmart; | |||||
| // | |||||
| //public class BftsmartCommitBlockConfig implements BftsmartCommitBlockSettings { | |||||
| // | |||||
| // private int txSizePerBlock; | |||||
| // | |||||
| // private long maxDelayMilliSecondsPerBlock; | |||||
| // | |||||
| // | |||||
| // public BftsmartCommitBlockConfig() { | |||||
| // | |||||
| // } | |||||
| // | |||||
| // public BftsmartCommitBlockConfig(int txSizePerBlock, long maxDelayMilliSecondsPerBlock) { | |||||
| // this.txSizePerBlock = txSizePerBlock; | |||||
| // this.maxDelayMilliSecondsPerBlock = maxDelayMilliSecondsPerBlock; | |||||
| // } | |||||
| // @Override | |||||
| // public int getTxSizePerBlock() { | |||||
| // return txSizePerBlock; | |||||
| // } | |||||
| // | |||||
| // public void setTxSizePerBlock(int txSizePerBlock) { | |||||
| // this.txSizePerBlock = txSizePerBlock; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public long getMaxDelayMilliSecondsPerBlock() { | |||||
| // return maxDelayMilliSecondsPerBlock; | |||||
| // } | |||||
| // | |||||
| // public void setMaxDelayMilliSecondsPerBlock(long maxDelayMilliSecondsPerBlock) { | |||||
| // this.maxDelayMilliSecondsPerBlock = maxDelayMilliSecondsPerBlock; | |||||
| // } | |||||
| //} | |||||
| @@ -1,17 +0,0 @@ | |||||
| //package com.jd.blockchain.consensus.bftsmart; | |||||
| // | |||||
| //import com.jd.blockchain.binaryproto.DataContract; | |||||
| //import com.jd.blockchain.binaryproto.DataField; | |||||
| //import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
| //import com.jd.blockchain.consts.DataCodes; | |||||
| // | |||||
| // | |||||
| //@DataContract(code = DataCodes.CONSENSUS_BFTSMART_BLOCK_SETTINGS) | |||||
| //public interface BftsmartCommitBlockSettings { | |||||
| // | |||||
| // @DataField(order = 0, primitiveType = PrimitiveType.INT32) | |||||
| // int getTxSizePerBlock(); | |||||
| // | |||||
| // @DataField(order = 1, primitiveType = PrimitiveType.INT64) | |||||
| // long getMaxDelayMilliSecondsPerBlock(); | |||||
| //} | |||||
| @@ -1,54 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart; | |||||
| import com.jd.blockchain.binaryproto.DataContractRegistry; | |||||
| import com.jd.blockchain.utils.Property; | |||||
| public class BftsmartConsensusConfig implements BftsmartConsensusSettings { | |||||
| private Property[] bftsmartSystemConfig; | |||||
| private BftsmartNodeSettings[] nodes; | |||||
| // private BftsmartCommitBlockSettings commitBlockSettings; | |||||
| static { | |||||
| DataContractRegistry.register(BftsmartConsensusSettings.class); | |||||
| } | |||||
| /** | |||||
| * 创建 bftsmart 共识配置; | |||||
| * | |||||
| * @param nodes | |||||
| * 节点列表 | |||||
| * @param commitBlockSettings | |||||
| * 结块设置; | |||||
| * @param bftsmartSystemConfigs | |||||
| * bftsmart系统配置; | |||||
| */ | |||||
| public BftsmartConsensusConfig(BftsmartNodeSettings[] nodes, | |||||
| // BftsmartCommitBlockSettings commitBlockSettings, | |||||
| Property[] bftsmartSystemConfigs) { | |||||
| this.nodes = nodes; | |||||
| // this.commitBlockSettings = commitBlockSettings; | |||||
| this.bftsmartSystemConfig = bftsmartSystemConfigs; | |||||
| } | |||||
| @Override | |||||
| public BftsmartNodeSettings[] getNodes() { | |||||
| return nodes; | |||||
| } | |||||
| @Override | |||||
| public Property[] getSystemConfigs() { | |||||
| return bftsmartSystemConfig; | |||||
| } | |||||
| // @Override | |||||
| // public BftsmartCommitBlockSettings getCommitBlockSettings() { | |||||
| // return commitBlockSettings; | |||||
| // } | |||||
| // | |||||
| // | |||||
| // public void setCommitBlockSettings(BftsmartCommitBlockSettings commitBlockSettings) { | |||||
| // this.commitBlockSettings = commitBlockSettings; | |||||
| // } | |||||
| } | |||||
| @@ -1,42 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart; | |||||
| import com.jd.blockchain.consensus.ConsensusProvider; | |||||
| import com.jd.blockchain.consensus.SettingsFactory; | |||||
| import com.jd.blockchain.consensus.bftsmart.client.BftsmartConsensusClientFactory; | |||||
| import com.jd.blockchain.consensus.bftsmart.service.BftsmartNodeServerFactory; | |||||
| import com.jd.blockchain.consensus.client.ClientFactory; | |||||
| import com.jd.blockchain.consensus.service.NodeServerFactory; | |||||
| public class BftsmartConsensusProvider implements ConsensusProvider { | |||||
| public static final String NAME = BftsmartConsensusProvider.class.getName(); | |||||
| private static BftsmartSettingsFactory settingsFactory = new BftsmartSettingsFactory(); | |||||
| private static BftsmartConsensusClientFactory clientFactory = new BftsmartConsensusClientFactory(); | |||||
| private static BftsmartNodeServerFactory nodeServerFactory = new BftsmartNodeServerFactory(); | |||||
| @Override | |||||
| public String getName() { | |||||
| return NAME; | |||||
| } | |||||
| @Override | |||||
| public SettingsFactory getSettingsFactory() { | |||||
| return settingsFactory; | |||||
| } | |||||
| @Override | |||||
| public ClientFactory getClientFactory() { | |||||
| return clientFactory; | |||||
| } | |||||
| @Override | |||||
| public NodeServerFactory getServerFactory() { | |||||
| return nodeServerFactory; | |||||
| } | |||||
| } | |||||
| @@ -1,20 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart; | |||||
| import com.jd.blockchain.binaryproto.DataContract; | |||||
| import com.jd.blockchain.binaryproto.DataField; | |||||
| import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
| import com.jd.blockchain.consensus.ConsensusSettings; | |||||
| import com.jd.blockchain.consts.DataCodes; | |||||
| import com.jd.blockchain.utils.Property; | |||||
| import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; | |||||
| @DataContract(code = DataCodes.CONSENSUS_BFTSMART_SETTINGS) | |||||
| public interface BftsmartConsensusSettings extends ConsensusSettings { | |||||
| @DataField(order = 1, primitiveType = PrimitiveType.BYTES, list=true) | |||||
| Property[] getSystemConfigs(); | |||||
| // @DataField(order = 2, refContract = true) | |||||
| // BftsmartCommitBlockSettings getCommitBlockSettings(); | |||||
| } | |||||
| @@ -1,308 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| import java.util.Properties; | |||||
| import com.jd.blockchain.consensus.ConsensusProviders; | |||||
| import com.jd.blockchain.consensus.NodeSettings; | |||||
| import com.jd.blockchain.ledger.ParticipantInfo; | |||||
| import com.jd.blockchain.ledger.ParticipantNode; | |||||
| import com.jd.blockchain.tools.keygen.KeyGenCommand; | |||||
| import com.jd.blockchain.utils.Bytes; | |||||
| import com.jd.blockchain.utils.PropertiesUtils; | |||||
| import com.jd.blockchain.utils.Property; | |||||
| import com.jd.blockchain.utils.codec.Base58Utils; | |||||
| import com.jd.blockchain.utils.io.BytesUtils; | |||||
| import com.jd.blockchain.utils.net.NetworkAddress; | |||||
| import org.springframework.core.io.ClassPathResource; | |||||
| import com.jd.blockchain.consensus.ConsensusSettings; | |||||
| import com.jd.blockchain.consensus.ConsensusSettingsBuilder; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| public class BftsmartConsensusSettingsBuilder implements ConsensusSettingsBuilder { | |||||
| private static final int DEFAULT_TXSIZE = 1000; | |||||
| private static final int DEFAULT_MAXDELAY = 1000; | |||||
| private static final String CONFIG_TEMPLATE_FILE = "bftsmart.config"; | |||||
| private static final String CONFIG_LEDGER_INIT = "ledger.init"; | |||||
| /** | |||||
| * 参数键:节点数量; | |||||
| */ | |||||
| public static final String SERVER_NUM_KEY = "system.servers.num"; | |||||
| /** | |||||
| * 参数键:结块条件设置; | |||||
| */ | |||||
| public static final String BFTSMART_BLOCK_TXSIZE_KEY = "system.block.txsize"; | |||||
| public static final String BFTSMART_BLOCK_MAXDELAY_KEY = "system.block.maxdelay"; | |||||
| // /** | |||||
| // * 参数键格式:节点地址; | |||||
| // */ | |||||
| // public static final String ADDRESS_PATTERN = "node.%s.address"; | |||||
| /** | |||||
| * 参数键格式:节点公钥; | |||||
| */ | |||||
| public static final String PUBKEY_PATTERN = "system.server.%s.pubkey"; | |||||
| /** | |||||
| * 参数键格式:节点共识服务的网络地址; | |||||
| */ | |||||
| public static final String CONSENSUS_HOST_PATTERN = "system.server.%s.network.host"; | |||||
| /** | |||||
| * 参数键格式:节点共识服务的端口; | |||||
| */ | |||||
| public static final String CONSENSUS_PORT_PATTERN = "system.server.%s.network.port"; | |||||
| /** | |||||
| * 参数键格式:节点共识服务的通讯是否开启安全选项; | |||||
| */ | |||||
| public static final String CONSENSUS_SECURE_PATTERN = "system.server.%s.network.secure"; | |||||
| public static final String BFTSMART_PROVIDER = "com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider"; | |||||
| private static Properties CONFIG_TEMPLATE; | |||||
| static { | |||||
| ClassPathResource configResource = new ClassPathResource(CONFIG_TEMPLATE_FILE); | |||||
| try { | |||||
| try (InputStream in = configResource.getInputStream()) { | |||||
| CONFIG_TEMPLATE = PropertiesUtils.load(in, BytesUtils.DEFAULT_CHARSET); | |||||
| } | |||||
| } catch (IOException e) { | |||||
| throw new IllegalStateException(e.getMessage(), e); | |||||
| } | |||||
| } | |||||
| //解析得到结块的相关配置信息 | |||||
| // public BftsmartCommitBlockConfig createBlockConfig(Properties resolvingProps) { | |||||
| // BftsmartCommitBlockConfig blockConfig = new BftsmartCommitBlockConfig(); | |||||
| // | |||||
| // String txSizeString = PropertiesUtils.getRequiredProperty(resolvingProps, BFTSMART_BLOCK_TXSIZE_KEY); | |||||
| // resolvingProps.remove(BFTSMART_BLOCK_TXSIZE_KEY); | |||||
| // | |||||
| // if (txSizeString == null || txSizeString.length() == 0) { | |||||
| // blockConfig.setTxSizePerBlock(DEFAULT_TXSIZE); | |||||
| // } | |||||
| // else { | |||||
| // blockConfig.setTxSizePerBlock(Integer.parseInt(txSizeString)); | |||||
| // } | |||||
| // | |||||
| // String maxDelayString = PropertiesUtils.getRequiredProperty(resolvingProps, BFTSMART_BLOCK_MAXDELAY_KEY); | |||||
| // resolvingProps.remove(BFTSMART_BLOCK_MAXDELAY_KEY); | |||||
| // | |||||
| // if (maxDelayString == null || maxDelayString.length() == 0) { | |||||
| // blockConfig.setMaxDelayMilliSecondsPerBlock(DEFAULT_MAXDELAY); | |||||
| // } | |||||
| // else { | |||||
| // blockConfig.setMaxDelayMilliSecondsPerBlock(Long.parseLong(maxDelayString)); | |||||
| // } | |||||
| // | |||||
| // return blockConfig; | |||||
| // } | |||||
| @Override | |||||
| public Properties createPropertiesTemplate() { | |||||
| return PropertiesUtils.cloneFrom(CONFIG_TEMPLATE); | |||||
| } | |||||
| @Override | |||||
| public BftsmartConsensusSettings createSettings(Properties props, ParticipantNode[] participantNodes) { | |||||
| Properties resolvingProps = PropertiesUtils.cloneFrom(props); | |||||
| int serversNum = PropertiesUtils.getInt(resolvingProps, SERVER_NUM_KEY); | |||||
| if (serversNum < 0) { | |||||
| throw new IllegalArgumentException(String.format("Property[%s] is negative!", SERVER_NUM_KEY)); | |||||
| } | |||||
| if (serversNum < 4) { | |||||
| throw new IllegalArgumentException(String.format("Property[%s] is less than 4!", SERVER_NUM_KEY)); | |||||
| } | |||||
| if (participantNodes == null) { | |||||
| throw new IllegalArgumentException("ParticipantNodes is Empty !!!"); | |||||
| } | |||||
| if (serversNum != participantNodes.length) { | |||||
| throw new IllegalArgumentException(String.format("Property[%s] which is [%s] unequal " + | |||||
| "ParticipantNodes's length which is [%s] !", SERVER_NUM_KEY, serversNum, participantNodes.length)); | |||||
| } | |||||
| // BftsmartCommitBlockConfig blockConfig = createBlockConfig(resolvingProps); | |||||
| BftsmartNodeSettings[] nodesSettings = new BftsmartNodeSettings[serversNum]; | |||||
| for (int i = 0; i < serversNum; i++) { | |||||
| int id = i; | |||||
| // String keyOfPubkey = keyOfNode(PUBKEY_PATTERN, id); | |||||
| // String base58PubKey = PropertiesUtils.getRequiredProperty(resolvingProps, keyOfPubkey); | |||||
| // PubKey pubKey = new PubKey(Base58Utils.decode(base58PubKey)); | |||||
| // PubKey pubKey = KeyGenCommand.decodePubKey(base58PubKey); | |||||
| PubKey pubKey = participantNodes[i].getPubKey(); | |||||
| // resolvingProps.remove(keyOfPubkey); | |||||
| String keyOfHost = keyOfNode(CONSENSUS_HOST_PATTERN, id); | |||||
| String networkAddressHost = PropertiesUtils.getRequiredProperty(resolvingProps, keyOfHost); | |||||
| resolvingProps.remove(keyOfHost); | |||||
| String keyOfPort = keyOfNode(CONSENSUS_PORT_PATTERN, id); | |||||
| int networkAddressPort = PropertiesUtils.getInt(resolvingProps, keyOfPort); | |||||
| resolvingProps.remove(keyOfPort); | |||||
| String keyOfSecure = keyOfNode(CONSENSUS_SECURE_PATTERN, id); | |||||
| boolean networkAddressSecure = PropertiesUtils.getBoolean(resolvingProps, keyOfSecure); | |||||
| resolvingProps.remove(keyOfSecure); | |||||
| BftsmartNodeConfig nodeConfig = new BftsmartNodeConfig(pubKey, id, | |||||
| new NetworkAddress(networkAddressHost, networkAddressPort, networkAddressSecure)); | |||||
| nodesSettings[i] = nodeConfig; | |||||
| } | |||||
| BftsmartConsensusConfig config = new BftsmartConsensusConfig(nodesSettings, | |||||
| // blockConfig, | |||||
| PropertiesUtils.getOrderedValues(resolvingProps)); | |||||
| return config; | |||||
| } | |||||
| @Override | |||||
| public Bytes updateSettings(Bytes oldConsensusSettings, ParticipantInfo participantInfo) { | |||||
| //update consensus setting through node and system config two aspects | |||||
| BftsmartConsensusSettings consensusSettings = (BftsmartConsensusSettings) ConsensusProviders.getProvider(BFTSMART_PROVIDER).getSettingsFactory().getConsensusSettingsEncoder().decode(oldConsensusSettings.toBytes()); | |||||
| Property[] systemConfigs = systemConfigs(consensusSettings.getSystemConfigs()); | |||||
| BftsmartNodeSettings[] nodeSettings = nodeSettings(consensusSettings.getNodes(), participantInfo); | |||||
| BftsmartConsensusConfig bftsmartConsensusConfig = new BftsmartConsensusConfig(nodeSettings, systemConfigs); | |||||
| // for(int i = 0 ;i < bftsmartConsensusConfig.getNodes().length; i++) { | |||||
| // System.out.printf("id = %d, host = %s, port = %d\r\n", bftsmartConsensusConfig.getNodes()[i].getId(), bftsmartConsensusConfig.getNodes()[i].getNetworkAddress().getHost(), bftsmartConsensusConfig.getNodes()[i].getNetworkAddress().getPort()); | |||||
| // } | |||||
| // | |||||
| // for(int i = 0 ;i < bftsmartConsensusConfig.getSystemConfigs().length; i++) { | |||||
| // System.out.printf("property name = %s, property value = %s\r\n",bftsmartConsensusConfig.getSystemConfigs()[i].getName(), bftsmartConsensusConfig.getSystemConfigs()[i].getValue()); | |||||
| // } | |||||
| return new Bytes(ConsensusProviders.getProvider(BFTSMART_PROVIDER).getSettingsFactory().getConsensusSettingsEncoder().encode(bftsmartConsensusConfig)); | |||||
| } | |||||
| private static String keyOfNode(String pattern, int id) { | |||||
| return String.format(pattern, id); | |||||
| } | |||||
| @Override | |||||
| public void writeSettings(ConsensusSettings settings, Properties props) { | |||||
| int serversNum = PropertiesUtils.getInt(props, SERVER_NUM_KEY); | |||||
| if (serversNum > 0) { | |||||
| for (int i = 0; i < serversNum; i++) { | |||||
| int id = i; | |||||
| // String keyOfPubkey = keyOfNode(PUBKEY_PATTERN, id); | |||||
| // props.remove(keyOfPubkey); | |||||
| String keyOfHost = keyOfNode(CONSENSUS_HOST_PATTERN, id); | |||||
| props.remove(keyOfHost); | |||||
| String keyOfPort = keyOfNode(CONSENSUS_PORT_PATTERN, id); | |||||
| props.remove(keyOfPort); | |||||
| String keyOfSecure = keyOfNode(CONSENSUS_SECURE_PATTERN, id); | |||||
| props.remove(keyOfSecure); | |||||
| } | |||||
| } | |||||
| BftsmartConsensusSettings bftsmartSettings = (BftsmartConsensusSettings) settings; | |||||
| BftsmartNodeSettings[] nodesSettings = (BftsmartNodeSettings[]) bftsmartSettings.getNodes(); | |||||
| serversNum = nodesSettings.length; | |||||
| props.setProperty(SERVER_NUM_KEY, serversNum + ""); | |||||
| //获得结块相关的属性信息 | |||||
| // BftsmartCommitBlockSettings blockSettings = bftsmartSettings.getCommitBlockSettings(); | |||||
| // if (blockSettings == null) { | |||||
| // props.setProperty(BFTSMART_BLOCK_TXSIZE_KEY, DEFAULT_TXSIZE + ""); | |||||
| // props.setProperty(BFTSMART_BLOCK_MAXDELAY_KEY, DEFAULT_MAXDELAY + ""); | |||||
| // } else { | |||||
| // int txSize = blockSettings.getTxSizePerBlock(); | |||||
| // long maxDelay = blockSettings.getMaxDelayMilliSecondsPerBlock(); | |||||
| // props.setProperty(BFTSMART_BLOCK_TXSIZE_KEY, txSize + ""); | |||||
| // props.setProperty(BFTSMART_BLOCK_MAXDELAY_KEY, maxDelay + ""); | |||||
| // } | |||||
| for (int i = 0; i < serversNum; i++) { | |||||
| BftsmartNodeSettings ns = nodesSettings[i]; | |||||
| int id = i; | |||||
| // String keyOfPubkey = keyOfNode(PUBKEY_PATTERN, id); | |||||
| // props.setProperty(keyOfPubkey, ns.getPubKey().toBase58()); | |||||
| String keyOfHost = keyOfNode(CONSENSUS_HOST_PATTERN, id); | |||||
| props.setProperty(keyOfHost, ns.getNetworkAddress() == null ? "" : ns.getNetworkAddress().getHost()); | |||||
| String keyOfPort = keyOfNode(CONSENSUS_PORT_PATTERN, id); | |||||
| props.setProperty(keyOfPort, ns.getNetworkAddress() == null ? "" : ns.getNetworkAddress().getPort() + ""); | |||||
| String keyOfSecure = keyOfNode(CONSENSUS_SECURE_PATTERN, id); | |||||
| props.setProperty(keyOfSecure, ns.getNetworkAddress() == null ? "false" : ns.getNetworkAddress().isSecure() + ""); | |||||
| } | |||||
| PropertiesUtils.setValues(props, bftsmartSettings.getSystemConfigs()); | |||||
| } | |||||
| /** | |||||
| * | |||||
| * update system.servers.num property | |||||
| * | |||||
| */ | |||||
| private Property[] systemConfigs(Property[] systemConfigs) { | |||||
| Map<String, Property> propertyMap = convert2Map(systemConfigs); | |||||
| int serverNum = Integer.parseInt(propertyMap.get("system.servers.num").getValue()); | |||||
| propertyMap.put("system.servers.num", new Property("system.servers.num", String.valueOf(serverNum + 1))); | |||||
| return convert2Array(propertyMap); | |||||
| } | |||||
| private Map<String, Property> convert2Map(Property[] properties) { | |||||
| Map<String, Property> propertyMap = new HashMap<>(); | |||||
| for (Property property : properties) { | |||||
| propertyMap.put(property.getName(), property); | |||||
| } | |||||
| return propertyMap; | |||||
| } | |||||
| private Property[] convert2Array(Map<String, Property> map) { | |||||
| Property[] properties = new Property[map.size()]; | |||||
| int index = 0; | |||||
| for (Map.Entry<String, Property> entry : map.entrySet()) { | |||||
| properties[index++] = entry.getValue(); | |||||
| } | |||||
| return properties; | |||||
| } | |||||
| /** | |||||
| * | |||||
| * update node setting | |||||
| * | |||||
| */ | |||||
| private BftsmartNodeSettings[] nodeSettings(NodeSettings[] nodeSettings, ParticipantInfo participantInfo) { | |||||
| BftsmartNodeConfig bftsmartNodeConfig = new BftsmartNodeConfig(participantInfo.getPubKey(), nodeSettings.length, participantInfo.getNetworkAddress()); | |||||
| BftsmartNodeSettings[] bftsmartNodeSettings = new BftsmartNodeSettings[nodeSettings.length + 1]; | |||||
| for (int i = 0; i < nodeSettings.length; i++) { | |||||
| bftsmartNodeSettings[i] = (BftsmartNodeSettings)nodeSettings[i]; | |||||
| } | |||||
| bftsmartNodeSettings[nodeSettings.length] = bftsmartNodeConfig; | |||||
| return bftsmartNodeSettings; | |||||
| } | |||||
| } | |||||
| @@ -1,66 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart; | |||||
| import com.jd.blockchain.binaryproto.DataContractRegistry; | |||||
| import com.jd.blockchain.crypto.AddressEncoding; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| import com.jd.blockchain.utils.net.NetworkAddress; | |||||
| public class BftsmartNodeConfig implements BftsmartNodeSettings { | |||||
| private int id; | |||||
| private String address; | |||||
| private PubKey pubKey; | |||||
| private NetworkAddress networkAddress; | |||||
| public BftsmartNodeConfig() { | |||||
| } | |||||
| static { | |||||
| DataContractRegistry.register(BftsmartNodeSettings.class); | |||||
| } | |||||
| public BftsmartNodeConfig(PubKey pubKey, int id, NetworkAddress networkAddress) { | |||||
| this.address = AddressEncoding.generateAddress(pubKey).toBase58(); | |||||
| this.pubKey = pubKey; | |||||
| this.id = id; | |||||
| this.networkAddress = networkAddress; | |||||
| } | |||||
| @Override | |||||
| public String getAddress() { | |||||
| return address; | |||||
| } | |||||
| @Override | |||||
| public int getId() { | |||||
| return id; | |||||
| } | |||||
| @Override | |||||
| public NetworkAddress getNetworkAddress() { | |||||
| return networkAddress; | |||||
| } | |||||
| public void setId(int id) { | |||||
| this.id = id; | |||||
| } | |||||
| public void setAddress(String address) { | |||||
| this.address = address; | |||||
| } | |||||
| public void setNetworkAddress(NetworkAddress networkAddress) { | |||||
| this.networkAddress = networkAddress; | |||||
| } | |||||
| public PubKey getPubKey() { | |||||
| return pubKey; | |||||
| } | |||||
| public void setPubKey(PubKey pubKey) { | |||||
| this.pubKey = pubKey; | |||||
| } | |||||
| } | |||||
| @@ -1,45 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart; | |||||
| import com.jd.blockchain.binaryproto.DataContract; | |||||
| import com.jd.blockchain.binaryproto.DataField; | |||||
| import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
| import com.jd.blockchain.consensus.NodeSettings; | |||||
| import com.jd.blockchain.consts.DataCodes; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| import com.jd.blockchain.utils.net.NetworkAddress; | |||||
| @DataContract(code = DataCodes.CONSENSUS_BFTSMART_NODE_SETTINGS) | |||||
| public interface BftsmartNodeSettings extends NodeSettings { | |||||
| /** | |||||
| * 节点所属的参与方的区块链地址; | |||||
| */ | |||||
| // @DataField(order = 0, primitiveType = ValueType.TEXT) | |||||
| // @Override | |||||
| // String getAddress(); | |||||
| /** | |||||
| * Base58 格式的公钥; | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| // @DataField(order = 1, primitiveType = ValueType.BYTES) | |||||
| // PubKey getPubKey(); | |||||
| /** | |||||
| * 节点的ID; | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| @DataField(order = 2, primitiveType = PrimitiveType.INT32) | |||||
| int getId(); | |||||
| /** | |||||
| * 共识协议的网络地址; | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| @DataField(order = 3, primitiveType = PrimitiveType.BYTES) | |||||
| NetworkAddress getNetworkAddress(); | |||||
| } | |||||
| @@ -1,73 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart; | |||||
| import com.jd.blockchain.binaryproto.BinaryProtocol; | |||||
| import com.jd.blockchain.binaryproto.DataContractRegistry; | |||||
| import com.jd.blockchain.consensus.*; | |||||
| import com.jd.blockchain.utils.io.BytesEncoder; | |||||
| public class BftsmartSettingsFactory implements SettingsFactory { | |||||
| private static ConsensusSettingsEncoder CS_ENCODER = new ConsensusSettingsEncoder(); | |||||
| private static ClientIncomingSettingsEncoder CI_ENCODER =new ClientIncomingSettingsEncoder(); | |||||
| static { | |||||
| DataContractRegistry.register(BftsmartConsensusSettings.class); | |||||
| DataContractRegistry.register(BftsmartClientIncomingSettings.class); | |||||
| } | |||||
| @Override | |||||
| public BftsmartConsensusSettingsBuilder getConsensusSettingsBuilder() { | |||||
| return new BftsmartConsensusSettingsBuilder(); | |||||
| } | |||||
| @Override | |||||
| public BytesEncoder<ConsensusSettings> getConsensusSettingsEncoder() { | |||||
| return CS_ENCODER; | |||||
| } | |||||
| @Override | |||||
| public BytesEncoder<ClientIncomingSettings> getIncomingSettingsEncoder() { | |||||
| return CI_ENCODER; | |||||
| } | |||||
| private static class ConsensusSettingsEncoder implements BytesEncoder<ConsensusSettings>{ | |||||
| @Override | |||||
| public byte[] encode(ConsensusSettings data) { | |||||
| if (data instanceof BftsmartConsensusSettings) { | |||||
| return BinaryProtocol.encode(data, BftsmartConsensusSettings.class); | |||||
| } | |||||
| throw new IllegalArgumentException("Settings data isn't supported! Accept BftsmartConsensusSettings only!"); | |||||
| } | |||||
| @Override | |||||
| public ConsensusSettings decode(byte[] bytes) { | |||||
| return BinaryProtocol.decodeAs(bytes, BftsmartConsensusSettings.class); | |||||
| } | |||||
| } | |||||
| private static class ClientIncomingSettingsEncoder implements BytesEncoder<ClientIncomingSettings>{ | |||||
| @Override | |||||
| public byte[] encode(ClientIncomingSettings data) { | |||||
| if (data instanceof BftsmartClientIncomingSettings) { | |||||
| return BinaryProtocol.encode(data, BftsmartClientIncomingSettings.class); | |||||
| } | |||||
| throw new IllegalArgumentException("Settings data isn't supported! Accept BftsmartClientIncomingSettings only!"); | |||||
| } | |||||
| @Override | |||||
| public ClientIncomingSettings decode(byte[] bytes) { | |||||
| return BinaryProtocol.decodeAs(bytes, BftsmartClientIncomingSettings.class); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,30 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart; | |||||
| import bftsmart.reconfiguration.views.View; | |||||
| import com.jd.blockchain.consensus.Topology; | |||||
| import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; | |||||
| public class BftsmartTopology implements Topology { | |||||
| private static final long serialVersionUID = -3042599438265726240L; | |||||
| private View view; | |||||
| public BftsmartTopology(View view){ | |||||
| this.view = view; | |||||
| } | |||||
| @Override | |||||
| public int getId() { | |||||
| return view.getId(); | |||||
| } | |||||
| @Override | |||||
| public Topology copyOf() { | |||||
| return BinarySerializeUtils.copyOf(this); | |||||
| } | |||||
| public View getView() { | |||||
| return view; | |||||
| } | |||||
| } | |||||
| @@ -1,16 +0,0 @@ | |||||
| //package com.jd.blockchain.consensus.bftsmart; | |||||
| // | |||||
| //public enum BftsmartTransactionType { | |||||
| // TRANSACTION((int)0), | |||||
| // COMMITBLOCK((int)1); | |||||
| // | |||||
| // public final int CODE; | |||||
| // | |||||
| // public int getCode() { | |||||
| // return CODE; | |||||
| // } | |||||
| // private BftsmartTransactionType(int code) { | |||||
| // this.CODE = code; | |||||
| // } | |||||
| // | |||||
| //} | |||||
| @@ -1,79 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart.client; | |||||
| import com.jd.blockchain.consensus.ConsensusSettings; | |||||
| import com.jd.blockchain.consensus.bftsmart.BftsmartClientIncomingSettings; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| public class BftsmartClientConfig implements BftsmartClientSettings { | |||||
| private int clientId; | |||||
| private PubKey clientPubkey; | |||||
| private ConsensusSettings consensusSettings; | |||||
| private byte[] topology; | |||||
| private byte[] tomConfig; | |||||
| BftsmartClientIncomingSettings clientIncomingSettings; | |||||
| public BftsmartClientConfig(int clientId, PubKey clientPubkey, ConsensusSettings consensusSettings, byte[] topology, byte[] tomConfig) { | |||||
| this.clientId = clientId; | |||||
| this.clientPubkey = clientPubkey; | |||||
| this.consensusSettings = consensusSettings; | |||||
| this.topology = topology; | |||||
| this.tomConfig = tomConfig; | |||||
| } | |||||
| public BftsmartClientConfig(BftsmartClientIncomingSettings clientIncomingSettings) { | |||||
| this.clientIncomingSettings = clientIncomingSettings; | |||||
| this.clientId = clientIncomingSettings.getClientId(); | |||||
| this.clientPubkey = clientIncomingSettings.getPubKey(); | |||||
| this.consensusSettings = clientIncomingSettings.getConsensusSettings(); | |||||
| this.topology = clientIncomingSettings.getTopology(); | |||||
| this.tomConfig = clientIncomingSettings.getTomConfig(); | |||||
| } | |||||
| @Override | |||||
| public int getClientId() { | |||||
| return clientId; | |||||
| } | |||||
| public void setClientId(int clientId) { | |||||
| this.clientId = clientId; | |||||
| } | |||||
| @Override | |||||
| public PubKey getClientPubKey() { | |||||
| return clientPubkey; | |||||
| } | |||||
| public void setClientPubkey(PubKey clientPubkey) { | |||||
| this.clientPubkey = clientPubkey; | |||||
| } | |||||
| @Override | |||||
| public ConsensusSettings getConsensusSettings() { | |||||
| return consensusSettings; | |||||
| } | |||||
| public void setConsensusSettings(ConsensusSettings consensusSettings) { | |||||
| this.consensusSettings = consensusSettings; | |||||
| } | |||||
| public byte[] getTopology() { | |||||
| return topology; | |||||
| } | |||||
| public void setTopology(byte[] topology) { | |||||
| this.topology = topology; | |||||
| } | |||||
| @Override | |||||
| public byte[] getTomConfig() { | |||||
| return tomConfig; | |||||
| } | |||||
| public void setTomConfig(byte[] tomConfig) { | |||||
| this.tomConfig = tomConfig; | |||||
| } | |||||
| } | |||||
| @@ -1,56 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart.client; | |||||
| import com.jd.blockchain.consensus.ClientIdentification; | |||||
| import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| import com.jd.blockchain.crypto.SignatureDigest; | |||||
| public class BftsmartClientIdentification implements ClientIdentification { | |||||
| private byte[] identityInfo; | |||||
| private PubKey pubKey; | |||||
| private SignatureDigest signatureDigest; | |||||
| public BftsmartClientIdentification() { | |||||
| } | |||||
| public BftsmartClientIdentification(ClientIdentification clientIdentification) { | |||||
| identityInfo = clientIdentification.getIdentityInfo(); | |||||
| pubKey = clientIdentification.getPubKey(); | |||||
| signatureDigest = clientIdentification.getSignature(); | |||||
| } | |||||
| @Override | |||||
| public byte[] getIdentityInfo() { | |||||
| return identityInfo; | |||||
| } | |||||
| public void setIdentityInfo(byte[] identityInfo) { | |||||
| this.identityInfo = identityInfo; | |||||
| } | |||||
| @Override | |||||
| public PubKey getPubKey() { | |||||
| return pubKey; | |||||
| } | |||||
| public void setPubKey(PubKey pubKey) { | |||||
| this.pubKey = pubKey; | |||||
| } | |||||
| @Override | |||||
| public SignatureDigest getSignature() { | |||||
| return signatureDigest; | |||||
| } | |||||
| @Override | |||||
| public String getProviderName() { | |||||
| return BftsmartConsensusProvider.NAME; | |||||
| } | |||||
| public void setSignatureDigest(SignatureDigest signatureDigest) { | |||||
| this.signatureDigest = signatureDigest; | |||||
| } | |||||
| } | |||||
| @@ -1,12 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart.client; | |||||
| import com.jd.blockchain.consensus.client.ClientSettings; | |||||
| public interface BftsmartClientSettings extends ClientSettings { | |||||
| byte[] getTopology(); | |||||
| byte[] getTomConfig(); | |||||
| } | |||||
| @@ -1,129 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart.client; | |||||
| import com.jd.blockchain.consensus.MessageService; | |||||
| import com.jd.blockchain.consensus.client.ClientSettings; | |||||
| import com.jd.blockchain.consensus.client.ConsensusClient; | |||||
| import java.util.concurrent.atomic.AtomicInteger; | |||||
| public class BftsmartConsensusClient implements ConsensusClient { | |||||
| private final AtomicInteger addId = new AtomicInteger(); | |||||
| private BftsmartPeerProxyPool asyncPeerProxyPool; | |||||
| private int gatewayId; | |||||
| private ClientSettings clientSettings; | |||||
| public BftsmartConsensusClient(ClientSettings clientSettings) { | |||||
| this.clientSettings = clientSettings; | |||||
| this.gatewayId = clientSettings.getClientId(); | |||||
| } | |||||
| public BftsmartPeerProxyPool getConsensusClientPool() { | |||||
| return this.asyncPeerProxyPool; | |||||
| } | |||||
| @Override | |||||
| public MessageService getMessageService() { | |||||
| return new BftsmartMessageService(asyncPeerProxyPool); | |||||
| } | |||||
| @Override | |||||
| public ClientSettings getSettings() { | |||||
| return clientSettings; | |||||
| } | |||||
| @Override | |||||
| public boolean isConnected() { | |||||
| return this.asyncPeerProxyPool != null; | |||||
| } | |||||
| @Override | |||||
| public synchronized void connect() { | |||||
| //consensus client pool | |||||
| BftsmartPeerProxyFactory peerProxyFactory = new BftsmartPeerProxyFactory((BftsmartClientSettings)clientSettings, gatewayId); | |||||
| this.asyncPeerProxyPool = new BftsmartPeerProxyPool(peerProxyFactory); | |||||
| // MemoryBasedViewStorage viewStorage = new MemoryBasedViewStorage(((BftsmartClientSettings)clientSettings).getTopology().getView()); | |||||
| // TOMConfiguration tomConfiguration = ((BftsmartConsensusConfig)clientSettings.getConsensusSettings()).getBftsmartConfig(); | |||||
| // | |||||
| // //by serialize keep origin tom config | |||||
| // byte[] tomBytes = BinarySerializeUtils.serialize(tomConfiguration); | |||||
| // TOMConfiguration decodeTom = BinarySerializeUtils.deserialize(tomBytes); | |||||
| // | |||||
| // int clientId = gatewayId *100 + addId.incrementAndGet(); | |||||
| // | |||||
| // //every proxy client has unique id; | |||||
| // decodeTom.setProcessId(clientId); | |||||
| // this.peerProxy = new AsynchServiceProxy(decodeTom, viewStorage); | |||||
| } | |||||
| @Override | |||||
| public void close() { | |||||
| if (asyncPeerProxyPool != null) { | |||||
| asyncPeerProxyPool.close(); | |||||
| } | |||||
| } | |||||
| // public void asyncSendOrdered(byte[] message, AsyncCallback<byte[]> callback) { | |||||
| // AsyncReplier replier = new AsyncReplier(callback, peerProxy); | |||||
| // peerProxy.invokeAsynchRequest(message, replier, TOMMessageType.ORDERED_REQUEST); | |||||
| // } | |||||
| // private static class AsyncReplier implements ReplyListener { | |||||
| // | |||||
| // private AsynchServiceProxy peerProxy; | |||||
| // | |||||
| // private AtomicInteger replies = new AtomicInteger(0); | |||||
| // | |||||
| // private AsyncCallback<byte[]> messageHandle; | |||||
| // | |||||
| // public AsyncReplier(AsyncCallback<byte[]> messageHandle, AsynchServiceProxy peerProxy) { | |||||
| // this.messageHandle = messageHandle; | |||||
| // this.peerProxy = peerProxy; | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public void reset() { | |||||
| // replies.set(0); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public void replyReceived(RequestContext context, TOMMessage reply) { | |||||
| // int replyCount = replies.incrementAndGet(); | |||||
| // | |||||
| // double q = Math.ceil((double) (peerProxy.getViewManager().getCurrentViewN() | |||||
| // + peerProxy.getViewManager().getCurrentViewF() + 1) / 2.0); | |||||
| // | |||||
| // if (replyCount >= q) { | |||||
| // peerProxy.cleanAsynchRequest(context.getOperationId()); | |||||
| // messageHandle.complete(reply.getContent(), null); | |||||
| // } | |||||
| // } | |||||
| // | |||||
| // } | |||||
| // private static class BftsmartAsyncFuture<T> extends CompletableAsyncFuture<T> { | |||||
| // @Override | |||||
| // public void setSuccess(T value) { | |||||
| // super.setSuccess(value); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public void setError(Throwable ex) { | |||||
| // super.setError(ex); | |||||
| // } | |||||
| // | |||||
| // @Override | |||||
| // public void setError(String errorCode) { | |||||
| // super.setError(errorCode); | |||||
| // } | |||||
| // } | |||||
| } | |||||
| @@ -1,133 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart.client; | |||||
| import java.util.concurrent.atomic.AtomicInteger; | |||||
| import java.util.regex.Matcher; | |||||
| import java.util.regex.Pattern; | |||||
| import com.jd.blockchain.consensus.ClientIncomingSettings; | |||||
| import com.jd.blockchain.consensus.ConsensusManageService; | |||||
| import com.jd.blockchain.consensus.bftsmart.BftsmartClientIncomingSettings; | |||||
| import com.jd.blockchain.consensus.client.ClientFactory; | |||||
| import com.jd.blockchain.consensus.client.ClientSettings; | |||||
| import com.jd.blockchain.consensus.client.ConsensusClient; | |||||
| import com.jd.blockchain.crypto.AsymmetricKeypair; | |||||
| import com.jd.blockchain.crypto.Crypto; | |||||
| import com.jd.blockchain.crypto.PrivKey; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| import com.jd.blockchain.crypto.SignatureDigest; | |||||
| import com.jd.blockchain.crypto.SignatureFunction; | |||||
| import com.jd.blockchain.utils.net.NetworkAddress; | |||||
| public class BftsmartConsensusClientFactory implements ClientFactory { | |||||
| private AtomicInteger addId = new AtomicInteger(); | |||||
| private String localDomain = "localhost"; | |||||
| private String localIp = "127.0.0.1"; | |||||
| public BftsmartConsensusClientFactory() { | |||||
| } | |||||
| @Override | |||||
| public BftsmartClientIdentification buildAuthId(AsymmetricKeypair clientKeyPair) { | |||||
| PubKey pubKey = clientKeyPair.getPubKey(); | |||||
| PrivKey privKey = clientKeyPair.getPrivKey(); | |||||
| SignatureFunction signatureFunction =Crypto.getSignatureFunction(pubKey.getAlgorithm()); | |||||
| SignatureDigest signatureDigest = signatureFunction.sign(privKey, pubKey.toBytes()); | |||||
| BftsmartClientIdentification bftsmartClientIdentification = new BftsmartClientIdentification(); | |||||
| bftsmartClientIdentification.setIdentityInfo(pubKey.toBytes()); | |||||
| bftsmartClientIdentification.setPubKey(pubKey); | |||||
| bftsmartClientIdentification.setSignatureDigest(signatureDigest); | |||||
| return bftsmartClientIdentification; | |||||
| } | |||||
| @Override | |||||
| public ClientSettings buildClientSettings(ClientIncomingSettings incomingSettings) { | |||||
| BftsmartClientIncomingSettings clientIncomingSettings = (BftsmartClientIncomingSettings) incomingSettings; | |||||
| BftsmartClientSettings clientSettings = new BftsmartClientConfig(clientIncomingSettings); | |||||
| return clientSettings; | |||||
| } | |||||
| @Override | |||||
| public ConsensusClient setupClient(ClientSettings settings) { | |||||
| return new BftsmartConsensusClient(settings); | |||||
| } | |||||
| @Override | |||||
| public ConsensusManageService createManageServiceClient(String[] serviceNodes) { | |||||
| // BftsmartConsensusManageService consensusManageService = null; | |||||
| // BftsmartClientIncomingSettings clientIncomingSettings; | |||||
| // | |||||
| // | |||||
| // try { | |||||
| // if (serviceNodes == null) { | |||||
| // throw new ConsensusSecurityException("createManageServiceClient param error!"); | |||||
| // } | |||||
| // | |||||
| // for (int i = 0; i < serviceNodes.length; i++) { | |||||
| // | |||||
| // NetworkAddress networkAddress = getIpPortFromUrl(serviceNodes[i]); | |||||
| // if (networkAddress == null) { | |||||
| // continue; | |||||
| // } | |||||
| // ServiceEndpoint peerServer = new ServiceEndpoint(networkAddress.getHost(), networkAddress.getPort(), false); | |||||
| // consensusManageService = HttpServiceAgent.createService(BftsmartConsensusManageService.class, peerServer); | |||||
| // clientIncomingSettings = consensusManageService.authClientIncoming(clientIdentification); | |||||
| // | |||||
| // if (clientIncomingSettings == null) { | |||||
| // consensusManageService = null; | |||||
| // } else { | |||||
| // //认证成功 | |||||
| // break; | |||||
| // } | |||||
| // } | |||||
| // | |||||
| // } catch (Exception e) { | |||||
| // e.printStackTrace(); | |||||
| // } | |||||
| // return consensusManageService; | |||||
| return null; | |||||
| } | |||||
| private NetworkAddress getIpPortFromUrl(String url) { | |||||
| // 1.check null | |||||
| if (url == null || url.trim().equals("")) { | |||||
| return null; | |||||
| } | |||||
| // 2. localhost replace to 127.0.0.1 | |||||
| if(url.startsWith("http://" + localDomain) ){ | |||||
| url = url.replace("http://" + localDomain, "http://" + localIp) ; | |||||
| } | |||||
| String host = ""; | |||||
| Pattern p = Pattern.compile("(?<=//|)((\\w)+\\.)+\\w+(:\\d{0,5})?"); | |||||
| Matcher matcher = p.matcher(url); | |||||
| if (matcher.find()) { | |||||
| host = matcher.group() ; | |||||
| } | |||||
| if(host.contains(":") == false){ | |||||
| //default port :80 | |||||
| return new NetworkAddress(host, 80); | |||||
| } | |||||
| else { | |||||
| String[] ipPortArr = host.split(":"); | |||||
| return new NetworkAddress(ipPortArr[0], Integer.parseInt(ipPortArr[1])); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,73 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart.client; | |||||
| import bftsmart.tom.AsynchServiceProxy; | |||||
| import com.jd.blockchain.consensus.MessageService; | |||||
| import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
| import com.jd.blockchain.utils.concurrent.CompletableAsyncFuture; | |||||
| import com.jd.blockchain.utils.io.BytesUtils; | |||||
| import java.util.concurrent.ExecutorService; | |||||
| import java.util.concurrent.Executors; | |||||
| public class BftsmartMessageService implements MessageService { | |||||
| private BftsmartPeerProxyPool asyncPeerProxyPool; | |||||
| public BftsmartMessageService(BftsmartPeerProxyPool peerProxyPool) { | |||||
| this.asyncPeerProxyPool = peerProxyPool; | |||||
| } | |||||
| @Override | |||||
| public AsyncFuture<byte[]> sendOrdered(byte[] message) { | |||||
| return sendOrderedMessage(message); | |||||
| } | |||||
| private AsyncFuture<byte[]> sendOrderedMessage(byte[] message) { | |||||
| CompletableAsyncFuture<byte[]> asyncFuture = new CompletableAsyncFuture<>(); | |||||
| AsynchServiceProxy asynchServiceProxy = null; | |||||
| try { | |||||
| asynchServiceProxy = asyncPeerProxyPool.borrowObject(); | |||||
| // //0: Transaction msg, 1: Commitblock msg | |||||
| // byte[] msgType = BytesUtils.toBytes(0); | |||||
| // byte[] wrapMsg = new byte[message.length + 4]; | |||||
| // System.arraycopy(message, 0, wrapMsg, 4, message.length); | |||||
| // System.arraycopy(msgType, 0, wrapMsg, 0, 4); | |||||
| // | |||||
| // System.out.printf("BftsmartMessageService invokeOrdered time = %s, id = %s threadId = %s \r\n", | |||||
| // System.currentTimeMillis(), asynchServiceProxy.getProcessId(), Thread.currentThread().getId()); | |||||
| byte[] result = asynchServiceProxy.invokeOrdered(message); | |||||
| asyncFuture.complete(result); | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } finally { | |||||
| asyncPeerProxyPool.returnObject(asynchServiceProxy); | |||||
| } | |||||
| return asyncFuture; | |||||
| } | |||||
| @Override | |||||
| public AsyncFuture<byte[]> sendUnordered(byte[] message) { | |||||
| return sendUnorderedMessage(message); | |||||
| } | |||||
| private AsyncFuture<byte[]> sendUnorderedMessage(byte[] message) { | |||||
| CompletableAsyncFuture<byte[]> asyncFuture = new CompletableAsyncFuture<>(); | |||||
| AsynchServiceProxy asynchServiceProxy = null; | |||||
| try { | |||||
| asynchServiceProxy = asyncPeerProxyPool.borrowObject(); | |||||
| byte[] result = asynchServiceProxy.invokeUnordered(message); | |||||
| asyncFuture.complete(result); | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } finally { | |||||
| asyncPeerProxyPool.returnObject(asynchServiceProxy); | |||||
| } | |||||
| return asyncFuture; | |||||
| } | |||||
| } | |||||
| @@ -1,47 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart.client; | |||||
| import bftsmart.reconfiguration.util.TOMConfiguration; | |||||
| import bftsmart.reconfiguration.views.MemoryBasedViewStorage; | |||||
| import bftsmart.tom.AsynchServiceProxy; | |||||
| import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusConfig; | |||||
| import com.jd.blockchain.consensus.bftsmart.BftsmartTopology; | |||||
| import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; | |||||
| import org.apache.commons.pool2.BasePooledObjectFactory; | |||||
| import org.apache.commons.pool2.PooledObject; | |||||
| import org.apache.commons.pool2.impl.DefaultPooledObject; | |||||
| import java.util.concurrent.atomic.AtomicInteger; | |||||
| public class BftsmartPeerProxyFactory extends BasePooledObjectFactory<AsynchServiceProxy> { | |||||
| private BftsmartClientSettings bftsmartClientSettings; | |||||
| private int gatewayId; | |||||
| private AtomicInteger index = new AtomicInteger(1); | |||||
| public BftsmartPeerProxyFactory(BftsmartClientSettings bftsmartClientSettings, int gatewayId) { | |||||
| this.bftsmartClientSettings = bftsmartClientSettings; | |||||
| this.gatewayId = gatewayId; | |||||
| } | |||||
| @Override | |||||
| public AsynchServiceProxy create() throws Exception { | |||||
| BftsmartTopology topology = BinarySerializeUtils.deserialize(bftsmartClientSettings.getTopology()); | |||||
| MemoryBasedViewStorage viewStorage = new MemoryBasedViewStorage(topology.getView()); | |||||
| TOMConfiguration tomConfiguration = BinarySerializeUtils.deserialize(bftsmartClientSettings.getTomConfig()); | |||||
| //every proxy client has unique id; | |||||
| tomConfiguration.setProcessId(gatewayId + index.getAndIncrement()); | |||||
| AsynchServiceProxy peerProxy = new AsynchServiceProxy(tomConfiguration, viewStorage); | |||||
| return peerProxy; | |||||
| } | |||||
| @Override | |||||
| public PooledObject<AsynchServiceProxy> wrap(AsynchServiceProxy asynchServiceProxy) { | |||||
| return new DefaultPooledObject<>(asynchServiceProxy); | |||||
| } | |||||
| } | |||||
| @@ -1,25 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.bft.BftsmartConsensusClientPool | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/10/30 下午6:50 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.bftsmart.client; | |||||
| import bftsmart.tom.AsynchServiceProxy; | |||||
| import org.apache.commons.pool2.PooledObjectFactory; | |||||
| import org.apache.commons.pool2.impl.GenericObjectPool; | |||||
| import org.apache.commons.pool2.impl.GenericObjectPoolConfig; | |||||
| public class BftsmartPeerProxyPool extends GenericObjectPool<AsynchServiceProxy> { | |||||
| public BftsmartPeerProxyPool(PooledObjectFactory<AsynchServiceProxy> factory) { | |||||
| this(factory, null); | |||||
| } | |||||
| public BftsmartPeerProxyPool(PooledObjectFactory<AsynchServiceProxy> factory, GenericObjectPoolConfig config) { | |||||
| super(factory, config == null ? new BftsmartPeerProxyPoolConfig() : config); | |||||
| } | |||||
| } | |||||
| @@ -1,32 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.bft.BftsmartConsensusClientPool | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/10/30 下午6:50 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.bftsmart.client; | |||||
| import org.apache.commons.pool2.impl.GenericObjectPoolConfig; | |||||
| public class BftsmartPeerProxyPoolConfig extends GenericObjectPoolConfig { | |||||
| public static final int MAX_TOTAL = 100; | |||||
| private final int MIN_IDLE = 0; | |||||
| private final int MAX_IDLE = 100; | |||||
| public BftsmartPeerProxyPoolConfig() { | |||||
| setMaxTotal(MAX_TOTAL); | |||||
| setMinIdle(MIN_IDLE); | |||||
| setMaxIdle(MAX_IDLE); | |||||
| } | |||||
| public BftsmartPeerProxyPoolConfig(int maxTotal, int minIdle, int maxIdle) { | |||||
| setMaxTotal(maxTotal); | |||||
| setMinIdle(minIdle); | |||||
| setMaxIdle(maxIdle); | |||||
| } | |||||
| } | |||||
| @@ -1,71 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart.service; | |||||
| import java.util.concurrent.locks.Lock; | |||||
| import java.util.concurrent.locks.ReentrantLock; | |||||
| import com.jd.blockchain.consensus.ClientIdentification; | |||||
| import com.jd.blockchain.consensus.ConsensusManageService; | |||||
| import com.jd.blockchain.consensus.bftsmart.BftsmartClientIncomingConfig; | |||||
| import com.jd.blockchain.consensus.bftsmart.BftsmartClientIncomingSettings; | |||||
| import com.jd.blockchain.crypto.Crypto; | |||||
| import com.jd.blockchain.crypto.SignatureFunction; | |||||
| import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; | |||||
| public class BftsmartConsensusManageService implements ConsensusManageService { | |||||
| public static final int GATEWAY_SIZE = 100; | |||||
| public static final int CLIENT_SIZE_PER_GATEWAY = 1000; | |||||
| public static final int CLIENT_RANGE = GATEWAY_SIZE * CLIENT_SIZE_PER_GATEWAY; | |||||
| private BftsmartNodeServer nodeServer; | |||||
| private int clientId; | |||||
| private static final Lock authLock = new ReentrantLock(); | |||||
| public BftsmartConsensusManageService(BftsmartNodeServer nodeServer) { | |||||
| this.nodeServer = nodeServer; | |||||
| // Assume that each peer node corresponds to up to 100 gateways | |||||
| clientId = nodeServer.getServerId() * CLIENT_RANGE; | |||||
| } | |||||
| @Override | |||||
| public BftsmartClientIncomingSettings authClientIncoming(ClientIdentification authId) { | |||||
| if (verify(authId)) { | |||||
| BftsmartClientIncomingConfig clientIncomingSettings = new BftsmartClientIncomingConfig(); | |||||
| clientIncomingSettings | |||||
| .setTopology(BinarySerializeUtils.serialize(nodeServer.getTopology())); | |||||
| clientIncomingSettings | |||||
| .setTomConfig(BinarySerializeUtils.serialize(nodeServer.getTomConfig())); | |||||
| clientIncomingSettings | |||||
| .setConsensusSettings(nodeServer.getConsensusSetting()); | |||||
| clientIncomingSettings.setPubKey(authId.getPubKey()); | |||||
| // compute gateway id | |||||
| authLock.lock(); | |||||
| try { | |||||
| clientIncomingSettings.setClientId(clientId++); | |||||
| clientId += CLIENT_SIZE_PER_GATEWAY; | |||||
| } finally { | |||||
| authLock.unlock(); | |||||
| } | |||||
| return clientIncomingSettings; | |||||
| } | |||||
| return null; | |||||
| } | |||||
| public boolean verify(ClientIdentification authId) { | |||||
| SignatureFunction signatureFunction = Crypto | |||||
| .getSignatureFunction(authId.getPubKey().getAlgorithm()); | |||||
| return signatureFunction.verify(authId.getSignature(), authId.getPubKey(), authId.getIdentityInfo()); | |||||
| } | |||||
| } | |||||
| @@ -1,549 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart.service; | |||||
| import java.io.ByteArrayOutputStream; | |||||
| import java.util.*; | |||||
| import java.util.concurrent.CopyOnWriteArrayList; | |||||
| import java.util.concurrent.ExecutorService; | |||||
| import java.util.concurrent.Executors; | |||||
| import bftsmart.consensus.app.BatchAppResultImpl; | |||||
| import bftsmart.tom.*; | |||||
| import com.jd.blockchain.binaryproto.BinaryProtocol; | |||||
| import com.jd.blockchain.consensus.service.*; | |||||
| import com.jd.blockchain.crypto.HashDigest; | |||||
| import com.jd.blockchain.ledger.*; | |||||
| import com.jd.blockchain.transaction.TxResponseMessage; | |||||
| import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import com.jd.blockchain.consensus.ConsensusManageService; | |||||
| import com.jd.blockchain.consensus.NodeSettings; | |||||
| import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider; | |||||
| import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings; | |||||
| import com.jd.blockchain.consensus.bftsmart.BftsmartNodeSettings; | |||||
| import com.jd.blockchain.consensus.bftsmart.BftsmartTopology; | |||||
| import com.jd.blockchain.utils.PropertiesUtils; | |||||
| import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
| import com.jd.blockchain.utils.io.BytesUtils; | |||||
| import bftsmart.reconfiguration.util.HostsConfig; | |||||
| import bftsmart.reconfiguration.util.TOMConfiguration; | |||||
| import bftsmart.tom.server.defaultservices.DefaultRecoverable; | |||||
| public class BftsmartNodeServer extends DefaultRecoverable implements NodeServer { | |||||
| private static Logger LOGGER = LoggerFactory.getLogger(BftsmartNodeServer.class); | |||||
| private static final String DEFAULT_BINDING_HOST = "0.0.0.0"; | |||||
| private List<StateHandle> stateHandles = new CopyOnWriteArrayList<>(); | |||||
| // TODO 暂不处理队列溢出问题 | |||||
| private ExecutorService notifyReplyExecutors = Executors.newSingleThreadExecutor(); | |||||
| private volatile Status status = Status.STOPPED; | |||||
| private final Object mutex = new Object(); | |||||
| private volatile ServiceReplica replica; | |||||
| private StateMachineReplicate stateMachineReplicate; | |||||
| private ServerSettings serverSettings; | |||||
| private BftsmartConsensusManageService manageService; | |||||
| private volatile BftsmartTopology topology; | |||||
| private volatile BftsmartConsensusSettings setting; | |||||
| private TOMConfiguration tomConfig; | |||||
| private TOMConfiguration outerTomConfig; | |||||
| private HostsConfig hostsConfig; | |||||
| private Properties systemConfig; | |||||
| private MessageHandle messageHandle; | |||||
| private String providerName; | |||||
| private String realmName; | |||||
| private int serverId; | |||||
| public BftsmartNodeServer() { | |||||
| } | |||||
| public BftsmartNodeServer(ServerSettings serverSettings, MessageHandle messageHandler, StateMachineReplicate stateMachineReplicate) { | |||||
| this.serverSettings = serverSettings; | |||||
| this.realmName = serverSettings.getRealmName(); | |||||
| //used later | |||||
| this.stateMachineReplicate = stateMachineReplicate; | |||||
| this.messageHandle = messageHandler; | |||||
| createConfig(); | |||||
| serverId = findServerId(); | |||||
| initConfig(serverId, systemConfig, hostsConfig); | |||||
| this.manageService = new BftsmartConsensusManageService(this); | |||||
| } | |||||
| protected int findServerId() { | |||||
| int serverId = 0; | |||||
| for (int i = 0; i < hostsConfig.getNum(); i++) { | |||||
| String host = ((BftsmartNodeSettings)serverSettings.getReplicaSettings()).getNetworkAddress().getHost(); | |||||
| int port = ((BftsmartNodeSettings)serverSettings.getReplicaSettings()).getNetworkAddress().getPort(); | |||||
| if (hostsConfig.getHost(i).equals(host) && hostsConfig.getPort(i) == port) { | |||||
| serverId = i; | |||||
| break; | |||||
| } | |||||
| } | |||||
| return serverId; | |||||
| } | |||||
| public int getServerId() { | |||||
| return serverId; | |||||
| } | |||||
| protected void createConfig() { | |||||
| setting = ((BftsmartServerSettings) serverSettings).getConsensusSettings(); | |||||
| List<HostsConfig.Config> configList = new ArrayList<>(); | |||||
| NodeSettings[] nodeSettingsArray = setting.getNodes(); | |||||
| for (NodeSettings nodeSettings : nodeSettingsArray) { | |||||
| BftsmartNodeSettings node = (BftsmartNodeSettings)nodeSettings; | |||||
| configList.add(new HostsConfig.Config(node.getId(), node.getNetworkAddress().getHost(), node.getNetworkAddress().getPort())); | |||||
| } | |||||
| //create HostsConfig instance based on consensus realm nodes | |||||
| hostsConfig = new HostsConfig(configList.toArray(new HostsConfig.Config[configList.size()])); | |||||
| systemConfig = PropertiesUtils.createProperties(setting.getSystemConfigs()); | |||||
| return; | |||||
| } | |||||
| protected void initConfig(int id, Properties systemsConfig, HostsConfig hostConfig) { | |||||
| byte[] serialHostConf = BinarySerializeUtils.serialize(hostConfig); | |||||
| Properties sysConfClone = (Properties)systemsConfig.clone(); | |||||
| int port = hostConfig.getPort(id); | |||||
| hostConfig.add(id, DEFAULT_BINDING_HOST, port); | |||||
| this.tomConfig = new TOMConfiguration(id, systemsConfig, hostConfig); | |||||
| this.outerTomConfig = new TOMConfiguration(id, sysConfClone, BinarySerializeUtils.deserialize(serialHostConf)); | |||||
| } | |||||
| @Override | |||||
| public ConsensusManageService getManageService() { | |||||
| return manageService; | |||||
| } | |||||
| @Override | |||||
| public ServerSettings getSettings() { | |||||
| return serverSettings; | |||||
| } | |||||
| @Override | |||||
| public String getProviderName() { | |||||
| return BftsmartConsensusProvider.NAME; | |||||
| } | |||||
| public TOMConfiguration getTomConfig() { | |||||
| return outerTomConfig; | |||||
| } | |||||
| public int getId() { | |||||
| return tomConfig.getProcessId(); | |||||
| } | |||||
| public void setId(int id) { | |||||
| if (id < 0) { | |||||
| throw new IllegalArgumentException("ReplicaID is negative!"); | |||||
| } | |||||
| this.tomConfig.setProcessId(id); | |||||
| this.outerTomConfig.setProcessId(id); | |||||
| } | |||||
| public BftsmartConsensusSettings getConsensusSetting() { | |||||
| return setting; | |||||
| } | |||||
| public BftsmartTopology getTopology() { | |||||
| return topology; | |||||
| } | |||||
| public Status getStatus() { | |||||
| return status; | |||||
| } | |||||
| @Override | |||||
| public boolean isRunning() { | |||||
| return status == Status.RUNNING; | |||||
| } | |||||
| public byte[] appExecuteUnordered(byte[] bytes, MessageContext messageContext) { | |||||
| return messageHandle.processUnordered(bytes).get(); | |||||
| } | |||||
| /** | |||||
| * | |||||
| * Only block, no reply, used by state transfer when peer start | |||||
| * | |||||
| */ | |||||
| private void block(List<byte[]> manageConsensusCmds) { | |||||
| String batchId = messageHandle.beginBatch(realmName); | |||||
| try { | |||||
| int msgId = 0; | |||||
| for (byte[] txContent : manageConsensusCmds) { | |||||
| AsyncFuture<byte[]> asyncFuture = messageHandle.processOrdered(msgId++, txContent, realmName, batchId); | |||||
| } | |||||
| messageHandle.completeBatch(realmName, batchId); | |||||
| messageHandle.commitBatch(realmName, batchId); | |||||
| } catch (Exception e) { | |||||
| // todo 需要处理应答码 404 | |||||
| LOGGER.error("Error occurred while processing ordered messages! --" + e.getMessage(), e); | |||||
| messageHandle.rollbackBatch(realmName, batchId, TransactionState.CONSENSUS_ERROR.CODE); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * | |||||
| * Local peer has cid diff with remote peer, used by state transfer when peer start | |||||
| * | |||||
| */ | |||||
| private byte[][] appExecuteDiffBatch(byte[][] commands, MessageContext[] msgCtxs) { | |||||
| int manageConsensusId = msgCtxs[0].getConsensusId(); | |||||
| List<byte[]> manageConsensusCmds = new ArrayList<>(); | |||||
| int index = 0; | |||||
| for (MessageContext msgCtx : msgCtxs) { | |||||
| if (msgCtx.getConsensusId() == manageConsensusId) { | |||||
| manageConsensusCmds.add(commands[index]); | |||||
| } else { | |||||
| // 达到结块标准,需要进行结块并应答 | |||||
| block(manageConsensusCmds); | |||||
| // 重置链表和共识ID | |||||
| manageConsensusCmds = new ArrayList<>(); | |||||
| manageConsensusId = msgCtx.getConsensusId(); | |||||
| manageConsensusCmds.add(commands[index]); | |||||
| } | |||||
| index++; | |||||
| } | |||||
| // 结束时,肯定有最后一个结块请求未处理 | |||||
| if (!manageConsensusCmds.isEmpty()) { | |||||
| block(manageConsensusCmds); | |||||
| } | |||||
| return null; | |||||
| } | |||||
| /** | |||||
| * | |||||
| * Invoked by state transfer when peer start | |||||
| * | |||||
| */ | |||||
| @Override | |||||
| public byte[][] appExecuteBatch(byte[][] commands, MessageContext[] msgCtxs, boolean fromConsensus) { | |||||
| // Not from consensus outcomes, from state transfer | |||||
| if (!fromConsensus) { | |||||
| return appExecuteDiffBatch(commands, msgCtxs); | |||||
| } | |||||
| return null; | |||||
| } | |||||
| /** | |||||
| * | |||||
| * From consensus outcomes, do nothing now | |||||
| * The operation of executing the batch was moved to the consensus stage 2 and 3, in order to guaranteed ledger consistency | |||||
| */ | |||||
| @Override | |||||
| public byte[][] appExecuteBatch(byte[][] commands, MessageContext[] msgCtxs, boolean fromConsensus, List<ReplyContextMessage> replyList) { | |||||
| // if (replyList == null || replyList.size() == 0) { | |||||
| // throw new IllegalArgumentException(); | |||||
| // } | |||||
| // // todo 此部分需要重新改造 | |||||
| // /** | |||||
| // * 默认BFTSmart接口提供的commands是一个或多个共识结果的顺序集合 | |||||
| // * 根据共识的规定,目前的做法是将其根据msgCtxs的内容进行分组,每组都作为一个结块标识来处理 | |||||
| // * 从msgCtxs可以获取对应commands的分组情况 | |||||
| // */ | |||||
| // int manageConsensusId = msgCtxs[0].getConsensusId(); | |||||
| // List<byte[]> manageConsensusCmds = new ArrayList<>(); | |||||
| // List<ReplyContextMessage> manageReplyMsgs = new ArrayList<>(); | |||||
| // | |||||
| // int index = 0; | |||||
| // for (MessageContext msgCtx : msgCtxs) { | |||||
| // if (msgCtx.getConsensusId() == manageConsensusId) { | |||||
| // manageConsensusCmds.add(commands[index]); | |||||
| // manageReplyMsgs.add(replyList.get(index)); | |||||
| // } else { | |||||
| // // 达到结块标准,需要进行结块并应答 | |||||
| // blockAndReply(manageConsensusCmds, manageReplyMsgs); | |||||
| // // 重置链表和共识ID | |||||
| // manageConsensusCmds = new ArrayList<>(); | |||||
| // manageReplyMsgs = new ArrayList<>(); | |||||
| // manageConsensusId = msgCtx.getConsensusId(); | |||||
| // manageConsensusCmds.add(commands[index]); | |||||
| // manageReplyMsgs.add(replyList.get(index)); | |||||
| // } | |||||
| // index++; | |||||
| // } | |||||
| // // 结束时,肯定有最后一个结块请求未处理 | |||||
| // if (!manageConsensusCmds.isEmpty()) { | |||||
| // blockAndReply(manageConsensusCmds, manageReplyMsgs); | |||||
| // } | |||||
| return null; | |||||
| } | |||||
| /** | |||||
| * | |||||
| * Block and reply are moved to consensus completion stage | |||||
| * | |||||
| */ | |||||
| private void blockAndReply(List<byte[]> manageConsensusCmds, List<ReplyContextMessage> replyList) { | |||||
| // consensusBatchId = messageHandle.beginBatch(realmName); | |||||
| // List<AsyncFuture<byte[]>> asyncFutureLinkedList = new ArrayList<>(manageConsensusCmds.size()); | |||||
| // try { | |||||
| // int msgId = 0; | |||||
| // for (byte[] txContent : manageConsensusCmds) { | |||||
| // AsyncFuture<byte[]> asyncFuture = messageHandle.processOrdered(msgId++, txContent, realmName, consensusBatchId); | |||||
| // asyncFutureLinkedList.add(asyncFuture); | |||||
| // } | |||||
| // messageHandle.completeBatch(realmName, consensusBatchId); | |||||
| // messageHandle.commitBatch(realmName, consensusBatchId); | |||||
| // } catch (Exception e) { | |||||
| // // todo 需要处理应答码 404 | |||||
| // LOGGER.error("Error occurred while processing ordered messages! --" + e.getMessage(), e); | |||||
| // messageHandle.rollbackBatch(realmName, consensusBatchId, TransactionState.CONSENSUS_ERROR.CODE); | |||||
| // } | |||||
| // | |||||
| // // 通知线程单独处理应答 | |||||
| // notifyReplyExecutors.execute(() -> { | |||||
| // // 应答对应的结果 | |||||
| // int replyIndex = 0; | |||||
| // for(ReplyContextMessage msg : replyList) { | |||||
| // msg.setReply(asyncFutureLinkedList.get(replyIndex).get()); | |||||
| // TOMMessage request = msg.getTomMessage(); | |||||
| // ReplyContext replyContext = msg.getReplyContext(); | |||||
| // request.reply = new TOMMessage(replyContext.getId(), request.getSession(), request.getSequence(), | |||||
| // request.getOperationId(), msg.getReply(), replyContext.getCurrentViewId(), | |||||
| // request.getReqType()); | |||||
| // | |||||
| // if (replyContext.getNumRepliers() > 0) { | |||||
| // bftsmart.tom.util.Logger.println("(ServiceReplica.receiveMessages) sending reply to " | |||||
| // + request.getSender() + " with sequence number " + request.getSequence() | |||||
| // + " and operation ID " + request.getOperationId() + " via ReplyManager"); | |||||
| // replyContext.getRepMan().send(request); | |||||
| // } else { | |||||
| // bftsmart.tom.util.Logger.println("(ServiceReplica.receiveMessages) sending reply to " | |||||
| // + request.getSender() + " with sequence number " + request.getSequence() | |||||
| // + " and operation ID " + request.getOperationId()); | |||||
| // replyContext.getReplier().manageReply(request, msg.getMessageContext()); | |||||
| // } | |||||
| // replyIndex++; | |||||
| // } | |||||
| // }); | |||||
| } | |||||
| /** | |||||
| * Used by consensus write phase, pre compute new block hash | |||||
| */ | |||||
| public BatchAppResultImpl preComputeAppHash(byte[][] commands) { | |||||
| List<AsyncFuture<byte[]>> asyncFutureLinkedList = new ArrayList<>(commands.length); | |||||
| List<byte[]> responseLinkedList = new ArrayList<>(); | |||||
| StateSnapshot newStateSnapshot = null; | |||||
| StateSnapshot preStateSnapshot = null; | |||||
| StateSnapshot genisStateSnapshot = null; | |||||
| BatchAppResultImpl result = null; | |||||
| String batchId = null; | |||||
| int msgId = 0; | |||||
| try { | |||||
| batchId = messageHandle.beginBatch(realmName); | |||||
| genisStateSnapshot = messageHandle.getGenisStateSnapshot(realmName); | |||||
| preStateSnapshot = messageHandle.getStateSnapshot(realmName); | |||||
| if (preStateSnapshot == null) { | |||||
| throw new IllegalStateException("Pre block state snapshot is null!"); | |||||
| } | |||||
| for (int i = 0; i < commands.length; i++) { | |||||
| byte[] txContent = commands[i]; | |||||
| AsyncFuture<byte[]> asyncFuture = messageHandle.processOrdered(msgId++, txContent, realmName, batchId); | |||||
| asyncFutureLinkedList.add(asyncFuture); | |||||
| } | |||||
| newStateSnapshot = messageHandle.completeBatch(realmName, batchId); | |||||
| for (int i = 0; i < asyncFutureLinkedList.size(); i++) { | |||||
| responseLinkedList.add(asyncFutureLinkedList.get(i).get()); | |||||
| } | |||||
| result = new BatchAppResultImpl(responseLinkedList, newStateSnapshot.getSnapshot(), batchId, genisStateSnapshot.getSnapshot()); | |||||
| result.setErrorCode((byte) 0); | |||||
| } catch (Exception e) { | |||||
| LOGGER.error("Error occurred while pre compute app! --" + e.getMessage(), e); | |||||
| for (int i = 0; i < commands.length; i++) { | |||||
| responseLinkedList.add(createAppResponse(commands[i],TransactionState.IGNORED_BY_BLOCK_FULL_ROLLBACK)); | |||||
| } | |||||
| result = new BatchAppResultImpl(responseLinkedList,preStateSnapshot.getSnapshot(), batchId, genisStateSnapshot.getSnapshot()); | |||||
| result.setErrorCode((byte) 1); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| // Block full rollback responses, generated in pre compute phase, due to tx exception | |||||
| private byte[] createAppResponse(byte[] command, TransactionState transactionState) { | |||||
| TransactionRequest txRequest = BinaryProtocol.decode(command); | |||||
| TxResponseMessage resp = new TxResponseMessage(txRequest.getTransactionContent().getHash()); | |||||
| resp.setExecutionState(transactionState); | |||||
| return BinaryProtocol.encode(resp, TransactionResponse.class); | |||||
| } | |||||
| public List<byte[]> updateAppResponses(List<byte[]> asyncResponseLinkedList, byte[] commonHash, boolean isConsistent) { | |||||
| List<byte[]> updatedResponses = new ArrayList<>(); | |||||
| TxResponseMessage resp = null; | |||||
| for(int i = 0; i < asyncResponseLinkedList.size(); i++) { | |||||
| TransactionResponse txResponse = BinaryProtocol.decode(asyncResponseLinkedList.get(i)); | |||||
| if (isConsistent) { | |||||
| resp = new TxResponseMessage(txResponse.getContentHash()); | |||||
| } | |||||
| else { | |||||
| resp = new TxResponseMessage(new HashDigest(commonHash)); | |||||
| } | |||||
| resp.setExecutionState(TransactionState.IGNORED_BY_BLOCK_FULL_ROLLBACK); | |||||
| updatedResponses.add(BinaryProtocol.encode(resp, TransactionResponse.class)); | |||||
| } | |||||
| return updatedResponses; | |||||
| } | |||||
| /** | |||||
| * | |||||
| * Decision has been made at the consensus stage, commit block | |||||
| * | |||||
| */ | |||||
| public void preComputeAppCommit(String batchId) { | |||||
| try { | |||||
| messageHandle.commitBatch(realmName, batchId); | |||||
| } catch (BlockRollbackException e) { | |||||
| LOGGER.error("Error occurred while pre compute commit --" + e.getMessage(), e); | |||||
| throw e; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * | |||||
| * Consensus write phase will terminate, new block hash values are inconsistent, rollback block | |||||
| * | |||||
| */ | |||||
| public void preComputeAppRollback(String batchId) { | |||||
| messageHandle.rollbackBatch(realmName, batchId, TransactionState.IGNORED_BY_BLOCK_FULL_ROLLBACK.CODE); | |||||
| LOGGER.debug("Rollback of operations that cause inconsistencies in the ledger"); | |||||
| } | |||||
| //notice | |||||
| public byte[] getSnapshot() { | |||||
| LOGGER.debug("------- GetSnapshot...[replica.id=" + this.getId() + "]"); | |||||
| ByteArrayOutputStream out = new ByteArrayOutputStream(); | |||||
| BytesUtils.writeInt(stateHandles.size(), out); | |||||
| for (StateHandle stateHandle : stateHandles) { | |||||
| // TODO: 测试代码; | |||||
| return stateHandle.takeSnapshot(); | |||||
| } | |||||
| return out.toByteArray(); | |||||
| } | |||||
| public void installSnapshot(byte[] snapshot) { | |||||
| // System.out.println("Not implement!"); | |||||
| } | |||||
| @Override | |||||
| public void start() { | |||||
| if (this.getId() < 0) { | |||||
| throw new IllegalStateException("Unset server node ID!"); | |||||
| } | |||||
| LOGGER.debug("=============================== Start replica ==================================="); | |||||
| if (status != Status.STOPPED) { | |||||
| return; | |||||
| } | |||||
| synchronized (mutex) { | |||||
| if (status != Status.STOPPED) { | |||||
| return; | |||||
| } | |||||
| status = Status.STARTING; | |||||
| try { | |||||
| LOGGER.debug("Start replica...[ID=" + getId() + "]"); | |||||
| this.replica = new ServiceReplica(tomConfig, this, this); | |||||
| this.topology = new BftsmartTopology(replica.getReplicaContext().getCurrentView()); | |||||
| status = Status.RUNNING; | |||||
| // createProxyClient(); | |||||
| LOGGER.debug( | |||||
| "=============================== Replica started success! ==================================="); | |||||
| } catch (RuntimeException e) { | |||||
| status = Status.STOPPED; | |||||
| throw e; | |||||
| } | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void stop() { | |||||
| if (status != Status.RUNNING) { | |||||
| return; | |||||
| } | |||||
| synchronized (mutex) { | |||||
| if (status != Status.RUNNING) { | |||||
| return; | |||||
| } | |||||
| status = Status.STOPPING; | |||||
| try { | |||||
| ServiceReplica rep = this.replica; | |||||
| if (rep != null) { | |||||
| LOGGER.debug("Stop replica...[ID=" + rep.getId() + "]"); | |||||
| this.replica = null; | |||||
| this.topology = null; | |||||
| rep.kill(); | |||||
| LOGGER.debug("Replica had stopped! --[ID=" + rep.getId() + "]"); | |||||
| } | |||||
| } finally { | |||||
| status = Status.STOPPED; | |||||
| } | |||||
| } | |||||
| } | |||||
| enum Status { | |||||
| STARTING, | |||||
| RUNNING, | |||||
| STOPPING, | |||||
| STOPPED | |||||
| } | |||||
| } | |||||
| @@ -1,110 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart.service; | |||||
| import com.jd.blockchain.consensus.ConsensusSettings; | |||||
| import com.jd.blockchain.consensus.NodeSettings; | |||||
| import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings; | |||||
| import com.jd.blockchain.consensus.bftsmart.BftsmartNodeSettings; | |||||
| import com.jd.blockchain.consensus.service.MessageHandle; | |||||
| import com.jd.blockchain.consensus.service.NodeServer; | |||||
| import com.jd.blockchain.consensus.service.NodeServerFactory; | |||||
| import com.jd.blockchain.consensus.service.ServerSettings; | |||||
| import com.jd.blockchain.consensus.service.StateMachineReplicate; | |||||
| import com.jd.blockchain.utils.net.NetworkAddress; | |||||
| import java.util.Arrays; | |||||
| import java.util.Map; | |||||
| import java.util.concurrent.ConcurrentHashMap; | |||||
| public class BftsmartNodeServerFactory implements NodeServerFactory { | |||||
| private static Map<String, NodeSettings[]> nodeServerMap = new ConcurrentHashMap<>(); | |||||
| @Override | |||||
| public ServerSettings buildServerSettings(String realmName, ConsensusSettings consensusSetting, String currentNodeAddress) { | |||||
| NodeSettings serverNode = null; | |||||
| BftsmartServerSettingConfig serverSettings = new BftsmartServerSettingConfig(); | |||||
| //find current node according to current address | |||||
| for (NodeSettings nodeSettings : consensusSetting.getNodes()) { | |||||
| if (nodeSettings.getAddress().equals(currentNodeAddress)) { | |||||
| serverNode = nodeSettings; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (serverNode == null) { | |||||
| throw new IllegalArgumentException(); | |||||
| } | |||||
| //set server settings | |||||
| serverSettings.setRealmName(realmName); | |||||
| serverSettings.setReplicaSettings(serverNode); | |||||
| serverSettings.setConsensusSettings((BftsmartConsensusSettings) consensusSetting); | |||||
| return serverSettings; | |||||
| } | |||||
| @Override | |||||
| public NodeServer setupServer(ServerSettings serverSettings, MessageHandle messageHandler, | |||||
| StateMachineReplicate stateMachineReplicator) { | |||||
| NodeSettings[] currNodeSettings = (((BftsmartServerSettings)serverSettings).getConsensusSettings()).getNodes(); | |||||
| //check conflict realm | |||||
| if (!hasIntersection(currNodeSettings)) { | |||||
| BftsmartNodeServer nodeServer = new BftsmartNodeServer(serverSettings, messageHandler, stateMachineReplicator); | |||||
| nodeServerMap.put(serverSettings.getRealmName(), currNodeSettings); | |||||
| return nodeServer; | |||||
| } | |||||
| else { | |||||
| throw new IllegalArgumentException("setupServer serverSettings parameters error!"); | |||||
| } | |||||
| } | |||||
| //check if consensus realm conflict, by this support multi ledgers | |||||
| private boolean hasIntersection(NodeSettings[] currNodeSettings) { | |||||
| int currHashCode = getHashcode(currNodeSettings); | |||||
| //first check if is same consensus realm | |||||
| for (NodeSettings[] exisitNodeSettings : nodeServerMap.values()) { | |||||
| if (currHashCode == getHashcode(exisitNodeSettings)) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| //check conflict | |||||
| for (NodeSettings[] exisitNodeSettings : nodeServerMap.values()) { | |||||
| for (NodeSettings curr : currNodeSettings) { | |||||
| for (NodeSettings exist : exisitNodeSettings) { | |||||
| if (((BftsmartNodeSettings)curr).getNetworkAddress().equals(((BftsmartNodeSettings)exist).getNetworkAddress())) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| //compute hashcode for consensus nodes | |||||
| private int getHashcode(NodeSettings[] nodeSettings) { | |||||
| int i = 0; | |||||
| NetworkAddress[] nodeAddrs = new NetworkAddress[nodeSettings.length]; | |||||
| for (NodeSettings setting : nodeSettings) { | |||||
| nodeAddrs[i++] = ((BftsmartNodeSettings)setting).getNetworkAddress(); | |||||
| } | |||||
| int hashCode = Arrays.hashCode(nodeAddrs); | |||||
| return hashCode; | |||||
| } | |||||
| } | |||||
| @@ -1,40 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart.service; | |||||
| import com.jd.blockchain.consensus.NodeSettings; | |||||
| import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings; | |||||
| public class BftsmartServerSettingConfig implements BftsmartServerSettings { | |||||
| private NodeSettings replicaSettings; | |||||
| private String realmName; | |||||
| private BftsmartConsensusSettings consensusSettings; | |||||
| @Override | |||||
| public String getRealmName() { | |||||
| return realmName; | |||||
| } | |||||
| public void setRealmName(String realmName) { | |||||
| this.realmName = realmName; | |||||
| } | |||||
| @Override | |||||
| public NodeSettings getReplicaSettings() { | |||||
| return replicaSettings; | |||||
| } | |||||
| public void setReplicaSettings(NodeSettings replicaSettings) { | |||||
| this.replicaSettings = replicaSettings; | |||||
| } | |||||
| @Override | |||||
| public BftsmartConsensusSettings getConsensusSettings() { | |||||
| return consensusSettings; | |||||
| } | |||||
| public void setConsensusSettings(BftsmartConsensusSettings consensusSettings) { | |||||
| this.consensusSettings = consensusSettings; | |||||
| } | |||||
| } | |||||
| @@ -1,10 +0,0 @@ | |||||
| package com.jd.blockchain.consensus.bftsmart.service; | |||||
| import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings; | |||||
| import com.jd.blockchain.consensus.service.ServerSettings; | |||||
| public interface BftsmartServerSettings extends ServerSettings { | |||||
| BftsmartConsensusSettings getConsensusSettings(); | |||||
| } | |||||
| @@ -1,144 +0,0 @@ | |||||
| # 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. | |||||
| ############################################ | |||||
| ####### Communication Configurations ####### | |||||
| ############################################ | |||||
| #HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) | |||||
| #This parameter is not currently being used 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 ### | |||||
| ############################################ | |||||
| #Timeout to asking for a client request | |||||
| system.totalordermulticast.timeout = 2000 | |||||
| #Maximum batch size (in number of messages) | |||||
| system.totalordermulticast.maxbatchsize = 400 | |||||
| #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 ###### | |||||
| ############################################ | |||||
| #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 | |||||
| #Number of servers in the group | |||||
| system.servers.num = 4 | |||||
| #Maximum number of faulty replicas | |||||
| system.servers.f = 1 | |||||
| #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 | |||||
| #Configuration of all node servers; | |||||
| #PubKey of node server with specified ID, with base58 encoding. | |||||
| system.server.0.pubkey= | |||||
| system.server.0.network.host=127.0.0.1 | |||||
| system.server.0.network.port=8900 | |||||
| system.server.0.network.secure=false | |||||
| system.server.1.pubkey= | |||||
| system.server.1.network.host=127.0.0.1 | |||||
| system.server.1.network.port=8910 | |||||
| system.server.1.network.secure=false | |||||
| system.server.2.pubkey= | |||||
| system.server.2.network.host=127.0.0.1 | |||||
| system.server.2.network.port=8920 | |||||
| system.server.2.network.secure=false | |||||
| system.server.3.pubkey= | |||||
| system.server.3.network.host=127.0.0.1 | |||||
| system.server.3.network.port=8920 | |||||
| system.server.3.network.secure=false | |||||
| @@ -1,137 +0,0 @@ | |||||
| package test.com.jd.blockchain.consensus.bftsmart; | |||||
| import com.jd.blockchain.consensus.NodeSettings; | |||||
| import com.jd.blockchain.consensus.bftsmart.*; | |||||
| import com.jd.blockchain.consensus.bftsmart.client.BftsmartClientConfig; | |||||
| import com.jd.blockchain.consensus.bftsmart.client.BftsmartConsensusClient; | |||||
| import com.jd.blockchain.consensus.bftsmart.client.BftsmartMessageService; | |||||
| import com.jd.blockchain.consensus.bftsmart.service.BftsmartNodeServer; | |||||
| import com.jd.blockchain.consensus.bftsmart.service.BftsmartServerSettingConfig; | |||||
| import com.jd.blockchain.consensus.service.ServerSettings; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||||
| import com.jd.blockchain.ledger.BlockchainKeypair; | |||||
| import com.jd.blockchain.utils.PropertiesUtils; | |||||
| import com.jd.blockchain.utils.io.BytesUtils; | |||||
| import com.jd.blockchain.utils.net.NetworkAddress; | |||||
| import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; | |||||
| import org.junit.Test; | |||||
| import org.springframework.core.io.ClassPathResource; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.util.Properties; | |||||
| import java.util.Random; | |||||
| import java.util.concurrent.CountDownLatch; | |||||
| import java.util.concurrent.ExecutorService; | |||||
| import java.util.concurrent.Executors; | |||||
| public class ProxyClientTest { | |||||
| int number = 1500000; | |||||
| int peerStartPort = 11000; | |||||
| int nodeNum = 4; | |||||
| Random random = new Random(); | |||||
| byte[] bytes = null; | |||||
| CountDownLatch startPeer = new CountDownLatch(nodeNum); | |||||
| private static Properties bftsmartConf; | |||||
| private final ExecutorService nodeStartPools = Executors.newCachedThreadPool(); | |||||
| private final ExecutorService txSendPools = Executors.newFixedThreadPool(20); | |||||
| static { | |||||
| ClassPathResource configResource = new ClassPathResource("system.config"); | |||||
| try { | |||||
| try (InputStream in = configResource.getInputStream()) { | |||||
| bftsmartConf = PropertiesUtils.load(in, BytesUtils.DEFAULT_CHARSET); | |||||
| } | |||||
| } catch (IOException e) { | |||||
| throw new IllegalStateException(e.getMessage(), e); | |||||
| } | |||||
| } | |||||
| public void peerStart(BftsmartNodeServer[] nodeServers) { | |||||
| BftsmartNodeSettings[] nodesSettings = new BftsmartNodeSettings[nodeNum]; | |||||
| for (int i = 0; i < nodeNum; i++) { | |||||
| BlockchainKeypair keyPair = BlockchainKeyGenerator.getInstance().generate(); | |||||
| PubKey pubKey = keyPair.getPubKey(); | |||||
| NetworkAddress peerNodeServ = new NetworkAddress("127.0.0.1", peerStartPort + i * 10); | |||||
| NodeSettings node = new BftsmartNodeConfig(pubKey, i, peerNodeServ); | |||||
| nodesSettings[i] = (BftsmartNodeSettings) node; | |||||
| } | |||||
| BftsmartConsensusConfig consensusConfig = new BftsmartConsensusConfig(nodesSettings, | |||||
| // null, | |||||
| PropertiesUtils.getOrderedValues(bftsmartConf)); | |||||
| for (int j = 0; j < nodeNum; j++) { | |||||
| BftsmartServerSettingConfig serverSettings = new BftsmartServerSettingConfig(); | |||||
| serverSettings.setReplicaSettings(nodesSettings[j]); | |||||
| serverSettings.setConsensusSettings(consensusConfig); | |||||
| BftsmartNodeServer server = new BftsmartNodeServer(serverSettings, null, null); | |||||
| nodeServers[j] = server; | |||||
| nodeStartPools.execute(() -> { | |||||
| server.start(); | |||||
| startPeer.countDown(); | |||||
| }); | |||||
| } | |||||
| } | |||||
| public void proxyClientSend(BftsmartNodeServer nodeServer) { | |||||
| BftsmartClientIncomingConfig clientIncomingConfig = new BftsmartClientIncomingConfig(); | |||||
| BlockchainKeypair keyPair = BlockchainKeyGenerator.getInstance().generate(); | |||||
| clientIncomingConfig.setPubKey(keyPair.getPubKey()); | |||||
| clientIncomingConfig.setClientId(0); | |||||
| clientIncomingConfig.setConsensusSettings(nodeServer.getConsensusSetting()); | |||||
| clientIncomingConfig.setTomConfig(BinarySerializeUtils.serialize(nodeServer.getTomConfig())); | |||||
| clientIncomingConfig.setTopology(BinarySerializeUtils.serialize(nodeServer.getTopology())); | |||||
| BftsmartClientConfig clientSettings = new BftsmartClientConfig(clientIncomingConfig); | |||||
| BftsmartConsensusClient consensusClient = new BftsmartConsensusClient(clientSettings); | |||||
| bytes = new byte[1024]; | |||||
| BftsmartMessageService messageService = (BftsmartMessageService) consensusClient.getMessageService(); | |||||
| for (int j = 0; j < number; j++) { | |||||
| txSendPools.execute(() -> { | |||||
| random.nextBytes(bytes); | |||||
| messageService.sendOrdered(bytes); | |||||
| }); | |||||
| } | |||||
| } | |||||
| // @Test | |||||
| public void sendTest() { | |||||
| BftsmartNodeServer[] nodeServers = new BftsmartNodeServer[nodeNum]; | |||||
| //启动服务 | |||||
| peerStart(nodeServers); | |||||
| try { | |||||
| startPeer.await(); | |||||
| Thread.sleep(5000); | |||||
| } catch (Exception e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| proxyClientSend(nodeServers[0]); | |||||
| try { | |||||
| Thread.sleep(50000); | |||||
| System.out.println("send test complete!"); | |||||
| } catch (Exception e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,178 +0,0 @@ | |||||
| # 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=15 | |||||
| ############################################ | |||||
| ###### Consensus Commit Block Parameters: delay time ###### | |||||
| ############################################ | |||||
| system.block.maxdelay=500 | |||||
| ############################################ | |||||
| ###### Consensus Participant0 ###### | |||||
| ############################################ | |||||
| system.server.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna | |||||
| system.server.0.network.host=127.0.0.1 | |||||
| system.server.0.network.port=8910 | |||||
| system.server.0.network.secure=false | |||||
| ############################################ | |||||
| ###### #Consensus Participant1 ###### | |||||
| ############################################ | |||||
| system.server.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ | |||||
| system.server.1.network.host=127.0.0.1 | |||||
| system.server.1.network.port=8920 | |||||
| system.server.1.network.secure=false | |||||
| ############################################ | |||||
| ###### #Consensus Participant2 ###### | |||||
| ############################################ | |||||
| system.server.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R | |||||
| system.server.2.network.host=127.0.0.1 | |||||
| system.server.2.network.port=8930 | |||||
| system.server.2.network.secure=false | |||||
| ############################################ | |||||
| ###### Consensus Participant3 ###### | |||||
| ############################################ | |||||
| system.server.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR | |||||
| system.server.3.network.host=127.0.0.1 | |||||
| system.server.3.network.port=8940 | |||||
| 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 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 = 400 | |||||
| #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 | |||||
| @@ -1,121 +0,0 @@ | |||||
| # 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. | |||||
| ############################################ | |||||
| ####### Communication Configurations ####### | |||||
| ############################################ | |||||
| #HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) | |||||
| #This parameter is not currently being used 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 = 400 | |||||
| #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 | |||||
| @@ -1,71 +0,0 @@ | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>consensus-core</artifactId> | |||||
| <version>1.2.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>consensus-mq</artifactId> | |||||
| <name>consensus-mq</name> | |||||
| <properties> | |||||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||||
| <maven.compiler.source>1.8</maven.compiler.source> | |||||
| <maven.compiler.target>1.8</maven.compiler.target> | |||||
| </properties> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>consensus-framework</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>tools-keygen</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>ledger-model</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.lmax</groupId> | |||||
| <artifactId>disruptor</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>io.nats</groupId> | |||||
| <artifactId>jnats</artifactId> | |||||
| </dependency> | |||||
| <!-- rabbitmq --> | |||||
| <dependency> | |||||
| <groupId>com.rabbitmq</groupId> | |||||
| <artifactId>amqp-client</artifactId> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>junit</groupId> | |||||
| <artifactId>junit</artifactId> | |||||
| <scope>test</scope> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <build> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-deploy-plugin</artifactId> | |||||
| <version>2.8.2</version> | |||||
| <configuration> | |||||
| <skip>true</skip> | |||||
| </configuration> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| </project> | |||||
| @@ -1,55 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/18 下午2:50 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq; | |||||
| import com.jd.blockchain.consensus.ConsensusProvider; | |||||
| import com.jd.blockchain.consensus.SettingsFactory; | |||||
| import com.jd.blockchain.consensus.client.ClientFactory; | |||||
| import com.jd.blockchain.consensus.mq.client.MsgQueueClientFactory; | |||||
| import com.jd.blockchain.consensus.mq.config.MsgQueueSettingsFactory; | |||||
| import com.jd.blockchain.consensus.mq.server.MsgQueueNodeServerFactory; | |||||
| import com.jd.blockchain.consensus.service.NodeServerFactory; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/18 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueConsensusProvider implements ConsensusProvider { | |||||
| public static final String NAME = MsgQueueConsensusProvider.class.getName(); | |||||
| private static MsgQueueSettingsFactory settingsFactory = new MsgQueueSettingsFactory(); | |||||
| private static MsgQueueClientFactory clientFactory = new MsgQueueClientFactory(); | |||||
| private static MsgQueueNodeServerFactory nodeServerFactory = new MsgQueueNodeServerFactory(); | |||||
| @Override | |||||
| public String getName() { | |||||
| return NAME; | |||||
| } | |||||
| @Override | |||||
| public SettingsFactory getSettingsFactory() { | |||||
| return settingsFactory; | |||||
| } | |||||
| @Override | |||||
| public ClientFactory getClientFactory() { | |||||
| return clientFactory; | |||||
| } | |||||
| @Override | |||||
| public NodeServerFactory getServerFactory() { | |||||
| return nodeServerFactory; | |||||
| } | |||||
| } | |||||
| @@ -1,301 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.mq.MsgQueueConsensusSettingsBuilder | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 下午1:46 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq; | |||||
| import com.jd.blockchain.consensus.ConsensusProviders; | |||||
| import com.jd.blockchain.consensus.ConsensusSettings; | |||||
| import com.jd.blockchain.consensus.ConsensusSettingsBuilder; | |||||
| import com.jd.blockchain.consensus.NodeSettings; | |||||
| import com.jd.blockchain.consensus.mq.config.MsgQueueBlockConfig; | |||||
| import com.jd.blockchain.consensus.mq.config.MsgQueueConsensusConfig; | |||||
| import com.jd.blockchain.consensus.mq.config.MsgQueueNetworkConfig; | |||||
| import com.jd.blockchain.consensus.mq.config.MsgQueueNodeConfig; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueBlockSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings; | |||||
| import com.jd.blockchain.crypto.AddressEncoding; | |||||
| import com.jd.blockchain.crypto.KeyGenUtils; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| import com.jd.blockchain.ledger.ParticipantInfo; | |||||
| import com.jd.blockchain.ledger.ParticipantNode; | |||||
| import com.jd.blockchain.utils.Bytes; | |||||
| import com.jd.blockchain.utils.PropertiesUtils; | |||||
| import com.jd.blockchain.utils.codec.Base58Utils; | |||||
| import com.jd.blockchain.utils.io.BytesEncoder; | |||||
| import com.jd.blockchain.utils.io.BytesUtils; | |||||
| import com.jd.blockchain.utils.io.FileUtils; | |||||
| import com.jd.blockchain.utils.net.NetworkAddress; | |||||
| import org.springframework.core.io.ClassPathResource; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.util.Properties; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueConsensusSettingsBuilder implements ConsensusSettingsBuilder { | |||||
| private static final String DEFAULT_TOPIC_TX = "tx-topic"; | |||||
| private static final String DEFAULT_TOPIC_BL = "bl-topic"; | |||||
| private static final String DEFAULT_TOPIC_MSG = "msg-topic"; | |||||
| private static final int DEFAULT_TXSIZE = 1000; | |||||
| private static final int DEFAULT_MAXDELAY = 1000; | |||||
| /** | |||||
| * | |||||
| */ | |||||
| private static final String CONFIG_TEMPLATE_FILE = "mq.config"; | |||||
| /** | |||||
| * 参数键:节点数量; | |||||
| */ | |||||
| public static final String SERVER_NUM_KEY = "system.servers.num"; | |||||
| /** | |||||
| * 参数键格式:节点公钥; | |||||
| */ | |||||
| public static final String PUBKEY_PATTERN = "system.server.%s.pubkey"; | |||||
| public static final String MSG_QUEUE_SERVER = "system.msg.queue.server"; | |||||
| public static final String MSG_QUEUE_TOPIC_TX = "system.msg.queue.topic.tx"; | |||||
| public static final String MSG_QUEUE_TOPIC_BL = "system.msg.queue.topic.bl"; | |||||
| public static final String MSG_QUEUE_TOPIC_MSG = "system.msg.queue.topic.msg"; | |||||
| public static final String MSG_QUEUE_BLOCK_TXSIZE = "system.msg.queue.block.txsize"; | |||||
| public static final String MSG_QUEUE_BLOCK_MAXDELAY = "system.msg.queue.block.maxdelay"; | |||||
| public static final String MSG_QUEUE_PROVIDER = "com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider"; | |||||
| private static Properties CONFIG_TEMPLATE; | |||||
| static { | |||||
| if (FileUtils.existFile(CONFIG_TEMPLATE_FILE)) { | |||||
| ClassPathResource configResource = new ClassPathResource(CONFIG_TEMPLATE_FILE); | |||||
| try { | |||||
| try (InputStream in = configResource.getInputStream()) { | |||||
| CONFIG_TEMPLATE = PropertiesUtils.load(in, BytesUtils.DEFAULT_CHARSET); | |||||
| } | |||||
| } catch (IOException e) { | |||||
| throw new IllegalStateException(e.getMessage(), e); | |||||
| } | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public MsgQueueConsensusSettings createSettings(Properties props, ParticipantNode[] participantNodes) { | |||||
| MsgQueueNetworkConfig networkConfig = new MsgQueueNetworkConfig(); | |||||
| Properties resolvingProps = PropertiesUtils.cloneFrom(props); | |||||
| String server = PropertiesUtils.getProperty(resolvingProps, MSG_QUEUE_SERVER, true); | |||||
| if (server == null || server.length()<= 0) { | |||||
| throw new IllegalArgumentException(String.format("Property[%s] is empty!", MSG_QUEUE_SERVER)); | |||||
| } | |||||
| networkConfig.setServer(server) | |||||
| .setTxTopic(initProp(resolvingProps, MSG_QUEUE_TOPIC_TX, DEFAULT_TOPIC_TX)) | |||||
| .setBlTopic(initProp(resolvingProps, MSG_QUEUE_TOPIC_BL, DEFAULT_TOPIC_BL)) | |||||
| .setMsgTopic(initProp(resolvingProps, MSG_QUEUE_TOPIC_MSG, DEFAULT_TOPIC_MSG)) | |||||
| ; | |||||
| MsgQueueBlockConfig blockConfig = new MsgQueueBlockConfig() | |||||
| .setTxSizePerBlock(initProp(resolvingProps, MSG_QUEUE_BLOCK_TXSIZE, DEFAULT_TXSIZE)) | |||||
| .setMaxDelayMilliSecondsPerBlock(initProp(resolvingProps, MSG_QUEUE_BLOCK_MAXDELAY, DEFAULT_MAXDELAY)) | |||||
| ; | |||||
| MsgQueueConsensusConfig consensusConfig = new MsgQueueConsensusConfig() | |||||
| .setBlockSettings(blockConfig) | |||||
| .setNetworkSettings(networkConfig) | |||||
| ; | |||||
| // load node settings | |||||
| int serversNum = PropertiesUtils.getInt(resolvingProps, SERVER_NUM_KEY); | |||||
| for (int i = 0; i < serversNum; i++) { | |||||
| int id = i; | |||||
| String keyOfPubkey = nodeKey(PUBKEY_PATTERN, id); | |||||
| String base58PubKey = PropertiesUtils.getRequiredProperty(resolvingProps, keyOfPubkey); | |||||
| PubKey pubKey = KeyGenUtils.decodePubKey(base58PubKey); | |||||
| // PubKey pubKey = new PubKey(Base58Utils.decode(base58PubKey)); | |||||
| resolvingProps.remove(keyOfPubkey); | |||||
| Bytes address = AddressEncoding.generateAddress(pubKey); | |||||
| String networkAddress = address.toBase58(); | |||||
| MsgQueueNodeConfig nodeConfig = new MsgQueueNodeConfig() | |||||
| .setAddress(networkAddress) | |||||
| .setPubKey(pubKey) | |||||
| ; | |||||
| consensusConfig.addNodeSettings(nodeConfig); | |||||
| } | |||||
| return consensusConfig; | |||||
| } | |||||
| private MsgQueueNodeSettings[] nodeSettings(NodeSettings[] nodeSettings, ParticipantInfo participantInfo) { | |||||
| MsgQueueNodeSettings msgQueueNodeSettings = new MsgQueueNodeConfig(); | |||||
| ((MsgQueueNodeConfig) msgQueueNodeSettings).setAddress(AddressEncoding.generateAddress(participantInfo.getPubKey()).toBase58()); | |||||
| ((MsgQueueNodeConfig) msgQueueNodeSettings).setPubKey(participantInfo.getPubKey()); | |||||
| MsgQueueNodeSettings[] msgQueuetNodeSettings = new MsgQueueNodeSettings[nodeSettings.length + 1]; | |||||
| for (int i = 0; i < nodeSettings.length; i++) { | |||||
| msgQueuetNodeSettings[i] = (MsgQueueNodeSettings)nodeSettings[i]; | |||||
| } | |||||
| msgQueuetNodeSettings[nodeSettings.length] = msgQueueNodeSettings; | |||||
| return msgQueuetNodeSettings; | |||||
| } | |||||
| @Override | |||||
| public Bytes updateSettings(Bytes oldConsensusSettings, ParticipantInfo participantInfo) { | |||||
| BytesEncoder<ConsensusSettings> consensusEncoder = ConsensusProviders.getProvider(MSG_QUEUE_PROVIDER).getSettingsFactory().getConsensusSettingsEncoder(); | |||||
| MsgQueueConsensusSettings consensusSettings = (MsgQueueConsensusSettings) consensusEncoder.decode(oldConsensusSettings.toBytes()); | |||||
| MsgQueueNodeSettings[] nodeSettings = nodeSettings(consensusSettings.getNodes(), participantInfo); | |||||
| MsgQueueConsensusConfig msgQueueConsensusConfig = new MsgQueueConsensusConfig(); | |||||
| for (int i = 0; i < nodeSettings.length; i++) { | |||||
| msgQueueConsensusConfig.addNodeSettings(nodeSettings[i]); | |||||
| } | |||||
| msgQueueConsensusConfig.setBlockSettings(consensusSettings.getBlockSettings()); | |||||
| msgQueueConsensusConfig.setNetworkSettings(consensusSettings.getNetworkSettings()); | |||||
| // for(int i = 0 ;i < msgQueueConsensusConfig.getNodes().length; i++) { | |||||
| // System.out.printf("node addr = %s\r\n", msgQueueConsensusConfig.getNodes()[i].getAddress()); | |||||
| // } | |||||
| return new Bytes(consensusEncoder.encode(msgQueueConsensusConfig)); | |||||
| } | |||||
| @Override | |||||
| public Properties createPropertiesTemplate() { | |||||
| return PropertiesUtils.cloneFrom(CONFIG_TEMPLATE); | |||||
| } | |||||
| @Override | |||||
| public void writeSettings(ConsensusSettings settings, Properties props) { | |||||
| if (!(settings instanceof MsgQueueConsensusSettings)) { | |||||
| throw new IllegalArgumentException("ConsensusSettings data isn't supported! Accept MsgQueueConsensusSettings only!"); | |||||
| } | |||||
| MsgQueueConsensusSettings consensusSettings = (MsgQueueConsensusSettings) settings; | |||||
| MsgQueueNetworkSettings networkSettings = consensusSettings.getNetworkSettings(); | |||||
| if (networkSettings == null || networkSettings.getServer() == null || networkSettings.getServer().length() <= 0) { | |||||
| throw new IllegalArgumentException("MsgQueue Consensus server is empty!"); | |||||
| } | |||||
| String server = networkSettings.getServer(); | |||||
| props.setProperty(MSG_QUEUE_SERVER, server); | |||||
| String txTopic = networkSettings.getTxTopic(); | |||||
| if (txTopic == null || txTopic.length() <= 0) { | |||||
| txTopic = DEFAULT_TOPIC_TX; | |||||
| } | |||||
| props.setProperty(MSG_QUEUE_TOPIC_TX, txTopic); | |||||
| String blTopic = networkSettings.getBlTopic(); | |||||
| if (blTopic == null || blTopic.length() <= 0) { | |||||
| blTopic = DEFAULT_TOPIC_BL; | |||||
| } | |||||
| props.setProperty(MSG_QUEUE_TOPIC_BL, blTopic); | |||||
| String msgTopic = networkSettings.getMsgTopic(); | |||||
| if (msgTopic == null || msgTopic.length() <= 0) { | |||||
| msgTopic = DEFAULT_TOPIC_MSG; | |||||
| } | |||||
| props.setProperty(MSG_QUEUE_TOPIC_MSG, msgTopic); | |||||
| MsgQueueBlockSettings blockSettings = consensusSettings.getBlockSettings(); | |||||
| if (blockSettings == null) { | |||||
| props.setProperty(MSG_QUEUE_BLOCK_TXSIZE, DEFAULT_TXSIZE + ""); | |||||
| props.setProperty(MSG_QUEUE_BLOCK_MAXDELAY, DEFAULT_MAXDELAY + ""); | |||||
| } else { | |||||
| int txSize = blockSettings.getTxSizePerBlock(); | |||||
| long maxDelay = blockSettings.getMaxDelayMilliSecondsPerBlock(); | |||||
| props.setProperty(MSG_QUEUE_BLOCK_TXSIZE, txSize + ""); | |||||
| props.setProperty(MSG_QUEUE_BLOCK_MAXDELAY, maxDelay + ""); | |||||
| } | |||||
| // int serversNum = PropertiesUtils.getInt(props, SERVER_NUM_KEY); | |||||
| // if (serversNum > 0) { | |||||
| // for (int i = 0; i < serversNum; i++) { | |||||
| // int id = i; | |||||
| // String keyOfPubkey = nodeKey(PUBKEY_PATTERN, id); | |||||
| // props.remove(keyOfPubkey); | |||||
| // | |||||
| // String keyOfHost = nodeKey(CONSENSUS_HOST_PATTERN, id); | |||||
| // props.remove(keyOfHost); | |||||
| // } | |||||
| // } | |||||
| // | |||||
| // NodeSettings[] nodesSettings = consensusSettings.getNodes(); | |||||
| // serversNum = nodesSettings.length; | |||||
| // props.setProperty(SERVER_NUM_KEY, serversNum + ""); | |||||
| // | |||||
| // for (int i = 0; i < serversNum; i++) { | |||||
| // MsgQueueNodeSettings mqns = (MsgQueueNodeSettings) nodesSettings[i]; | |||||
| // int id = i; | |||||
| // String keyOfPubkey = nodeKey(PUBKEY_PATTERN, id); | |||||
| // props.setProperty(keyOfPubkey, mqns.getPubKey().toBase58()); | |||||
| // | |||||
| // String keyOfHost = nodeKey(CONSENSUS_HOST_PATTERN, id); | |||||
| // props.setProperty(keyOfHost, mqns.getAddress() == null ? "" : mqns.getAddress()); | |||||
| // } | |||||
| } | |||||
| private String initProp(Properties resolvingProps, String key, String defaultVal) { | |||||
| try { | |||||
| String value = PropertiesUtils.getProperty(resolvingProps, key, true); | |||||
| if (value == null || value.length() <= 0) { | |||||
| value = defaultVal; | |||||
| } | |||||
| return value; | |||||
| } catch (Exception e) { | |||||
| return defaultVal; | |||||
| } | |||||
| } | |||||
| private int initProp(Properties resolvingProps, String key, int defaultVal) { | |||||
| try { | |||||
| int value = PropertiesUtils.getInt(resolvingProps, key); | |||||
| if (value <= 0) { | |||||
| value = defaultVal; | |||||
| } | |||||
| return value; | |||||
| } catch (Exception e) { | |||||
| return defaultVal; | |||||
| } | |||||
| } | |||||
| private static String nodeKey(String pattern, int id) { | |||||
| return String.format(pattern, id); | |||||
| } | |||||
| } | |||||
| @@ -1,311 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.client.DefaultMessageTransmitter | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 下午3:05 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.client; | |||||
| import java.util.Map; | |||||
| import java.util.concurrent.ConcurrentHashMap; | |||||
| import java.util.concurrent.ExecutorService; | |||||
| import java.util.concurrent.Executors; | |||||
| import java.util.concurrent.atomic.AtomicBoolean; | |||||
| import com.jd.blockchain.consensus.event.EventEntity; | |||||
| import com.lmax.disruptor.EventHandler; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import com.jd.blockchain.consensus.MessageService; | |||||
| import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; | |||||
| import com.jd.blockchain.consensus.mq.event.TxBlockedEvent; | |||||
| import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; | |||||
| import com.jd.blockchain.consensus.mq.util.MessageConvertUtil; | |||||
| import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
| import com.jd.blockchain.utils.concurrent.CompletableAsyncFuture; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class DefaultMessageTransmitter implements MessageTransmitter, MessageService { | |||||
| private static final Logger LOGGER = LoggerFactory.getLogger(DefaultMessageTransmitter.class); | |||||
| private final ExecutorService messageExecutorArray = Executors.newFixedThreadPool(10); | |||||
| // private final ExecutorService blockExecutor = Executors.newSingleThreadExecutor(); | |||||
| // | |||||
| // private final ExecutorService extendExecutor = Executors.newSingleThreadExecutor(); | |||||
| private final Map<String, MessageListener> messageListeners = new ConcurrentHashMap<>(); | |||||
| private final BlockEventHandler blockEventHandler = new BlockEventHandler(); | |||||
| private final ExtendEventHandler extendEventHandler = new ExtendEventHandler(); | |||||
| private MsgQueueProducer txProducer; | |||||
| private MsgQueueProducer msgProducer; | |||||
| private MsgQueueConsumer blConsumer; | |||||
| private MsgQueueConsumer msgConsumer; | |||||
| private boolean isConnected = false; | |||||
| public DefaultMessageTransmitter setTxProducer(MsgQueueProducer txProducer) { | |||||
| this.txProducer = txProducer; | |||||
| return this; | |||||
| } | |||||
| public DefaultMessageTransmitter setMsgProducer(MsgQueueProducer msgProducer) { | |||||
| this.msgProducer = msgProducer; | |||||
| return this; | |||||
| } | |||||
| public DefaultMessageTransmitter setBlConsumer(MsgQueueConsumer blConsumer) { | |||||
| this.blConsumer = blConsumer; | |||||
| return this; | |||||
| } | |||||
| public DefaultMessageTransmitter setMsgConsumer(MsgQueueConsumer msgConsumer) { | |||||
| this.msgConsumer = msgConsumer; | |||||
| return this; | |||||
| } | |||||
| @Override | |||||
| public AsyncFuture<byte[]> sendOrdered(byte[] message) { | |||||
| AsyncFuture<byte[]> messageFuture; | |||||
| try { | |||||
| publishMessage(txProducer, message); | |||||
| messageFuture = messageHandle(message); | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| return messageFuture; | |||||
| } | |||||
| @Override | |||||
| public AsyncFuture<byte[]> sendUnordered(byte[] message) { | |||||
| AsyncFuture<byte[]> messageFuture; | |||||
| try { | |||||
| publishMessage(msgProducer, message); | |||||
| messageFuture = messageHandle(message); | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| return messageFuture; | |||||
| } | |||||
| @Override | |||||
| public void connect() throws Exception{ | |||||
| if (!isConnected) { | |||||
| this.txProducer.connect(); | |||||
| this.blConsumer.connect(blockEventHandler); | |||||
| this.msgProducer.connect(); | |||||
| this.msgConsumer.connect(extendEventHandler); | |||||
| isConnected = true; | |||||
| blConsumer.start(); | |||||
| msgConsumer.start(); | |||||
| // blockConsumerListening(); | |||||
| // extendConsumerListening(); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void publishMessage(MsgQueueProducer producer, byte[] message) throws Exception { | |||||
| producer.publish(message); | |||||
| } | |||||
| @Override | |||||
| public void close() { | |||||
| try { | |||||
| txProducer.close(); | |||||
| blConsumer.close(); | |||||
| msgProducer.close(); | |||||
| msgConsumer.close(); | |||||
| isConnected = false; | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| private AsyncFuture<byte[]> messageHandle(byte[] message) throws Exception { | |||||
| // 异步回调 | |||||
| // 需要监听MQ结块的应答 | |||||
| // 首先需要一个Consumer,在子类已实现 | |||||
| String messageKey = messageKey(message); | |||||
| AsyncFuture<byte[]> messageFuture = registerMessageListener(messageKey); | |||||
| return messageFuture; | |||||
| } | |||||
| private String messageKey(byte[] message) { | |||||
| return MessageConvertUtil.messageKey(message); | |||||
| } | |||||
| private AsyncFuture<byte[]> registerMessageListener(String messageKey) { | |||||
| CompletableAsyncFuture<byte[]> future = new CompletableAsyncFuture<>(); | |||||
| MessageListener messageListener = new MessageListener(messageKey, future); | |||||
| messageListener.addListener(); | |||||
| return future; | |||||
| } | |||||
| // private void blockConsumerListening() { | |||||
| // // 区块事件由单独一个线程处理 | |||||
| // blockExecutor.execute(() -> { | |||||
| // while(isConnected) { | |||||
| // try { | |||||
| // byte[] txBlockedEventBytes = blConsumer.start(); | |||||
| // // 交由事件处理机制来处理 | |||||
| // if (txBlockedEventBytes != null && txBlockedEventBytes.length > 0) { | |||||
| // txBlockedEventHandle(txBlockedEventBytes); | |||||
| // } | |||||
| // } catch (Exception e) { | |||||
| // LOGGER.error("process block listening message exception {}", e.getMessage()); | |||||
| // } | |||||
| // } | |||||
| // }); | |||||
| // } | |||||
| // private void extendConsumerListening() { | |||||
| // extendExecutor.execute(() -> { | |||||
| // while (isConnected) { | |||||
| // try { | |||||
| // byte[] msgBytes = msgConsumer.start(); | |||||
| // // 交由事件处理机制来处理 | |||||
| // if (msgBytes != null && msgBytes.length > 0) { | |||||
| // extendMessageHandle(msgBytes); | |||||
| // } | |||||
| // } catch (Exception e) { | |||||
| // LOGGER.error("process extend listening message exception {}", e.getMessage()); | |||||
| // } | |||||
| // } | |||||
| // }); | |||||
| // } | |||||
| private void txBlockedEventHandle(byte[] bytes) { | |||||
| messageExecutorArray.execute(() -> { | |||||
| if (!this.messageListeners.isEmpty()) { | |||||
| // 首先将字节数组转换为BlockEvent | |||||
| final TxBlockedEvent txBlockedEvent = | |||||
| MessageConvertUtil.convertBytes2TxBlockedEvent(bytes); | |||||
| if (txBlockedEvent != null) { | |||||
| // 需要判断该区块是否需要处理 | |||||
| if (isTxBlockedEventNeedManage(txBlockedEvent)) { | |||||
| dealTxBlockedEvent(txBlockedEvent); | |||||
| } | |||||
| } | |||||
| } | |||||
| }); | |||||
| } | |||||
| private void extendMessageHandle(byte[] message) { | |||||
| messageExecutorArray.execute(() -> { | |||||
| String messageKey = messageKey(message); | |||||
| if (messageListeners.containsKey(messageKey)) { | |||||
| dealExtendMessage(messageKey, message); | |||||
| } | |||||
| }); | |||||
| } | |||||
| private boolean isTxBlockedEventNeedManage(final TxBlockedEvent txBlockedEvent) { | |||||
| if (this.messageListeners.isEmpty()) { | |||||
| return false; | |||||
| } | |||||
| if (messageListeners.containsKey(txBlockedEvent.getTxKey())) { | |||||
| return true; | |||||
| } | |||||
| // 无须处理区块高度 | |||||
| return false; | |||||
| } | |||||
| private void dealTxBlockedEvent(final TxBlockedEvent txBlockedEvent) { | |||||
| String txKey = txBlockedEvent.getTxKey(); | |||||
| MessageListener txListener = this.messageListeners.get(txKey); | |||||
| if (txListener != null) { | |||||
| txListener.received(txBlockedEvent); | |||||
| this.messageListeners.remove(txKey); | |||||
| } | |||||
| } | |||||
| private void dealExtendMessage(final String messageKey, final byte[] message) { | |||||
| MessageListener txListener = this.messageListeners.get(messageKey); | |||||
| if (txListener != null) { | |||||
| txListener.received(message); | |||||
| this.messageListeners.remove(messageKey); | |||||
| } | |||||
| } | |||||
| private class MessageListener { | |||||
| final String messageKey; | |||||
| final CompletableAsyncFuture<byte[]> future; | |||||
| final AtomicBoolean isDeal = new AtomicBoolean(false); | |||||
| public MessageListener(String messageKey, CompletableAsyncFuture<byte[]> future) { | |||||
| this.messageKey = messageKey; | |||||
| this.future = future; | |||||
| addListener(); | |||||
| } | |||||
| public void addListener() { | |||||
| synchronized (messageListeners) { | |||||
| messageListeners.put(messageKey, this); | |||||
| } | |||||
| } | |||||
| public void received(final TxBlockedEvent txBlockedEvent) { | |||||
| // 期望是false,假设是false则设置为true,成功的情况下表示是第一次 | |||||
| byte[] txResp = txBlockedEvent.txResponseBytes(); | |||||
| if (txResp != null) { | |||||
| if (isDeal.compareAndSet(false, true)) { | |||||
| //生成对应的交易应答 | |||||
| future.complete(txResp); | |||||
| } | |||||
| } | |||||
| } | |||||
| public void received(final byte[] message) { | |||||
| // 期望是false,假设是false则设置为true,成功的情况下表示是第一次 | |||||
| if (message != null) { | |||||
| if (isDeal.compareAndSet(false, true)) { | |||||
| //生成对应的交易应答 | |||||
| future.complete(message); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| public class BlockEventHandler implements EventHandler<EventEntity<byte[]>> { | |||||
| @Override | |||||
| public void onEvent(EventEntity<byte[]> event, long sequence, boolean endOfBatch) throws Exception { | |||||
| byte[] txBlockedEventBytes = event.getEntity(); | |||||
| if (txBlockedEventBytes != null && txBlockedEventBytes.length > 0) { | |||||
| txBlockedEventHandle(txBlockedEventBytes); | |||||
| } | |||||
| } | |||||
| } | |||||
| public class ExtendEventHandler implements EventHandler<EventEntity<byte[]>> { | |||||
| @Override | |||||
| public void onEvent(EventEntity<byte[]> event, long sequence, boolean endOfBatch) throws Exception { | |||||
| byte[] msgBytes = event.getEntity(); | |||||
| if (msgBytes != null && msgBytes.length > 0) { | |||||
| extendMessageHandle(msgBytes); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,30 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.mq.client.MessageTransmitter | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 上午11:21 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.client; | |||||
| import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public interface MessageTransmitter { | |||||
| void connect() throws Exception; | |||||
| void publishMessage(MsgQueueProducer producer, byte[] message) throws Exception; | |||||
| // void processMsg(byte[] message); | |||||
| void close(); | |||||
| } | |||||
| @@ -1,92 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.mq.client.MsgQueueClientFactory | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 上午11:23 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.client; | |||||
| import com.jd.blockchain.consensus.ClientIncomingSettings; | |||||
| import com.jd.blockchain.consensus.ConsensusManageService; | |||||
| import com.jd.blockchain.consensus.client.ClientFactory; | |||||
| import com.jd.blockchain.consensus.client.ClientSettings; | |||||
| import com.jd.blockchain.consensus.mq.config.MsgQueueClientConfig; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueClientIncomingSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueClientSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; | |||||
| import com.jd.blockchain.crypto.AsymmetricKeypair; | |||||
| import com.jd.blockchain.crypto.Crypto; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| import com.jd.blockchain.crypto.SignatureDigest; | |||||
| import com.jd.blockchain.crypto.SignatureFunction; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueClientFactory implements ClientFactory { | |||||
| @Override | |||||
| public MsgQueueClientIdentification buildAuthId(AsymmetricKeypair clientKeyPair) { | |||||
| PubKey pubKey = clientKeyPair.getPubKey(); | |||||
| byte[] address = pubKey.toBytes(); // 使用公钥地址作为认证信息 | |||||
| SignatureFunction signatureFunction = Crypto.getSignatureFunction(pubKey.getAlgorithm()); | |||||
| SignatureDigest signatureDigest = signatureFunction.sign(clientKeyPair.getPrivKey(), address); | |||||
| MsgQueueClientIdentification mqci = new MsgQueueClientIdentification() | |||||
| .setPubKey(clientKeyPair.getPubKey()) | |||||
| .setIdentityInfo(address) | |||||
| .setSignature(signatureDigest) | |||||
| ; | |||||
| return mqci; | |||||
| } | |||||
| @Override | |||||
| public MsgQueueClientSettings buildClientSettings(ClientIncomingSettings incomingSettings) { | |||||
| MsgQueueClientIncomingSettings mqcic = (MsgQueueClientIncomingSettings)incomingSettings; | |||||
| if (mqcic != null) { | |||||
| return buildClientSettings(mqcic.getClientId(), mqcic.getPubKey(), (MsgQueueConsensusSettings)(mqcic.getConsensusSettings())); | |||||
| } | |||||
| throw new IllegalArgumentException("ClientIncomingSettings data isn't supported! Accept MsgQueueClientIncomingSettings only!"); | |||||
| } | |||||
| private MsgQueueClientSettings buildClientSettings(int clientId, PubKey pubKey, MsgQueueConsensusSettings mqcs) { | |||||
| MsgQueueClientSettings msgQueueClientConfig = new MsgQueueClientConfig() | |||||
| .setId(clientId) | |||||
| .setPubKey(pubKey) | |||||
| .setConsensusSettings(mqcs) | |||||
| ; | |||||
| return msgQueueClientConfig; | |||||
| } | |||||
| @Override | |||||
| public ConsensusManageService createManageServiceClient(String[] serviceNodes) { | |||||
| // todo serviceNodes // IP:port | |||||
| return null; | |||||
| } | |||||
| @Override | |||||
| public MsgQueueConsensusClient setupClient(ClientSettings settings) { | |||||
| if (settings instanceof MsgQueueClientSettings) { | |||||
| return setupClient((MsgQueueClientSettings)settings); | |||||
| } | |||||
| throw new IllegalArgumentException("ClientSettings data isn't supported! Accept MsgQueueClientSettings only!"); | |||||
| } | |||||
| private MsgQueueConsensusClient setupClient(MsgQueueClientSettings settings) { | |||||
| MsgQueueConsensusClient mqcc = new MsgQueueConsensusClient() | |||||
| .setClientSettings(settings) | |||||
| .setMsgQueueNetworkSettings(settings.getMsgQueueNetworkSettings()) | |||||
| ; | |||||
| mqcc.init(); | |||||
| return mqcc; | |||||
| } | |||||
| } | |||||
| @@ -1,74 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.mq.client.MsgQueueClientIdentification | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 下午2:04 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.client; | |||||
| import com.jd.blockchain.consensus.ClientIdentification; | |||||
| import com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| import com.jd.blockchain.crypto.SignatureDigest; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueClientIdentification implements ClientIdentification { | |||||
| private byte[] identityInfo; | |||||
| private PubKey pubKey; | |||||
| private SignatureDigest signature; | |||||
| public MsgQueueClientIdentification() { | |||||
| } | |||||
| public MsgQueueClientIdentification(ClientIdentification clientIdentification) { | |||||
| identityInfo = clientIdentification.getIdentityInfo(); | |||||
| pubKey = clientIdentification.getPubKey(); | |||||
| signature = clientIdentification.getSignature(); | |||||
| } | |||||
| public MsgQueueClientIdentification setIdentityInfo(byte[] identityInfo) { | |||||
| this.identityInfo = identityInfo; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueClientIdentification setPubKey(PubKey pubKey) { | |||||
| this.pubKey = pubKey; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueClientIdentification setSignature(SignatureDigest signature) { | |||||
| this.signature = signature; | |||||
| return this; | |||||
| } | |||||
| @Override | |||||
| public byte[] getIdentityInfo() { | |||||
| return this.identityInfo; | |||||
| } | |||||
| @Override | |||||
| public PubKey getPubKey() { | |||||
| return this.pubKey; | |||||
| } | |||||
| @Override | |||||
| public SignatureDigest getSignature() { | |||||
| return this.signature; | |||||
| } | |||||
| @Override | |||||
| public String getProviderName() { | |||||
| return MsgQueueConsensusProvider.NAME; | |||||
| } | |||||
| } | |||||
| @@ -1,100 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.client.MsgQueueConsensusClient | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 下午3:23 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.client; | |||||
| import com.jd.blockchain.consensus.MessageService; | |||||
| import com.jd.blockchain.consensus.client.ClientSettings; | |||||
| import com.jd.blockchain.consensus.client.ConsensusClient; | |||||
| import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; | |||||
| import com.jd.blockchain.consensus.mq.factory.MsgQueueFactory; | |||||
| import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueClientSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueConsensusClient implements ConsensusClient { | |||||
| private boolean isConnected; | |||||
| private DefaultMessageTransmitter transmitter; | |||||
| private MsgQueueNetworkSettings msgQueueNetworkSettings; | |||||
| private MsgQueueClientSettings clientSettings; | |||||
| public MsgQueueConsensusClient setClientSettings(MsgQueueClientSettings clientSettings) { | |||||
| this.clientSettings = clientSettings; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueConsensusClient setMsgQueueNetworkSettings(MsgQueueNetworkSettings msgQueueNetworkSettings) { | |||||
| this.msgQueueNetworkSettings = msgQueueNetworkSettings; | |||||
| return this; | |||||
| } | |||||
| public void init() { | |||||
| String server = msgQueueNetworkSettings.getServer(); | |||||
| String txTopic = msgQueueNetworkSettings.getTxTopic(); | |||||
| String blTopic = msgQueueNetworkSettings.getBlTopic(); | |||||
| String msgTopic = msgQueueNetworkSettings.getMsgTopic(); | |||||
| MsgQueueProducer txProducer = MsgQueueFactory.newProducer(server, txTopic); | |||||
| MsgQueueProducer msgProducer = MsgQueueFactory.newProducer(server, msgTopic); | |||||
| MsgQueueConsumer blConsumer = MsgQueueFactory.newConsumer(server, blTopic); | |||||
| MsgQueueConsumer msgConsumer = MsgQueueFactory.newConsumer(server, msgTopic); | |||||
| transmitter = new DefaultMessageTransmitter() | |||||
| .setTxProducer(txProducer) | |||||
| .setMsgProducer(msgProducer) | |||||
| .setBlConsumer(blConsumer) | |||||
| .setMsgConsumer(msgConsumer) | |||||
| ; | |||||
| } | |||||
| @Override | |||||
| public MessageService getMessageService() { | |||||
| return transmitter; | |||||
| } | |||||
| @Override | |||||
| public ClientSettings getSettings() { | |||||
| return clientSettings; | |||||
| } | |||||
| @Override | |||||
| public boolean isConnected() { | |||||
| return isConnected; | |||||
| } | |||||
| @Override | |||||
| public synchronized void connect() { | |||||
| if (!isConnected) { | |||||
| try { | |||||
| this.transmitter.connect(); | |||||
| isConnected = true; | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public synchronized void close() { | |||||
| if (isConnected) { | |||||
| transmitter.close(); | |||||
| isConnected = false; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,47 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueBlockConfig | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/13 下午2:57 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.config; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueBlockSettings; | |||||
| import java.lang.reflect.Method; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/13 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueBlockConfig implements MsgQueueBlockSettings { | |||||
| private int txSizePerBlock; | |||||
| private long maxDelayMilliSecondsPerBlock; | |||||
| @Override | |||||
| public int getTxSizePerBlock() { | |||||
| return txSizePerBlock; | |||||
| } | |||||
| public MsgQueueBlockConfig setTxSizePerBlock(int txSizePerBlock) { | |||||
| this.txSizePerBlock = txSizePerBlock; | |||||
| return this; | |||||
| } | |||||
| @Override | |||||
| public long getMaxDelayMilliSecondsPerBlock() { | |||||
| return maxDelayMilliSecondsPerBlock; | |||||
| } | |||||
| public MsgQueueBlockConfig setMaxDelayMilliSecondsPerBlock(long maxDelayMilliSecondsPerBlock) { | |||||
| this.maxDelayMilliSecondsPerBlock = maxDelayMilliSecondsPerBlock; | |||||
| return this; | |||||
| } | |||||
| } | |||||
| @@ -1,65 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueClientConfig | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 下午2:23 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.config; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueClientSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueClientConfig implements MsgQueueClientSettings { | |||||
| private int id; | |||||
| private PubKey pubKey; | |||||
| private MsgQueueConsensusSettings consensusSettings; | |||||
| public MsgQueueClientConfig setId(int id) { | |||||
| this.id = id; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueClientConfig setPubKey(PubKey pubKey) { | |||||
| this.pubKey = pubKey; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueClientConfig setConsensusSettings(MsgQueueConsensusSettings consensusSettings) { | |||||
| this.consensusSettings = consensusSettings; | |||||
| return this; | |||||
| } | |||||
| @Override | |||||
| public int getClientId() { | |||||
| return this.id; | |||||
| } | |||||
| @Override | |||||
| public PubKey getClientPubKey() { | |||||
| return this.pubKey; | |||||
| } | |||||
| @Override | |||||
| public MsgQueueConsensusSettings getConsensusSettings() { | |||||
| return this.consensusSettings; | |||||
| } | |||||
| @Override | |||||
| public MsgQueueNetworkSettings getMsgQueueNetworkSettings() { | |||||
| return this.consensusSettings.getNetworkSettings(); | |||||
| } | |||||
| } | |||||
| @@ -1,69 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.mq.config.MsgQueueClientIncomingConfig | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 上午11:50 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.config; | |||||
| import com.jd.blockchain.consensus.ClientIncomingSettings; | |||||
| import com.jd.blockchain.consensus.ConsensusSettings; | |||||
| import com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueClientIncomingSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| import java.lang.reflect.Method; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueClientIncomingConfig implements MsgQueueClientIncomingSettings { | |||||
| private int clientId; | |||||
| private PubKey pubKey; | |||||
| private MsgQueueConsensusSettings consensusSettings; | |||||
| public MsgQueueClientIncomingConfig setConsensusSettings(MsgQueueConsensusSettings consensusSettings) { | |||||
| this.consensusSettings = consensusSettings; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueClientIncomingConfig setClientId(int clientId) { | |||||
| this.clientId = clientId; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueClientIncomingConfig setPubKey(PubKey pubKey) { | |||||
| this.pubKey = pubKey; | |||||
| return this; | |||||
| } | |||||
| @Override | |||||
| public int getClientId() { | |||||
| return this.clientId; | |||||
| } | |||||
| @Override | |||||
| public String getProviderName() { | |||||
| return MsgQueueConsensusProvider.NAME; | |||||
| } | |||||
| @Override | |||||
| public MsgQueueConsensusSettings getConsensusSettings() { | |||||
| return this.consensusSettings; | |||||
| } | |||||
| @Override | |||||
| public PubKey getPubKey() { | |||||
| return pubKey; | |||||
| } | |||||
| } | |||||
| @@ -1,65 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.mq.config.MsgQueueConsensusConfig | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 上午11:26 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.config; | |||||
| import com.jd.blockchain.consensus.ConsensusSettings; | |||||
| import com.jd.blockchain.consensus.NodeSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.*; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| import java.lang.reflect.Method; | |||||
| import java.lang.reflect.Proxy; | |||||
| import java.util.ArrayList; | |||||
| import java.util.List; | |||||
| /** | |||||
| * 设置消息队列的信息 | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueConsensusConfig implements MsgQueueConsensusSettings { | |||||
| private List<NodeSettings> nodeSettingsList = new ArrayList<>(); | |||||
| private MsgQueueNetworkSettings networkSettings; | |||||
| private MsgQueueBlockSettings blockSettings; | |||||
| public MsgQueueConsensusConfig addNodeSettings(MsgQueueNodeSettings nodeSettings) { | |||||
| nodeSettingsList.add(nodeSettings); | |||||
| return this; | |||||
| } | |||||
| public MsgQueueConsensusConfig setNetworkSettings(MsgQueueNetworkSettings networkSettings) { | |||||
| this.networkSettings = networkSettings; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueConsensusConfig setBlockSettings(MsgQueueBlockSettings blockSettings) { | |||||
| this.blockSettings = blockSettings; | |||||
| return this; | |||||
| } | |||||
| @Override | |||||
| public NodeSettings[] getNodes() { | |||||
| return nodeSettingsList.toArray(new NodeSettings[nodeSettingsList.size()]); | |||||
| } | |||||
| @Override | |||||
| public MsgQueueNetworkSettings getNetworkSettings() { | |||||
| return networkSettings; | |||||
| } | |||||
| @Override | |||||
| public MsgQueueBlockSettings getBlockSettings() { | |||||
| return blockSettings; | |||||
| } | |||||
| } | |||||
| @@ -1,71 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueNetworkConfig | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 下午4:55 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.config; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings; | |||||
| import java.lang.reflect.Method; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueNetworkConfig implements MsgQueueNetworkSettings { | |||||
| private String server; | |||||
| private String txTopic; | |||||
| private String blTopic; | |||||
| private String msgTopic; | |||||
| public MsgQueueNetworkConfig setServer(String server) { | |||||
| this.server = server; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueNetworkConfig setTxTopic(String txTopic) { | |||||
| this.txTopic = txTopic; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueNetworkConfig setBlTopic(String blTopic) { | |||||
| this.blTopic = blTopic; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueNetworkConfig setMsgTopic(String msgTopic) { | |||||
| this.msgTopic = msgTopic; | |||||
| return this; | |||||
| } | |||||
| @Override | |||||
| public String getServer() { | |||||
| return server; | |||||
| } | |||||
| @Override | |||||
| public String getTxTopic() { | |||||
| return txTopic; | |||||
| } | |||||
| @Override | |||||
| public String getBlTopic() { | |||||
| return blTopic; | |||||
| } | |||||
| @Override | |||||
| public String getMsgTopic() { | |||||
| return msgTopic; | |||||
| } | |||||
| } | |||||
| @@ -1,46 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.mq.config.MsgQueueNodeConfig | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 上午11:33 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.config; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| /** | |||||
| * peer节点IP | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueNodeConfig implements MsgQueueNodeSettings { | |||||
| private String address; | |||||
| private PubKey pubKey; | |||||
| public MsgQueueNodeConfig setAddress(String address) { | |||||
| this.address = address; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueNodeConfig setPubKey(PubKey pubKey) { | |||||
| this.pubKey = pubKey; | |||||
| return this; | |||||
| } | |||||
| @Override | |||||
| public String getAddress() { | |||||
| return this.address; | |||||
| } | |||||
| @Override | |||||
| public PubKey getPubKey() { | |||||
| return this.pubKey; | |||||
| } | |||||
| } | |||||
| @@ -1,72 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.mq.config.MsgQueueServerConfig | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 上午11:32 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.config; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueBlockSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueServerSettings; | |||||
| /** | |||||
| * peer节点配置 | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueServerConfig implements MsgQueueServerSettings { | |||||
| private MsgQueueBlockSettings blockSettings; | |||||
| private MsgQueueConsensusSettings consensusSettings; | |||||
| private MsgQueueNodeSettings nodeSettings; | |||||
| private String realmName; | |||||
| public MsgQueueServerConfig setRealmName(String realmName) { | |||||
| this.realmName = realmName; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueServerConfig setBlockSettings(MsgQueueBlockSettings blockSettings) { | |||||
| this.blockSettings = blockSettings; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueServerConfig setConsensusSettings(MsgQueueConsensusSettings consensusSettings) { | |||||
| this.consensusSettings = consensusSettings; | |||||
| return setBlockSettings(consensusSettings.getBlockSettings()); | |||||
| } | |||||
| public MsgQueueServerConfig setNodeSettings(MsgQueueNodeSettings nodeSettings) { | |||||
| this.nodeSettings = nodeSettings; | |||||
| return this; | |||||
| } | |||||
| @Override | |||||
| public String getRealmName() { | |||||
| return this.realmName; | |||||
| } | |||||
| @Override | |||||
| public MsgQueueNodeSettings getReplicaSettings() { | |||||
| return nodeSettings; | |||||
| } | |||||
| @Override | |||||
| public MsgQueueBlockSettings getBlockSettings() { | |||||
| return blockSettings; | |||||
| } | |||||
| @Override | |||||
| public MsgQueueConsensusSettings getConsensusSettings() { | |||||
| return consensusSettings; | |||||
| } | |||||
| } | |||||
| @@ -1,105 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.mq.config.MsgQueueSettingsFactory | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 上午11:49 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.config; | |||||
| import com.jd.blockchain.binaryproto.BinaryProtocol; | |||||
| import com.jd.blockchain.binaryproto.DataContractRegistry; | |||||
| import com.jd.blockchain.consensus.ClientIncomingSettings; | |||||
| import com.jd.blockchain.consensus.ConsensusSettings; | |||||
| import com.jd.blockchain.consensus.NodeSettings; | |||||
| import com.jd.blockchain.consensus.SettingsFactory; | |||||
| import com.jd.blockchain.consensus.mq.MsgQueueConsensusSettingsBuilder; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueBlockSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueClientIncomingSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings; | |||||
| import com.jd.blockchain.utils.io.BytesEncoder; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueSettingsFactory implements SettingsFactory { | |||||
| static { | |||||
| DataContractRegistry.register(NodeSettings.class); | |||||
| DataContractRegistry.register(MsgQueueNodeSettings.class); | |||||
| DataContractRegistry.register(ConsensusSettings.class); | |||||
| DataContractRegistry.register(MsgQueueConsensusSettings.class); | |||||
| DataContractRegistry.register(MsgQueueNetworkSettings.class); | |||||
| DataContractRegistry.register(MsgQueueBlockSettings.class); | |||||
| DataContractRegistry.register(MsgQueueClientIncomingSettings.class); | |||||
| DataContractRegistry.register(ClientIncomingSettings.class); | |||||
| } | |||||
| private static final MsgQueueConsensusSettingsEncoder MQCS_ENCODER = new MsgQueueConsensusSettingsEncoder(); | |||||
| private static final MsgQueueClientIncomingSettingsEncoder MQCIS_ENCODER = new MsgQueueClientIncomingSettingsEncoder(); | |||||
| private static final MsgQueueConsensusSettingsBuilder BUILDER = new MsgQueueConsensusSettingsBuilder(); | |||||
| @Override | |||||
| public MsgQueueConsensusSettingsBuilder getConsensusSettingsBuilder() { | |||||
| return BUILDER; | |||||
| } | |||||
| @Override | |||||
| public BytesEncoder<ConsensusSettings> getConsensusSettingsEncoder() { | |||||
| return MQCS_ENCODER; | |||||
| } | |||||
| @Override | |||||
| public BytesEncoder<ClientIncomingSettings> getIncomingSettingsEncoder() { | |||||
| return MQCIS_ENCODER; | |||||
| } | |||||
| private static class MsgQueueConsensusSettingsEncoder implements BytesEncoder<ConsensusSettings>{ | |||||
| @Override | |||||
| public byte[] encode(ConsensusSettings data) { | |||||
| if (data instanceof MsgQueueConsensusSettings) { | |||||
| return BinaryProtocol.encode(data, MsgQueueConsensusSettings.class); | |||||
| } | |||||
| throw new IllegalArgumentException("Settings data isn't supported! Accept MsgQueueConsensusSettings only!"); | |||||
| } | |||||
| @Override | |||||
| public MsgQueueConsensusSettings decode(byte[] bytes) { | |||||
| return BinaryProtocol.decodeAs(bytes, MsgQueueConsensusSettings.class); | |||||
| } | |||||
| } | |||||
| private static class MsgQueueClientIncomingSettingsEncoder implements BytesEncoder<ClientIncomingSettings>{ | |||||
| @Override | |||||
| public byte[] encode(ClientIncomingSettings data) { | |||||
| if (data instanceof MsgQueueClientIncomingSettings) { | |||||
| return BinaryProtocol.encode(data, MsgQueueClientIncomingSettings.class); | |||||
| } | |||||
| throw new IllegalArgumentException("Settings data isn't supported! Accept MsgQueueClientIncomingSettings only!"); | |||||
| } | |||||
| @Override | |||||
| public MsgQueueClientIncomingSettings decode(byte[] bytes) { | |||||
| return BinaryProtocol.decodeAs(bytes, MsgQueueClientIncomingSettings.class); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,44 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.consumer.AbstractConsumer | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/29 下午12:31 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.consumer; | |||||
| import com.jd.blockchain.consensus.event.EventEntity; | |||||
| import com.jd.blockchain.consensus.event.EventProducer; | |||||
| import com.jd.blockchain.consensus.mq.exchange.BytesEventFactory; | |||||
| import com.jd.blockchain.consensus.mq.exchange.BytesEventProducer; | |||||
| import com.lmax.disruptor.BlockingWaitStrategy; | |||||
| import com.lmax.disruptor.EventHandler; | |||||
| import com.lmax.disruptor.RingBuffer; | |||||
| import com.lmax.disruptor.dsl.Disruptor; | |||||
| import com.lmax.disruptor.dsl.ProducerType; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/29 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public abstract class AbstractConsumer implements MsgQueueConsumer { | |||||
| protected EventProducer eventProducer; | |||||
| protected void initEventHandler(EventHandler eventHandler) { | |||||
| Disruptor<EventEntity<byte[]>> disruptor = | |||||
| new Disruptor<>(new BytesEventFactory(), | |||||
| BytesEventFactory.BUFFER_SIZE, r -> { | |||||
| return new Thread(r); | |||||
| }, ProducerType.SINGLE, new BlockingWaitStrategy()); | |||||
| disruptor.handleEventsWith(eventHandler); | |||||
| disruptor.start(); | |||||
| RingBuffer<EventEntity<byte[]>> ringBuffer = disruptor.getRingBuffer(); | |||||
| this.eventProducer = new BytesEventProducer(ringBuffer); | |||||
| } | |||||
| } | |||||
| @@ -1,28 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: MsgQueueConsumer | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 下午10:38 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.consumer; | |||||
| import com.lmax.disruptor.EventHandler; | |||||
| import java.io.Closeable; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public interface MsgQueueConsumer extends Closeable { | |||||
| void connect(EventHandler eventHandler) throws Exception; | |||||
| void start() throws Exception; | |||||
| } | |||||
| @@ -1,76 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.sdk.nats.RabbitConsumer | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 下午10:40 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.consumer; | |||||
| import com.jd.blockchain.utils.ConsoleUtils; | |||||
| import com.lmax.disruptor.EventHandler; | |||||
| import io.nats.client.*; | |||||
| import java.io.IOException; | |||||
| import java.time.Duration; | |||||
| import java.util.concurrent.ExecutorService; | |||||
| import java.util.concurrent.Executors; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class NatsConsumer extends AbstractConsumer implements MsgQueueConsumer { | |||||
| private final ExecutorService msgListener = Executors.newSingleThreadExecutor(); | |||||
| private Connection nc; | |||||
| private Subscription sub; | |||||
| private String server; | |||||
| private String topic; | |||||
| public NatsConsumer(String server, String topic) { | |||||
| this.server = server; | |||||
| this.topic = topic; | |||||
| } | |||||
| @Override | |||||
| public void connect(EventHandler eventHandler) throws Exception { | |||||
| initEventHandler(eventHandler); | |||||
| Options options = new Options.Builder().server(server).noReconnect().build(); | |||||
| this.nc = Nats.connect(options); | |||||
| this.sub = nc.subscribe(topic); | |||||
| this.nc.flush(Duration.ZERO); | |||||
| ConsoleUtils.info("[*] NatsConsumer[%s, %s] connect success !!!", this.server, this.topic); | |||||
| } | |||||
| @Override | |||||
| public void start() { | |||||
| msgListener.execute(() -> { | |||||
| for (;;) { | |||||
| try { | |||||
| Message msg = this.sub.nextMessage(Duration.ZERO); | |||||
| eventProducer.publish(msg.getData()); | |||||
| } catch (Exception e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| } | |||||
| }); | |||||
| } | |||||
| @Override | |||||
| public void close() throws IOException { | |||||
| try { | |||||
| nc.close(); | |||||
| } catch (Exception e) { | |||||
| throw new IOException(e); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,92 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.sdk.nats.RabbitConsumer | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 下午10:40 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.consumer; | |||||
| import com.jd.blockchain.consensus.mq.factory.RabbitFactory; | |||||
| import com.jd.blockchain.utils.ConsoleUtils; | |||||
| import com.lmax.disruptor.EventHandler; | |||||
| import com.rabbitmq.client.*; | |||||
| import java.io.IOException; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class RabbitConsumer extends AbstractConsumer implements MsgQueueConsumer { | |||||
| private Connection connection; | |||||
| private Channel channel; | |||||
| private String exchangeName; | |||||
| private String server; | |||||
| private String queueName; | |||||
| public RabbitConsumer(String server, String topic) { | |||||
| this.server = server; | |||||
| this.exchangeName = topic; | |||||
| } | |||||
| private void rabbitConsumerHandle() throws Exception { | |||||
| rabbitConsumerHandleByQueue(); | |||||
| } | |||||
| private void rabbitConsumerHandleByQueue() throws IOException { | |||||
| DefaultConsumer consumer = new DefaultConsumer(channel) { | |||||
| @Override | |||||
| public void handleDelivery(String consumerTag, Envelope envelope, | |||||
| AMQP.BasicProperties properties, byte[] body) { | |||||
| // 此处将收到的消息加入队列即可 | |||||
| try { | |||||
| eventProducer.publish(body); | |||||
| channel.basicAck(envelope.getDeliveryTag(), false); | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| }; | |||||
| this.channel.basicConsume(this.queueName, false, consumer); | |||||
| } | |||||
| @Override | |||||
| public void connect(EventHandler eventHandler) throws Exception { | |||||
| initEventHandler(eventHandler); | |||||
| ConnectionFactory factory = RabbitFactory.initConnectionFactory(server); | |||||
| connection = factory.newConnection(); | |||||
| channel = connection.createChannel(); | |||||
| channel.exchangeDeclare(this.exchangeName, "fanout"); | |||||
| queueName = channel.queueDeclare().getQueue(); | |||||
| channel.queueBind(queueName, this.exchangeName, ""); | |||||
| channel.basicQos(8); | |||||
| ConsoleUtils.info("[*] RabbitConsumer[%s, %s] connect success !!!", this.server, this.exchangeName); | |||||
| } | |||||
| @Override | |||||
| public void start() throws Exception { | |||||
| rabbitConsumerHandle(); | |||||
| } | |||||
| @Override | |||||
| public void close() throws IOException { | |||||
| try { | |||||
| this.channel.close(); | |||||
| this.connection.close(); | |||||
| } catch (Exception e) { | |||||
| throw new IOException(e); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,64 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: BlockEvent | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/20 上午11:32 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.event; | |||||
| import com.jd.blockchain.consensus.mq.util.MessageConvertUtil; | |||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/20 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class BlockEvent { | |||||
| private Map<String, String> txMap = new HashMap<>(); | |||||
| public Map<String, String> getTxMap() { | |||||
| return txMap; | |||||
| } | |||||
| public void setTxMap(Map<String, String> txMap) { | |||||
| this.txMap = txMap; | |||||
| } | |||||
| public void put(String txKey, String txResp) { | |||||
| txMap.put(txKey, txResp); | |||||
| } | |||||
| public void put(String txKey, byte[] txResp) { | |||||
| put(txKey, MessageConvertUtil.base64Encode(txResp)); | |||||
| } | |||||
| public String getTxResp(String txKey) { | |||||
| return txMap.get(txKey); | |||||
| } | |||||
| public byte[] getTxRespBytes(String txKey) { | |||||
| String txResp = getTxResp(txKey); | |||||
| if (txResp != null && txResp.length() > 0) { | |||||
| // 字符串转字节数组 | |||||
| return MessageConvertUtil.base64Decode(txResp); | |||||
| } | |||||
| return null; | |||||
| } | |||||
| public boolean containTxResp(String txKey) { | |||||
| return txMap.containsKey(txKey); | |||||
| } | |||||
| public boolean isEmpty() { | |||||
| if (txMap == null) return true; | |||||
| return txMap.isEmpty(); | |||||
| } | |||||
| } | |||||
| @@ -1,44 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.peer.consensus.MessageEvent | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/23 上午11:45 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.event; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/23 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MessageEvent { | |||||
| String messageKey; | |||||
| byte[] message; | |||||
| public MessageEvent(String messageKey, byte[] message) { | |||||
| this.messageKey = messageKey; | |||||
| this.message = message; | |||||
| } | |||||
| public String getMessageKey() { | |||||
| return messageKey; | |||||
| } | |||||
| public void setMessageKey(String messageKey) { | |||||
| this.messageKey = messageKey; | |||||
| } | |||||
| public byte[] getMessage() { | |||||
| return message; | |||||
| } | |||||
| public void setMessage(byte[] message) { | |||||
| this.message = message; | |||||
| } | |||||
| } | |||||
| @@ -1,58 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: BlockEvent | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/20 上午11:32 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.event; | |||||
| import com.jd.blockchain.consensus.mq.util.MessageConvertUtil; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/20 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class TxBlockedEvent { | |||||
| private String txKey; | |||||
| private String transaction; | |||||
| public TxBlockedEvent() { | |||||
| } | |||||
| public TxBlockedEvent(String txKey, String transaction) { | |||||
| this.txKey = txKey; | |||||
| this.transaction = transaction; | |||||
| } | |||||
| public void setTxKey(String txKey) { | |||||
| this.txKey = txKey; | |||||
| } | |||||
| public void setTransaction(String transaction) { | |||||
| this.transaction = transaction; | |||||
| } | |||||
| public String getTxKey() { | |||||
| return txKey; | |||||
| } | |||||
| public String getTransaction() { | |||||
| return transaction; | |||||
| } | |||||
| public byte[] txResponseBytes() { | |||||
| if (transaction != null && transaction.length() > 0) { | |||||
| // 字符串转字节数组 | |||||
| return MessageConvertUtil.base64Decode(transaction); | |||||
| } | |||||
| return null; | |||||
| } | |||||
| } | |||||
| @@ -1,30 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.rabbitmq.nats.consensus.disruptor.ExchangeEventFactory | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 上午10:48 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.exchange; | |||||
| import com.jd.blockchain.consensus.event.EventEntity; | |||||
| import com.lmax.disruptor.EventFactory; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class BytesEventFactory implements EventFactory<EventEntity<byte[]>> { | |||||
| public static final int BUFFER_SIZE = 256 * 1024; | |||||
| // public static final int BUFFER_SIZE = 8 * 1024; | |||||
| @Override | |||||
| public EventEntity<byte[]> newInstance() { | |||||
| return new EventEntity<>(); | |||||
| } | |||||
| } | |||||
| @@ -1,40 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.rabbitmq.nats.consensus.disruptor.ExchangeEventProducer | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 上午10:50 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.exchange; | |||||
| import com.jd.blockchain.consensus.event.EventEntity; | |||||
| import com.jd.blockchain.consensus.event.EventProducer; | |||||
| import com.lmax.disruptor.RingBuffer; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class BytesEventProducer implements EventProducer<byte[]> { | |||||
| private final RingBuffer<EventEntity<byte[]>> ringBuffer; | |||||
| public BytesEventProducer(RingBuffer<EventEntity<byte[]>> ringBuffer) { | |||||
| this.ringBuffer = ringBuffer; | |||||
| } | |||||
| @Override | |||||
| public void publish(byte[] entity) { | |||||
| long sequence = ringBuffer.next(); | |||||
| try { | |||||
| EventEntity<byte[]> event = ringBuffer.get(sequence); | |||||
| event.setEntity(entity); | |||||
| } finally { | |||||
| this.ringBuffer.publish(sequence); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,31 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.sdk.ExchangeEntityFactory | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 下午4:08 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.exchange; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class ExchangeEntityFactory { | |||||
| public static ExchangeEventInnerEntity newBlockInstance() { | |||||
| return new ExchangeEventInnerEntity(ExchangeType.BLOCK); | |||||
| } | |||||
| public static ExchangeEventInnerEntity newEmptyInstance() { | |||||
| return new ExchangeEventInnerEntity(ExchangeType.EMPTY); | |||||
| } | |||||
| public static ExchangeEventInnerEntity newTransactionInstance(byte[] content) { | |||||
| return new ExchangeEventInnerEntity(ExchangeType.TRANSACTION, content); | |||||
| } | |||||
| } | |||||
| @@ -1,30 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.rabbitmq.nats.consensus.disruptor.ExchangeEventFactory | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 上午10:48 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.exchange; | |||||
| import com.jd.blockchain.consensus.event.EventEntity; | |||||
| import com.lmax.disruptor.EventFactory; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class ExchangeEventFactory implements EventFactory<EventEntity<ExchangeEventInnerEntity>> { | |||||
| public static final int BUFFER_SIZE = 256 * 1024; | |||||
| // public static final int BUFFER_SIZE = 8 * 1024; | |||||
| @Override | |||||
| public EventEntity<ExchangeEventInnerEntity> newInstance() { | |||||
| return new EventEntity<>(); | |||||
| } | |||||
| } | |||||
| @@ -1,53 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.sdk.ExchangeEventInnerEntity | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 下午4:04 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.exchange; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class ExchangeEventInnerEntity { | |||||
| private ExchangeType type; | |||||
| private byte[] content; | |||||
| public ExchangeEventInnerEntity() { | |||||
| } | |||||
| public ExchangeEventInnerEntity(ExchangeType type) { | |||||
| this.type = type; | |||||
| } | |||||
| public ExchangeEventInnerEntity(ExchangeType type, byte[] content) { | |||||
| this.type = type; | |||||
| this.content = content; | |||||
| } | |||||
| public ExchangeType getType() { | |||||
| return type; | |||||
| } | |||||
| public void setType(ExchangeType type) { | |||||
| this.type = type; | |||||
| } | |||||
| public byte[] getContent() { | |||||
| return content; | |||||
| } | |||||
| public void setContent(byte[] content) { | |||||
| this.content = content; | |||||
| } | |||||
| } | |||||
| @@ -1,40 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.rabbitmq.nats.consensus.disruptor.ExchangeEventProducer | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 上午10:50 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.exchange; | |||||
| import com.jd.blockchain.consensus.event.EventEntity; | |||||
| import com.jd.blockchain.consensus.event.EventProducer; | |||||
| import com.lmax.disruptor.RingBuffer; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class ExchangeEventProducer implements EventProducer<ExchangeEventInnerEntity> { | |||||
| private final RingBuffer<EventEntity<ExchangeEventInnerEntity>> ringBuffer; | |||||
| public ExchangeEventProducer(RingBuffer<EventEntity<ExchangeEventInnerEntity>> ringBuffer) { | |||||
| this.ringBuffer = ringBuffer; | |||||
| } | |||||
| @Override | |||||
| public void publish(ExchangeEventInnerEntity entity) { | |||||
| long sequence = ringBuffer.next(); | |||||
| try { | |||||
| EventEntity<ExchangeEventInnerEntity> event = ringBuffer.get(sequence); | |||||
| event.setEntity(entity); | |||||
| } finally { | |||||
| this.ringBuffer.publish(sequence); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,23 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.sdk.nats.ExchangeType | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 下午3:34 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.exchange; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public enum ExchangeType { | |||||
| BLOCK, | |||||
| EMPTY, | |||||
| TRANSACTION, | |||||
| ; | |||||
| } | |||||
| @@ -1,23 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.factory.MsgQueueConfig | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 下午5:16 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.factory; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public final class MsgQueueConfig { | |||||
| public static final String NATS_PREFIX = "nats"; | |||||
| public static final String RABBIT_PREFIX = "rabbit"; | |||||
| } | |||||
| @@ -1,52 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: MsgQueueFactory | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 下午10:13 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.factory; | |||||
| import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; | |||||
| import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; | |||||
| import static com.jd.blockchain.consensus.mq.factory.MsgQueueConfig.NATS_PREFIX; | |||||
| import static com.jd.blockchain.consensus.mq.factory.MsgQueueConfig.RABBIT_PREFIX; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueFactory { | |||||
| public static MsgQueueProducer newProducer(String server, String topic) { | |||||
| try { | |||||
| if (server.startsWith(NATS_PREFIX)) { | |||||
| return NatsFactory.newProducer(server, topic); | |||||
| } else if (server.startsWith(RABBIT_PREFIX)) { | |||||
| return RabbitFactory.newProducer(server, topic); | |||||
| } | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| return null; | |||||
| } | |||||
| public static MsgQueueConsumer newConsumer(String server, String topic) { | |||||
| try { | |||||
| if (server.startsWith(NATS_PREFIX)) { | |||||
| return NatsFactory.newConsumer(server, topic); | |||||
| } else if (server.startsWith(RABBIT_PREFIX)) { | |||||
| return RabbitFactory.newConsumer(server, topic); | |||||
| } | |||||
| return null; | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,32 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: NatsFactory | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 下午10:15 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.factory; | |||||
| import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; | |||||
| import com.jd.blockchain.consensus.mq.consumer.NatsConsumer; | |||||
| import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; | |||||
| import com.jd.blockchain.consensus.mq.producer.NatsProducer; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class NatsFactory { | |||||
| public static MsgQueueProducer newProducer(String server, String topic) throws Exception { | |||||
| return new NatsProducer(server, topic); | |||||
| } | |||||
| public static MsgQueueConsumer newConsumer(String server, String topic) throws Exception { | |||||
| return new NatsConsumer(server, topic); | |||||
| } | |||||
| } | |||||
| @@ -1,52 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.sdk.nats.RabbitFactory | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 下午10:15 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.factory; | |||||
| import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; | |||||
| import com.jd.blockchain.consensus.mq.consumer.RabbitConsumer; | |||||
| import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; | |||||
| import com.jd.blockchain.consensus.mq.producer.RabbitProducer; | |||||
| import com.rabbitmq.client.ConnectionFactory; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class RabbitFactory { | |||||
| public static MsgQueueProducer newProducer(String server, String topic) throws Exception { | |||||
| return new RabbitProducer(server, topic); | |||||
| } | |||||
| public static MsgQueueConsumer newConsumer(String server, String topic) throws Exception { | |||||
| return new RabbitConsumer(server, topic); | |||||
| } | |||||
| public static ConnectionFactory initConnectionFactory(String server) { | |||||
| ConnectionFactory factory = new ConnectionFactory(); | |||||
| // 解析server,生成host+port,默认格式:rabbit://localhost:5672 | |||||
| try { | |||||
| String[] hostAndPort = server.split("//")[1].split(":"); | |||||
| if (hostAndPort == null || hostAndPort.length == 0) { | |||||
| factory.setHost("localhost"); | |||||
| } else if (hostAndPort.length == 1) { | |||||
| factory.setHost(hostAndPort[0]); | |||||
| } else { | |||||
| factory.setHost(hostAndPort[0]); | |||||
| factory.setPort(Integer.parseInt(hostAndPort[1])); | |||||
| } | |||||
| } catch (Exception e) { | |||||
| factory.setHost("localhost"); | |||||
| } | |||||
| return factory; | |||||
| } | |||||
| } | |||||
| @@ -1,36 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: MsgQueueProducer | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 下午10:37 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.producer; | |||||
| import java.io.Closeable; | |||||
| import java.util.List; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public interface MsgQueueProducer extends Closeable { | |||||
| void connect() throws Exception; | |||||
| void publish(byte[] message) throws Exception; | |||||
| void publishString(String message) throws Exception; | |||||
| void publishStringList(List<String> messages) throws Exception; | |||||
| void publishStringArray(String[] messages) throws Exception; | |||||
| void publishBytesArray(byte[][] message) throws Exception; | |||||
| void publishBytesList(List<byte[]> messages) throws Exception; | |||||
| } | |||||
| @@ -1,100 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: NatsProducer | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 下午10:39 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.producer; | |||||
| import io.nats.client.Connection; | |||||
| import io.nats.client.Nats; | |||||
| import io.nats.client.Options; | |||||
| import java.io.IOException; | |||||
| import java.nio.charset.StandardCharsets; | |||||
| import java.util.List; | |||||
| import com.jd.blockchain.utils.ConsoleUtils; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class NatsProducer implements MsgQueueProducer { | |||||
| // 主要操作:发送MQ请求 | |||||
| private Connection nc; | |||||
| private String server; | |||||
| // 主题 | |||||
| private String topic; | |||||
| public NatsProducer() { | |||||
| } | |||||
| public NatsProducer(String server, String topic) { | |||||
| this.topic = topic; | |||||
| this.server = server; | |||||
| } | |||||
| @Override | |||||
| public void connect() throws Exception{ | |||||
| Options o = new Options.Builder().server(server).noReconnect().build(); | |||||
| this.nc = Nats.connect(o); | |||||
| ConsoleUtils.info("[*] NatsProducer[%s, %s] connect success !!!", this.server, this.topic); | |||||
| } | |||||
| @Override | |||||
| public void publish(byte[] message) { | |||||
| nc.publish(topic, message); | |||||
| } | |||||
| @Override | |||||
| public void publishString(String message) { | |||||
| publish(message.getBytes(StandardCharsets.UTF_8)); | |||||
| } | |||||
| @Override | |||||
| public void publishStringList(List<String> messages) { | |||||
| for (String message : messages) { | |||||
| publishString(message); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void publishStringArray(String[] messages) { | |||||
| for (String message : messages) { | |||||
| publishString(message); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void publishBytesArray(byte[][] message) { | |||||
| for (byte[] bytes : message) { | |||||
| publish(bytes); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void publishBytesList(List<byte[]> messages) { | |||||
| for (byte[] message : messages) { | |||||
| publish(message); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void close() throws IOException { | |||||
| try { | |||||
| nc.close(); | |||||
| } catch (Exception e) { | |||||
| throw new IOException(e); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,106 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.sdk.nats.RabbitProducer | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/5 下午10:39 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.producer; | |||||
| import com.jd.blockchain.consensus.mq.factory.RabbitFactory; | |||||
| import com.jd.blockchain.utils.ConsoleUtils; | |||||
| import com.rabbitmq.client.Channel; | |||||
| import com.rabbitmq.client.Connection; | |||||
| import com.rabbitmq.client.ConnectionFactory; | |||||
| import java.io.IOException; | |||||
| import java.nio.charset.StandardCharsets; | |||||
| import java.util.List; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/5 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class RabbitProducer implements MsgQueueProducer { | |||||
| // 主要操作时发送JMQ请求 | |||||
| private Channel channel; | |||||
| private Connection connection; | |||||
| private String exchangeName; | |||||
| private String server; | |||||
| public RabbitProducer() { | |||||
| } | |||||
| public RabbitProducer(String server, String topic) throws Exception { | |||||
| this.exchangeName = topic; | |||||
| this.server = server; | |||||
| } | |||||
| @Override | |||||
| public void connect() throws Exception { | |||||
| ConnectionFactory factory = RabbitFactory.initConnectionFactory(server); | |||||
| connection = factory.newConnection(); | |||||
| channel = connection.createChannel(); | |||||
| channel.exchangeDeclare(this.exchangeName, "fanout"); | |||||
| ConsoleUtils.info("[*] RabbitProducer[%s, %s] connect success !!!", this.server, this.exchangeName); | |||||
| } | |||||
| @Override | |||||
| public void publish(byte[] message) throws Exception { | |||||
| channel.basicPublish(this.exchangeName, "", null, message); | |||||
| } | |||||
| @Override | |||||
| public void publishString(String message) throws Exception { | |||||
| publish(message.getBytes(StandardCharsets.UTF_8)); | |||||
| } | |||||
| @Override | |||||
| public void publishStringList(List<String> messages) throws Exception { | |||||
| for (String message : messages) { | |||||
| publishString(message); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void publishStringArray(String[] messages) throws Exception { | |||||
| for (String message : messages) { | |||||
| publishString(message); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void publishBytesArray(byte[][] message) throws Exception { | |||||
| for (byte[] bytes : message) { | |||||
| publish(bytes); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void publishBytesList(List<byte[]> messages) throws Exception { | |||||
| for (byte[] message : messages) { | |||||
| publish(message); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void close() throws IOException { | |||||
| try { | |||||
| channel.close(); | |||||
| connection.close(); | |||||
| } catch (Exception e) { | |||||
| throw new IOException(e); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,297 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.server.DefaultMsgQueueMessageDispatcher | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/13 上午11:05 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.server; | |||||
| import com.jd.blockchain.consensus.event.EventEntity; | |||||
| import com.jd.blockchain.consensus.event.EventProducer; | |||||
| import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; | |||||
| import com.jd.blockchain.consensus.mq.exchange.ExchangeEntityFactory; | |||||
| import com.jd.blockchain.consensus.mq.exchange.ExchangeEventFactory; | |||||
| import com.jd.blockchain.consensus.mq.exchange.ExchangeEventInnerEntity; | |||||
| import com.jd.blockchain.consensus.mq.exchange.ExchangeEventProducer; | |||||
| import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; | |||||
| import com.lmax.disruptor.BlockingWaitStrategy; | |||||
| import com.lmax.disruptor.EventHandler; | |||||
| import com.lmax.disruptor.RingBuffer; | |||||
| import com.lmax.disruptor.dsl.Disruptor; | |||||
| import com.lmax.disruptor.dsl.ProducerType; | |||||
| import java.io.IOException; | |||||
| import java.util.concurrent.*; | |||||
| import java.util.concurrent.atomic.AtomicLong; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/13 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class DefaultMsgQueueMessageDispatcher implements MsgQueueMessageDispatcher, EventHandler<EventEntity<byte[]>> { | |||||
| private static final byte[] blockCommitBytes = new byte[]{0x00}; | |||||
| private final BlockingQueue<byte[]> dataQueue = new ArrayBlockingQueue<>(1024 * 16); | |||||
| private final ExecutorService dataExecutor = Executors.newSingleThreadExecutor(); | |||||
| private final ScheduledThreadPoolExecutor timeHandleExecutor = new ScheduledThreadPoolExecutor(2); | |||||
| private final AtomicLong blockIndex = new AtomicLong(); | |||||
| private long syncIndex = 0L; | |||||
| private MsgQueueProducer txProducer; | |||||
| private MsgQueueConsumer txConsumer; | |||||
| private EventProducer eventProducer; | |||||
| private EventHandler eventHandler; | |||||
| private final int TX_SIZE_PER_BLOCK; | |||||
| private final long MAX_DELAY_MILLISECONDS_PER_BLOCK; | |||||
| private boolean isRunning; | |||||
| private boolean isConnected; | |||||
| public DefaultMsgQueueMessageDispatcher(int txSizePerBlock, long maxDelayMilliSecondsPerBlock) { | |||||
| this.TX_SIZE_PER_BLOCK = txSizePerBlock; | |||||
| this.MAX_DELAY_MILLISECONDS_PER_BLOCK = maxDelayMilliSecondsPerBlock; | |||||
| } | |||||
| public DefaultMsgQueueMessageDispatcher setTxProducer(MsgQueueProducer txProducer) { | |||||
| this.txProducer = txProducer; | |||||
| return this; | |||||
| } | |||||
| public DefaultMsgQueueMessageDispatcher setTxConsumer(MsgQueueConsumer txConsumer) { | |||||
| this.txConsumer = txConsumer; | |||||
| return this; | |||||
| } | |||||
| public DefaultMsgQueueMessageDispatcher setEventHandler(EventHandler eventHandler) { | |||||
| this.eventHandler = eventHandler; | |||||
| return this; | |||||
| } | |||||
| public void init() { | |||||
| handleDisruptor(eventHandler); | |||||
| } | |||||
| private void handleDisruptor(EventHandler eventHandler) { | |||||
| Disruptor<EventEntity<ExchangeEventInnerEntity>> disruptor = | |||||
| new Disruptor<>(new ExchangeEventFactory(), | |||||
| ExchangeEventFactory.BUFFER_SIZE, r -> { | |||||
| return new Thread(r); | |||||
| }, ProducerType.SINGLE, new BlockingWaitStrategy()); | |||||
| disruptor.handleEventsWith(eventHandler); | |||||
| disruptor.start(); | |||||
| RingBuffer<EventEntity<ExchangeEventInnerEntity>> ringBuffer = disruptor.getRingBuffer(); | |||||
| this.eventProducer = new ExchangeEventProducer(ringBuffer); | |||||
| } | |||||
| public synchronized void connect() throws Exception { | |||||
| if (!isConnected) { | |||||
| txProducer.connect(); | |||||
| txConsumer.connect(this); | |||||
| isConnected = true; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public synchronized void stop() throws Exception { | |||||
| isRunning = false; | |||||
| close(); | |||||
| } | |||||
| @Override | |||||
| public void run() { | |||||
| this.isRunning = true; | |||||
| try { | |||||
| txConsumer.start(); | |||||
| } catch (Exception e) { | |||||
| } | |||||
| // handleData(); | |||||
| // listen(); | |||||
| } | |||||
| // private void listen() { | |||||
| // while (isRunning) { | |||||
| // try { | |||||
| // byte[] data = this.txConsumer.start(); | |||||
| // dataQueue.put(data); | |||||
| // // 收到数据后由队列处理 | |||||
| //// handleData(data); | |||||
| // } catch (Exception e) { | |||||
| // // 日志打印 | |||||
| // ConsoleUtils.info("ERROR dispatcher start data exception {%s}", e.getMessage()); | |||||
| // } | |||||
| // } | |||||
| // } | |||||
| // private void handleData() { | |||||
| // dataExecutor.execute(() -> { | |||||
| // byte[] data; | |||||
| // for (;;) { | |||||
| // try { | |||||
| // data = dataQueue.take(); | |||||
| // if (data.length == 1) { | |||||
| // // 结块标识优先处理 | |||||
| // syncIndex = 0L; | |||||
| // this.blockIndex.getAndIncrement(); | |||||
| // eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); | |||||
| // } else { | |||||
| // if (syncIndex == 0) { // 收到第一个交易 | |||||
| // // 需要判断是否需要进行定时任务 | |||||
| // if (MAX_DELAY_MILLISECONDS_PER_BLOCK > 0) { | |||||
| // this.timeHandleExecutor.schedule( | |||||
| // timeBlockTask(this.blockIndex.get()), | |||||
| // MAX_DELAY_MILLISECONDS_PER_BLOCK, TimeUnit.MILLISECONDS); | |||||
| // } | |||||
| // } | |||||
| // syncIndex++; | |||||
| // eventProducer.publish(ExchangeEntityFactory.newTransactionInstance(data)); | |||||
| // if (syncIndex == TX_SIZE_PER_BLOCK) { | |||||
| // syncIndex = 0L; | |||||
| // this.blockIndex.getAndIncrement(); | |||||
| // eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); | |||||
| // } | |||||
| // } | |||||
| // | |||||
| // } catch (Exception e) { | |||||
| // e.printStackTrace(); | |||||
| // } | |||||
| // } | |||||
| // }); | |||||
| // for (;;) { | |||||
| // try { | |||||
| // final byte[] data = dataQueue.take(); | |||||
| // dataExecutor.execute(() -> { | |||||
| // if (data.length == 1) { | |||||
| // // 结块标识优先处理 | |||||
| // syncIndex = 0L; | |||||
| // this.blockIndex.getAndIncrement(); | |||||
| // eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); | |||||
| // } else { | |||||
| // if (syncIndex == 0) { // 收到第一个交易 | |||||
| // // 需要判断是否需要进行定时任务 | |||||
| // if (MAX_DELAY_MILLISECONDS_PER_BLOCK > 0) { | |||||
| // this.timeHandleExecutor.schedule( | |||||
| // timeBlockTask(this.blockIndex.get()), | |||||
| // MAX_DELAY_MILLISECONDS_PER_BLOCK, TimeUnit.MILLISECONDS); | |||||
| // } | |||||
| // } | |||||
| // syncIndex++; | |||||
| // eventProducer.publish(ExchangeEntityFactory.newTransactionInstance(data)); | |||||
| // if (syncIndex == TX_SIZE_PER_BLOCK) { | |||||
| // syncIndex = 0L; | |||||
| // this.blockIndex.getAndIncrement(); | |||||
| // eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); | |||||
| // } | |||||
| // } | |||||
| // } | |||||
| // ); | |||||
| // } catch (Exception e) { | |||||
| // e.printStackTrace(); | |||||
| // } | |||||
| // } | |||||
| // } | |||||
| // private void handleData(final byte[] data) { | |||||
| // dataExecutor.execute(() -> { | |||||
| // try { | |||||
| // if (data.length == 1) { | |||||
| // // 结块标识优先处理 | |||||
| // syncIndex = 0L; | |||||
| // this.blockIndex.getAndIncrement(); | |||||
| // eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); | |||||
| // } else { | |||||
| // if (syncIndex == 0) { // 收到第一个交易 | |||||
| // // 需要判断是否需要进行定时任务 | |||||
| // if (MAX_DELAY_MILLISECONDS_PER_BLOCK > 0) { | |||||
| // this.timeHandleExecutor.schedule( | |||||
| // timeBlockTask(this.blockIndex.get()), | |||||
| // MAX_DELAY_MILLISECONDS_PER_BLOCK, TimeUnit.MILLISECONDS); | |||||
| // } | |||||
| // } | |||||
| // syncIndex++; | |||||
| // eventProducer.publish(ExchangeEntityFactory.newTransactionInstance(data)); | |||||
| // if (syncIndex == TX_SIZE_PER_BLOCK) { | |||||
| // syncIndex = 0L; | |||||
| // this.blockIndex.getAndIncrement(); | |||||
| // eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); | |||||
| // } | |||||
| // } | |||||
| // } catch (Exception e) { | |||||
| // // 记录日志 | |||||
| // ConsoleUtils.info("ERROR TransactionDispatcher process queue data exception {%s}", e.getMessage()); | |||||
| // } | |||||
| // }); | |||||
| // | |||||
| // } | |||||
| private Runnable timeBlockTask(final long currentBlockIndex) { | |||||
| return () -> { | |||||
| final boolean isEqualBlock = this.blockIndex.compareAndSet( | |||||
| currentBlockIndex, currentBlockIndex + 1); | |||||
| if (isEqualBlock) { | |||||
| try { | |||||
| txProducer.publish(blockCommitBytes); | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| }; | |||||
| } | |||||
| @Override | |||||
| public void close() throws IOException { | |||||
| this.txProducer.close(); | |||||
| this.txConsumer.close(); | |||||
| } | |||||
| @Override | |||||
| public void onEvent(EventEntity<byte[]> event, long sequence, boolean endOfBatch) throws Exception { | |||||
| try { | |||||
| byte[] data = event.getEntity(); | |||||
| // System.out.printf("Thread [%s, $s] on event !!!\r\n", | |||||
| // Thread.currentThread().getId(), Thread.currentThread().getName()); | |||||
| if (data.length == 1) { | |||||
| // 结块标识优先处理 | |||||
| syncIndex = 0L; | |||||
| this.blockIndex.getAndIncrement(); | |||||
| eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); | |||||
| } else { | |||||
| if (syncIndex == 0) { // 收到第一个交易 | |||||
| // 需要判断是否需要进行定时任务 | |||||
| if (MAX_DELAY_MILLISECONDS_PER_BLOCK > 0) { | |||||
| this.timeHandleExecutor.schedule( | |||||
| timeBlockTask(this.blockIndex.get()), | |||||
| MAX_DELAY_MILLISECONDS_PER_BLOCK, TimeUnit.MILLISECONDS); | |||||
| } | |||||
| } | |||||
| syncIndex++; | |||||
| eventProducer.publish(ExchangeEntityFactory.newTransactionInstance(data)); | |||||
| if (syncIndex == TX_SIZE_PER_BLOCK) { | |||||
| syncIndex = 0L; | |||||
| this.blockIndex.getAndIncrement(); | |||||
| eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); | |||||
| } | |||||
| } | |||||
| } catch (Exception e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,126 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.server.DefaultMsgQueueMessageDispatcher | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/13 上午11:05 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.server; | |||||
| import com.jd.blockchain.consensus.event.EventEntity; | |||||
| import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; | |||||
| import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; | |||||
| import com.jd.blockchain.consensus.service.MessageHandle; | |||||
| import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
| import com.lmax.disruptor.EventHandler; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import java.io.IOException; | |||||
| import java.util.concurrent.ExecutorService; | |||||
| import java.util.concurrent.Executors; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/13 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class ExtendMsgQueueMessageExecutor implements MsgQueueMessageDispatcher, EventHandler<EventEntity<byte[]>> { | |||||
| private static final Logger LOGGER = LoggerFactory.getLogger(ExtendMsgQueueMessageExecutor.class); | |||||
| private final ExecutorService dataExecutor = Executors.newSingleThreadExecutor(); | |||||
| private MsgQueueProducer msgProducer; | |||||
| private MsgQueueConsumer msgConsumer; | |||||
| private MessageHandle messageHandle; | |||||
| private boolean isRunning; | |||||
| private boolean isConnected; | |||||
| public ExtendMsgQueueMessageExecutor setMsgProducer(MsgQueueProducer msgProducer) { | |||||
| this.msgProducer = msgProducer; | |||||
| return this; | |||||
| } | |||||
| public ExtendMsgQueueMessageExecutor setMsgConsumer(MsgQueueConsumer msgConsumer) { | |||||
| this.msgConsumer = msgConsumer; | |||||
| return this; | |||||
| } | |||||
| public ExtendMsgQueueMessageExecutor setMessageHandle(MessageHandle messageHandle) { | |||||
| this.messageHandle = messageHandle; | |||||
| return this; | |||||
| } | |||||
| @Override | |||||
| public void init() { | |||||
| // do nothing | |||||
| } | |||||
| public synchronized void connect() throws Exception { | |||||
| if (!isConnected) { | |||||
| msgProducer.connect(); | |||||
| msgConsumer.connect(this); | |||||
| msgConsumer.start(); | |||||
| isConnected = true; | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public synchronized void stop() throws Exception { | |||||
| isRunning = false; | |||||
| close(); | |||||
| } | |||||
| @Override | |||||
| public void run() { | |||||
| this.isRunning = true; | |||||
| // this.msgConsumer.start(); | |||||
| // listen(); | |||||
| } | |||||
| // private void listen() { | |||||
| // while (isRunning) { | |||||
| // try { | |||||
| // byte[] data = this.msgConsumer.start(); | |||||
| // // 收到数据后由队列处理 | |||||
| // handleData(data); | |||||
| // } catch (Exception e) { | |||||
| // // 日志打印 | |||||
| // LOGGER.error("extend message handle exception {}", e.getMessage()); | |||||
| // } | |||||
| // } | |||||
| // } | |||||
| private void handleData(byte[] data) { | |||||
| dataExecutor.execute(() -> { | |||||
| try { | |||||
| AsyncFuture<byte[]> result = messageHandle.processUnordered(data); | |||||
| msgProducer.publish(result.get()); | |||||
| } catch (Exception e) { | |||||
| LOGGER.error("process Unordered message exception {}", e.getMessage()); | |||||
| } | |||||
| }); | |||||
| } | |||||
| @Override | |||||
| public void close() throws IOException { | |||||
| isConnected = false; | |||||
| this.msgProducer.close(); | |||||
| this.msgConsumer.close(); | |||||
| } | |||||
| @Override | |||||
| public void onEvent(EventEntity<byte[]> event, long sequence, boolean endOfBatch) throws Exception { | |||||
| byte[] data = event.getEntity(); | |||||
| handleData(data); | |||||
| } | |||||
| } | |||||
| @@ -1,70 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.mq.server.MsgQueueConsensusManageService | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 下午1:46 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.server; | |||||
| import java.util.Arrays; | |||||
| import com.jd.blockchain.consensus.ClientIdentification; | |||||
| import com.jd.blockchain.consensus.ConsensusManageService; | |||||
| import com.jd.blockchain.consensus.ConsensusSecurityException; | |||||
| import com.jd.blockchain.consensus.mq.config.MsgQueueClientIncomingConfig; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueClientIncomingSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; | |||||
| import com.jd.blockchain.crypto.Crypto; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| import com.jd.blockchain.crypto.SignatureFunction; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueConsensusManageService implements ConsensusManageService { | |||||
| private MsgQueueConsensusSettings consensusSettings; | |||||
| public MsgQueueConsensusManageService setConsensusSettings(MsgQueueConsensusSettings consensusSettings) { | |||||
| this.consensusSettings = consensusSettings; | |||||
| return this; | |||||
| } | |||||
| @Override | |||||
| public MsgQueueClientIncomingSettings authClientIncoming(ClientIdentification authId) throws ConsensusSecurityException { | |||||
| boolean isLegal = isLegal(authId); | |||||
| if (isLegal) { | |||||
| MsgQueueClientIncomingSettings mqcis = new MsgQueueClientIncomingConfig() | |||||
| .setPubKey(authId.getPubKey()) | |||||
| .setClientId(clientId(authId.getIdentityInfo())) | |||||
| .setConsensusSettings(this.consensusSettings) | |||||
| ; | |||||
| return mqcis; | |||||
| } | |||||
| return null; | |||||
| } | |||||
| private int clientId(byte[] identityInfo) { | |||||
| // todo | |||||
| return 0; | |||||
| } | |||||
| public boolean isLegal(ClientIdentification authId) { | |||||
| boolean isLegal = false; | |||||
| PubKey pubKey = authId.getPubKey(); | |||||
| byte[] identityInfo = authId.getIdentityInfo(); | |||||
| byte[] address = pubKey.toBytes(); // 使用公钥地址作为认证信息 | |||||
| if (Arrays.equals(address, identityInfo)) { | |||||
| SignatureFunction signatureFunction = Crypto.getSignatureFunction(pubKey.getAlgorithm()); | |||||
| isLegal = signatureFunction.verify(authId.getSignature(), pubKey, identityInfo); | |||||
| } | |||||
| return isLegal; | |||||
| } | |||||
| } | |||||
| @@ -1,28 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.mq.server.MsgQueueMessageDispatcher | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 上午11:30 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.server; | |||||
| import java.io.Closeable; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public interface MsgQueueMessageDispatcher extends Runnable, Closeable { | |||||
| void init(); | |||||
| void connect() throws Exception; | |||||
| void stop() throws Exception; | |||||
| } | |||||
| @@ -1,182 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.server.MsgQueueMessageExecutor | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/13 下午2:10 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.server; | |||||
| import com.jd.blockchain.consensus.event.EventEntity; | |||||
| import com.jd.blockchain.consensus.mq.event.MessageEvent; | |||||
| import com.jd.blockchain.consensus.mq.event.TxBlockedEvent; | |||||
| import com.jd.blockchain.consensus.mq.exchange.ExchangeEventInnerEntity; | |||||
| import com.jd.blockchain.consensus.mq.exchange.ExchangeType; | |||||
| import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; | |||||
| import com.jd.blockchain.consensus.mq.util.MessageConvertUtil; | |||||
| import com.jd.blockchain.consensus.service.MessageHandle; | |||||
| import com.jd.blockchain.consensus.service.StateMachineReplicate; | |||||
| import com.jd.blockchain.ledger.TransactionState; | |||||
| import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||||
| import com.lmax.disruptor.EventHandler; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import java.util.ArrayList; | |||||
| import java.util.HashMap; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| import java.util.concurrent.ExecutorService; | |||||
| import java.util.concurrent.Executors; | |||||
| import java.util.concurrent.atomic.AtomicInteger; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/13 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueMessageExecutor implements EventHandler<EventEntity<ExchangeEventInnerEntity>> { | |||||
| private static final Logger LOGGER = LoggerFactory.getLogger(MsgQueueMessageExecutor.class); | |||||
| // todo 暂不处理队列溢出导致的OOM | |||||
| private final ExecutorService blockEventExecutor = Executors.newFixedThreadPool(10); | |||||
| private MsgQueueProducer blProducer; | |||||
| private List<MessageEvent> exchangeEvents = new ArrayList<>(); | |||||
| private String realmName; | |||||
| private MessageHandle messageHandle; | |||||
| private final AtomicInteger messageId = new AtomicInteger(); | |||||
| private int txSizePerBlock = 1000; | |||||
| private StateMachineReplicate stateMachineReplicator; | |||||
| public MsgQueueMessageExecutor setRealmName(String realmName) { | |||||
| this.realmName = realmName; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueMessageExecutor setBlProducer(MsgQueueProducer blProducer) { | |||||
| this.blProducer = blProducer; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueMessageExecutor setTxSizePerBlock(int txSizePerBlock) { | |||||
| this.txSizePerBlock = txSizePerBlock; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueMessageExecutor setMessageHandle(MessageHandle messageHandle) { | |||||
| this.messageHandle = messageHandle; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueMessageExecutor setStateMachineReplicator(StateMachineReplicate stateMachineReplicator) { | |||||
| this.stateMachineReplicator = stateMachineReplicator; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueMessageExecutor init() { | |||||
| try { | |||||
| long latestStateId = stateMachineReplicator.getLatestStateID(realmName); | |||||
| // 设置基础消息ID | |||||
| messageId.set(((int)latestStateId + 1) * txSizePerBlock); | |||||
| blProducer.connect(); | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| return this; | |||||
| } | |||||
| @Override | |||||
| public void onEvent(EventEntity<ExchangeEventInnerEntity> event, long sequence, boolean endOfBatch) throws Exception { | |||||
| ExchangeEventInnerEntity entity = event.getEntity(); | |||||
| if (entity != null) { | |||||
| if (entity.getType() == ExchangeType.BLOCK || entity.getType() == ExchangeType.EMPTY) { | |||||
| if (!exchangeEvents.isEmpty()) { | |||||
| process(exchangeEvents); | |||||
| exchangeEvents.clear(); | |||||
| } | |||||
| } else { | |||||
| byte[] bytes = event.getEntity().getContent(); | |||||
| String key = bytes2Key(bytes); | |||||
| exchangeEvents.add(new MessageEvent(key, bytes)); | |||||
| } | |||||
| } | |||||
| } | |||||
| private void process(List<MessageEvent> messageEvents) { | |||||
| if (messageEvents != null && !messageEvents.isEmpty()) { | |||||
| try { | |||||
| Map<String, AsyncFuture<byte[]>> txResponseMap = execute(messageEvents); | |||||
| if (txResponseMap != null && !txResponseMap.isEmpty()) { | |||||
| // byte[] asyncFuture; | |||||
| for (Map.Entry<String, AsyncFuture<byte[]>> entry : txResponseMap.entrySet()) { | |||||
| final String txKey = entry.getKey(); | |||||
| final AsyncFuture<byte[]> asyncFuture = entry.getValue(); | |||||
| // asyncFuture = entry.getValue().get(); | |||||
| blockEventExecutor.execute(() -> { | |||||
| TxBlockedEvent txBlockedEvent = new TxBlockedEvent(txKey, | |||||
| MessageConvertUtil.base64Encode(asyncFuture.get())); | |||||
| byte[] serializeBytes = MessageConvertUtil.serializeTxBlockedEvent(txBlockedEvent); | |||||
| // 通过消息队列发送该消息 | |||||
| try { | |||||
| this.blProducer.publish(serializeBytes); | |||||
| } catch (Exception e) { | |||||
| LOGGER.error("publish block event message exception {}", e.getMessage()); | |||||
| } | |||||
| }); | |||||
| } | |||||
| } | |||||
| } catch (Exception e) { | |||||
| // 打印日志 | |||||
| LOGGER.error("process message exception {}", e.getMessage()); | |||||
| } | |||||
| } | |||||
| } | |||||
| private Map<String, AsyncFuture<byte[]>> execute(List<MessageEvent> messageEvents) { | |||||
| // System.out.printf("Thread[%s %s] execute messageEvents !!! \r\n", | |||||
| // Thread.currentThread().getId(), Thread.currentThread().getName()); | |||||
| Map<String, AsyncFuture<byte[]>> asyncFutureMap = new HashMap<>(); | |||||
| // 使用MessageHandle处理 | |||||
| // long startTime = System.currentTimeMillis(); | |||||
| // int txSize = messageEvents.size(); | |||||
| String batchId = messageHandle.beginBatch(realmName); | |||||
| try { | |||||
| for (MessageEvent messageEvent : messageEvents) { | |||||
| String txKey = messageEvent.getMessageKey(); | |||||
| byte[] txContent = messageEvent.getMessage(); | |||||
| AsyncFuture<byte[]> asyncFuture = messageHandle.processOrdered(messageId.getAndIncrement(), txContent, realmName, batchId); | |||||
| asyncFutureMap.put(txKey, asyncFuture); | |||||
| } | |||||
| messageHandle.completeBatch(realmName, batchId); | |||||
| messageHandle.commitBatch(realmName, batchId); | |||||
| // long totalTime = System.currentTimeMillis() - startTime; | |||||
| // String content = String.format("batch[%s] process, time = {%s}ms, TPS = %.2f \r\n", | |||||
| // batchId, totalTime, txSize * 1000.0D / totalTime); | |||||
| // System.out.println(content); | |||||
| // logQueue.put(content); | |||||
| // 提交之后需要获取对应的结果 | |||||
| } catch (Exception e) { | |||||
| // todo 需要处理应答码 404 | |||||
| messageHandle.rollbackBatch(realmName, batchId, TransactionState.CONSENSUS_ERROR.CODE); | |||||
| } | |||||
| return asyncFutureMap; | |||||
| } | |||||
| private String bytes2Key(byte[] bytes) { | |||||
| return MessageConvertUtil.messageKey(bytes); | |||||
| } | |||||
| } | |||||
| @@ -1,196 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.server.MsgQueueNodeServer | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/13 上午11:20 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.server; | |||||
| import com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider; | |||||
| import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; | |||||
| import com.jd.blockchain.consensus.mq.factory.MsgQueueFactory; | |||||
| import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueBlockSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueServerSettings; | |||||
| import com.jd.blockchain.consensus.service.MessageHandle; | |||||
| import com.jd.blockchain.consensus.service.NodeServer; | |||||
| import com.jd.blockchain.consensus.service.StateMachineReplicate; | |||||
| import java.util.concurrent.Executors; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/13 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueNodeServer implements NodeServer { | |||||
| private DefaultMsgQueueMessageDispatcher dispatcher; | |||||
| private ExtendMsgQueueMessageExecutor extendExecutor; | |||||
| private MessageHandle messageHandle; | |||||
| private StateMachineReplicate stateMachineReplicator; | |||||
| private MsgQueueMessageExecutor messageExecutor; | |||||
| private MsgQueueNetworkSettings networkSettings; | |||||
| private MsgQueueConsensusManageService manageService; | |||||
| private int txSizePerBlock = 1000; | |||||
| private long maxDelayMilliSecondsPerBlock = 1000; | |||||
| private MsgQueueServerSettings serverSettings; | |||||
| private boolean isRunning; | |||||
| public MsgQueueNodeServer setMessageHandle(MessageHandle messageHandle) { | |||||
| this.messageHandle = messageHandle; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueNodeServer setStateMachineReplicator(StateMachineReplicate stateMachineReplicator) { | |||||
| this.stateMachineReplicator = stateMachineReplicator; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueNodeServer setTxSizePerBlock(int txSizePerBlock) { | |||||
| this.txSizePerBlock = txSizePerBlock; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueNodeServer setMaxDelayMilliSecondsPerBlock(long maxDelayMilliSecondsPerBlock) { | |||||
| this.maxDelayMilliSecondsPerBlock = maxDelayMilliSecondsPerBlock; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueNodeServer setMsgQueueNetworkSettings(MsgQueueNetworkSettings networkSettings) { | |||||
| this.networkSettings = networkSettings; | |||||
| return this; | |||||
| } | |||||
| public MsgQueueNodeServer setServerSettings(MsgQueueServerSettings serverSettings) { | |||||
| this.serverSettings = serverSettings; | |||||
| this.manageService = new MsgQueueConsensusManageService() | |||||
| .setConsensusSettings(serverSettings.getConsensusSettings()); | |||||
| return this; | |||||
| } | |||||
| public MsgQueueNodeServer init() { | |||||
| String realmName = this.serverSettings.getRealmName(); | |||||
| MsgQueueBlockSettings blockSettings = this.serverSettings.getBlockSettings(); | |||||
| MsgQueueConsensusSettings consensusSettings = this.serverSettings.getConsensusSettings(); | |||||
| this.setTxSizePerBlock(blockSettings.getTxSizePerBlock()) | |||||
| .setMaxDelayMilliSecondsPerBlock(blockSettings.getMaxDelayMilliSecondsPerBlock()) | |||||
| .setMsgQueueNetworkSettings(consensusSettings.getNetworkSettings()) | |||||
| ; | |||||
| String server = networkSettings.getServer(), | |||||
| txTopic = networkSettings.getTxTopic(), | |||||
| blTopic = networkSettings.getBlTopic(), | |||||
| msgTopic = networkSettings.getMsgTopic(); | |||||
| MsgQueueProducer blProducer = MsgQueueFactory.newProducer(server, blTopic), | |||||
| txProducer = MsgQueueFactory.newProducer(server, txTopic), | |||||
| msgProducer = MsgQueueFactory.newProducer(server, msgTopic); | |||||
| MsgQueueConsumer txConsumer = MsgQueueFactory.newConsumer(server, txTopic), | |||||
| msgConsumer = MsgQueueFactory.newConsumer(server, msgTopic); | |||||
| initMessageExecutor(blProducer, realmName); | |||||
| initDispatcher(txProducer, txConsumer); | |||||
| initExtendExecutor(msgProducer, msgConsumer); | |||||
| return this; | |||||
| } | |||||
| @Override | |||||
| public String getProviderName() { | |||||
| return MsgQueueConsensusProvider.NAME; | |||||
| } | |||||
| @Override | |||||
| public MsgQueueConsensusManageService getManageService() { | |||||
| return this.manageService; | |||||
| } | |||||
| @Override | |||||
| public MsgQueueServerSettings getSettings() { | |||||
| return serverSettings; | |||||
| } | |||||
| @Override | |||||
| public boolean isRunning() { | |||||
| return isRunning; | |||||
| } | |||||
| @Override | |||||
| public synchronized void start() { | |||||
| if (!isRunning) { | |||||
| try { | |||||
| dispatcher.connect(); | |||||
| Executors.newSingleThreadExecutor().execute(dispatcher); | |||||
| extendExecutor.connect(); | |||||
| Executors.newSingleThreadExecutor().execute(extendExecutor); | |||||
| isRunning = true; | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public synchronized void stop() { | |||||
| if (isRunning) { | |||||
| try { | |||||
| dispatcher.stop(); | |||||
| extendExecutor.stop(); | |||||
| isRunning = false; | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| } | |||||
| } | |||||
| private void initMessageExecutor(MsgQueueProducer blProducer, final String realmName) { | |||||
| messageExecutor = new MsgQueueMessageExecutor() | |||||
| .setRealmName(realmName) | |||||
| .setMessageHandle(messageHandle) | |||||
| .setBlProducer(blProducer) | |||||
| .setStateMachineReplicator(stateMachineReplicator) | |||||
| .setTxSizePerBlock(txSizePerBlock) | |||||
| .init() | |||||
| ; | |||||
| } | |||||
| private void initDispatcher(MsgQueueProducer txProducer, MsgQueueConsumer txConsumer) { | |||||
| dispatcher = new DefaultMsgQueueMessageDispatcher(txSizePerBlock, maxDelayMilliSecondsPerBlock) | |||||
| .setTxProducer(txProducer) | |||||
| .setTxConsumer(txConsumer) | |||||
| .setEventHandler(messageExecutor) | |||||
| ; | |||||
| dispatcher.init(); | |||||
| } | |||||
| private void initExtendExecutor(MsgQueueProducer msgProducer, MsgQueueConsumer msgConsumer) { | |||||
| extendExecutor = new ExtendMsgQueueMessageExecutor() | |||||
| .setMessageHandle(messageHandle) | |||||
| .setMsgConsumer(msgConsumer) | |||||
| .setMsgProducer(msgProducer) | |||||
| ; | |||||
| extendExecutor.init(); | |||||
| } | |||||
| } | |||||
| @@ -1,61 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.mq.server.MsgQueueNodeServerFactory | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 上午11:30 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.server; | |||||
| import com.jd.blockchain.consensus.ConsensusSettings; | |||||
| import com.jd.blockchain.consensus.mq.config.MsgQueueNodeConfig; | |||||
| import com.jd.blockchain.consensus.mq.config.MsgQueueServerConfig; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings; | |||||
| import com.jd.blockchain.consensus.mq.settings.MsgQueueServerSettings; | |||||
| import com.jd.blockchain.consensus.service.*; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MsgQueueNodeServerFactory implements NodeServerFactory { | |||||
| @Override | |||||
| public MsgQueueServerSettings buildServerSettings(String realmName, ConsensusSettings consensusSetting, String currentNodeAddress) { | |||||
| if (!(consensusSetting instanceof MsgQueueConsensusSettings)) { | |||||
| throw new IllegalArgumentException("ConsensusSettings data isn't supported! Accept MsgQueueConsensusSettings only!"); | |||||
| } | |||||
| MsgQueueNodeSettings nodeSettings = new MsgQueueNodeConfig().setAddress(currentNodeAddress); | |||||
| MsgQueueServerSettings serverSettings = new MsgQueueServerConfig() | |||||
| .setRealmName(realmName) | |||||
| .setNodeSettings(nodeSettings) | |||||
| .setConsensusSettings((MsgQueueConsensusSettings) consensusSetting) | |||||
| ; | |||||
| return serverSettings; | |||||
| } | |||||
| @Override | |||||
| public MsgQueueNodeServer setupServer(ServerSettings serverSettings, MessageHandle messageHandler, StateMachineReplicate stateMachineReplicator) { | |||||
| if (!(serverSettings instanceof MsgQueueServerSettings)) { | |||||
| throw new IllegalArgumentException("ServerSettings data isn't supported! Accept MsgQueueServerSettings only!"); | |||||
| } | |||||
| MsgQueueNodeServer nodeServer = new MsgQueueNodeServer() | |||||
| .setServerSettings((MsgQueueServerSettings) serverSettings) | |||||
| .setMessageHandle(messageHandler) | |||||
| .setStateMachineReplicator(stateMachineReplicator) | |||||
| .init() | |||||
| ; | |||||
| return nodeServer; | |||||
| } | |||||
| } | |||||
| @@ -1,30 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueBlockSettings | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/13 下午4:28 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.settings; | |||||
| import com.jd.blockchain.binaryproto.DataContract; | |||||
| import com.jd.blockchain.binaryproto.DataField; | |||||
| import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
| import com.jd.blockchain.consts.DataCodes; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/13 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| @DataContract(code = DataCodes.CONSENSUS_MSGQUEUE_BLOCK_SETTINGS) | |||||
| public interface MsgQueueBlockSettings { | |||||
| @DataField(order = 0, primitiveType = PrimitiveType.INT32) | |||||
| int getTxSizePerBlock(); | |||||
| @DataField(order = 1, primitiveType = PrimitiveType.INT64) | |||||
| long getMaxDelayMilliSecondsPerBlock(); | |||||
| } | |||||
| @@ -1,30 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueClientIncomingSettings | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/13 下午4:35 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.settings; | |||||
| import com.jd.blockchain.binaryproto.DataContract; | |||||
| import com.jd.blockchain.binaryproto.DataField; | |||||
| import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
| import com.jd.blockchain.consensus.ClientIncomingSettings; | |||||
| import com.jd.blockchain.consensus.ConsensusSettings; | |||||
| import com.jd.blockchain.consts.DataCodes; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/13 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| @DataContract(code = DataCodes.CONSENSUS_MSGQUEUE_CLI_INCOMING_SETTINGS) | |||||
| public interface MsgQueueClientIncomingSettings extends ClientIncomingSettings { | |||||
| @DataField(order = 1, primitiveType=PrimitiveType.BYTES) | |||||
| PubKey getPubKey(); | |||||
| } | |||||
| @@ -1,23 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueClientSettings | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/13 下午4:30 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.settings; | |||||
| import com.jd.blockchain.consensus.client.ClientSettings; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/13 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public interface MsgQueueClientSettings extends ClientSettings { | |||||
| MsgQueueNetworkSettings getMsgQueueNetworkSettings(); | |||||
| } | |||||
| @@ -1,33 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueConsensusSettings | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/13 下午4:37 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.settings; | |||||
| import com.jd.blockchain.binaryproto.DataContract; | |||||
| import com.jd.blockchain.binaryproto.DataField; | |||||
| import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
| import com.jd.blockchain.consensus.ConsensusSettings; | |||||
| import com.jd.blockchain.consensus.mq.config.MsgQueueBlockConfig; | |||||
| import com.jd.blockchain.consts.DataCodes; | |||||
| import com.jd.blockchain.utils.Property; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/13 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| @DataContract(code = DataCodes.CONSENSUS_MSGQUEUE_SETTINGS) | |||||
| public interface MsgQueueConsensusSettings extends ConsensusSettings { | |||||
| @DataField(order = 0, refContract = true) | |||||
| MsgQueueNetworkSettings getNetworkSettings(); | |||||
| @DataField(order = 1, refContract = true) | |||||
| MsgQueueBlockSettings getBlockSettings(); | |||||
| } | |||||
| @@ -1,36 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.mq.config.MsgQueueNetworkSettings | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/12 上午11:43 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.settings; | |||||
| import com.jd.blockchain.binaryproto.DataContract; | |||||
| import com.jd.blockchain.binaryproto.DataField; | |||||
| import com.jd.blockchain.binaryproto.PrimitiveType; | |||||
| import com.jd.blockchain.consts.DataCodes; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/12 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| @DataContract(code = DataCodes.CONSENSUS_MSGQUEUE_NETWORK_SETTINGS) | |||||
| public interface MsgQueueNetworkSettings { | |||||
| @DataField(order = 0, primitiveType = PrimitiveType.TEXT) | |||||
| String getServer(); | |||||
| @DataField(order = 1, primitiveType = PrimitiveType.TEXT) | |||||
| String getTxTopic(); | |||||
| @DataField(order = 2, primitiveType = PrimitiveType.TEXT) | |||||
| String getBlTopic(); | |||||
| @DataField(order = 3, primitiveType = PrimitiveType.TEXT) | |||||
| String getMsgTopic(); | |||||
| } | |||||
| @@ -1,25 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/13 下午4:50 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.settings; | |||||
| import com.jd.blockchain.binaryproto.DataContract; | |||||
| import com.jd.blockchain.consensus.NodeSettings; | |||||
| import com.jd.blockchain.consts.DataCodes; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/13 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| @DataContract(code=DataCodes.CONSENSUS_MSGQUEUE_NODE_SETTINGS) | |||||
| public interface MsgQueueNodeSettings extends NodeSettings { | |||||
| } | |||||
| @@ -1,25 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueServerSettings | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/12/13 下午4:39 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.settings; | |||||
| import com.jd.blockchain.consensus.service.ServerSettings; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/12/13 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public interface MsgQueueServerSettings extends ServerSettings { | |||||
| MsgQueueBlockSettings getBlockSettings(); | |||||
| MsgQueueConsensusSettings getConsensusSettings(); | |||||
| } | |||||
| @@ -1,95 +0,0 @@ | |||||
| /** | |||||
| * Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
| * FileName: com.jd.blockchain.sdk.mq.event.MessageConvertUtil | |||||
| * Author: shaozhuguang | |||||
| * Department: 区块链研发部 | |||||
| * Date: 2018/11/21 下午7:28 | |||||
| * Description: | |||||
| */ | |||||
| package com.jd.blockchain.consensus.mq.util; | |||||
| import com.alibaba.fastjson.JSON; | |||||
| import com.jd.blockchain.consensus.mq.event.BlockEvent; | |||||
| import com.jd.blockchain.consensus.mq.event.TxBlockedEvent; | |||||
| import com.jd.blockchain.utils.security.ShaUtils; | |||||
| import org.springframework.util.Base64Utils; | |||||
| /** | |||||
| * | |||||
| * @author shaozhuguang | |||||
| * @create 2018/11/21 | |||||
| * @since 1.0.0 | |||||
| */ | |||||
| public class MessageConvertUtil { | |||||
| public static final String defaultCharsetName = "UTF-8"; | |||||
| public static String base64Encode(byte[] src) { | |||||
| return Base64Utils.encodeToString(src); | |||||
| } | |||||
| public static byte[] base64Decode(String src) { | |||||
| return Base64Utils.decodeFromString(src); | |||||
| } | |||||
| public static String messageKey(byte[] src) { | |||||
| return base64Encode(ShaUtils.hash_256(src)); | |||||
| } | |||||
| public static BlockEvent convertBytes2BlockEvent(byte[] serializeBytes) { | |||||
| String text; | |||||
| try{ | |||||
| text = new String(serializeBytes, defaultCharsetName); | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| return convertString2BlockEvent(text); | |||||
| } | |||||
| public static BlockEvent convertString2BlockEvent(String serializeString) { | |||||
| return JSON.parseObject(serializeString, BlockEvent.class); | |||||
| } | |||||
| public static TxBlockedEvent convertBytes2TxBlockedEvent(byte[] serializeBytes) { | |||||
| String text; | |||||
| try{ | |||||
| text = new String(serializeBytes, defaultCharsetName); | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| return convertString2TxBlockedEvent(text); | |||||
| } | |||||
| public static TxBlockedEvent convertString2TxBlockedEvent(String serializeString) { | |||||
| return JSON.parseObject(serializeString, TxBlockedEvent.class); | |||||
| } | |||||
| public static byte[] serializeBlockEvent(BlockEvent blockEvent) { | |||||
| String serializeString = serializeEvent(blockEvent); | |||||
| byte[] serializeBytes; | |||||
| try { | |||||
| serializeBytes = serializeString.getBytes(defaultCharsetName); | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| return serializeBytes; | |||||
| } | |||||
| public static byte[] serializeTxBlockedEvent(TxBlockedEvent txBlockedEvent) { | |||||
| String serializeString = JSON.toJSONString(txBlockedEvent); | |||||
| byte[] serializeBytes; | |||||
| try { | |||||
| serializeBytes = serializeString.getBytes(defaultCharsetName); | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException(e); | |||||
| } | |||||
| return serializeBytes; | |||||
| } | |||||
| public static String serializeEvent(BlockEvent blockEvent) { | |||||
| return JSON.toJSONString(blockEvent); | |||||
| } | |||||
| } | |||||
| @@ -1,17 +0,0 @@ | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>jdchain-core</artifactId> | |||||
| <version>1.2.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>consensus-core</artifactId> | |||||
| <packaging>pom</packaging> | |||||
| <modules> | |||||
| <module>consensus-bftsmart</module> | |||||
| <module>consensus-mq</module> | |||||
| </modules> | |||||
| </project> | |||||
| @@ -1,27 +0,0 @@ | |||||
| ### 合约相关说明 | |||||
| 1. 编译合约入口:ContractCompilerCmdTest.java; | |||||
| 2. 账本调用合约测试入口:ContractEventSendOperationHandleTest.java; | |||||
| ### 单元测试注意事项 | |||||
| 使用ContractEventSendOperationHandleTest.java进行单元测试,注意事项: | |||||
| 1. 设置合约使用PUB_CLASS_PATH、CORE_CLASS_PATH位置(sys-contract.properties); | |||||
| PUB包在根目录中的contract-libs文件夹;core包需要编译来生成。具体如下: | |||||
| - 进入contract-jar模块,执行maven命令: | |||||
| ``` | |||||
| mvn clean assembly:assembly | |||||
| ``` | |||||
| - 生成的core包位于模块的target中的contract-jar-xxx所在的coreLib中; | |||||
| - 将此coreLib目录作为CORE_CLASS_PATH指向的目录。 | |||||
| 2. 编译生成合约压缩包,即执行:ContractCompilerCmdTest.java对应的mainTestOk(); | |||||
| 在编译之前,修改sys-contract.properties文件的变量CONTRACT_FROM_PATH(合约源文件位置)、CONTRACT_SAVE_TO_PATH(合约保存位置)。 | |||||
| 3. 在合约保存位置中可看到生成的压缩包:xxx.contract;然后执行 ContractEventSendOperationHandleTest.java测试用例test1()即可。 | |||||
| ### 20180910版本改造 | |||||
| 1. 在contract-jar中添加了mvn assembly处理逻辑,将合约用到的lib包全部放置其target/xxx/pubLib文件夹中,执行: | |||||
| ``` | |||||
| mvn clean assembly:assembly | |||||
| ``` | |||||
| 2. 修改了sys-contract.properties文件,新增了CONTRACT_CLASS_LIBS参数; | |||||
| - 将CONTRACT_CLASS_PATH专用于存储路径 | |||||
| - CONTRACT_CLASS_LIBS来存放所有的jar包 | |||||
| 3. 删减了contract-libs文件夹中需要动态生成的jar。 | |||||
| @@ -1,43 +0,0 @@ | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>contract-core</artifactId> | |||||
| <version>1.2.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>contract-jvm</artifactId> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>contract-framework</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>runtime-context</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>runtime-modular</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <build> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-deploy-plugin</artifactId> | |||||
| <version>2.8.2</version> | |||||
| <configuration> | |||||
| <skip>true</skip> | |||||
| </configuration> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| </project> | |||||
| @@ -1,105 +0,0 @@ | |||||
| package com.jd.blockchain.contract.jvm; | |||||
| import java.lang.reflect.Method; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.util.ReflectionUtils; | |||||
| import com.jd.blockchain.contract.ContractEventContext; | |||||
| import com.jd.blockchain.contract.ContractException; | |||||
| import com.jd.blockchain.contract.EventProcessingAware; | |||||
| import com.jd.blockchain.contract.engine.ContractCode; | |||||
| import com.jd.blockchain.ledger.BytesValue; | |||||
| import com.jd.blockchain.ledger.BytesValueEncoding; | |||||
| import com.jd.blockchain.ledger.BytesValueList; | |||||
| import com.jd.blockchain.utils.Bytes; | |||||
| /** | |||||
| * @author huanghaiquan | |||||
| * | |||||
| */ | |||||
| public abstract class AbstractContractCode implements ContractCode { | |||||
| private static final Logger LOGGER = LoggerFactory.getLogger(AbstractContractCode.class); | |||||
| private Bytes address; | |||||
| private long version; | |||||
| private ContractDefinition contractDefinition; | |||||
| public AbstractContractCode(Bytes address, long version, ContractDefinition contractDefinition) { | |||||
| this.address = address; | |||||
| this.version = version; | |||||
| this.contractDefinition = contractDefinition; | |||||
| } | |||||
| public ContractDefinition getContractDefinition() { | |||||
| return contractDefinition; | |||||
| } | |||||
| @Override | |||||
| public Bytes getAddress() { | |||||
| return address; | |||||
| } | |||||
| @Override | |||||
| public long getVersion() { | |||||
| return version; | |||||
| } | |||||
| @Override | |||||
| public BytesValue processEvent(ContractEventContext eventContext) { | |||||
| EventProcessingAware evtProcAwire = null; | |||||
| Object retn = null; | |||||
| Method handleMethod = null; | |||||
| Exception error = null; | |||||
| try { | |||||
| // 执行预处理; | |||||
| Object contractInstance = getContractInstance(); | |||||
| if (contractInstance instanceof EventProcessingAware) { | |||||
| evtProcAwire = (EventProcessingAware) contractInstance; | |||||
| } | |||||
| if (evtProcAwire != null) { | |||||
| evtProcAwire.beforeEvent(eventContext); | |||||
| } | |||||
| // 反序列化参数; | |||||
| handleMethod = contractDefinition.getType().getHandleMethod(eventContext.getEvent()); | |||||
| if (handleMethod == null) { | |||||
| throw new ContractException( | |||||
| String.format("Contract[%s:%s] has no handle method to handle event[%s]!", address.toString(), | |||||
| contractDefinition.getType().getName(), eventContext.getEvent())); | |||||
| } | |||||
| BytesValueList bytesValues = eventContext.getArgs(); | |||||
| Object[] args = BytesValueEncoding.decode(bytesValues, handleMethod.getParameterTypes()); | |||||
| retn = ReflectionUtils.invokeMethod(handleMethod, contractInstance, args); | |||||
| } catch (Exception e) { | |||||
| error = e; | |||||
| } | |||||
| if (evtProcAwire != null) { | |||||
| try { | |||||
| evtProcAwire.postEvent(eventContext, error); | |||||
| } catch (Exception e) { | |||||
| String errorMessage = "Error occurred while posting contract event! --" + e.getMessage(); | |||||
| LOGGER.error(errorMessage, e); | |||||
| throw new ContractException(errorMessage, e); | |||||
| } | |||||
| } | |||||
| if (error != null) { | |||||
| // Rethrow error; | |||||
| throw new ContractException(String.format("Error occurred while processing event[%s] of contract[%s]! --%s", | |||||
| eventContext.getEvent(), address.toString(), error.getMessage()), error); | |||||
| } | |||||
| BytesValue retnBytes = BytesValueEncoding.encodeSingle(retn, handleMethod.getReturnType()); | |||||
| return retnBytes; | |||||
| } | |||||
| protected abstract Object getContractInstance(); | |||||
| } | |||||
| @@ -1,24 +0,0 @@ | |||||
| package com.jd.blockchain.contract.jvm; | |||||
| import com.jd.blockchain.contract.ContractType; | |||||
| public class ContractDefinition { | |||||
| private ContractType type; | |||||
| private Class<?> mainClass; | |||||
| public Class<?> getMainClass() { | |||||
| return mainClass; | |||||
| } | |||||
| public ContractType getType() { | |||||
| return type; | |||||
| } | |||||
| public ContractDefinition(ContractType type, Class<?> mainClass) { | |||||
| this.type = type; | |||||
| this.mainClass = mainClass; | |||||
| } | |||||
| } | |||||
| @@ -1,25 +0,0 @@ | |||||
| package com.jd.blockchain.contract.jvm; | |||||
| import com.jd.blockchain.contract.ContractType; | |||||
| import com.jd.blockchain.utils.Bytes; | |||||
| public class InstantiatedContractCode<T> extends AbstractContractCode { | |||||
| private T instance; | |||||
| public InstantiatedContractCode(Bytes address, long version, Class<T> delaredInterface, T instance) { | |||||
| super(address, version, resolveContractDefinition(delaredInterface, instance.getClass())); | |||||
| this.instance = instance; | |||||
| } | |||||
| private static ContractDefinition resolveContractDefinition(Class<?> declaredIntf, Class<?> implementedClass) { | |||||
| ContractType contractType = ContractType.resolve(declaredIntf); | |||||
| return new ContractDefinition(contractType, implementedClass); | |||||
| } | |||||
| @Override | |||||
| protected T getContractInstance() { | |||||
| return instance; | |||||
| } | |||||
| } | |||||
| @@ -1,41 +0,0 @@ | |||||
| package com.jd.blockchain.contract.jvm; | |||||
| import com.jd.blockchain.contract.engine.ContractCode; | |||||
| import com.jd.blockchain.contract.engine.ContractEngine; | |||||
| import com.jd.blockchain.runtime.Module; | |||||
| import com.jd.blockchain.runtime.RuntimeContext; | |||||
| import com.jd.blockchain.utils.Bytes; | |||||
| public class JVMContractEngine implements ContractEngine { | |||||
| private RuntimeContext runtimeContext = RuntimeContext.get(); | |||||
| private String getCodeName(Bytes address, long version) { | |||||
| return address.toBase58() + "_" + version; | |||||
| } | |||||
| @Override | |||||
| public ContractCode getContract(Bytes address, long version) { | |||||
| String codeName = getCodeName(address, version); | |||||
| Module module = runtimeContext.getDynamicModule(codeName); | |||||
| if (module == null) { | |||||
| return null; | |||||
| } | |||||
| return new JavaContractCode(address, version, module); | |||||
| } | |||||
| @Override | |||||
| public ContractCode setupContract(Bytes address, long version, byte[] code) { | |||||
| //is there the contractCode before setup? if yes ,then return; | |||||
| ContractCode contractCode = getContract(address,version); | |||||
| if(contractCode != null){ | |||||
| return contractCode; | |||||
| } | |||||
| String codeName = getCodeName(address, version); | |||||
| Module module = runtimeContext.createDynamicModule(codeName,code); | |||||
| if (module == null) { | |||||
| return null; | |||||
| } | |||||
| return new JavaContractCode(address, version, module); | |||||
| } | |||||
| } | |||||
| @@ -1,20 +0,0 @@ | |||||
| package com.jd.blockchain.contract.jvm; | |||||
| import com.jd.blockchain.contract.engine.ContractEngine; | |||||
| import com.jd.blockchain.contract.engine.ContractServiceProvider; | |||||
| public class JVMContractServiceProvider implements ContractServiceProvider { | |||||
| @Override | |||||
| public String getName() { | |||||
| return JVMContractServiceProvider.class.getName(); | |||||
| } | |||||
| @Override | |||||
| public ContractEngine getEngine() { | |||||
| return InnerEngine.INSTANCE; | |||||
| } | |||||
| private static class InnerEngine { | |||||
| private static final ContractEngine INSTANCE = new JVMContractEngine(); | |||||
| } | |||||
| } | |||||
| @@ -1,108 +0,0 @@ | |||||
| package com.jd.blockchain.contract.jvm; | |||||
| import java.util.concurrent.Callable; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import com.jd.blockchain.contract.Contract; | |||||
| import com.jd.blockchain.contract.ContractEventContext; | |||||
| import com.jd.blockchain.contract.ContractException; | |||||
| import com.jd.blockchain.contract.ContractType; | |||||
| import com.jd.blockchain.ledger.BytesValue; | |||||
| import com.jd.blockchain.runtime.Module; | |||||
| import com.jd.blockchain.utils.Bytes; | |||||
| /** | |||||
| * 基于 java jar 包并且以模块化方式独立加载的合约代码; | |||||
| * | |||||
| * @author huanghaiquan | |||||
| * | |||||
| */ | |||||
| public class JavaContractCode extends AbstractContractCode { | |||||
| private static final Logger LOGGER = LoggerFactory.getLogger(JavaContractCode.class); | |||||
| private Module codeModule; | |||||
| private Bytes address; | |||||
| private long version; | |||||
| public JavaContractCode(Bytes address, long version, Module codeModule) { | |||||
| super(address, version, resolveContractDefinition(codeModule)); | |||||
| this.address = address; | |||||
| this.version = version; | |||||
| this.codeModule = codeModule; | |||||
| } | |||||
| protected static ContractDefinition resolveContractDefinition(Module codeModule) { | |||||
| String mainClassName = codeModule.getMainClass(); | |||||
| Class<?> mainClass = codeModule.loadClass(mainClassName); | |||||
| Class<?>[] interfaces = mainClass.getInterfaces(); | |||||
| Class<?> contractInterface = null; | |||||
| for (Class<?> itf : interfaces) { | |||||
| Contract annoContract = itf.getAnnotation(Contract.class); | |||||
| if (annoContract != null) { | |||||
| if (contractInterface == null) { | |||||
| contractInterface = itf; | |||||
| } else { | |||||
| throw new ContractException( | |||||
| "One contract definition is only allowed to implement one contract type!"); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (contractInterface == null) { | |||||
| throw new ContractException("No contract type is implemented!"); | |||||
| } | |||||
| ContractType type = ContractType.resolve(contractInterface); | |||||
| return new ContractDefinition(type, mainClass); | |||||
| } | |||||
| @Override | |||||
| public Bytes getAddress() { | |||||
| return address; | |||||
| } | |||||
| @Override | |||||
| public long getVersion() { | |||||
| return version; | |||||
| } | |||||
| @Override | |||||
| public BytesValue processEvent(ContractEventContext eventContext) { | |||||
| if (LOGGER.isDebugEnabled()) { | |||||
| LOGGER.debug("Start processing event{} of contract{}...", eventContext.getEvent(), address.toString()); | |||||
| } | |||||
| try { | |||||
| return codeModule.call(new ContractExecution(eventContext)); | |||||
| } catch (Exception ex) { | |||||
| LOGGER.error(String.format("Error occurred while processing event[%s] of contract[%s]! --%s", | |||||
| eventContext.getEvent(), address.toString(), ex.getMessage()), ex); | |||||
| throw ex; | |||||
| } finally { | |||||
| if (LOGGER.isDebugEnabled()) { | |||||
| LOGGER.debug("End processing event{} of contract{}. ", eventContext.getEvent(), address.toString()); | |||||
| } | |||||
| } | |||||
| } | |||||
| protected Object getContractInstance() { | |||||
| try { | |||||
| // 每一次调用都通过反射创建合约的实例; | |||||
| return getContractDefinition().getMainClass().newInstance(); | |||||
| } catch (InstantiationException | IllegalAccessException e) { | |||||
| throw new IllegalStateException(e.getMessage(), e); | |||||
| } | |||||
| } | |||||
| private class ContractExecution implements Callable<BytesValue> { | |||||
| private ContractEventContext eventContext; | |||||
| public ContractExecution(ContractEventContext contractEventContext) { | |||||
| this.eventContext = contractEventContext; | |||||
| } | |||||
| @Override | |||||
| public BytesValue call() throws Exception { | |||||
| return JavaContractCode.super.processEvent(eventContext); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,274 +0,0 @@ | |||||
| # 合约编译插件使用教程 | |||||
| ### 1、maven引入 | |||||
| 在pom.xml文件中引入合约编译插件: | |||||
| ```xml | |||||
| <plugin> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>contract-maven-plugin</artifactId> | |||||
| <version>1.0.0.RELEASE</version> | |||||
| <executions> | |||||
| <execution> | |||||
| <id>make-contract</id> | |||||
| <phase>package</phase> | |||||
| <goals> | |||||
| <goal>compile</goal> | |||||
| </goals> | |||||
| </execution> | |||||
| </executions> | |||||
| <configuration> | |||||
| <archive> | |||||
| <manifest> | |||||
| <mainClass>com.jd.chain.contracts.ContractTestInfImpl</mainClass> | |||||
| </manifest> | |||||
| </archive> | |||||
| <finalName>contract</finalName> | |||||
| </configuration> | |||||
| </plugin> | |||||
| ``` | |||||
| 需要说明的几点如下: | |||||
| + 1)version:请根据实际JDChain发布版本确认,不同版本会有区别; | |||||
| + 2)executions->execution->id:该值请随意指定; | |||||
| + 3)executions->execution->phase:建议使用package及其后续阶段(若不了解phase含义,请自行查阅相关信息); | |||||
| + 4)executions->execution->goals->goal:必须使用compile; | |||||
| + 5)mainClass:必填,该类为需要发布的合约执行类(注意此处是类,不是接口),必须正确配置; | |||||
| + 6)finalName:必填,最终在编译正常的情况下,会产生{finalName}-JDChain-Contract.jar文件,只有该文件是可以发布到JDChain的合约包; | |||||
| ### 2、执行命令 | |||||
| 使用mvn执行命令,下面两种方式均可: | |||||
| 方式一:只执行contract插件命令 | |||||
| ```shell | |||||
| mvn clean compile contract:compile | |||||
| ``` | |||||
| 方式二:直接执行打包命令: | |||||
| ```shell | |||||
| mvn clean package | |||||
| ``` | |||||
| ### 3、合约编写要求 | |||||
| 合约的执行结果会对整条链产生比较深刻的影响,为了使用户能够更好、更合理的使用合约,目前JDChain约定合约编写规则包括以下几点: | |||||
| (违反其中任何一点都可能导致合约编译失败,但即使合约编译通过也不能保证合约可百分百运行正常) | |||||
| + 1)合约工程必须引入com.jd.blockchain:sdk-pack:该包中有合约正常编写需要使用的基本类; | |||||
| + 2)com.jd.blockchain:sdk-pack的scope必须定义为provided; | |||||
| + 3)合约发布必须通过合约编译插件进行打包:合约编译插件不但会对Jar包进行校验,同时也会加入JDChain独有的特征,只有具有该特征的Jar才能正常发布; | |||||
| + 4)合约中严禁使用随机数、IO、NIO等操作; | |||||
| + 5)合约打包时,请使用<scope>provided</scope>排除常用的工具包,例如FastJson、apache下的一些工具包等; | |||||
| + 6)合约必须有一个接口和该接口的实现类,详细要求如下: | |||||
| - a. 接口必须有@Contract注解; | |||||
| - b. 接口的可调用方法上必须有@ContractEvent注解,且每个注解中的name属性不能重复; | |||||
| - c. 合约方法支持入参和返回值,其主要包括所有基本类型; | |||||
| ### 4、合约案例 | |||||
| #### 4.1、代码实例 | |||||
| 以下是一个可创建银行账户,指定具体金额,并可以转账的合约代码(逻辑较简单,仅供参考): | |||||
| 合约接口代码如下: | |||||
| ```java | |||||
| package com.jd.chain.contract; | |||||
| @Contract | |||||
| public interface TransferContract { | |||||
| @ContractEvent(name = "create") | |||||
| String create(String address, String account, long money); | |||||
| @ContractEvent(name = "transfer") | |||||
| String transfer(String address, String from, String to, long money); | |||||
| @ContractEvent(name = "read") | |||||
| long read(String address, String account); | |||||
| @ContractEvent(name = "readAll") | |||||
| String readAll(String address, String account); | |||||
| } | |||||
| ``` | |||||
| 合约实现类代码如下: | |||||
| ```java | |||||
| package com.jd.chain.contract; | |||||
| import com.alibaba.fastjson.JSON; | |||||
| import com.jd.blockchain.crypto.HashDigest; | |||||
| import com.jd.blockchain.ledger.KVDataEntry; | |||||
| import com.jd.blockchain.ledger.KVDataVO; | |||||
| import com.jd.blockchain.ledger.KVInfoVO; | |||||
| public class TransferContractImpl implements EventProcessingAware, TransferContract { | |||||
| private ContractEventContext eventContext; | |||||
| private HashDigest ledgerHash; | |||||
| @Override | |||||
| public String create(String address, String account, long money) { | |||||
| KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, account); | |||||
| // 肯定有返回值,但若不存在则返回version=-1 | |||||
| if (kvDataEntries != null && kvDataEntries.length > 0) { | |||||
| long currVersion = kvDataEntries[0].getVersion(); | |||||
| if (currVersion > -1) { | |||||
| throw new IllegalStateException(String.format("%s -> %s already have created !!!", address, account)); | |||||
| } | |||||
| eventContext.getLedger().dataAccount(address).setInt64(account, money, -1L); | |||||
| } else { | |||||
| throw new IllegalStateException(String.format("Ledger[%s] inner Error !!!", ledgerHash.toBase58())); | |||||
| } | |||||
| return String.format("DataAccountAddress[%s] -> Create(By Contract Operation) Account = %s and Money = %s Success!!! \r\n", | |||||
| address, account, money); | |||||
| } | |||||
| @Override | |||||
| public String transfer(String address, String from, String to, long money) { | |||||
| // 首先查询余额 | |||||
| KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, from, to); | |||||
| if (kvDataEntries == null || kvDataEntries.length != 2) { | |||||
| throw new IllegalStateException(String.format("%s -> %s - %s may be not created !!!", address, from, to)); | |||||
| } else { | |||||
| // 判断from账号中钱数量是否足够 | |||||
| long fromMoney = 0L, toMoney = 0L, fromVersion = 0L, toVersion = 0L; | |||||
| for (KVDataEntry kvDataEntry : kvDataEntries) { | |||||
| if (kvDataEntry.getKey().equals(from)) { | |||||
| fromMoney = (long) kvDataEntry.getValue(); | |||||
| fromVersion = kvDataEntry.getVersion(); | |||||
| } else { | |||||
| toMoney = (long) kvDataEntry.getValue(); | |||||
| toVersion = kvDataEntry.getVersion(); | |||||
| } | |||||
| } | |||||
| if (fromMoney < money) { | |||||
| throw new IllegalStateException(String.format("%s -> %s not have enough money !!!", address, from)); | |||||
| } | |||||
| long fromNewMoney = fromMoney - money; | |||||
| long toNewMoney = toMoney + money; | |||||
| // 重新设置 | |||||
| eventContext.getLedger().dataAccount(address).setInt64(from, fromNewMoney, fromVersion); | |||||
| eventContext.getLedger().dataAccount(address).setInt64(to, toNewMoney, toVersion); | |||||
| } | |||||
| return String.format("DataAccountAddress[%s] transfer from [%s] to [%s] and [money = %s] Success !!!", address, from, to, money); | |||||
| } | |||||
| @Override | |||||
| public long read(String address, String account) { | |||||
| KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, account); | |||||
| if (kvDataEntries == null || kvDataEntries.length == 0) { | |||||
| return -1; | |||||
| } | |||||
| return (long)kvDataEntries[0].getValue(); | |||||
| } | |||||
| @Override | |||||
| public String readAll(String address, String account) { | |||||
| KVDataEntry[] kvDataEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, account); | |||||
| // 获取最新的版本号 | |||||
| if (kvDataEntries == null || kvDataEntries.length == 0) { | |||||
| return ""; | |||||
| } | |||||
| long newestVersion = kvDataEntries[0].getVersion(); | |||||
| if (newestVersion == -1) { | |||||
| return ""; | |||||
| } | |||||
| KVDataVO[] kvDataVOS = new KVDataVO[1]; | |||||
| long[] versions = new long[(int)newestVersion + 1]; | |||||
| for (int i = 0; i < versions.length; i++) { | |||||
| versions[i] = i; | |||||
| } | |||||
| KVDataVO kvDataVO = new KVDataVO(account, versions); | |||||
| kvDataVOS[0] = kvDataVO; | |||||
| KVInfoVO kvInfoVO = new KVInfoVO(kvDataVOS); | |||||
| KVDataEntry[] allEntries = eventContext.getLedger().getDataEntries(ledgerHash, address, kvInfoVO); | |||||
| return JSON.toJSONString(allEntries); | |||||
| } | |||||
| @Override | |||||
| public void beforeEvent(ContractEventContext eventContext) { | |||||
| this.eventContext = eventContext; | |||||
| this.ledgerHash = eventContext.getCurrentLedgerHash(); | |||||
| } | |||||
| @Override | |||||
| public void postEvent(ContractEventContext eventContext, Exception error) { | |||||
| } | |||||
| } | |||||
| ``` | |||||
| #### 4.2、pom.xml文件实例 | |||||
| ```xml | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <groupId>com.jd.chain</groupId> | |||||
| <version>1.0.0.RELEASE</version> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <artifactId>contract-samples</artifactId> | |||||
| <name>contract-samples</name> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>sdk-pack</artifactId> | |||||
| <version>1.0.0.RELEASE</version> | |||||
| <scope>provided</scope> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.alibaba</groupId> | |||||
| <artifactId>fastjson</artifactId> | |||||
| <version>1.2.32</version> | |||||
| <scope>provided</scope> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <build> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>contract-maven-plugin</artifactId> | |||||
| <version>1.0.0.RELEASE</version> | |||||
| <executions> | |||||
| <execution> | |||||
| <id>make-contract</id> | |||||
| <phase>package</phase> | |||||
| <goals> | |||||
| <goal>compile</goal> | |||||
| </goals> | |||||
| </execution> | |||||
| </executions> | |||||
| <configuration> | |||||
| <archive> | |||||
| <manifest> | |||||
| <mainClass>com.jd.chain.contract.TransferContractImpl</mainClass> | |||||
| </manifest> | |||||
| </archive> | |||||
| <finalName>contract</finalName> | |||||
| </configuration> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| </project> | |||||
| ``` | |||||
| @@ -1 +0,0 @@ | |||||
| 177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X | |||||
| @@ -1 +0,0 @@ | |||||
| endPsK36imXrY66pru6ttZ8dZ3TynWekmdqoM1K7ZRRoRBBiYVzM | |||||
| @@ -1 +0,0 @@ | |||||
| abc | |||||
| @@ -1,74 +0,0 @@ | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>contract-core</artifactId> | |||||
| <version>1.2.0-SNAPSHOT</version> | |||||
| </parent> | |||||
| <artifactId>contract-maven-plugin</artifactId> | |||||
| <packaging>maven-plugin</packaging> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>ledger-model</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>utils-common</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>sdk-client</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.jd.blockchain</groupId> | |||||
| <artifactId>tools-keygen</artifactId> | |||||
| <version>${project.version}</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.apache.maven</groupId> | |||||
| <artifactId>maven-plugin-api</artifactId> | |||||
| <version>3.3.9</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.apache.maven.plugin-tools</groupId> | |||||
| <artifactId>maven-plugin-annotations</artifactId> | |||||
| <version>3.6.0</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-assembly-plugin</artifactId> | |||||
| <version>2.6</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>org.ow2.asm</groupId> | |||||
| <artifactId>asm</artifactId> | |||||
| <version>5.0.4</version> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <build> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>org.apache.maven.plugins</groupId> | |||||
| <artifactId>maven-plugin-plugin</artifactId> | |||||
| <version>3.5</version> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| </project> | |||||
| @@ -1,19 +0,0 @@ | |||||
| 说明 | |||||
| 1.编译:mvn clean install | |||||
| 快速自测: | |||||
| 1.ContractRemoteAutoMojoTest类用于快速自测发布和执行,快速自测是在测试链的环境中发布和执行合约; | |||||
| 2.修改sys-contract.properties文件中的相关信息; | |||||
| 3.合约发布之后,会在控制台生成合约地址,待5秒钟之后,会执行此合约。sys-contract.properties的contractArgs参数可修改,查看其不同效果; | |||||
| ### | |||||
| contract's address=5SmEqUsnLY4APVfS32xYDpRPuz55Rsuupdt1 | |||||
| execute the contract,result=true | |||||
| exeContract(),SUCCESS | |||||
| ### | |||||
| 4.在peer节点的控制台可以看到输出的结果信息。 | |||||
| 通过maven插件中通过ContractAllAutoMojo做简单的编译、发布和执行测试,对应单元测试类ContractAllAutoMojoTest; | |||||
| @@ -1,178 +0,0 @@ | |||||
| package com.jd.blockchain; | |||||
| import java.io.File; | |||||
| import java.io.FileInputStream; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import com.jd.blockchain.binaryproto.DataContractRegistry; | |||||
| import com.jd.blockchain.crypto.HashDigest; | |||||
| import com.jd.blockchain.crypto.KeyGenUtils; | |||||
| import com.jd.blockchain.crypto.PrivKey; | |||||
| import com.jd.blockchain.crypto.PubKey; | |||||
| import com.jd.blockchain.ledger.*; | |||||
| import com.jd.blockchain.sdk.BlockchainService; | |||||
| import com.jd.blockchain.sdk.client.GatewayServiceFactory; | |||||
| import com.jd.blockchain.utils.Bytes; | |||||
| import com.jd.blockchain.utils.codec.Base58Utils; | |||||
| import com.jd.blockchain.utils.net.NetworkAddress; | |||||
| /** | |||||
| * @Author zhaogw | |||||
| * @Date 2018/11/2 10:18 | |||||
| */ | |||||
| public enum ContractDeployExeUtil { | |||||
| instance; | |||||
| private BlockchainService bcsrv; | |||||
| private Bytes contractAddress; | |||||
| public BlockchainKeypair getKeyPair(String pubPath, String prvPath, String rawPassword){ | |||||
| PubKey pub = null; | |||||
| PrivKey prv = null; | |||||
| try { | |||||
| prv = KeyGenUtils.readPrivKey(prvPath, KeyGenUtils.encodePassword(rawPassword)); | |||||
| pub = KeyGenUtils.readPubKey(pubPath); | |||||
| } catch (Exception e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| return new BlockchainKeypair(pub, prv); | |||||
| } | |||||
| public PubKey getPubKey(String pubPath){ | |||||
| PubKey pub = null; | |||||
| try { | |||||
| if(pubPath == null){ | |||||
| BlockchainKeypair contractKeyPair = BlockchainKeyGenerator.getInstance().generate(); | |||||
| pub = contractKeyPair.getPubKey(); | |||||
| }else { | |||||
| pub = KeyGenUtils.readPubKey(pubPath); | |||||
| } | |||||
| } catch (Exception e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| return pub; | |||||
| } | |||||
| public byte[] getChainCode(String path){ | |||||
| byte[] chainCode = null; | |||||
| File file = null; | |||||
| InputStream input = null; | |||||
| try { | |||||
| file = new File(path); | |||||
| input = new FileInputStream(file); | |||||
| chainCode = new byte[input.available()]; | |||||
| input.read(chainCode); | |||||
| } catch (IOException e) { | |||||
| e.printStackTrace(); | |||||
| } finally { | |||||
| try { | |||||
| if(input!=null){ | |||||
| input.close(); | |||||
| } | |||||
| } catch (IOException e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| } | |||||
| return chainCode; | |||||
| } | |||||
| private void register(){ | |||||
| 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(DataAccountKVSetOperation.class); | |||||
| DataContractRegistry.register(DataAccountKVSetOperation.KVWriteEntry.class); | |||||
| DataContractRegistry.register(Operation.class); | |||||
| DataContractRegistry.register(ContractCodeDeployOperation.class); | |||||
| DataContractRegistry.register(ContractEventSendOperation.class); | |||||
| DataContractRegistry.register(DataAccountRegisterOperation.class); | |||||
| DataContractRegistry.register(UserRegisterOperation.class); | |||||
| DataContractRegistry.register(ParticipantRegisterOperation.class); | |||||
| DataContractRegistry.register(ParticipantStateUpdateOperation.class); | |||||
| } | |||||
| public BlockchainService initBcsrv(String host, int port) { | |||||
| if(bcsrv!=null){ | |||||
| return bcsrv; | |||||
| } | |||||
| NetworkAddress addr = new NetworkAddress(host, port); | |||||
| GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(addr); | |||||
| bcsrv = gwsrvFact.getBlockchainService(); | |||||
| return bcsrv; | |||||
| } | |||||
| public boolean deploy(HashDigest ledgerHash, BlockchainIdentity contractIdentity, BlockchainKeypair ownerKey, byte[] chainCode){ | |||||
| register(); | |||||
| TransactionTemplate txTpl = bcsrv.newTransaction(ledgerHash); | |||||
| txTpl.contracts().deploy(contractIdentity, chainCode); | |||||
| PreparedTransaction ptx = txTpl.prepare(); | |||||
| ptx.sign(ownerKey); | |||||
| // 提交并等待共识返回; | |||||
| TransactionResponse txResp = ptx.commit(); | |||||
| // 验证结果; | |||||
| contractAddress = contractIdentity.getAddress(); | |||||
| this.setContractAddress(contractAddress); | |||||
| System.out.println("contract's address="+contractAddress); | |||||
| return txResp.isSuccess(); | |||||
| } | |||||
| public boolean deploy(String host, int port, HashDigest ledgerHash, BlockchainKeypair ownerKey, byte[] chainCode){ | |||||
| register(); | |||||
| BlockchainIdentity contractIdentity = BlockchainKeyGenerator.getInstance().generate().getIdentity(); | |||||
| initBcsrv(host,port); | |||||
| return deploy(ledgerHash, contractIdentity, ownerKey, chainCode); | |||||
| } | |||||
| // 根据用户指定的公钥生成合约地址 | |||||
| public boolean deploy(String host, int port, String ledger,String ownerPubPath, String ownerPrvPath, | |||||
| String ownerPassword, String chainCodePath,String pubPath){ | |||||
| PubKey pubKey = getPubKey(pubPath); | |||||
| BlockchainIdentity contractIdentity = new BlockchainIdentityData(pubKey); | |||||
| byte[] chainCode = getChainCode(chainCodePath); | |||||
| BlockchainKeypair ownerKey = getKeyPair(ownerPubPath, ownerPrvPath, ownerPassword); | |||||
| HashDigest ledgerHash = new HashDigest(Base58Utils.decode(ledger)); | |||||
| initBcsrv(host,port); | |||||
| return deploy(ledgerHash, contractIdentity, ownerKey, chainCode); | |||||
| } | |||||
| // 暂不支持从插件执行合约;此外,由于合约参数调用的格式发生变化,故此方法被废弃;by: huanghaiquan at 2019-04-30; | |||||
| // public boolean exeContract(String ledger,String ownerPubPath, String ownerPrvPath, | |||||
| // String ownerPassword,String event,String contractArgs){ | |||||
| // BlockchainKeypair ownerKey = getKeyPair(ownerPubPath, ownerPrvPath, ownerPassword); | |||||
| // HashDigest ledgerHash = new HashDigest(Base58Utils.decode(ledger)); | |||||
| // | |||||
| // // 定义交易,传输最简单的数字、字符串、提取合约中的地址; | |||||
| // TransactionTemplate txTpl = bcsrv.newTransaction(ledgerHash); | |||||
| // txTpl.contractEvents().send(getContractAddress(),event,contractArgs.getBytes()); | |||||
| // | |||||
| // // 签名; | |||||
| // PreparedTransaction ptx = txTpl.prepare(); | |||||
| // ptx.sign(ownerKey); | |||||
| // | |||||
| // // 提交并等待共识返回; | |||||
| // TransactionResponse txResp = ptx.commit(); | |||||
| // | |||||
| // // 验证结果; | |||||
| // return txResp.isSuccess(); | |||||
| // } | |||||
| public Bytes getContractAddress() { | |||||
| return contractAddress; | |||||
| } | |||||
| public void setContractAddress(Bytes contractAddress) { | |||||
| this.contractAddress = contractAddress; | |||||
| } | |||||
| } | |||||