├── .idea
├── GrepConsole.xml
├── PBFT.iml
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── compiler.xml
├── encodings.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── jarRepositories.xml
├── markdown-navigator-enh.xml
├── markdown-navigator.xml
├── misc.xml
├── uiDesigner.xml
├── vcs.xml
└── workspace.xml
├── PBFT.iml
├── README.md
├── imgs
├── image-20200616110901064.png
├── image-20200616113250619.png
├── image-20200616113601203.png
├── image-20200616113811834.png
├── image-20210115214021655.png
├── image-20210115214211047.png
└── image-20210115222620783.png
├── pom.xml
└── src
└── main
├── java
├── Main.java
└── cc
│ └── weno
│ ├── config
│ ├── AllNodeCommonMsg.java
│ └── StartConfig.java
│ ├── dao
│ ├── bean
│ │ ├── DbDao.java
│ │ └── ReplayJson.java
│ ├── node
│ │ ├── Node.java
│ │ ├── NodeAddress.java
│ │ └── NodeBasicInfo.java
│ └── pbft
│ │ ├── MsgCollection.java
│ │ ├── MsgType.java
│ │ └── PbftMsg.java
│ ├── p2p
│ ├── P2PConnectionMsg.java
│ ├── client
│ │ ├── ClientAction.java
│ │ ├── P2PClientLinstener.java
│ │ └── P2pClientAioHandler.java
│ ├── common
│ │ ├── Const.java
│ │ └── MsgPacket.java
│ └── server
│ │ ├── P2PServerAioHandler.java
│ │ ├── ServerAction.java
│ │ └── ServerListener.java
│ └── util
│ ├── ClientUtil.java
│ ├── DbUtil.java
│ ├── MsgUtil.java
│ ├── Pbft.java
│ ├── PbftUtil.java
│ └── StartPbft.java
└── resources
├── ip.json
└── log4j.properties
/.idea/GrepConsole.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/PBFT.iml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator-enh.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 | -
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 | -
24 |
25 |
26 |
27 |
28 |
29 | -
30 |
31 |
32 |
33 |
34 |
35 | -
36 |
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 |
56 | -
57 |
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 |
66 | -
67 |
68 |
69 |
70 |
71 | -
72 |
73 |
74 | -
75 |
76 |
77 |
78 |
79 | -
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
87 |
88 |
89 | -
90 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | -
103 |
104 |
105 | -
106 |
107 |
108 | -
109 |
110 |
111 | -
112 |
113 |
114 |
115 |
116 | -
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | 1579675202670
124 |
125 |
126 | 1579675202670
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 | 1579681270431
197 |
198 |
199 |
200 | 1579681270431
201 |
202 |
203 | 1581702207433
204 |
205 |
206 |
207 | 1581702207433
208 |
209 |
210 | 1581780567273
211 |
212 |
213 |
214 | 1581780567273
215 |
216 |
217 | 1582210700896
218 |
219 |
220 |
221 | 1582210700896
222 |
223 |
224 | 1582211173913
225 |
226 |
227 |
228 | 1582211173913
229 |
230 |
231 | 1582214449083
232 |
233 |
234 |
235 | 1582214449083
236 |
237 |
238 | 1582216123591
239 |
240 |
241 |
242 | 1582216123591
243 |
244 |
245 | 1582478171774
246 |
247 |
248 |
249 | 1582478171774
250 |
251 |
252 | 1582478782881
253 |
254 |
255 |
256 | 1582478782881
257 |
258 |
259 | 1582553422488
260 |
261 |
262 |
263 | 1582553422488
264 |
265 |
266 | 1582554750137
267 |
268 |
269 |
270 | 1582554750137
271 |
272 |
273 | 1582656417178
274 |
275 |
276 |
277 | 1582656417178
278 |
279 |
280 | 1582656600005
281 |
282 |
283 |
284 | 1582656600005
285 |
286 |
287 | 1610719821463
288 |
289 |
290 |
291 | 1610719821463
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
--------------------------------------------------------------------------------
/PBFT.iml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 关于代码介绍方法,可以去看我的[博客](https://www.cnblogs.com/xiaohuiduan/category/1635542.html)。里面介绍了一些功能的实现思路和方法,以及使用到的一些库的介绍。
4 |
5 | - [PBFT && RBFT算法流程](https://www.cnblogs.com/xiaohuiduan/p/12210891.html)
6 | - [t-io Java构建p2p网络](https://www.cnblogs.com/xiaohuiduan/p/12302024.html)
7 | - [PBFT算法java实现](https://www.cnblogs.com/xiaohuiduan/p/12339955.html)
8 | - [PBFT 算法 java实现(下)](https://www.cnblogs.com/xiaohuiduan/p/12359271.html)
9 |
10 |
11 |
12 | # 使用方法
13 |
14 |
15 |
16 | ## 打包
17 |
18 | 首先在maven中更改jar包生成位置
19 |
20 | 
21 |
22 | 然后使用命令生成jar包
23 |
24 | ```bash
25 | mvn package
26 | ```
27 |
28 | ## 运行方法
29 |
30 | 在**IDEA运行**着将第二块区域进行注释(第一块区域不要注释),使用jar包运行则将第一块区域进行注释(第二块区域不要注释)
31 |
32 | 
33 |
34 | ### 包运行方法
35 |
36 | ```bash
37 | java -jar 包名 ip地址 端口号 序号 文件保存位置
38 | ```
39 |
40 | - ip地址和端口号代表节点作为server需要占用ip和端口号
41 | - 序号:节点的序号,必须独一无二
42 | - 文件保存位置
43 |
44 | 例如:
45 |
46 | ```bash
47 | java -jar oldpbft-jar-with-dependencies.jar 127.0.0.1 8080 0 C:\\Users\\XiaoHui\\Desktop\\data\\
48 | ```
49 |
50 | 因此,你可以在本机上运行多个节点(保证端口号和序号不同即可)。
51 |
52 | ### 在IDEA中运行的方法
53 |
54 | 首先配置启动,允许多个main执行
55 |
56 | 
57 |
58 | 然后,每次启动一个节点,更改 `i` 就可以启动不同的节点。
59 |
60 | ```java
61 | int i = 0;
62 | String ip = "127.0.0.1";
63 | int port = 8080 + i;
64 | StartConfig.basePath = "C:\\Users\\XiaoHui\\Desktop\\data\\";
65 | int index = i;
66 | ```
67 |
68 | # 注意点
69 |
70 |
71 |
72 | 1. 程序会自动新建一个json文件,里面保存节点的ip信息,`StartConfig.basePath`代表json文件保存位置。
73 |
74 | 
75 |
76 |
77 |
78 |
79 |
80 | 2. 如果结束所有节点,然后重新启动程序,需要将`ip.json`中的内容全部删除。(比如说你启动了1节点,2节点,然后你关闭了这个程序,又想重新启动1节点2节点就必须删除),否则会报错,如下图所示:
81 |
82 | 
83 |
84 | 3. 只有主节点能够发送消息,其他节点会发送消息失败。如何想使用非主节点发送消息,可以去修改代码。如下图所示:将红框内的代码注释即可。
85 |
86 |
87 |
88 | # 完成功能
89 |
90 | 实际上代码完成的功能很少很少,就是完成了PBFT中的节点加入功能,然后还有消息发送PBFT认证功能,以及交易数据保存的功能**<只写了函数,需要自己去调用>**。其他的就emm没有做。本人对区块链也不是特别的了解,所以有些功能感觉实现起来的代码怪怪的。
91 |
92 | 这个仓库代码大概率不会进行更新了,因为emmmmmm,考研去了。
93 |
94 | 考研结束,重新更新一波。
--------------------------------------------------------------------------------
/imgs/image-20200616110901064.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaohuiduan/pbft/4b9008fb5089e80f90f7fbdcf5b3e5c1d981d114/imgs/image-20200616110901064.png
--------------------------------------------------------------------------------
/imgs/image-20200616113250619.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaohuiduan/pbft/4b9008fb5089e80f90f7fbdcf5b3e5c1d981d114/imgs/image-20200616113250619.png
--------------------------------------------------------------------------------
/imgs/image-20200616113601203.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaohuiduan/pbft/4b9008fb5089e80f90f7fbdcf5b3e5c1d981d114/imgs/image-20200616113601203.png
--------------------------------------------------------------------------------
/imgs/image-20200616113811834.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaohuiduan/pbft/4b9008fb5089e80f90f7fbdcf5b3e5c1d981d114/imgs/image-20200616113811834.png
--------------------------------------------------------------------------------
/imgs/image-20210115214021655.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaohuiduan/pbft/4b9008fb5089e80f90f7fbdcf5b3e5c1d981d114/imgs/image-20210115214021655.png
--------------------------------------------------------------------------------
/imgs/image-20210115214211047.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaohuiduan/pbft/4b9008fb5089e80f90f7fbdcf5b3e5c1d981d114/imgs/image-20210115214211047.png
--------------------------------------------------------------------------------
/imgs/image-20210115222620783.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaohuiduan/pbft/4b9008fb5089e80f90f7fbdcf5b3e5c1d981d114/imgs/image-20210115222620783.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.example
8 | PBFT
9 | 1.0-SNAPSHOT
10 |
11 | UTF-8
12 | UTF-8
13 | UTF-8
14 |
15 |
16 |
17 | oldpbft
18 |
19 |
20 |
21 | org.apache.maven.plugins
22 | maven-compiler-plugin
23 |
24 | 8
25 | 8
26 |
27 |
28 |
29 |
30 | org.apache.maven.plugins
31 | maven-assembly-plugin
32 | 2.5.5
33 |
34 |
35 |
36 | Main
37 |
38 |
39 |
40 | jar-with-dependencies
41 |
42 |
43 | C:\Users\XiaoHui\Desktop\windows
44 |
45 |
46 |
47 |
48 |
49 | make-assembly
50 | package
51 |
52 | single
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | org.iq80.leveldb
65 | leveldb
66 | 0.12
67 |
68 |
69 |
70 | org.iq80.leveldb
71 | leveldb-api
72 | 0.12
73 |
74 |
75 |
76 |
77 |
78 | cn.hutool
79 | hutool-all
80 | 5.0.7
81 |
82 |
83 |
84 | com.alibaba
85 | fastjson
86 | 1.2.62
87 |
88 |
89 |
90 |
91 | org.t-io
92 | tio-core
93 | 3.5.8.v20191228-RELEASE
94 |
95 |
96 |
97 |
98 | org.projectlombok
99 | lombok
100 | 1.18.10
101 | provided
102 |
103 |
104 | org.apache.logging.log4j
105 | log4j-core
106 | 2.17.1
107 |
108 |
109 | org.slf4j
110 | slf4j-log4j12
111 | 2.0.0-alpha1
112 |
113 |
114 |
115 |
116 | com.google.guava
117 | guava
118 | 29.0-jre
119 |
120 |
121 |
122 |
123 |
124 |
125 |
--------------------------------------------------------------------------------
/src/main/java/Main.java:
--------------------------------------------------------------------------------
1 | import cc.weno.config.StartConfig;
2 | import cc.weno.dao.node.Node;
3 | import cc.weno.dao.node.NodeAddress;
4 | import cc.weno.dao.pbft.MsgType;
5 | import cc.weno.dao.pbft.PbftMsg;
6 | import cc.weno.util.ClientUtil;
7 | import cc.weno.util.StartPbft;
8 | import lombok.extern.slf4j.Slf4j;
9 |
10 | import java.io.File;
11 | import java.io.IOException;
12 | import java.util.Scanner;
13 |
14 |
15 | /**
16 | * // _ooOoo_
17 | * // o8888888o
18 | * // 88" . "88
19 | * // (| -_- |)
20 | * // O\ = /O
21 | * // ____/`---'\____
22 | * // . ' \\| |// `.
23 | * // / \\||| : |||// \
24 | * // / _||||| -:- |||||- \
25 | * // | | \\\ - /// | |
26 | * // | \_| ''\---/'' | |
27 | * // \ .-\__ `-` ___/-. /
28 | * // ___`. .' /--.--\ `. . __
29 | * // ."" '< `.___\_<|>_/___.' >'"".
30 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
31 | * // \ \ `-. \_ __\ /__ _/ .-` / /
32 | * // ======`-.____`-.___\_____/___.-`____.-'======
33 | * // `=---='
34 | * //
35 | * // .............................................
36 | * // 佛祖镇楼 BUG辟易
37 | *
38 | * @author: xiaohuiduan
39 | * @data: 2020/1/22 下午2:46
40 | * @description: 程序运行开始类
41 | * 启动参数顺序:ip,port,index,认证请求消息
42 | */
43 | @Slf4j
44 | public class Main {
45 |
46 | public static void main(String[] args) {
47 |
48 | int i = 0;
49 | String ip = "127.0.0.1";
50 | int port = 8080 + i;
51 |
52 | StartConfig.basePath = "C:\\Users\\XiaoHui\\Desktop\\data\\";
53 | int index = i;
54 |
55 | // if (args.length != 4) {
56 | // log.error("参数错误");
57 | // return;
58 | // }
59 | //// 程序启动ip地址
60 | // String ip = args[0];
61 | //// 端口
62 | // int port = Integer.parseInt(args[1]);
63 | //// 程序启动index
64 | // int index = Integer.parseInt(args[2]);
65 | //// 文件保存位置,在文件保存位置必须存在一个oldIp.json的文件
66 | // StartConfig.basePath = args[3];
67 |
68 |
69 | createIpJsonFile(StartConfig.basePath);
70 |
71 | Node node = Node.getInstance();
72 | node.setIndex(index);
73 | NodeAddress nodeAddress = new NodeAddress();
74 | nodeAddress.setIp(ip);
75 | nodeAddress.setPort(port);
76 | node.setAddress(nodeAddress);
77 | StartPbft.start();
78 |
79 | // 可以在这里发送消息
80 | Scanner scanner = new Scanner(System.in);
81 | while (true) {
82 | String str = scanner.next();
83 | PbftMsg msg = new PbftMsg(MsgType.PRE_PREPARE, 0);
84 | msg.setBody(str);
85 | ClientUtil.prePrepare(msg);
86 | }
87 | }
88 |
89 | /**
90 | * 如果文件或者文件夹不存在则创建
91 | *
92 | * @param basePath
93 | */
94 | private static void createIpJsonFile(String basePath) {
95 | File dir = new File(basePath);
96 |
97 | // 如果目录不存在
98 | if (!dir.exists()) {
99 | dir.mkdirs();
100 | }
101 | File file = new File(basePath + "ip.json");
102 | if (!file.exists()) {
103 | try {
104 | file.createNewFile();
105 | } catch (IOException e) {
106 | log.error("json文件创建失败");
107 | e.printStackTrace();
108 | }
109 | }
110 | }
111 | }
112 |
113 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/config/AllNodeCommonMsg.java:
--------------------------------------------------------------------------------
1 | package cc.weno.config;
2 |
3 | import cc.weno.dao.node.NodeBasicInfo;
4 | import lombok.extern.slf4j.Slf4j;
5 |
6 | import java.util.Map;
7 | import java.util.concurrent.ConcurrentHashMap;
8 |
9 | /**
10 | * // _ooOoo_
11 | * // o8888888o
12 | * // 88" . "88
13 | * // (| -_- |)
14 | * // O\ = /O
15 | * // ____/`---'\____
16 | * // . ' \\| |// `.
17 | * // / \\||| : |||// \
18 | * // / _||||| -:- |||||- \
19 | * // | | \\\ - /// | |
20 | * // | \_| ''\---/'' | |
21 | * // \ .-\__ `-` ___/-. /
22 | * // ___`. .' /--.--\ `. . __
23 | * // ."" '< `.___\_<|>_/___.' >'"".
24 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
25 | * // \ \ `-. \_ __\ /__ _/ .-` / /
26 | * // ======`-.____`-.___\_____/___.-`____.-'======
27 | * // `=---='
28 | * //
29 | * // .............................................
30 | * // 佛祖镇楼 BUG辟易
31 | *
32 | * @author: xiaohuiduan
33 | * @data: 2020/1/22 下午3:27
34 | * @description: 所有的结点的需要相同的信息,(假如不相同,则需要同步)
35 | */
36 | @Slf4j
37 | public class AllNodeCommonMsg {
38 |
39 | /**
40 | * 获得最大失效结点的数量
41 | *
42 | * @return
43 | */
44 | public static int getMaxf() {
45 | return (getSize() - 1) / 3;
46 | }
47 |
48 | public static int getAgreeNum(){
49 | return 2 * AllNodeCommonMsg.getMaxf() + 1;
50 | }
51 |
52 | /**
53 | * 获得主节点的index序号
54 | *
55 | * @return
56 | */
57 | public static int getPriIndex() {
58 | return view % getSize();
59 | }
60 |
61 | /**
62 | * 保存结点对应的ip地址和端口号
63 | */
64 | public static ConcurrentHashMap allNodeAddressMap = new ConcurrentHashMap<>(2 << 10);
65 |
66 | /**
67 | * 所有节点公钥
68 | */
69 | public static Map publicKeyMap = new ConcurrentHashMap<>(2<<10);
70 |
71 |
72 | /**
73 | * view的值,0代表view未被初始化
74 | * 当前视图的编号,通过这个编号可以算出主节点的序号
75 | */
76 | public volatile static int view = 0;
77 |
78 | /**
79 | * @return 区块链中结点的总结点数
80 | */
81 | public static int getSize() {
82 | return allNodeAddressMap.size() + 1;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/config/StartConfig.java:
--------------------------------------------------------------------------------
1 | package cc.weno.config;
2 |
3 | import cn.hutool.core.io.file.FileReader;
4 | import com.alibaba.fastjson.JSON;
5 | import cc.weno.dao.bean.ReplayJson;
6 | import cc.weno.dao.node.Node;
7 | import cc.weno.dao.node.NodeAddress;
8 | import cc.weno.dao.node.NodeBasicInfo;
9 | import lombok.extern.slf4j.Slf4j;
10 | import org.tio.client.ClientChannelContext;
11 | import org.tio.server.ServerTioConfig;
12 | import org.tio.server.TioServer;
13 | import org.tio.server.intf.ServerAioHandler;
14 | import org.tio.server.intf.ServerAioListener;
15 | import cc.weno.p2p.P2PConnectionMsg;
16 | import cc.weno.p2p.common.Const;
17 | import cc.weno.p2p.server.P2PServerAioHandler;
18 | import cc.weno.p2p.server.ServerListener;
19 | import cc.weno.util.ClientUtil;
20 | import cc.weno.util.PbftUtil;
21 |
22 | import java.io.IOException;
23 | import java.util.List;
24 | import java.util.concurrent.ConcurrentHashMap;
25 |
26 | /**
27 | * // _ooOoo_
28 | * // o8888888o
29 | * // 88" . "88
30 | * // (| -_- |)
31 | * // O\ = /O
32 | * // ____/`---'\____
33 | * // . ' \\| |// `.
34 | * // / \\||| : |||// \
35 | * // / _||||| -:- |||||- \
36 | * // | | \\\ - /// | |
37 | * // | \_| ''\---/'' | |
38 | * // \ .-\__ `-` ___/-. /
39 | * // ___`. .' /--.--\ `. . __
40 | * // ."" '< `.___\_<|>_/___.' >'"".
41 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
42 | * // \ \ `-. \_ __\ /__ _/ .-` / /
43 | * // ======`-.____`-.___\_____/___.-`____.-'======
44 | * // `=---='
45 | * //
46 | * // .............................................
47 | * // 佛祖镇楼 BUG辟易
48 | *
49 | * @author: xiaohuiduan
50 | * @data: 2020/2/13 下午7:20
51 | * @description: 启动的时候需要进行的配置
52 | * 在这里会进行下面的操作:
53 | * 初始化网络链接 使用tio构建P2P网络
54 | */
55 | @Slf4j
56 | public class StartConfig {
57 | private Node node = Node.getInstance();
58 | public static String basePath;
59 |
60 | /**
61 | * 初始化操作
62 | *
63 | * @return 成功返回true,other false
64 | */
65 | public boolean startConfig() {
66 | return initAddress() && initServer() && initClient();
67 | }
68 |
69 | /**
70 | * 开启客户端
71 | *
72 | * @return
73 | */
74 | private boolean initClient() {
75 | P2PConnectionMsg.CLIENTS = new ConcurrentHashMap<>(AllNodeCommonMsg.allNodeAddressMap.size());
76 |
77 | // allNodeAddressMap保存了结点index和address信息。
78 | for (NodeBasicInfo basicInfo : AllNodeCommonMsg.allNodeAddressMap.values()) {
79 | NodeAddress address = basicInfo.getAddress();
80 | log.info(String.format("节点%d尝试链接%s", node.getIndex(), basicInfo));
81 | ClientChannelContext context = ClientUtil.clientConnect(address.getIp(), address.getPort());
82 | if (context != null) {
83 | // 将通道进行保存
84 | ClientUtil.addClient(basicInfo.getIndex(), context);
85 | } else {
86 | log.warn(String.format("结点%d-->%s:%d连接失败", basicInfo.getIndex(), address.getIp(), address.getPort()));
87 | }
88 | }
89 | log.info("client链接服务端的数量" + P2PConnectionMsg.CLIENTS.size());
90 | return true;
91 | }
92 |
93 |
94 | /**
95 | * 开启服务端
96 | *
97 | * @return
98 | */
99 | private boolean initServer() {
100 | // 处理消息
101 | ServerAioHandler handler = new P2PServerAioHandler();
102 | // 监听
103 | ServerAioListener listener = new ServerListener();
104 | // 配置
105 | ServerTioConfig config = new ServerTioConfig("服务端", handler, listener);
106 | // 设置timeout,如果一定时间内client没有消息发送过来,则断开与client的连接
107 | config.setHeartbeatTimeout(Const.TIMEOUT);
108 | TioServer tioServer = new TioServer(config);
109 | try {
110 | // 启动
111 | tioServer.start(node.getAddress().getIp(), node.getAddress().getPort());
112 | } catch (IOException e) {
113 | log.error("服务端启动错误!!" + e.getMessage());
114 | return false;
115 | }
116 | return true;
117 | }
118 |
119 | /**
120 | * 获得结点的ip地址
121 | *
122 | * @return 成功返回true
123 | */
124 | private boolean initAddress() {
125 | FileReader fileReader = new FileReader(PbftUtil.ipJsonPath);
126 | List ipJsonStr = fileReader.readLines();
127 | for (String s : ipJsonStr) {
128 | ReplayJson replayJson = JSON.parseObject(s, ReplayJson.class);
129 | NodeAddress nodeAddress = new NodeAddress();
130 | nodeAddress.setIp(replayJson.getIp());
131 | nodeAddress.setPort(replayJson.getPort());
132 | NodeBasicInfo nodeBasicInfo = new NodeBasicInfo();
133 | nodeBasicInfo.setAddress(nodeAddress);
134 | nodeBasicInfo.setIndex(replayJson.getIndex());
135 | AllNodeCommonMsg.allNodeAddressMap.put(replayJson.getIndex(), nodeBasicInfo);
136 | AllNodeCommonMsg.publicKeyMap.put(replayJson.getIndex(), replayJson.getPublicKey());
137 | }
138 |
139 | // 将自己节点信息写入文件
140 | if (AllNodeCommonMsg.allNodeAddressMap.values().size() < 3 && !AllNodeCommonMsg.allNodeAddressMap.containsKey(node.getIndex())) {
141 | PbftUtil.writeIpToFile(node);
142 | return true;
143 | }
144 | if (AllNodeCommonMsg.allNodeAddressMap.containsKey(node.getIndex())) {
145 | log.error("已经存在此节点");
146 | return false;
147 | }
148 | log.info(String.format("ip.json文件数量%s", ipJsonStr.size()));
149 | log.info(String.format("内存地址%s", AllNodeCommonMsg.allNodeAddressMap.values().size()));
150 | return AllNodeCommonMsg.allNodeAddressMap.values().size() == ipJsonStr.size();
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/dao/bean/DbDao.java:
--------------------------------------------------------------------------------
1 | package cc.weno.dao.bean;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 |
7 | /**
8 | * // _ooOoo_
9 | * // o8888888o
10 | * // 88" . "88
11 | * // (| -_- |)
12 | * // O\ = /O
13 | * // ____/`---'\____
14 | * // . ' \\| |// `.
15 | * // / \\||| : |||// \
16 | * // / _||||| -:- |||||- \
17 | * // | | \\\ - /// | |
18 | * // | \_| ''\---/'' | |
19 | * // \ .-\__ `-` ___/-. /
20 | * // ___`. .' /--.--\ `. . __
21 | * // ."" '< `.___\_<|>_/___.' >'"".
22 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
23 | * // \ \ `-. \_ __\ /__ _/ .-` / /
24 | * // ======`-.____`-.___\_____/___.-`____.-'======
25 | * // `=---='
26 | * //
27 | * // .............................................
28 | * // 佛祖镇楼 BUG辟易
29 | *
30 | * @author: xiaohuiduan
31 | * @data: 2020/3/1 上午12:05
32 | * @description: 讲结果认证保存在levelDB中
33 | */
34 | @Data
35 | public class DbDao implements Serializable {
36 | /**
37 | * PBFTmsg消息的时间
38 | */
39 | private long time;
40 | /**
41 | * 参与认证的节点序号
42 | */
43 | private int node;
44 | /**
45 | * 参与认证节点的publicKey
46 | */
47 | private String publicKey;
48 | /**
49 | * 认证结果的视图view
50 | */
51 | private int viewNum;
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/dao/bean/ReplayJson.java:
--------------------------------------------------------------------------------
1 | package cc.weno.dao.bean;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * // _ooOoo_
7 | * // o8888888o
8 | * // 88" . "88
9 | * // (| -_- |)
10 | * // O\ = /O
11 | * // ____/`---'\____
12 | * // . ' \\| |// `.
13 | * // / \\||| : |||// \
14 | * // / _||||| -:- |||||- \
15 | * // | | \\\ - /// | |
16 | * // | \_| ''\---/'' | |
17 | * // \ .-\__ `-` ___/-. /
18 | * // ___`. .' /--.--\ `. . __
19 | * // ."" '< `.___\_<|>_/___.' >'"".
20 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
21 | * // \ \ `-. \_ __\ /__ _/ .-` / /
22 | * // ======`-.____`-.___\_____/___.-`____.-'======
23 | * // `=---='
24 | * //
25 | * // .............................................
26 | * // 佛祖镇楼 BUG辟易
27 | *
28 | * @author: xiaohuiduan
29 | * @data: 2020/2/18 下午11:59
30 | * @description: ip json信息
31 | */
32 | @Data
33 | public class ReplayJson {
34 | private int index;
35 | private String ip;
36 | private int port;
37 | private String publicKey;
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/dao/node/Node.java:
--------------------------------------------------------------------------------
1 | package cc.weno.dao.node;
2 |
3 | import cc.weno.config.AllNodeCommonMsg;
4 | import cn.hutool.crypto.asymmetric.RSA;
5 | import lombok.Data;
6 |
7 | /**
8 | * // _ooOoo_
9 | * // o8888888o
10 | * // 88" . "88
11 | * // (| -_- |)
12 | * // O\ = /O
13 | * // ____/`---'\____
14 | * // . ' \\| |// `.
15 | * // / \\||| : |||// \
16 | * // / _||||| -:- |||||- \
17 | * // | | \\\ - /// | |
18 | * // | \_| ''\---/'' | |
19 | * // \ .-\__ `-` ___/-. /
20 | * // ___`. .' /--.--\ `. . __
21 | * // ."" '< `.___\_<|>_/___.' >'"".
22 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
23 | * // \ \ `-. \_ __\ /__ _/ .-` / /
24 | * // ======`-.____`-.___\_____/___.-`____.-'======
25 | * // `=---='
26 | * //
27 | * // .............................................
28 | * // 佛祖镇楼 BUG辟易
29 | *
30 | * @author: xiaohuiduan
31 | * @data: 2020/1/22 下午3:22
32 | * @description: 结点自身的信息
33 | */
34 | @Data
35 | public class Node extends NodeBasicInfo {
36 |
37 |
38 | private static Node node = new Node();
39 |
40 | /**
41 | * 单例设计模式
42 | *
43 | * @return
44 | */
45 | public static Node getInstance() {
46 | return node;
47 | }
48 |
49 | private Node() {
50 | RSA rsa = new RSA();
51 | this.setPrivateKey(rsa.getPrivateKeyBase64());
52 | this.setPublicKey(rsa.getPublicKeyBase64());
53 | }
54 |
55 |
56 | /**
57 | * 判断结点是否运行
58 | */
59 | private boolean isRun = false;
60 |
61 | /**
62 | * 视图状态,判断是否ok,
63 | */
64 | private volatile boolean viewOK;
65 |
66 | /**
67 | * 公钥
68 | */
69 | private String publicKey;
70 | /**
71 | * 私钥
72 | */
73 | private String privateKey;
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/dao/node/NodeAddress.java:
--------------------------------------------------------------------------------
1 | package cc.weno.dao.node;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * // _ooOoo_
7 | * // o8888888o
8 | * // 88" . "88
9 | * // (| -_- |)
10 | * // O\ = /O
11 | * // ____/`---'\____
12 | * // . ' \\| |// `.
13 | * // / \\||| : |||// \
14 | * // / _||||| -:- |||||- \
15 | * // | | \\\ - /// | |
16 | * // | \_| ''\---/'' | |
17 | * // \ .-\__ `-` ___/-. /
18 | * // ___`. .' /--.--\ `. . __
19 | * // ."" '< `.___\_<|>_/___.' >'"".
20 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
21 | * // \ \ `-. \_ __\ /__ _/ .-` / /
22 | * // ======`-.____`-.___\_____/___.-`____.-'======
23 | * // `=---='
24 | * //
25 | * // .............................................
26 | * // 佛祖镇楼 BUG辟易
27 | *
28 | * @author: xiaohuiduan
29 | * @data: 2020/1/22 下午4:04
30 | * @description: nodeAddress里面保存了结点的通信地址
31 | */
32 | @Data
33 | public class NodeAddress {
34 | /**
35 | * ip地址
36 | */
37 | private String ip;
38 | /**
39 | * 通信地址的端口号
40 | */
41 | private int port;
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/dao/node/NodeBasicInfo.java:
--------------------------------------------------------------------------------
1 | package cc.weno.dao.node;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * // _ooOoo_
7 | * // o8888888o
8 | * // 88" . "88
9 | * // (| -_- |)
10 | * // O\ = /O
11 | * // ____/`---'\____
12 | * // . ' \\| |// `.
13 | * // / \\||| : |||// \
14 | * // / _||||| -:- |||||- \
15 | * // | | \\\ - /// | |
16 | * // | \_| ''\---/'' | |
17 | * // \ .-\__ `-` ___/-. /
18 | * // ___`. .' /--.--\ `. . __
19 | * // ."" '< `.___\_<|>_/___.' >'"".
20 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
21 | * // \ \ `-. \_ __\ /__ _/ .-` / /
22 | * // ======`-.____`-.___\_____/___.-`____.-'======
23 | * // `=---='
24 | * //
25 | * // .............................................
26 | * // 佛祖镇楼 BUG辟易
27 | *
28 | * @author: xiaohuiduan
29 | * @data: 2020/2/13 下午8:08
30 | * @description: 结点节本信息
31 | */
32 | @Data
33 | public class NodeBasicInfo {
34 | /**
35 | * 结点地址的信息
36 | */
37 | private NodeAddress address;
38 | /**
39 | * 这个代表了结点的序号
40 | */
41 | private int index;
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/dao/pbft/MsgCollection.java:
--------------------------------------------------------------------------------
1 | package cc.weno.dao.pbft;
2 |
3 | import cc.weno.dao.bean.DbDao;
4 | import cn.hutool.db.Db;
5 | import com.google.common.collect.Sets;
6 | import com.google.common.util.concurrent.AtomicLongMap;
7 | import lombok.Data;
8 |
9 | import java.util.Set;
10 | import java.util.concurrent.BlockingQueue;
11 | import java.util.concurrent.CopyOnWriteArrayList;
12 | import java.util.concurrent.LinkedBlockingQueue;
13 | import java.util.concurrent.atomic.AtomicLong;
14 |
15 | /**
16 | * // _ooOoo_
17 | * // o8888888o
18 | * // 88" . "88
19 | * // (| -_- |)
20 | * // O\ = /O
21 | * // ____/`---'\____
22 | * // . ' \\| |// `.
23 | * // / \\||| : |||// \
24 | * // / _||||| -:- |||||- \
25 | * // | | \\\ - /// | |
26 | * // | \_| ''\---/'' | |
27 | * // \ .-\__ `-` ___/-. /
28 | * // ___`. .' /--.--\ `. . __
29 | * // ."" '< `.___\_<|>_/___.' >'"".
30 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
31 | * // \ \ `-. \_ __\ /__ _/ .-` / /
32 | * // ======`-.____`-.___\_____/___.-`____.-'======
33 | * // `=---='
34 | * //
35 | * // .............................................
36 | * // 佛祖镇楼 BUG辟易
37 | *
38 | * @author: xiaohuiduan
39 | * @data: 2020/2/14 上午10:54
40 | * @description: 这个是进行PBFT算法保存的消息
41 | * 使用单例模式进行设计
42 | */
43 | @Data
44 | public class MsgCollection {
45 |
46 | private static MsgCollection msgCollection = new MsgCollection();
47 |
48 | /**
49 | * 空的构造方法
50 | */
51 | private MsgCollection() {
52 | }
53 |
54 | public static MsgCollection getInstance() {
55 | return msgCollection;
56 | }
57 |
58 | /**
59 | * 进行处理的消息队列,don‘t care about what is , just get it !!
60 | */
61 | private BlockingQueue msgQueue = new LinkedBlockingQueue();
62 |
63 | /**
64 | * 这个是在初始化视图的时候会用到
65 | */
66 | private AtomicLongMap viewNumCount = AtomicLongMap.create();
67 |
68 | /**
69 | * 参与认证的节点
70 | */
71 | private CopyOnWriteArrayList dbDaos = new CopyOnWriteArrayList();
72 | /**
73 | * 不赞同视图的数量
74 | */
75 | private AtomicLong disagreeViewNum = new AtomicLong();
76 |
77 |
78 | /**
79 | * 预准备阶段
80 | */
81 | private Set votePrePrepare = Sets.newConcurrentHashSet();
82 |
83 | /**
84 | * 准备阶段
85 | */
86 | private AtomicLongMap agreePrepare = AtomicLongMap.create();
87 |
88 | /**
89 | * commit阶段
90 | */
91 | private AtomicLongMap agreeCommit = AtomicLongMap.create();
92 |
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/dao/pbft/MsgType.java:
--------------------------------------------------------------------------------
1 | package cc.weno.dao.pbft;
2 | /**
3 | * // _ooOoo_
4 | * // o8888888o
5 | * // 88" . "88
6 | * // (| -_- |)
7 | * // O\ = /O
8 | * // ____/`---'\____
9 | * // . ' \\| |// `.
10 | * // / \\||| : |||// \
11 | * // / _||||| -:- |||||- \
12 | * // | | \\\ - /// | |
13 | * // | \_| ''\---/'' | |
14 | * // \ .-\__ `-` ___/-. /
15 | * // ___`. .' /--.--\ `. . __
16 | * // ."" '< `.___\_<|>_/___.' >'"".
17 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
18 | * // \ \ `-. \_ __\ /__ _/ .-` / /
19 | * // ======`-.____`-.___\_____/___.-`____.-'======
20 | * // `=---='
21 | * //
22 | * // .............................................
23 | * // 佛祖镇楼 BUG辟易
24 | *
25 | * @author: xiaohuiduan
26 | * @data: 2020/2/14 上午11:35
27 | * @description: 消息类型
28 | */
29 | public class MsgType {
30 | /**
31 | * 请求视图
32 | */
33 | public static final int GET_VIEW = -1;
34 |
35 | /**
36 | * 变更视图
37 | */
38 | public static final int CHANGE_VIEW = 0;
39 |
40 | /**
41 | * 预准备阶段
42 | */
43 | public static final int PRE_PREPARE = 1;
44 |
45 | /**
46 | * 准备阶段
47 | */
48 | public static final int PREPARE = 2;
49 |
50 | /**
51 | * 提交阶段
52 | */
53 | public static final int COMMIT = 3;
54 |
55 | /**
56 | * ip消息回复回复阶段
57 | */
58 | public static final int CLIENT_REPLAY = 4;
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/dao/pbft/PbftMsg.java:
--------------------------------------------------------------------------------
1 | package cc.weno.dao.pbft;
2 |
3 | import cn.hutool.core.util.IdUtil;
4 | import cc.weno.config.AllNodeCommonMsg;
5 | import lombok.Data;
6 |
7 | import java.util.Objects;
8 |
9 | /**
10 | * // _ooOoo_
11 | * // o8888888o
12 | * // 88" . "88
13 | * // (| -_- |)
14 | * // O\ = /O
15 | * // ____/`---'\____
16 | * // . ' \\| |// `.
17 | * // / \\||| : |||// \
18 | * // / _||||| -:- |||||- \
19 | * // | | \\\ - /// | |
20 | * // | \_| ''\---/'' | |
21 | * // \ .-\__ `-` ___/-. /
22 | * // ___`. .' /--.--\ `. . __
23 | * // ."" '< `.___\_<|>_/___.' >'"".
24 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
25 | * // \ \ `-. \_ __\ /__ _/ .-` / /
26 | * // ======`-.____`-.___\_____/___.-`____.-'======
27 | * // `=---='
28 | * //
29 | * // .............................................
30 | * // 佛祖镇楼 BUG辟易
31 | *
32 | * @author: xiaohuiduan
33 | * @data: 2020/1/22 下午3:56
34 | * @description: 进行Pbft发送的消息、
35 | */
36 | @Data
37 | public class PbftMsg {
38 | /**
39 | * 消息类型
40 | */
41 | private int msgType;
42 |
43 | /**
44 | * 消息发起的结点编号
45 | */
46 | private int node;
47 |
48 | /**
49 | * 消息发送的目的地
50 | */
51 | private int toNode;
52 |
53 | /**
54 | * 消息时间戳
55 | */
56 | private long time;
57 |
58 | /**
59 | * 消息体
60 | */
61 | private String body;
62 |
63 | /**
64 | * 检测是否通过
65 | */
66 | private boolean isOk;
67 |
68 | /**
69 | * 结点视图
70 | */
71 | private int viewNum;
72 |
73 | /**
74 | * 使用UUID进行生成
75 | */
76 | private String id;
77 |
78 | /**
79 | * 消息的签名
80 | */
81 | private String sign;
82 |
83 | private PbftMsg() {
84 | }
85 |
86 | public PbftMsg(int msgType, int node) {
87 | this.msgType = msgType;
88 | this.node = node;
89 | this.time = System.currentTimeMillis();
90 | this.id = IdUtil.randomUUID();
91 | this.viewNum = AllNodeCommonMsg.view;
92 | }
93 |
94 |
95 | @Override
96 | public boolean equals(Object o) {
97 | if (this == o) {
98 | return true;
99 | }
100 | if (!(o instanceof PbftMsg)) {
101 | return false;
102 | }
103 | PbftMsg msg = (PbftMsg) o;
104 | return getMsgType() == msg.getMsgType() &&
105 | getNode() == msg.getNode() &&
106 | getToNode() == msg.getToNode() &&
107 | getTime() == msg.getTime() &&
108 | isOk() == msg.isOk() &&
109 | getViewNum() == msg.getViewNum() &&
110 | Objects.equals(getBody(), msg.getBody()) &&
111 | Objects.equals(getId(), msg.getId());
112 | }
113 |
114 | @Override
115 | public int hashCode() {
116 | return Objects.hash(getMsgType(), getBody(), getNode(), getToNode(), getTime(), getViewNum(), getId());
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/p2p/P2PConnectionMsg.java:
--------------------------------------------------------------------------------
1 | package cc.weno.p2p;
2 |
3 | import org.tio.client.ClientChannelContext;
4 |
5 | import java.util.Map;
6 |
7 | /**
8 | * // _ooOoo_
9 | * // o8888888o
10 | * // 88" . "88
11 | * // (| -_- |)
12 | * // O\ = /O
13 | * // ____/`---'\____
14 | * // . ' \\| |// `.
15 | * // / \\||| : |||// \
16 | * // / _||||| -:- |||||- \
17 | * // | | \\\ - /// | |
18 | * // | \_| ''\---/'' | |
19 | * // \ .-\__ `-` ___/-. /
20 | * // ___`. .' /--.--\ `. . __
21 | * // ."" '< `.___\_<|>_/___.' >'"".
22 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
23 | * // \ \ `-. \_ __\ /__ _/ .-` / /
24 | * // ======`-.____`-.___\_____/___.-`____.-'======
25 | * // `=---='
26 | * //
27 | * // .............................................
28 | * // 佛祖镇楼 BUG辟易
29 | *
30 | * @author: xiaohuiduan
31 | * @data: 2020/2/13 下午7:47
32 | * @description: p2p网络的连接信息
33 | * 这个里面有:
34 | * 1. 自己作为服务端所连接的client信息
35 | * 2. 自己作为客户端与server的上下文
36 | */
37 | public class P2PConnectionMsg {
38 |
39 | /**
40 | * 代表结点的client
41 | */
42 | public static Map CLIENTS;
43 |
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/p2p/client/ClientAction.java:
--------------------------------------------------------------------------------
1 | package cc.weno.p2p.client;
2 |
3 | import cc.weno.config.AllNodeCommonMsg;
4 | import cc.weno.dao.bean.DbDao;
5 | import cc.weno.dao.node.Node;
6 | import cc.weno.dao.pbft.MsgCollection;
7 | import cc.weno.dao.pbft.MsgType;
8 | import cc.weno.dao.pbft.PbftMsg;
9 | import cc.weno.util.*;
10 | import cn.hutool.db.Db;
11 | import lombok.extern.slf4j.Slf4j;
12 | import org.tio.core.ChannelContext;
13 |
14 | /**
15 | * // _ooOoo_
16 | * // o8888888o
17 | * // 88" . "88
18 | * // (| -_- |)
19 | * // O\ = /O
20 | * // ____/`---'\____
21 | * // . ' \\| |// `.
22 | * // / \\||| : |||// \
23 | * // / _||||| -:- |||||- \
24 | * // | | \\\ - /// | |
25 | * // | \_| ''\---/'' | |
26 | * // \ .-\__ `-` ___/-. /
27 | * // ___`. .' /--.--\ `. . __
28 | * // ."" '< `.___\_<|>_/___.' >'"".
29 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
30 | * // \ \ `-. \_ __\ /__ _/ .-` / /
31 | * // ======`-.____`-.___\_____/___.-`____.-'======
32 | * // `=---='
33 | * //
34 | * // .............................................
35 | * // 佛祖镇楼 BUG辟易
36 | *
37 | * @author: xiaohuiduan
38 | * @data: 2020/2/14 下午9:24
39 | * @description: 当client接受到消息时,会在这里进行处理
40 | */
41 | @Slf4j
42 | public class ClientAction {
43 | private MsgCollection collection = MsgCollection.getInstance();
44 | private Node node = Node.getInstance();
45 |
46 | /**
47 | * 使用单例设计模式
48 | */
49 | private static ClientAction action = new ClientAction();
50 |
51 | public static ClientAction getInstance() {
52 | return action;
53 | }
54 |
55 | private ClientAction() {
56 | }
57 |
58 | /**
59 | * 对消息进行处理
60 | *
61 | * @param channelContext
62 | */
63 | public void doAction(ChannelContext channelContext) {
64 | try {
65 | PbftMsg msg = collection.getMsgQueue().take();
66 |
67 | // 当视图还未好的时候,不处理非视图事务
68 | if (!node.isViewOK() && msg.getMsgType() != MsgType.GET_VIEW && msg.getMsgType() != MsgType.CHANGE_VIEW) {
69 | collection.getMsgQueue().put(msg);
70 | return;
71 | }
72 | switch (msg.getMsgType()) {
73 | case MsgType.GET_VIEW:
74 | getView(msg);
75 | break;
76 | case MsgType.CHANGE_VIEW:
77 | onChangeView(msg);
78 | break;
79 | case MsgType.PREPARE:
80 | prepare(msg);
81 | break;
82 | case MsgType.COMMIT:
83 | commit(msg);
84 | default:
85 | break;
86 | }
87 | } catch (InterruptedException e) {
88 | log.debug(String.format("消息队列take错误:%s", e.getMessage()));
89 | }
90 | }
91 |
92 | /**
93 | * 提交commit
94 | *
95 | * @param msg
96 | */
97 | private void commit(PbftMsg msg) {
98 | ClientUtil.clientPublish(msg);
99 | }
100 |
101 | /**
102 | * 向所有节点发送prepare消息
103 | *
104 | * @param msg
105 | */
106 | private void prepare(PbftMsg msg) {
107 | ClientUtil.clientPublish(msg);
108 | }
109 |
110 | /**
111 | * 发送重新选举的消息
112 | */
113 | private void onChangeView(PbftMsg msg) {
114 | // view进行加1处理
115 | int viewNum = AllNodeCommonMsg.view + 1;
116 | msg.setViewNum(viewNum);
117 | ClientUtil.clientPublish(msg);
118 | }
119 |
120 | /**
121 | * 获得view
122 | *
123 | * @param msg
124 | */
125 | synchronized private void getView(PbftMsg msg) {
126 | int fromNode = msg.getNode();
127 | if (node.isViewOK()) {
128 | return;
129 | }
130 |
131 | if (!MsgUtil.isRealMsg(msg) || !msg.isOk()) {
132 | long count = collection.getDisagreeViewNum().incrementAndGet();
133 | if (count >= AllNodeCommonMsg.getMaxf()) {
134 | log.error("视图获取失败");
135 | System.exit(0);
136 | }
137 | return;
138 | }
139 | // 将消息添加到list中
140 | // DbUtil.addDaotoList(fromNode,msg);
141 |
142 | long count = collection.getViewNumCount().incrementAndGet(msg.getViewNum());
143 | if (count >= AllNodeCommonMsg.getAgreeNum() && !node.isViewOK()) {
144 | // 将节点认证消息保存
145 | // DbUtil.save();
146 |
147 | collection.getViewNumCount().clear();
148 |
149 | node.setViewOK(true);
150 | AllNodeCommonMsg.view = msg.getViewNum();
151 | log.info("视图初始化完成OK");
152 | // 将节点写入文件
153 | PbftUtil.writeIpToFile(node);
154 | ClientUtil.publishIpPort(node.getIndex(), node.getAddress().getIp(), node.getAddress().getPort());
155 | }
156 | }
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/p2p/client/P2PClientLinstener.java:
--------------------------------------------------------------------------------
1 | package cc.weno.p2p.client;
2 |
3 | import cc.weno.config.AllNodeCommonMsg;
4 | import cc.weno.dao.node.Node;
5 | import cc.weno.dao.pbft.MsgCollection;
6 | import cc.weno.dao.pbft.MsgType;
7 | import cc.weno.dao.pbft.PbftMsg;
8 | import cn.hutool.core.util.RandomUtil;
9 | import lombok.extern.slf4j.Slf4j;
10 | import org.tio.client.intf.ClientAioListener;
11 | import org.tio.core.ChannelContext;
12 | import org.tio.core.intf.Packet;
13 | import cc.weno.p2p.P2PConnectionMsg;
14 |
15 | import java.util.concurrent.BlockingQueue;
16 |
17 | /**
18 | * // _ooOoo_
19 | * // o8888888o
20 | * // 88" . "88
21 | * // (| -_- |)
22 | * // O\ = /O
23 | * // ____/`---'\____
24 | * // . ' \\| |// `.
25 | * // / \\||| : |||// \
26 | * // / _||||| -:- |||||- \
27 | * // | | \\\ - /// | |
28 | * // | \_| ''\---/'' | |
29 | * // \ .-\__ `-` ___/-. /
30 | * // ___`. .' /--.--\ `. . __
31 | * // ."" '< `.___\_<|>_/___.' >'"".
32 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
33 | * // \ \ `-. \_ __\ /__ _/ .-` / /
34 | * // ======`-.____`-.___\_____/___.-`____.-'======
35 | * // `=---='
36 | * //
37 | * // .............................................
38 | * // 佛祖镇楼 BUG辟易
39 | *
40 | * @author: xiaohuiduan
41 | * @data: 2020/2/12 下午9:43
42 | * @description: 客户端监听器
43 | */
44 | @Slf4j
45 | public class P2PClientLinstener implements ClientAioListener {
46 |
47 | private ClientAction action = ClientAction.getInstance();
48 |
49 | private Node node = Node.getInstance();
50 |
51 | /**
52 | * 消息队列
53 | */
54 | private BlockingQueue msgQueue = MsgCollection.getInstance().getMsgQueue();
55 |
56 |
57 | /**
58 | * 建链后触发本方法,注:建链不一定成功,需要关注参数isConnected
59 | *
60 | * @param channelContext
61 | * @param isConnected 是否连接成功,true:表示连接成功,false:表示连接失败
62 | * @param isReconnect 是否是重连, true: 表示这是重新连接,false: 表示这是第一次连接
63 | * @throws Exception
64 | * @author: tanyaowu
65 | */
66 | @Override
67 | public void onAfterConnected(ChannelContext channelContext, boolean isConnected, boolean isReconnect) throws Exception {
68 | if (isReconnect) {
69 | log.warn(String.format("结点%重新连接服务端", channelContext));
70 | }
71 | if (isConnected) {
72 | log.info(String.format("结点%s连接服务端成功", channelContext));
73 | }else{
74 | log.warn(String.format("结点%s连接服务端%s失败", node.getIndex(),channelContext.getServerNode()));
75 | }
76 | }
77 |
78 |
79 | /**
80 | * 原方法名:onAfterDecoded
81 | * 解码成功后触发本方法
82 | *
83 | * @param channelContext
84 | * @param packet
85 | * @param packetSize
86 | * @throws Exception
87 | * @author: tanyaowu
88 | */
89 | @Override
90 | public void onAfterDecoded(ChannelContext channelContext, Packet packet, int packetSize) throws Exception {
91 |
92 | }
93 |
94 | /**
95 | * 接收到TCP层传过来的数据后
96 | *
97 | * @param channelContext
98 | * @param receivedBytes 本次接收了多少字节
99 | * @throws Exception
100 | */
101 | @Override
102 | public void onAfterReceivedBytes(ChannelContext channelContext, int receivedBytes) throws Exception {
103 |
104 | }
105 |
106 | /**
107 | * 消息包发送之后触发本方法
108 | *
109 | * @param channelContext
110 | * @param packet
111 | * @param isSentSuccess true:发送成功,false:发送失败
112 | * @throws Exception
113 | * @author tanyaowu
114 | */
115 | @Override
116 | public void onAfterSent(ChannelContext channelContext, Packet packet, boolean isSentSuccess) throws Exception {
117 |
118 | }
119 |
120 | /**
121 | * 当处理好消息是就行action
122 | * @param channelContext
123 | * @param packet
124 | * @param cost 本次处理消息耗时,单位:毫秒
125 | * @throws Exception
126 | */
127 | @Override
128 | public void onAfterHandled(ChannelContext channelContext, Packet packet, long cost) throws Exception {
129 | action.doAction(channelContext);
130 | }
131 |
132 | /**
133 | * 连接关闭前触发本方法
134 | *
135 | * @param channelContext the channelcontext
136 | * @param throwable the throwable 有可能为空
137 | * @param remark the remark 有可能为空
138 | * @param isRemove
139 | * @throws Exception
140 | * @author tanyaowu
141 | */
142 | @Override
143 | public void onBeforeClose(final ChannelContext channelContext, Throwable throwable, String remark, boolean isRemove) throws Exception {
144 | log.warn(String.format("客户端%s连接关闭", channelContext));
145 | // 假如连接中断则移除
146 | P2PConnectionMsg.CLIENTS.values().removeIf(v -> v.equals(channelContext));
147 |
148 | /**
149 | * 假如中断的是主结点
150 | */
151 | if (channelContext.equals(P2PConnectionMsg.CLIENTS.get(AllNodeCommonMsg.getPriIndex()))){
152 | log.warn("主节点链接失败,决定发起视图选举");
153 | node.setViewOK(false);
154 | PbftMsg msg = new PbftMsg(MsgType.CHANGE_VIEW,node.getIndex());
155 | msgQueue.put(msg);
156 | action.doAction(channelContext);
157 | }
158 |
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/p2p/client/P2pClientAioHandler.java:
--------------------------------------------------------------------------------
1 | package cc.weno.p2p.client;
2 |
3 | import cc.weno.dao.pbft.MsgType;
4 | import cn.hutool.core.util.RandomUtil;
5 | import com.alibaba.fastjson.JSON;
6 | import cc.weno.dao.pbft.MsgCollection;
7 | import cc.weno.util.MsgUtil;
8 | import cc.weno.dao.pbft.PbftMsg;
9 | import lombok.extern.slf4j.Slf4j;
10 | import org.tio.client.intf.ClientAioHandler;
11 | import org.tio.core.ChannelContext;
12 | import org.tio.core.TioConfig;
13 | import org.tio.core.exception.AioDecodeException;
14 | import org.tio.core.intf.Packet;
15 | import cc.weno.p2p.common.MsgPacket;
16 |
17 | import java.nio.ByteBuffer;
18 | import java.util.concurrent.BlockingQueue;
19 |
20 | /**
21 | * // _ooOoo_
22 | * // o8888888o
23 | * // 88" . "88
24 | * // (| -_- |)
25 | * // O\ = /O
26 | * // ____/`---'\____
27 | * // . ' \\| |// `.
28 | * // / \\||| : |||// \
29 | * // / _||||| -:- |||||- \
30 | * // | | \\\ - /// | |
31 | * // | \_| ''\---/'' | |
32 | * // \ .-\__ `-` ___/-. /
33 | * // ___`. .' /--.--\ `. . __
34 | * // ."" '< `.___\_<|>_/___.' >'"".
35 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
36 | * // \ \ `-. \_ __\ /__ _/ .-` / /
37 | * // ======`-.____`-.___\_____/___.-`____.-'======
38 | * // `=---='
39 | * //
40 | * // .............................................
41 | * // 佛祖镇楼 BUG辟易
42 | *
43 | * @author: xiaohuiduan
44 | * @data: 2020/2/12 下午6:08
45 | * @description: 客户端处理
46 | */
47 | @Slf4j
48 | public class P2pClientAioHandler implements ClientAioHandler {
49 | /**
50 | * this is heart packet,目的是告诉服务器端我存在
51 | */
52 | private static MsgPacket heartPacket = new MsgPacket();
53 | /**
54 | * 消息队列
55 | */
56 | private BlockingQueue msgQueue = MsgCollection.getInstance().getMsgQueue();
57 |
58 | /**
59 | * 创建心跳包
60 | *
61 | * @param channelContext
62 | * @return
63 | * @author tanyaowu
64 | */
65 | @Override
66 | public Packet heartbeatPacket(ChannelContext channelContext) {
67 | return heartPacket;
68 | }
69 |
70 | /**
71 | * 根据ByteBuffer解码成业务需要的Packet对象.
72 | * 如果收到的数据不全,导致解码失败,请返回null,在下次消息来时框架层会自动续上前面的收到的数据
73 | *
74 | * @param buffer 参与本次希望解码的ByteBuffer
75 | * @param limit ByteBuffer的limit
76 | * @param position ByteBuffer的position,不一定是0哦
77 | * @param readableLength ByteBuffer参与本次解码的有效数据(= limit - position)
78 | * @param channelContext
79 | * @return
80 | * @throws AioDecodeException
81 | */
82 | @Override
83 | public Packet decode(ByteBuffer buffer, int limit, int position, int readableLength, ChannelContext channelContext) throws AioDecodeException {
84 |
85 |
86 | if (readableLength < MsgPacket.HEADER_LENGHT) {
87 | return null;
88 | }
89 |
90 | int bodyLength = buffer.getInt();
91 | if (bodyLength < 0) {
92 | throw new AioDecodeException("body length is invalid.romote: " + channelContext.getServerNode());
93 | }
94 |
95 | int usefulLength = MsgPacket.HEADER_LENGHT + bodyLength;
96 |
97 | if (usefulLength > readableLength) {
98 | return null;
99 | } else {
100 | MsgPacket packet = new MsgPacket();
101 | byte[] body = new byte[bodyLength];
102 | buffer.get(body);
103 | packet.setBody(body);
104 | return packet;
105 | }
106 |
107 | }
108 |
109 | /**
110 | * 编码
111 | *
112 | * @param packet
113 | * @param tioConfig
114 | * @param channelContext
115 | * @return
116 | * @author: tanyaowu
117 | */
118 | @Override
119 | public ByteBuffer encode(Packet packet, TioConfig tioConfig, ChannelContext channelContext) {
120 | MsgPacket msgPacket = (MsgPacket) packet;
121 | byte[] body = msgPacket.getBody();
122 |
123 | int bodyLength = 0;
124 |
125 | if (body != null) {
126 | bodyLength = body.length;
127 | }
128 |
129 | int len = MsgPacket.HEADER_LENGHT + bodyLength;
130 |
131 | ByteBuffer byteBuffer = ByteBuffer.allocate(len);
132 | byteBuffer.order(tioConfig.getByteOrder());
133 | byteBuffer.putInt(bodyLength);
134 |
135 | if (body != null) {
136 | byteBuffer.put(body);
137 | }
138 | return byteBuffer;
139 | }
140 |
141 | /**
142 | * 处理消息包
143 | *
144 | * @param packet
145 | * @param channelContext
146 | * @throws Exception
147 | * @author: tanyaowu
148 | */
149 | @Override
150 | public void handler(Packet packet, ChannelContext channelContext) throws Exception {
151 | MsgPacket msgPacket = (MsgPacket) packet;
152 | byte[] body = msgPacket.getBody();
153 | // 空的很可能为心跳包
154 | if (body == null) {
155 | return;
156 | }
157 |
158 | String msg = new String(body, MsgPacket.CHARSET);
159 | // 如果数据不是json数据,代表数据有问题
160 | if (!JSON.isValid(msg)) {
161 | return;
162 | }
163 | PbftMsg pbftMsg = JSON.parseObject(msg, PbftMsg.class);
164 | if (pbftMsg == null) {
165 | log.error("客户端将Json数据解析成pbft数据失败");
166 | return;
167 | }
168 |
169 | if (pbftMsg.getMsgType() != MsgType.GET_VIEW && !MsgUtil.afterMsg(pbftMsg)) {
170 | log.warn("数据检查签名或者解密失败");
171 | return;
172 | }
173 | this.msgQueue.put(pbftMsg);
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/p2p/common/Const.java:
--------------------------------------------------------------------------------
1 | package cc.weno.p2p.common;
2 |
3 | /**
4 | * // _ooOoo_
5 | * // o8888888o
6 | * // 88" . "88
7 | * // (| -_- |)
8 | * // O\ = /O
9 | * // ____/`---'\____
10 | * // . ' \\| |// `.
11 | * // / \\||| : |||// \
12 | * // / _||||| -:- |||||- \
13 | * // | | \\\ - /// | |
14 | * // | \_| ''\---/'' | |
15 | * // \ .-\__ `-` ___/-. /
16 | * // ___`. .' /--.--\ `. . __
17 | * // ."" '< `.___\_<|>_/___.' >'"".
18 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
19 | * // \ \ `-. \_ __\ /__ _/ .-` / /
20 | * // ======`-.____`-.___\_____/___.-`____.-'======
21 | * // `=---='
22 | * //
23 | * // .............................................
24 | * // 佛祖镇楼 BUG辟易
25 | *
26 | * @author: xiaohuiduan
27 | * @data: 2020/2/12 上午12:07
28 | * @description: 常量
29 | */
30 | public interface Const {
31 | /**
32 | * 服务器地址
33 | */
34 | public static final String SERVER = "127.0.0.1";
35 |
36 | /**
37 | * 监听端口
38 | */
39 | public static final int PORT = 8080;
40 |
41 | /**
42 | * 心跳超时时间
43 | */
44 | public static final int TIMEOUT = 1000 * 60;
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/p2p/common/MsgPacket.java:
--------------------------------------------------------------------------------
1 | package cc.weno.p2p.common;
2 |
3 | import org.tio.core.intf.Packet;
4 |
5 | /**
6 | * // _ooOoo_
7 | * // o8888888o
8 | * // 88" . "88
9 | * // (| -_- |)
10 | * // O\ = /O
11 | * // ____/`---'\____
12 | * // . ' \\| |// `.
13 | * // / \\||| : |||// \
14 | * // / _||||| -:- |||||- \
15 | * // | | \\\ - /// | |
16 | * // | \_| ''\---/'' | |
17 | * // \ .-\__ `-` ___/-. /
18 | * // ___`. .' /--.--\ `. . __
19 | * // ."" '< `.___\_<|>_/___.' >'"".
20 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
21 | * // \ \ `-. \_ __\ /__ _/ .-` / /
22 | * // ======`-.____`-.___\_____/___.-`____.-'======
23 | * // `=---='
24 | * //
25 | * // .............................................
26 | * // 佛祖镇楼 BUG辟易
27 | *
28 | * @author: xiaohuiduan
29 | * @data: 2020/2/12 上午12:06
30 | * @description: Packet包
31 | */
32 | public class MsgPacket extends Packet {
33 |
34 | private static final long serialVersionUID = -172060606924066412L;
35 | /**
36 | * 消息头的长度
37 | */
38 | public static final int HEADER_LENGHT = 4;
39 | public static final String CHARSET = "utf-8";
40 | private byte[] body;
41 |
42 | public byte[] getBody() {
43 | return body;
44 | }
45 |
46 | public void setBody(byte[] body) {
47 | this.body = body;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/p2p/server/P2PServerAioHandler.java:
--------------------------------------------------------------------------------
1 | package cc.weno.p2p.server;
2 |
3 | import cn.hutool.core.util.RandomUtil;
4 | import com.alibaba.fastjson.JSON;
5 | import cc.weno.dao.pbft.MsgType;
6 | import cc.weno.util.MsgUtil;
7 | import cc.weno.dao.pbft.PbftMsg;
8 | import lombok.extern.slf4j.Slf4j;
9 | import org.tio.core.ChannelContext;
10 | import org.tio.core.TioConfig;
11 | import org.tio.core.exception.AioDecodeException;
12 | import org.tio.core.intf.Packet;
13 | import org.tio.server.intf.ServerAioHandler;
14 | import cc.weno.p2p.common.MsgPacket;
15 |
16 | import java.nio.ByteBuffer;
17 |
18 | /**
19 | * // _ooOoo_
20 | * // o8888888o
21 | * // 88" . "88
22 | * // (| -_- |)
23 | * // O\ = /O
24 | * // ____/`---'\____
25 | * // . ' \\| |// `.
26 | * // / \\||| : |||// \
27 | * // / _||||| -:- |||||- \
28 | * // | | \\\ - /// | |
29 | * // | \_| ''\---/'' | |
30 | * // \ .-\__ `-` ___/-. /
31 | * // ___`. .' /--.--\ `. . __
32 | * // ."" '< `.___\_<|>_/___.' >'"".
33 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
34 | * // \ \ `-. \_ __\ /__ _/ .-` / /
35 | * // ======`-.____`-.___\_____/___.-`____.-'======
36 | * // `=---='
37 | * //
38 | * // .............................................
39 | * // 佛祖镇楼 BUG辟易
40 | *
41 | * @author: xiaohuiduan
42 | * @data: 2020/2/12 上午12:09
43 | * @description: 服务端的Handler
44 | */
45 | @Slf4j
46 | public class P2PServerAioHandler implements ServerAioHandler {
47 |
48 | private ServerAction action = ServerAction.getInstance();
49 |
50 | /**
51 | * 根据ByteBuffer解码成业务需要的Packet对象.
52 | * 如果收到的数据不全,导致解码失败,请返回null,在下次消息来时框架层会自动续上前面的收到的数据
53 | *
54 | * @param buffer 参与本次希望解码的ByteBuffer
55 | * @param limit ByteBuffer的limit
56 | * @param position ByteBuffer的position,不一定是0哦
57 | * @param readableLength ByteBuffer参与本次解码的有效数据(= limit - position)
58 | * @param channelContext
59 | * @return
60 | * @throws AioDecodeException
61 | */
62 | @Override
63 | public Packet decode(ByteBuffer buffer, int limit, int position, int readableLength, ChannelContext channelContext) throws AioDecodeException {
64 | // 假如包的长度小于基本长度,毋庸置疑,包没有接收完
65 | if (readableLength < MsgPacket.HEADER_LENGHT) {
66 | return null;
67 | }
68 | // 读取发送消息的长度
69 | int bodyLength = buffer.getInt();
70 |
71 | //数据不正确,则抛出AioDecodeException异常
72 | if (bodyLength < 0) {
73 | throw new AioDecodeException("bodyLength [" + bodyLength + "] is not right, remote:" + channelContext.getClientNode());
74 | }
75 |
76 | // 客户端发送消息的长度
77 | int neededLength = MsgPacket.HEADER_LENGHT + bodyLength;
78 |
79 | // 不够消息体长度(剩下的buffe组不了消息体)
80 | if (readableLength < neededLength) {
81 | return null;
82 | } else { //组包成功
83 | MsgPacket imPacket = new MsgPacket();
84 | if (bodyLength > 0) {
85 | byte[] dst = new byte[bodyLength];
86 | buffer.get(dst);
87 | imPacket.setBody(dst);
88 | }
89 | return imPacket;
90 | }
91 | }
92 |
93 | /**
94 | * 编码
95 | *
96 | * @param packet
97 | * @param tioConfig
98 | * @param channelContext
99 | * @return
100 | * @author: xiaohuiduan
101 | */
102 | @Override
103 | public ByteBuffer encode(Packet packet, TioConfig tioConfig, ChannelContext channelContext) {
104 | MsgPacket msgPacket = (MsgPacket) packet;
105 | byte[] body = msgPacket.getBody();
106 | int bodyLen = 0;
107 |
108 | if (body != null) {
109 | bodyLen = body.length;
110 | }
111 |
112 | //bytebuffer的总长度是 = 消息头的长度 + 消息体的长度
113 | int allLen = MsgPacket.HEADER_LENGHT + bodyLen;
114 | //创建一个新的bytebuffer
115 | ByteBuffer buffer = ByteBuffer.allocate(allLen);
116 |
117 | //设置字节序
118 | buffer.order(tioConfig.getByteOrder());
119 |
120 | //写入消息头----消息头的内容就是消息体的长度
121 | buffer.putInt(bodyLen);
122 |
123 | //写入消息体
124 | if (body != null) {
125 | buffer.put(body);
126 | }
127 | return buffer;
128 | }
129 |
130 | /**
131 | * 处理消息包
132 | *
133 | * @param packet
134 | * @param channelContext
135 | * @throws Exception
136 | * @author: xiaohuiduan
137 | */
138 | @Override
139 | public void handler(Packet packet, ChannelContext channelContext) throws Exception {
140 |
141 | MsgPacket msgPacket = (MsgPacket) packet;
142 | byte[] body = msgPacket.getBody();
143 | // 空的很可能为心跳包
144 | if (body == null) {
145 | return;
146 | }
147 |
148 | String msg = new String(body, MsgPacket.CHARSET);
149 | // 如果数据不是json数据,代表数据有问题
150 | if (!JSON.isValid(msg)) {
151 | return;
152 | }
153 | log.info("服务端接受消息:" + msg);
154 | PbftMsg pbftMsg = JSON.parseObject(msg, PbftMsg.class);
155 | if (pbftMsg == null) {
156 | log.error("客户端将Json数据解析成pbft数据失败");
157 | return;
158 | }
159 |
160 | if ((pbftMsg.getMsgType() != MsgType.CLIENT_REPLAY && pbftMsg.getMsgType() != MsgType.GET_VIEW) && !MsgUtil.afterMsg(pbftMsg)) {
161 | log.warn("数据检查签名或者解密失败");
162 | return;
163 | }
164 | // 服务端对消息进行处理
165 | action.doAction(channelContext, pbftMsg);
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/p2p/server/ServerAction.java:
--------------------------------------------------------------------------------
1 | package cc.weno.p2p.server;
2 |
3 | import cc.weno.config.AllNodeCommonMsg;
4 | import cc.weno.dao.bean.ReplayJson;
5 | import cc.weno.dao.node.Node;
6 | import cc.weno.dao.node.NodeAddress;
7 | import cc.weno.dao.node.NodeBasicInfo;
8 | import cc.weno.dao.pbft.MsgCollection;
9 | import cc.weno.dao.pbft.MsgType;
10 | import cc.weno.dao.pbft.PbftMsg;
11 | import cc.weno.p2p.client.ClientAction;
12 | import cc.weno.p2p.common.MsgPacket;
13 | import cc.weno.util.ClientUtil;
14 | import cc.weno.util.MsgUtil;
15 | import cc.weno.util.PbftUtil;
16 | import com.alibaba.fastjson.JSON;
17 | import lombok.extern.slf4j.Slf4j;
18 | import org.tio.client.ClientChannelContext;
19 | import org.tio.core.ChannelContext;
20 | import org.tio.core.Tio;
21 |
22 | import java.io.UnsupportedEncodingException;
23 |
24 |
25 | /**
26 | * // _ooOoo_
27 | * // o8888888o
28 | * // 88" . "88
29 | * // (| -_- |)
30 | * // O\ = /O
31 | * // ____/`---'\____
32 | * // . ' \\| |// `.
33 | * // / \\||| : |||// \
34 | * // / _||||| -:- |||||- \
35 | * // | | \\\ - /// | |
36 | * // | \_| ''\---/'' | |
37 | * // \ .-\__ `-` ___/-. /
38 | * // ___`. .' /--.--\ `. . __
39 | * // ."" '< `.___\_<|>_/___.' >'"".
40 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
41 | * // \ \ `-. \_ __\ /__ _/ .-` / /
42 | * // ======`-.____`-.___\_____/___.-`____.-'======
43 | * // `=---='
44 | * //
45 | * // .............................................
46 | * // 佛祖镇楼 BUG辟易
47 | *
48 | * @author: xiaohuiduan
49 | * @data: 2020/2/14 下午10:07
50 | * @description: 服务端的Action
51 | */
52 | @Slf4j
53 | public class ServerAction {
54 | private Node node = Node.getInstance();
55 | private MsgCollection msgCollection = MsgCollection.getInstance();
56 | /**
57 | * 单例模式构建action
58 | */
59 | private static ServerAction action = new ServerAction();
60 |
61 | private MsgCollection collection = MsgCollection.getInstance();
62 |
63 | public static ServerAction getInstance() {
64 | return action;
65 | }
66 |
67 | private ServerAction() {
68 | }
69 |
70 |
71 | /**
72 | * 对PBFT消息做出回应
73 | *
74 | * @param channelContext 谁发送的请求
75 | * @param msg 消息内容
76 | */
77 | public void doAction(ChannelContext channelContext, PbftMsg msg) {
78 | switch (msg.getMsgType()) {
79 | case MsgType.GET_VIEW:
80 | onGetView(channelContext, msg);
81 | break;
82 | case MsgType.CHANGE_VIEW:
83 | changeView(channelContext, msg);
84 | break;
85 | case MsgType.PRE_PREPARE:
86 | prePrepare(msg);
87 | break;
88 | case MsgType.PREPARE:
89 | prepare(msg);
90 | break;
91 | case MsgType.COMMIT:
92 | commit(msg);
93 | case MsgType.CLIENT_REPLAY:
94 | addClient(msg);
95 | break;
96 | default:
97 | break;
98 | }
99 | }
100 |
101 | /**
102 | * commit阶段
103 | *
104 | * @param msg
105 | */
106 | private void commit(PbftMsg msg) {
107 |
108 | long count = collection.getAgreeCommit().incrementAndGet(msg.getId());
109 |
110 | log.info(String.format("server接受到commit消息:%s", msg));
111 | if (count >= AllNodeCommonMsg.getAgreeNum()) {
112 | log.info("数据符合,commit成功,数据可以生成块");
113 | collection.getAgreeCommit().remove(msg.getId());
114 | PbftUtil.save(msg);
115 | }
116 | }
117 |
118 | /**
119 | * 节点将prepare消息进行广播然后被接收到
120 | *
121 | * @param msg
122 | */
123 | private void prepare(PbftMsg msg) {
124 | log.info(msgCollection.getVotePrePrepare().contains(msg) + ">>>>");
125 | if (!msgCollection.getVotePrePrepare().contains(msg.getId()) || !PbftUtil.checkMsg(msg)) {
126 | return;
127 | }
128 |
129 | long count = collection.getAgreePrepare().incrementAndGet(msg.getId());
130 | log.info(String.format("server接受到prepare消息:%s", msg));
131 | if (count >= AllNodeCommonMsg.getAgreeNum()) {
132 | log.info("数据符合,发送commit操作");
133 | collection.getVotePrePrepare().remove(msg.getId());
134 | collection.getAgreePrepare().remove(msg.getId());
135 |
136 | // 进入Commit阶段
137 | msg.setMsgType(MsgType.COMMIT);
138 | try {
139 | collection.getMsgQueue().put(msg);
140 | ClientAction.getInstance().doAction(null);
141 | } catch (InterruptedException e) {
142 | e.printStackTrace();
143 | }
144 | }
145 |
146 | }
147 |
148 | /**
149 | * 主节点发送过来的pre_prepare消息
150 | *
151 | * @param msg
152 | */
153 | private void prePrepare(PbftMsg msg) {
154 |
155 | log.info(String.format("server接受到pre-prepare消息:%s", msg));
156 |
157 | msgCollection.getVotePrePrepare().add(msg.getId());
158 | if (!PbftUtil.checkMsg(msg)) {
159 | return;
160 | }
161 |
162 | msg.setMsgType(MsgType.PREPARE);
163 | try {
164 | msgCollection.getMsgQueue().put(msg);
165 | ClientAction.getInstance().doAction(null);
166 | } catch (InterruptedException e) {
167 | e.printStackTrace();
168 | return;
169 | }
170 | }
171 |
172 | /**
173 | * 重新设置view
174 | *
175 | * @param channelContext
176 | * @param msg
177 | */
178 | private void changeView(ChannelContext channelContext, PbftMsg msg) {
179 | if (node.isViewOK()) {
180 | return;
181 | }
182 | long count = collection.getViewNumCount().incrementAndGet(msg.getViewNum());
183 |
184 | if (count >= AllNodeCommonMsg.getAgreeNum() && !node.isViewOK()) {
185 | collection.getViewNumCount().clear();
186 | node.setViewOK(true);
187 | AllNodeCommonMsg.view = msg.getViewNum();
188 | log.info("视图变更完成OK");
189 | }
190 | }
191 |
192 | /**
193 | * 添加未连接的结点
194 | *
195 | * @param msg
196 | */
197 | private void addClient(PbftMsg msg) {
198 | if (!ClientUtil.haveClient(msg.getNode())) {
199 | String ipStr = msg.getBody();
200 | ReplayJson replayJson = JSON.parseObject(ipStr, ReplayJson.class);
201 | ClientChannelContext context = ClientUtil.clientConnect(replayJson.getIp(), replayJson.getPort());
202 |
203 | NodeAddress address = new NodeAddress();
204 | address.setIp(replayJson.getIp());
205 | address.setPort(replayJson.getPort());
206 | NodeBasicInfo info = new NodeBasicInfo();
207 | info.setIndex(msg.getNode());
208 | info.setAddress(address);
209 | // 添加ip地址
210 | AllNodeCommonMsg.allNodeAddressMap.put(msg.getNode(), info);
211 | AllNodeCommonMsg.publicKeyMap.put(msg.getNode(), replayJson.getPublicKey());
212 |
213 | log.info(String.format("节点%s添加ip地址:%s", node, info));
214 | if (context != null) {
215 | // 添加client
216 | ClientUtil.addClient(msg.getNode(), context);
217 | }
218 | }
219 | }
220 |
221 |
222 | /**
223 | * 将自己的view发送给client
224 | *
225 | * @param channelContext
226 | * @param msg
227 | */
228 | private void onGetView(ChannelContext channelContext, PbftMsg msg) {
229 | log.info("server结点回复视图请求操作");
230 | int fromNode = msg.getNode();
231 | // 设置消息的发送方
232 | msg.setNode(node.getIndex());
233 | // 设置消息的目的地
234 | msg.setToNode(fromNode);
235 | log.info(String.format("同意此节点%s的申请", msg));
236 | msg.setOk(true);
237 | msg.setViewNum(AllNodeCommonMsg.view);
238 | MsgUtil.signMsg(msg);
239 | String jsonView = JSON.toJSONString(msg);
240 | MsgPacket msgPacket = new MsgPacket();
241 | try {
242 | msgPacket.setBody(jsonView.getBytes(MsgPacket.CHARSET));
243 | Tio.send(channelContext, msgPacket);
244 | } catch (UnsupportedEncodingException e) {
245 | log.error(String.format("server结点发送view消息失败%s", e.getMessage()));
246 | }
247 | }
248 | }
249 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/p2p/server/ServerListener.java:
--------------------------------------------------------------------------------
1 | package cc.weno.p2p.server;
2 |
3 | import cn.hutool.core.util.RandomUtil;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.tio.core.ChannelContext;
6 | import org.tio.core.intf.Packet;
7 | import org.tio.server.intf.ServerAioListener;
8 |
9 | /**
10 | * // _ooOoo_
11 | * // o8888888o
12 | * // 88" . "88
13 | * // (| -_- |)
14 | * // O\ = /O
15 | * // ____/`---'\____
16 | * // . ' \\| |// `.
17 | * // / \\||| : |||// \
18 | * // / _||||| -:- |||||- \
19 | * // | | \\\ - /// | |
20 | * // | \_| ''\---/'' | |
21 | * // \ .-\__ `-` ___/-. /
22 | * // ___`. .' /--.--\ `. . __
23 | * // ."" '< `.___\_<|>_/___.' >'"".
24 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
25 | * // \ \ `-. \_ __\ /__ _/ .-` / /
26 | * // ======`-.____`-.___\_____/___.-`____.-'======
27 | * // `=---='
28 | * //
29 | * // .............................................
30 | * // 佛祖镇楼 BUG辟易
31 | *
32 | * @author: xiaohuiduan
33 | * @data: 2020/2/12 上午10:52
34 | * @description: 服务监听器
35 | */
36 | @Slf4j
37 | public class ServerListener implements ServerAioListener {
38 | /**
39 | * 服务器检查到心跳超时时,会调用这个函数(一般场景,该方法只需要直接返回false即可)
40 | *
41 | * @param channelContext
42 | * @param interval 已经多久没有收发消息了,单位:毫秒
43 | * @param heartbeatTimeoutCount 心跳超时次数,第一次超时此值是1,以此类推。此值被保存在:channelContext.stat.heartbeatTimeoutCount
44 | * @return 返回true,那么服务器则不关闭此连接;返回false,服务器将按心跳超时关闭该连接
45 | */
46 | @Override
47 | public boolean onHeartbeatTimeout(ChannelContext channelContext, Long interval, int heartbeatTimeoutCount) {
48 | if (channelContext.stat.heartbeatTimeoutCount.intValue() > 5){
49 | log.warn(String.format("结点%s连接超时5次,关闭此连接", channelContext));
50 | return false;
51 | }
52 | return true;
53 | }
54 |
55 | /**
56 | * 建链后触发本方法,注:建链不一定成功,需要关注参数isConnected
57 | *
58 | * @param channelContext
59 | * @param isConnected 是否连接成功,true:表示连接成功,false:表示连接失败
60 | * @param isReconnect 是否是重连, true: 表示这是重新连接,false: 表示这是第一次连接
61 | * @throws Exception
62 | * @author: tanyaowu
63 | */
64 | @Override
65 | public void onAfterConnected(ChannelContext channelContext, boolean isConnected, boolean isReconnect) throws Exception {
66 | // if (isReconnect || !isConnected){
67 | // return;
68 | // }
69 | //
70 | }
71 |
72 | /**
73 | * 原方法名:onAfterDecoded
74 | * 解码成功后触发本方法
75 | *
76 | * @param channelContext
77 | * @param packet
78 | * @param packetSize
79 | * @throws Exception
80 | * @author: tanyaowu
81 | */
82 | @Override
83 | public void onAfterDecoded(ChannelContext channelContext, Packet packet, int packetSize) throws Exception {
84 | }
85 |
86 | /**
87 | * 接收到TCP层传过来的数据后
88 | *
89 | * @param channelContext
90 | * @param receivedBytes 本次接收了多少字节
91 | * @throws Exception
92 | */
93 | @Override
94 | public void onAfterReceivedBytes(ChannelContext channelContext, int receivedBytes) throws Exception {
95 |
96 | }
97 |
98 | /**
99 | * 消息包发送之后触发本方法
100 | *
101 | * @param channelContext
102 | * @param packet
103 | * @param isSentSuccess true:发送成功,false:发送失败
104 | * @throws Exception
105 | * @author tanyaowu
106 | */
107 | @Override
108 | public void onAfterSent(ChannelContext channelContext, Packet packet, boolean isSentSuccess) throws Exception {
109 |
110 | }
111 |
112 | /**
113 | * 处理一个消息包后
114 | *
115 | * @param channelContext
116 | * @param packet
117 | * @param cost 本次处理消息耗时,单位:毫秒
118 | * @throws Exception
119 | */
120 | @Override
121 | public void onAfterHandled(ChannelContext channelContext, Packet packet, long cost) throws Exception {
122 |
123 | }
124 |
125 | /**
126 | * 连接关闭前触发本方法
127 | *
128 | * @param channelContext the channelcontext
129 | * @param throwable the throwable 有可能为空
130 | * @param remark the remark 有可能为空
131 | * @param isRemove
132 | * @throws Exception
133 | * @author tanyaowu
134 | */
135 | @Override
136 | public void onBeforeClose(ChannelContext channelContext, Throwable throwable, String remark, boolean isRemove) throws Exception {
137 |
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/util/ClientUtil.java:
--------------------------------------------------------------------------------
1 | package cc.weno.util;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import cc.weno.config.AllNodeCommonMsg;
5 | import cc.weno.dao.bean.ReplayJson;
6 | import cc.weno.dao.node.Node;
7 | import cc.weno.dao.pbft.MsgCollection;
8 | import cc.weno.dao.pbft.MsgType;
9 | import cc.weno.dao.pbft.PbftMsg;
10 | import lombok.extern.slf4j.Slf4j;
11 | import org.tio.client.ClientChannelContext;
12 | import org.tio.client.ClientTioConfig;
13 | import org.tio.client.ReconnConf;
14 | import org.tio.client.TioClient;
15 | import org.tio.core.Tio;
16 | import cc.weno.p2p.P2PConnectionMsg;
17 | import cc.weno.p2p.client.ClientAction;
18 | import cc.weno.p2p.client.P2PClientLinstener;
19 | import cc.weno.p2p.client.P2pClientAioHandler;
20 | import cc.weno.p2p.common.Const;
21 | import cc.weno.p2p.common.MsgPacket;
22 |
23 | import java.io.UnsupportedEncodingException;
24 |
25 | /**
26 | * // _ooOoo_
27 | * // o8888888o
28 | * // 88" . "88
29 | * // (| -_- |)
30 | * // O\ = /O
31 | * // ____/`---'\____
32 | * // . ' \\| |// `.
33 | * // / \\||| : |||// \
34 | * // / _||||| -:- |||||- \
35 | * // | | \\\ - /// | |
36 | * // | \_| ''\---/'' | |
37 | * // \ .-\__ `-` ___/-. /
38 | * // ___`. .' /--.--\ `. . __
39 | * // ."" '< `.___\_<|>_/___.' >'"".
40 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
41 | * // \ \ `-. \_ __\ /__ _/ .-` / /
42 | * // ======`-.____`-.___\_____/___.-`____.-'======
43 | * // `=---='
44 | * //
45 | * // .............................................
46 | * // 佛祖镇楼 BUG辟易
47 | *
48 | * @author: xiaohuiduan
49 | * @data: 2020/2/15 上午1:12
50 | * @description: 服务端工具
51 | */
52 | @Slf4j
53 | public class ClientUtil {
54 |
55 | /**
56 | * client 的配置
57 | */
58 | private static ClientTioConfig clientTioConfig = new ClientTioConfig(
59 | new P2pClientAioHandler(),
60 | new P2PClientLinstener(),
61 | new ReconnConf(Const.TIMEOUT)
62 | );
63 |
64 | public static ClientChannelContext clientConnect(String ip, int port) {
65 |
66 | clientTioConfig.setHeartbeatTimeout(Const.TIMEOUT);
67 | ClientChannelContext context;
68 | try {
69 | TioClient client = new TioClient(clientTioConfig);
70 | context = client.connect(new org.tio.core.Node(ip, port), Const.TIMEOUT);
71 | return context;
72 | } catch (Exception e) {
73 | log.error("%s:%d连接错误" + e.getMessage());
74 | return null;
75 | }
76 | }
77 |
78 | /**
79 | * 添加client到 P2PConnectionMsg.CLIENTS中
80 | *
81 | * @param index 结点序号
82 | * @param client
83 | */
84 | public static void addClient(int index, ClientChannelContext client) {
85 | P2PConnectionMsg.CLIENTS.put(index, client);
86 | }
87 |
88 | /**
89 | * 判断该结点是否保存在CLIENTS中
90 | *
91 | * @param index
92 | * @return
93 | */
94 | public static boolean haveClient(int index) {
95 | if (P2PConnectionMsg.CLIENTS.containsKey(index)) {
96 | return true;
97 | } else {
98 | return false;
99 | }
100 | }
101 |
102 | /**
103 | * client对所有的server广播
104 | *
105 | * @param msg
106 | */
107 | public static void clientPublish(PbftMsg msg) {
108 | // 设置目标消息发送方
109 | msg.setNode(Node.getInstance().getIndex());
110 | // 设置消息的目标方 -1 代表all
111 | msg.setToNode(-1);
112 |
113 | for (int index : P2PConnectionMsg.CLIENTS.keySet()) {
114 | ClientChannelContext client = P2PConnectionMsg.CLIENTS.get(index);
115 |
116 | // 如果不是CLIENT_REPLAY的消息类型,就进行加密和签名操作
117 | if (msg.getMsgType() != MsgType.CLIENT_REPLAY && msg.getMsgType() != MsgType.GET_VIEW) {
118 | if (!MsgUtil.preMsg(index, msg)) {
119 | log.error("消息加密失败");
120 | return;
121 | }
122 | }
123 | String json = JSON.toJSONString(msg);
124 | MsgPacket msgPacket = new MsgPacket();
125 | try {
126 | msgPacket.setBody(json.getBytes(MsgPacket.CHARSET));
127 | Tio.send(client, msgPacket);
128 | } catch (UnsupportedEncodingException e) {
129 | log.error("数据utf-8编码错误" + e.getMessage());
130 | }
131 | }
132 | }
133 |
134 | /**
135 | * 将自己的节点ip和公钥消息广播出去
136 | *
137 | * @param index
138 | * @param ip
139 | * @param port
140 | */
141 | public static void publishIpPort(int index, String ip, int port) {
142 | PbftMsg replayMsg = new PbftMsg(MsgType.CLIENT_REPLAY, index);
143 | replayMsg.setViewNum(AllNodeCommonMsg.view);
144 | // 将节点消息数据发送过去
145 | ReplayJson replayJson = new ReplayJson();
146 | replayJson.setIp(ip);
147 | replayJson.setPort(port);
148 | // 公钥部分
149 | replayJson.setPublicKey(Node.getInstance().getPublicKey());
150 |
151 | replayMsg.setBody(JSON.toJSONString(replayJson));
152 | ClientUtil.clientPublish(replayMsg);
153 | log.info(String.format("广播ip消息:%s", replayMsg));
154 | }
155 |
156 | /**
157 | * 主节点进行广播
158 | *
159 | * @param msg
160 | */
161 | public static void prePrepare(PbftMsg msg) {
162 | msg.setNode(Node.getInstance().getIndex());
163 | msg.setToNode(-1);
164 | msg.setViewNum(AllNodeCommonMsg.view);
165 | Node node = Node.getInstance();
166 | // 限制只有主节点能够发送消息
167 | if (node.getIndex() != AllNodeCommonMsg.getPriIndex()) {
168 | log.warn("该节点非主节点,不能发送preprepare消息");
169 | return;
170 | }
171 | msg.setMsgType(MsgType.PRE_PREPARE);
172 | ClientUtil.clientPublish(msg);
173 |
174 | MsgCollection msgCollection = MsgCollection.getInstance();
175 | msg.setMsgType(MsgType.PREPARE);
176 | msgCollection.getVotePrePrepare().add(msg.getId());
177 | if (!PbftUtil.checkMsg(msg)) {
178 | return;
179 | }
180 | try {
181 | msgCollection.getMsgQueue().put(msg);
182 | ClientAction.getInstance().doAction(null);
183 | } catch (InterruptedException e) {
184 | e.printStackTrace();
185 | return;
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/util/DbUtil.java:
--------------------------------------------------------------------------------
1 | package cc.weno.util;
2 |
3 | import cc.weno.config.AllNodeCommonMsg;
4 | import cc.weno.dao.bean.DbDao;
5 | import cc.weno.dao.node.Node;
6 | import cc.weno.dao.pbft.MsgCollection;
7 | import cc.weno.dao.pbft.PbftMsg;
8 | import lombok.extern.slf4j.Slf4j;
9 | import org.iq80.leveldb.DB;
10 | import org.iq80.leveldb.DBIterator;
11 | import org.iq80.leveldb.Options;
12 | import org.iq80.leveldb.impl.Iq80DBFactory;
13 |
14 | import java.io.*;
15 | import java.util.*;
16 |
17 | import static org.iq80.leveldb.impl.Iq80DBFactory.factory;
18 |
19 | /**
20 | * // _ooOoo_
21 | * // o8888888o
22 | * // 88" . "88
23 | * // (| -_- |)
24 | * // O\ = /O
25 | * // ____/`---'\____
26 | * // . ' \\| |// `.
27 | * // / \\||| : |||// \
28 | * // / _||||| -:- |||||- \
29 | * // | | \\\ - /// | |
30 | * // | \_| ''\---/'' | |
31 | * // \ .-\__ `-` ___/-. /
32 | * // ___`. .' /--.--\ `. . __
33 | * // ."" '< `.___\_<|>_/___.' >'"".
34 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
35 | * // \ \ `-. \_ __\ /__ _/ .-` / /
36 | * // ======`-.____`-.___\_____/___.-`____.-'======
37 | * // `=---='
38 | * //
39 | * // .............................................
40 | * // 佛祖镇楼 BUG辟易
41 | *
42 | * @author: xiaohuiduan
43 | * @data: 2020/3/1 上午12:13
44 | * @description: levelDB操作
45 | */
46 | @Slf4j
47 | public class DbUtil {
48 | public static String dbFilePath;
49 |
50 | private static DB db = null;
51 | private static Options options = new Options();
52 | private static boolean flag = true;
53 |
54 | private static boolean init() {
55 | options = new Options();
56 | options.createIfMissing(true);
57 | try {
58 | db = factory.open(new File(dbFilePath), options);
59 | } catch (IOException e) {
60 | log.warn(String.format("数据库获取失败%s", e.getMessage()));
61 | return false;
62 | }
63 | return true;
64 | }
65 |
66 |
67 | /**
68 | * 插入一个dao
69 | *
70 | * @param dao
71 | */
72 | private static void insert(DbDao dao) {
73 | try {
74 | db.put(String.valueOf(dao.getNode()).getBytes(), daoToBytes(dao));
75 | } catch (IOException e) {
76 | log.warn(String.format("对象序列化失败%s", e.getMessage()));
77 | }
78 | }
79 |
80 | synchronized public static void save() {
81 | if (!flag) {
82 | return;
83 | }
84 | flag = false;
85 | log.info(String.format("保存的大小%s", MsgCollection.getInstance().getDbDaos().size()));
86 | if (init()) {
87 | for (DbDao dao :
88 | MsgCollection.getInstance().getDbDaos()) {
89 | insert(dao);
90 | }
91 | try {
92 | db.close();
93 | } catch (IOException e) {
94 | log.warn(String.format("levelDB关闭失败%s", e.getMessage()));
95 | }
96 | }
97 |
98 | }
99 |
100 | /**
101 | * 进行遍历
102 | */
103 | private static void get() {
104 | Options options = new Options();
105 | options.createIfMissing(true);
106 | DB db = null;
107 | try {
108 | db = factory.open(new File(dbFilePath), options);
109 | } catch (IOException e) {
110 | log.warn(String.format("数据库获取失败%s", e.getMessage()));
111 | return;
112 | }
113 | DBIterator iterator = db.iterator();
114 | List list = new ArrayList();
115 | while (iterator.hasNext()) {
116 | Map.Entry next = iterator.next();
117 | byte[] value = next.getValue();
118 | list.add(value);
119 | }
120 | System.out.println(list.size());
121 | for (byte[] bytes : list) {
122 | try {
123 | DbDao dbDao = (DbDao) bytesToDao(bytes);
124 | } catch (IOException | ClassNotFoundException e) {
125 | e.printStackTrace();
126 | }
127 | }
128 |
129 | }
130 |
131 | /**
132 | * @param node 谁发送的信息
133 | * @param msg
134 | */
135 | public static void addDaotoList(int node, PbftMsg msg) {
136 | DbDao dbDao = new DbDao();
137 | dbDao.setNode(node);
138 | dbDao.setPublicKey(AllNodeCommonMsg.publicKeyMap.get(node));
139 | dbDao.setTime(msg.getTime());
140 | dbDao.setViewNum(msg.getViewNum());
141 | MsgCollection.getInstance().getDbDaos().add(dbDao);
142 | }
143 |
144 | /**
145 | * 将对象序列化
146 | *
147 | * @param dao
148 | * @return
149 | * @throws IOException
150 | */
151 | private static byte[] daoToBytes(DbDao dao) throws IOException {
152 | //创建内存输出流
153 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
154 | ObjectOutputStream oos = new ObjectOutputStream(baos);
155 | oos.writeObject(dao);
156 | oos.writeObject(null);
157 | oos.close();
158 | baos.close();
159 | return baos.toByteArray();
160 | }
161 |
162 | /**
163 | * 将对象反序列化
164 | *
165 | * @param bytes
166 | * @return
167 | * @throws IOException
168 | */
169 | private static Object bytesToDao(byte[] bytes) throws IOException, ClassNotFoundException {
170 | //创建内存输出流
171 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
172 | ObjectInputStream inputStream = new ObjectInputStream(byteArrayInputStream);
173 | Object obj = null;
174 | while ((obj = inputStream.readObject()) != null) {
175 | DbDao dbDao = (DbDao) obj;
176 | System.out.println(dbDao);
177 | }
178 | inputStream.close();
179 | byteArrayInputStream.close();
180 | return obj;
181 |
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/util/MsgUtil.java:
--------------------------------------------------------------------------------
1 | package cc.weno.util;
2 |
3 | import cn.hutool.crypto.asymmetric.KeyType;
4 | import cn.hutool.crypto.asymmetric.RSA;
5 | import cc.weno.config.AllNodeCommonMsg;
6 | import cc.weno.dao.node.Node;
7 | import cc.weno.dao.pbft.PbftMsg;
8 | import lombok.extern.slf4j.Slf4j;
9 |
10 | import java.util.Map;
11 |
12 | /**
13 | * // _ooOoo_
14 | * // o8888888o
15 | * // 88" . "88
16 | * // (| -_- |)
17 | * // O\ = /O
18 | * // ____/`---'\____
19 | * // . ' \\| |// `.
20 | * // / \\||| : |||// \
21 | * // / _||||| -:- |||||- \
22 | * // | | \\\ - /// | |
23 | * // | \_| ''\---/'' | |
24 | * // \ .-\__ `-` ___/-. /
25 | * // ___`. .' /--.--\ `. . __
26 | * // ."" '< `.___\_<|>_/___.' >'"".
27 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
28 | * // \ \ `-. \_ __\ /__ _/ .-` / /
29 | * // ======`-.____`-.___\_____/___.-`____.-'======
30 | * // `=---='
31 | * //
32 | * // .............................................
33 | * // 佛祖镇楼 BUG辟易
34 | *
35 | * @author: xiaohuiduan
36 | * @data: 2020/2/25 下午6:27
37 | * @description: 进行加密和签名的pbftmsg工具类
38 | */
39 | @Slf4j
40 | public class MsgUtil {
41 | /**
42 | * 直接的RSA
43 | */
44 | private static RSA selfRsa = new RSA(Node.getInstance().getPrivateKey(), Node.getInstance().getPublicKey());
45 |
46 | private static Map publicKeyMap = AllNodeCommonMsg.publicKeyMap;
47 |
48 | /**
49 | * 使用自己的私钥进行签名
50 | *
51 | * @param msg
52 | */
53 | public static void signMsg(PbftMsg msg) {
54 | String hash = String.valueOf(msg.hashCode());
55 | String sign = selfRsa.encryptBase64(hash, KeyType.PrivateKey);
56 | msg.setSign(sign);
57 | }
58 |
59 | /**
60 | * 使用对方的公钥进行加密
61 | *
62 | * @param msg
63 | */
64 | private static boolean encryptMsg(int index, PbftMsg msg) {
65 | String publicKey;
66 | if ((publicKey = publicKeyMap.get(index)) == null) {
67 | log.error("对方公钥为空");
68 | return false;
69 | }
70 |
71 | if (msg.getBody() == null) {
72 | log.warn("消息体为空,不进行加密");
73 | return true;
74 | }
75 |
76 | RSA encryptRsa;
77 | try {
78 | encryptRsa = new RSA(null, publicKey);
79 | } catch (Exception e) {
80 | log.error("使用对方公钥创建RSA对象失败");
81 | return false;
82 | }
83 |
84 | // 进行加密
85 | msg.setBody(encryptRsa.encryptBase64(msg.getBody(), KeyType.PublicKey));
86 | return true;
87 | }
88 |
89 | /**
90 | * 使用自己的私钥进行解密
91 | *
92 | * @param msg
93 | */
94 | private static boolean decryptMsg(PbftMsg msg) {
95 | if (msg.getBody() == null) {
96 | log.warn("消息体为空,不进行解密");
97 | return true;
98 | }
99 | String body;
100 | try {
101 | body = selfRsa.decryptStr(msg.getBody(), KeyType.PrivateKey);
102 | } catch (Exception e) {
103 | log.error(String.format("私钥解密失败!%s", e.getMessage()));
104 | return false;
105 | }
106 | msg.setBody(body);
107 | return true;
108 | }
109 |
110 | /**
111 | * 消息的前置处理:
112 | * ①:进行加密
113 | * ②:进行签名
114 | *
115 | * @param index
116 | * @param msg
117 | * @return
118 | */
119 | public static boolean preMsg(int index, PbftMsg msg) {
120 | if (!encryptMsg(index, msg)) {
121 | return false;
122 | }
123 | signMsg(msg);
124 | return true;
125 | }
126 |
127 | /**
128 | * 对消息进行后置处理
129 | *
130 | * @param msg
131 | * @return
132 | */
133 | public static boolean afterMsg(PbftMsg msg) {
134 | if (!isRealMsg(msg) || !decryptMsg(msg)) {
135 | return false;
136 | }
137 | return true;
138 | }
139 |
140 | /**
141 | * 判断消息是否被改变
142 | * 首先判断签名是否有问题
143 | *
144 | * @param msg 解密之前的消息!!!!!
145 | * @return
146 | */
147 | public static boolean isRealMsg(PbftMsg msg) {
148 | String publicKey = publicKeyMap.get(msg.getNode());
149 | RSA rsa;
150 | try {
151 | rsa = new RSA(null, publicKey);
152 | } catch (Exception e) {
153 | log.error(String.format("创建公钥RSA失败%s", e.getMessage()));
154 | return false;
155 | }
156 | // 获得此时消息的hash值
157 | String nowHash = String.valueOf(msg.hashCode());
158 |
159 | String sign = msg.getSign();
160 | try {
161 | // 获得hash值
162 | String hash = rsa.decryptStr(sign, KeyType.PublicKey);
163 | if (nowHash.equals(hash)) {
164 | return true;
165 | }
166 | } catch (Exception e) {
167 | log.warn(String.format("验证签名失效%s", e.getMessage()));
168 | }
169 | return false;
170 | }
171 |
172 | }
173 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/util/Pbft.java:
--------------------------------------------------------------------------------
1 | package cc.weno.util;
2 |
3 | import cc.weno.config.AllNodeCommonMsg;
4 | import cc.weno.dao.node.Node;
5 | import cc.weno.dao.pbft.MsgType;
6 | import cc.weno.dao.pbft.PbftMsg;
7 | import lombok.extern.slf4j.Slf4j;
8 |
9 | /**
10 | * // _ooOoo_
11 | * // o8888888o
12 | * // 88" . "88
13 | * // (| -_- |)
14 | * // O\ = /O
15 | * // ____/`---'\____
16 | * // . ' \\| |// `.
17 | * // / \\||| : |||// \
18 | * // / _||||| -:- |||||- \
19 | * // | | \\\ - /// | |
20 | * // | \_| ''\---/'' | |
21 | * // \ .-\__ `-` ___/-. /
22 | * // ___`. .' /--.--\ `. . __
23 | * // ."" '< `.___\_<|>_/___.' >'"".
24 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
25 | * // \ \ `-. \_ __\ /__ _/ .-` / /
26 | * // ======`-.____`-.___\_____/___.-`____.-'======
27 | * // `=---='
28 | * //
29 | * // .............................................
30 | * // 佛祖镇楼 BUG辟易
31 | *
32 | * @author: xiaohuiduan
33 | * @data: 2020/1/22 下午3:53
34 | * @description: pbft的工作流程
35 | * this is the most important thing
36 | * 这个是整个算法的流程
37 | */
38 | @Slf4j
39 | public class Pbft {
40 |
41 | private Node node = Node.getInstance();
42 |
43 | /**
44 | * 发送view请求
45 | *
46 | * @return
47 | */
48 | public boolean pubView() {
49 |
50 | /**
51 | * 如果区块链中的网络节点小于3
52 | */
53 | if (AllNodeCommonMsg.allNodeAddressMap.size() < 3) {
54 | log.warn("区块链中的节点小于等于3");
55 | node.setViewOK(true);
56 | // 将节点消息广播出去
57 | ClientUtil.publishIpPort(node.getIndex(), node.getAddress().getIp(), node.getAddress().getPort());
58 | return true;
59 | }
60 |
61 | log.info("结点开始进行view同步操作");
62 | // 初始化view的msg
63 | PbftMsg view = new PbftMsg(MsgType.GET_VIEW, node.getIndex());
64 | ClientUtil.clientPublish(view);
65 | return true;
66 | }
67 |
68 | /**
69 | * 视图发送该表
70 | *
71 | * @return
72 | */
73 | public boolean changeView() {
74 |
75 | return true;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/util/PbftUtil.java:
--------------------------------------------------------------------------------
1 | package cc.weno.util;
2 |
3 | import cc.weno.config.StartConfig;
4 | import cn.hutool.core.io.file.FileWriter;
5 | import cn.hutool.json.JSONUtil;
6 | import cc.weno.dao.bean.ReplayJson;
7 | import cc.weno.dao.node.Node;
8 | import cc.weno.dao.pbft.PbftMsg;
9 | import lombok.extern.slf4j.Slf4j;
10 |
11 | /**
12 | * // _ooOoo_
13 | * // o8888888o
14 | * // 88" . "88
15 | * // (| -_- |)
16 | * // O\ = /O
17 | * // ____/`---'\____
18 | * // . ' \\| |// `.
19 | * // / \\||| : |||// \
20 | * // / _||||| -:- |||||- \
21 | * // | | \\\ - /// | |
22 | * // | \_| ''\---/'' | |
23 | * // \ .-\__ `-` ___/-. /
24 | * // ___`. .' /--.--\ `. . __
25 | * // ."" '< `.___\_<|>_/___.' >'"".
26 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
27 | * // \ \ `-. \_ __\ /__ _/ .-` / /
28 | * // ======`-.____`-.___\_____/___.-`____.-'======
29 | * // `=---='
30 | * //
31 | * // .............................................
32 | * // 佛祖镇楼 BUG辟易
33 | *
34 | * @author: xiaohuiduan
35 | * @data: 2020/2/15 下午10:38
36 | * @description: pbft算法的工具类
37 | */
38 | @Slf4j
39 | public class PbftUtil {
40 |
41 | private static boolean flag = true;
42 |
43 | public static String ipJsonPath = StartConfig.basePath + "ip.json";
44 |
45 |
46 | public static boolean checkMsg(PbftMsg msg) {
47 |
48 | return true;
49 | }
50 |
51 | public static void save(PbftMsg msg) {
52 | log.warn(String.format("Pbft消息可以写入块%s", msg));
53 | log.warn("请根据自己的需要使用DbUtil类进行操作");
54 | }
55 |
56 | /**
57 | * 信息写入文件和AllNodeCommonMsg.allNodeAddressMap
58 | *
59 | * @param node
60 | */
61 | synchronized public static void writeIpToFile(Node node) {
62 | if (!flag) {
63 | return;
64 | }
65 | log.info(String.format("节点%s写入文件", node.getIndex()));
66 | FileWriter writer = new FileWriter(ipJsonPath);
67 | ReplayJson replayJson = new ReplayJson();
68 | replayJson.setIndex(node.getIndex());
69 | replayJson.setIp(node.getAddress().getIp());
70 | replayJson.setPort(node.getAddress().getPort());
71 | replayJson.setPublicKey(node.getPublicKey());
72 | String json = JSONUtil.toJsonStr(replayJson);
73 | writer.append(json + "\n");
74 | flag = false;
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/cc/weno/util/StartPbft.java:
--------------------------------------------------------------------------------
1 | package cc.weno.util;
2 |
3 | import cc.weno.config.StartConfig;
4 | import cc.weno.dao.node.Node;
5 | import lombok.extern.slf4j.Slf4j;
6 |
7 | /**
8 | * // _ooOoo_
9 | * // o8888888o
10 | * // 88" . "88
11 | * // (| -_- |)
12 | * // O\ = /O
13 | * // ____/`---'\____
14 | * // . ' \\| |// `.
15 | * // / \\||| : |||// \
16 | * // / _||||| -:- |||||- \
17 | * // | | \\\ - /// | |
18 | * // | \_| ''\---/'' | |
19 | * // \ .-\__ `-` ___/-. /
20 | * // ___`. .' /--.--\ `. . __
21 | * // ."" '< `.___\_<|>_/___.' >'"".
22 | * // | | : `- \`.;`\ _ /`;.`/ - ` : | |
23 | * // \ \ `-. \_ __\ /__ _/ .-` / /
24 | * // ======`-.____`-.___\_____/___.-`____.-'======
25 | * // `=---='
26 | * //
27 | * // .............................................
28 | * // 佛祖镇楼 BUG辟易
29 | *
30 | * @author: xiaohuiduan
31 | * @data: 2020/2/19 下午7:56
32 | * @description: 启动
33 | */
34 | @Slf4j
35 | public class StartPbft {
36 |
37 |
38 | public static boolean start() {
39 | StartConfig startConfig = new StartConfig();
40 | if (startConfig.startConfig()) {
41 | if (new Pbft().pubView()) {
42 | return true;
43 | }
44 | }
45 | return false;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/resources/ip.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaohuiduan/pbft/4b9008fb5089e80f90f7fbdcf5b3e5c1d981d114/src/main/resources/ip.json
--------------------------------------------------------------------------------
/src/main/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | # 关闭其他框架的日志
2 | log4j.rootLogger=OFF
3 | # 只保留自己的日志
4 | log4j.logger.cc.weno = DEBUG,console
5 | log4j.appender.console=org.apache.log4j.ConsoleAppender
6 | log4j.appender.console.layout=org.apache.log4j.PatternLayout
7 | log4j.appender.console.layout.ConversionPattern = [%c]-[%p] %m%n
--------------------------------------------------------------------------------