├── .env
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── Cross.toml
├── README.md
├── build.rs
├── core
├── .gitignore
├── Cargo.toml
└── src
│ ├── client
│ ├── encry.rs
│ ├── fee.rs
│ ├── handle_stream.rs
│ ├── handle_stream_all.rs
│ ├── handle_stream_nofee.rs
│ ├── handle_stream_timer.rs
│ ├── mod.rs
│ ├── monitor.rs
│ ├── pools.rs
│ ├── tcp.rs
│ └── tls.rs
│ ├── lib.rs
│ ├── protocol
│ ├── eth_stratum.rs
│ ├── ethjson.rs
│ ├── mod.rs
│ ├── rpc
│ │ ├── eth
│ │ │ └── mod.rs
│ │ └── mod.rs
│ └── stratum.rs
│ ├── proxy
│ └── mod.rs
│ ├── state
│ └── mod.rs
│ ├── util
│ ├── config.rs
│ ├── logger.rs
│ └── mod.rs
│ └── web
│ ├── data
│ └── mod.rs
│ ├── handles
│ ├── auth.rs
│ ├── mod.rs
│ ├── server.rs
│ └── user.rs
│ └── mod.rs
├── doc
├── 0.0.1 版本日志
├── 0.1.0开发步骤
├── 0.1.2 开发日志
├── 0.1.3 开发日志
├── 0.1.4开发日志
├── 0.1.5开发日志
├── 0.1.6开发日志
├── 0.1.7开发日志
├── 0.1.8开发日志
├── 0.1.9开发日志
├── 0.2.0开发日志
├── 0.2.1开发日志
├── 0.2.2开发日志
├── 0.2.3开发日志
├── 0.2.4.org
├── 0.2.4开发日志
├── fake
├── images
│ ├── fee.jpg
│ ├── logo.png
│ ├── web.jpg
│ └── web1.jpg
└── install.md
├── dockerfile
├── makefile
├── mining_proxy
├── .gitignore
├── Cargo.toml
├── build.rs
└── src
│ └── main.rs
├── monitor
├── Cargo.toml
├── build.rs
└── src
│ └── main.rs
├── proxy
└── src
│ └── main.rs
├── rustfmt.toml
└── utils
├── Cargo.toml
├── Cargo.toml~
└── src
├── lib.rs
└── lib.rs~
/.env:
--------------------------------------------------------------------------------
1 | MINING_PROXY_WEB_PORT=8020
2 | MINING_PROXY_WEB_PASSWORD=123456789
3 | JWT_SECRET=test
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | .DS_Store
3 | default.yaml
4 | release/
5 | configs.yaml
6 | logs
7 | # Editor directories and files
8 | .idea
9 | .vscode
10 | *.suo
11 | *.ntvs*
12 | *.njsproj
13 | *.sln
14 | *.pem
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "core",
4 | "utils",
5 | "monitor",
6 | "mining_proxy"
7 | ]
8 |
--------------------------------------------------------------------------------
/Cross.toml:
--------------------------------------------------------------------------------
1 | [target.x86_64-unknown-linux-musl]
2 | image = "wangyusong/x86_64-unknown-linux-musl:latest"
3 | [target.aarch64-unknown-linux-musl]
4 | image = "wangyusong/aarch64-unknown-linux-musl:latest"
5 | [target.armv7-unknown-linux-gnueabihf]
6 | image = "rustembedded/cross:armv7-unknown-linux-gnueabihf-0.1.16"
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 全开源 - 无内置开发者钱包
7 | Rust语言编写 基于tokio生态的ETH/ETC/CFX 代理抽水软件
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | 最新版本见Release Github Release
24 | 历史版本: https://github.com/dothinkdone/mining_proxy/releases
25 |
26 | Coffee: Eth+BSC+HECO+Matic: 0x3602b50d3086edefcd9318bcceb6389004fb14ee
27 |
28 |
29 |
30 | Telegram 群 •
31 | QQ 群
32 |
33 |
34 | 
35 |
36 | ## :sparkles: 特性
37 |
38 | - :cloud: 支持ETH ETC CFX 转发
39 | - :zap: 性能强劲,CPU占用低。
40 | - 💻 可以自定义抽水比例
41 | - 📚 可以自定义抽水算法。
42 | - 💾 安全稳定: 支持TCP SSL 及加密方式(轻量算法,非SSR一类的垃圾东西)3种不通的协议
43 | - :outbox_tray: 一台机器只需要开启一个Web界面。可配置多矿池转发(没有上限)
44 | - :rocket: 開箱即用:All-In-One 打包,一鍵搭建運行,一鍵配置
45 | - :family_woman_girl_boy: 支持Liunx Windows
46 |
47 | ## :hammer_and_wrench: 部署
48 |
49 | - 自行编译
50 | 编译遇到问题基本都是web的源码没有clone
51 | 看这里:https://github.com/YusongWang/mining_proxy/issues/26
52 |
53 | 在软件运行目录下创建 .env 文件
54 | ```env
55 | MINING_PROXY_WEB_PORT=8020
56 | MINING_PROXY_WEB_PASSWORD=123456789
57 | JWT_SECRET=test
58 | ```
59 | 第一行是网页的端口
60 | 第二行是网页管理的密码
61 | 第三行是登录密码的加密秘钥。建议用随机字符串不少于32位的字符串
62 |
63 |
64 | ## 其他说明
65 | Web界面地址
66 |
67 |
--------------------------------------------------------------------------------
/build.rs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YouNeedWork/mining_proxy/213e7d78528c9d92daaa9cd4fc7d1a8d7f646576/build.rs
--------------------------------------------------------------------------------
/core/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | .DS_Store
3 | default.yaml
4 | configs.yaml
5 | # Editor directories and files
6 | .idea
7 | .vscode
8 | *.suo
9 | *.ntvs*
10 | *.njsproj
11 | *.sln
--------------------------------------------------------------------------------
/core/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | authors = ["YusongWang admin@wangyusong.com"]
3 | description = "A simple Eth Proxy\n一个简单的矿工代理工具\n本工具是开放软件,任何人都可以免费下载和使用。\n请遵循本地法律的情况下使用。如非法使用由软件使用人承担一切责任\n"
4 | edition = "2018"
5 | name = "core"
6 | version = "0.2.4"
7 |
8 | [dependencies]
9 | actix-web = "4.0"
10 | actix-web-grants = "3.0.0-beta.6"
11 | actix-web-static-files = "4.0"
12 | anyhow = "1.0.51"
13 | async-channel = "1.6.1"
14 | base64 = "0.13.0"
15 | bytes = "1"
16 | cfg-if = "1.0.0"
17 | chrono = "0.4"
18 | clap = "2.34.0"
19 | config = "0.11"
20 | dotenv = "0.15.0"
21 | ethereum-hexutil = "0.2.3"
22 | hex = "0.4.3"
23 | hostname = "0.3.1"
24 | human-panic = "1.0.3"
25 | jsonwebtoken = "7"
26 | lazy_static = "1.4.0"
27 | native-tls = "0.2.8"
28 |
29 | num_enum = "0.5.6"
30 | rand = "0.8.3"
31 | rand_chacha = "0.3.1"
32 | serde = {version = "1", features = ["derive"]}
33 | serde_derive = "1"
34 | serde_json = "1"
35 | serde_millis = "0.1.1"
36 | serde_yaml = "0.8.23"
37 | static-files = "0.2.1"
38 | time = "*"
39 | tokio-rustls = "0.23.2"
40 | tokio = {version = "1.17.0", features = ["full"]}
41 | tokio-native-tls = "0.3.0"
42 | tracing = "0.1.30"
43 | tracing-appender = "0.2.0"
44 | tracing-subscriber = "0.3.3"
45 | aes-gcm = "0.9.4"
46 |
47 | [build-dependencies]
48 | static-files = "0.2.1"
49 | vergen = "0.1"
50 |
51 | [profile.release]
52 | lto = true
53 | opt-level = "s"
54 | panic = 'abort'
55 |
--------------------------------------------------------------------------------
/core/src/client/encry.rs:
--------------------------------------------------------------------------------
1 | use anyhow::Result;
2 | use tokio::{
3 | io::{split, BufReader},
4 | net::{TcpListener, TcpStream},
5 | sync::RwLockReadGuard,
6 | };
7 | use tracing::info;
8 |
9 | use crate::{state::Worker, util::config::Settings};
10 |
11 | use super::*;
12 | pub async fn accept_en_tcp(proxy: Arc) -> Result<()> {
13 | let config: Settings;
14 | {
15 | let rconfig = RwLockReadGuard::map(proxy.config.read().await, |s| s);
16 | config = rconfig.clone();
17 | }
18 |
19 | if config.encrypt_port == 0 {
20 | return Ok(());
21 | }
22 |
23 | let address = format!("0.0.0.0:{}", config.encrypt_port);
24 | let listener = match TcpListener::bind(address.clone()).await {
25 | Ok(listener) => listener,
26 | Err(_) => {
27 | tracing::info!("本地端口被占用 {}", address);
28 | std::process::exit(1);
29 | }
30 | };
31 |
32 | tracing::info!("本地TCP加密协议端口{}启动成功!!!", &address);
33 | loop {
34 | let (stream, addr) = listener.accept().await?;
35 |
36 | let p = Arc::clone(&proxy);
37 |
38 | tokio::spawn(async move {
39 | // 矿工状态管理
40 | let mut worker: Worker = Worker::default();
41 | let worker_tx = p.worker_tx.clone();
42 | match transfer(p, &mut worker, stream).await {
43 | Ok(_) => {
44 | if worker.is_online() {
45 | worker.offline();
46 | info!("IP: {} 安全下线", addr);
47 | worker_tx.send(worker).unwrap();
48 | } else {
49 | info!("IP: {} 下线", addr);
50 | }
51 | }
52 | Err(e) => {
53 | if worker.is_online() {
54 | worker.offline();
55 | worker_tx.send(worker).unwrap();
56 | info!("IP: {} 下线原因 {}", addr, e);
57 | } else {
58 | debug!("IP: {} 恶意链接断开: {}", addr, e);
59 | }
60 | }
61 | }
62 | });
63 | }
64 | }
65 |
66 | async fn transfer(
67 | proxy: Arc, worker: &mut Worker, tcp_stream: TcpStream,
68 | ) -> Result<()> {
69 | let (worker_r, worker_w) = split(tcp_stream);
70 | let worker_r = BufReader::new(worker_r);
71 |
72 | let mut pool_address: Vec = Vec::new();
73 | {
74 | let config = RwLockReadGuard::map(proxy.config.read().await, |s| s);
75 | pool_address = config.pool_address.to_vec();
76 | }
77 |
78 | let (stream_type, pools) =
79 | match crate::client::get_pool_ip_and_type_from_vec(&pool_address) {
80 | Ok(pool) => pool,
81 | Err(_) => {
82 | bail!("未匹配到矿池 或 均不可链接。请修改后重试");
83 | }
84 | };
85 |
86 | handle_tcp_random(
87 | worker,
88 | worker_r,
89 | worker_w,
90 | &pools,
91 | proxy,
92 | stream_type,
93 | true,
94 | )
95 | .await
96 | //handle_tcp_random(worker, worker_r, worker_w, &pools, proxy, true).await
97 | }
98 |
--------------------------------------------------------------------------------
/core/src/client/fee.rs:
--------------------------------------------------------------------------------
1 | use std::sync::{Arc, RwLockReadGuard};
2 |
3 | use anyhow::{anyhow, Result};
4 |
5 | use tokio::{
6 | io::{AsyncRead, AsyncWrite, BufReader, Lines, WriteHalf},
7 | select,
8 | sync::mpsc::Receiver,
9 | sync::RwLockWriteGuard,
10 | };
11 |
12 | use crate::{
13 | protocol::ethjson::EthClientObject,
14 | proxy::{Job, Proxy},
15 | util::config::Settings,
16 | };
17 |
18 | use crate::{
19 | client::lines_unwrap,
20 | protocol::ethjson::{
21 | EthClientRootObject, EthClientWorkerObject, EthServer,
22 | EthServerRootObject,
23 | },
24 | };
25 |
26 | use super::write_to_socket_byte;
27 |
28 | use tracing::{debug, info};
29 |
30 | pub async fn develop_fee_ssl(
31 | mut rx: Receiver>, job: Job,
32 | mut proxy_lines: Lines<
33 | BufReader<
34 | tokio::io::ReadHalf<
35 | tokio_native_tls::TlsStream,
36 | >,
37 | >,
38 | >,
39 | mut w: tokio::io::WriteHalf<
40 | tokio_native_tls::TlsStream,
41 | >,
42 | worker_name: String, proxy: Arc,
43 | ) -> Result<()> {
44 | let mut config: Settings;
45 | {
46 | let rconfig = proxy.config.read().await;
47 | config = rconfig.clone();
48 | }
49 | let mut get_work = EthClientRootObject {
50 | id: 6,
51 | method: "eth_getWork".into(),
52 | params: vec![],
53 | };
54 |
55 | let mut json_rpc = EthClientWorkerObject {
56 | id: 40,
57 | method: "eth_submitWork".into(),
58 | params: vec![],
59 | worker: worker_name.clone(),
60 | };
61 |
62 | let sleep = tokio::time::sleep(tokio::time::Duration::from_secs(20));
63 | tokio::pin!(sleep);
64 | let mut share_job_idx: u64 = 0;
65 |
66 | loop {
67 | select! {
68 | res = proxy_lines.next_line() => {
69 | let buffer = match lines_unwrap(res,&worker_name,"矿池").await {
70 | Ok(buf) => buf,
71 | Err(_) => {
72 |
73 | let (dev_lines, dev_w) =
74 | crate::client::dev_pool_ssl_login(worker_name.clone())
75 | .await?;
76 |
77 | //同时加2个值
78 | w = dev_w;
79 | proxy_lines = dev_lines;
80 | info!(worker_name = ?worker_name,"重新登录成功!!");
81 |
82 | continue;
83 | },
84 | };
85 | #[cfg(debug_assertions)]
86 | debug!("1 : 矿池 -> 矿机 {} #{:?}",worker_name, buffer);
87 | if let Ok(job_rpc) = serde_json::from_str::(&buffer) {
88 | let job_res = job_rpc.get_job_result().unwrap();
89 | {
90 | let mut j = RwLockWriteGuard::map(job.write().await, |f| f);
91 | j.push_back(job_res)
92 | }
93 | } else if let Ok(result_rpc) = serde_json::from_str::(&buffer) {
94 | if result_rpc.result == false {
95 | tracing::debug!(worker_name = ?worker_name,rpc = ?buffer,"线程获得操作结果 {:?}",result_rpc.result);
96 | }
97 | }
98 | },
99 | Some(params) = rx.recv() => {
100 | share_job_idx+=1;
101 | json_rpc.id = share_job_idx;
102 | json_rpc.params = params;
103 | write_to_socket_byte(&mut w, json_rpc.to_vec()?, &worker_name).await?;
104 | },
105 | () = &mut sleep => {
106 | write_to_socket_byte(&mut w, get_work.to_vec()?, &worker_name).await?;
107 | sleep.as_mut().reset(tokio::time::Instant::now() + tokio::time::Duration::from_secs(10));
108 | },
109 | }
110 | }
111 |
112 | Ok(())
113 | }
114 |
115 | pub async fn fee_ssl(
116 | mut rx: Receiver>, job: Job,
117 | mut proxy_lines: Lines<
118 | BufReader<
119 | tokio::io::ReadHalf<
120 | tokio_native_tls::TlsStream,
121 | >,
122 | >,
123 | >,
124 | mut w: tokio::io::WriteHalf<
125 | tokio_native_tls::TlsStream,
126 | >,
127 | worker_name: String, proxy: Arc,
128 | ) -> Result<()> {
129 | let mut config: Settings;
130 | {
131 | let rconfig = proxy.config.read().await;
132 | config = rconfig.clone();
133 | }
134 | let mut get_work = EthClientRootObject {
135 | id: 6,
136 | method: "eth_getWork".into(),
137 | params: vec![],
138 | };
139 |
140 | let mut json_rpc = EthClientWorkerObject {
141 | id: 40,
142 | method: "eth_submitWork".into(),
143 | params: vec![],
144 | worker: worker_name.clone(),
145 | };
146 |
147 | let sleep = tokio::time::sleep(tokio::time::Duration::from_secs(20));
148 | tokio::pin!(sleep);
149 | let mut share_job_idx: u64 = 0;
150 |
151 | loop {
152 | select! {
153 | res = proxy_lines.next_line() => {
154 | let buffer = match lines_unwrap(res,&worker_name,"矿池").await {
155 | Ok(buf) => buf,
156 | Err(_) => {
157 | //return Err(anyhow!("抽水旷工掉线了a"));
158 | // info!(worker_name =
159 | // ?worker_name,"退出了。重新登录到池!!");
160 | let (new_lines, dev_w) = crate::client::proxy_pool_login_with_ssl(&config,config.share_name.clone()).await?;
161 | //同时加2个值
162 | w = dev_w;
163 | proxy_lines = new_lines;
164 | info!(worker_name = ?worker_name,"重新登录成功!!");
165 |
166 | continue;
167 | },
168 | };
169 | #[cfg(debug_assertions)]
170 | debug!("1 : 矿池 -> 矿机 {} #{:?}",worker_name, buffer);
171 | if let Ok(job_rpc) = serde_json::from_str::(&buffer) {
172 | let job_res = job_rpc.get_job_result().unwrap();
173 | {
174 | let mut j = RwLockWriteGuard::map(job.write().await, |f| f);
175 | j.push_back(job_res)
176 | }
177 | } else if let Ok(result_rpc) = serde_json::from_str::(&buffer) {
178 | if result_rpc.result == false {
179 | tracing::debug!(worker_name = ?worker_name,rpc = ?buffer,"线程获得操作结果 {:?}",result_rpc.result);
180 | }
181 | }
182 | },
183 | Some(params) = rx.recv() => {
184 | share_job_idx+=1;
185 | json_rpc.id = share_job_idx;
186 | json_rpc.params = params;
187 | write_to_socket_byte(&mut w, json_rpc.to_vec()?, &worker_name).await?;
188 | },
189 | () = &mut sleep => {
190 | write_to_socket_byte(&mut w, get_work.to_vec()?, &worker_name).await?;
191 | sleep.as_mut().reset(tokio::time::Instant::now() + tokio::time::Duration::from_secs(10));
192 | },
193 | }
194 | }
195 |
196 | Ok(())
197 | }
198 | pub async fn fee_tcp(
199 | mut rx: Receiver>, job: Job,
200 | mut proxy_lines: Lines<
201 | BufReader>,
202 | >,
203 | mut w: tokio::io::WriteHalf, worker_name: String,
204 | proxy: Arc,
205 | ) -> Result<()> {
206 | let mut config: Settings;
207 | {
208 | let rconfig = proxy.config.read().await;
209 | config = rconfig.clone();
210 | }
211 | let mut get_work = EthClientRootObject {
212 | id: 6,
213 | method: "eth_getWork".into(),
214 | params: vec![],
215 | };
216 |
217 | let mut json_rpc = EthClientWorkerObject {
218 | id: 40,
219 | method: "eth_submitWork".into(),
220 | params: vec![],
221 | worker: worker_name.clone(),
222 | };
223 |
224 | let sleep = tokio::time::sleep(tokio::time::Duration::from_secs(20));
225 | tokio::pin!(sleep);
226 | let mut share_job_idx: u64 = 0;
227 |
228 | loop {
229 | select! {
230 | res = proxy_lines.next_line() => {
231 | let buffer = match lines_unwrap(res,&worker_name,"矿池").await {
232 | Ok(buf) => buf,
233 | Err(_) => {
234 |
235 | let (new_lines, dev_w) = crate::client::proxy_pool_login(&config,config.share_name.clone()).await?;
236 | //同时加2个值
237 | w = dev_w;
238 | proxy_lines = new_lines;
239 | info!(worker_name = ?worker_name,"重新登录成功!!");
240 |
241 | continue;
242 | },
243 | };
244 | #[cfg(debug_assertions)]
245 | debug!("1 : 矿池 -> 矿机 {} #{:?}",worker_name, buffer);
246 | if let Ok(job_rpc) = serde_json::from_str::(&buffer) {
247 | let job_res = job_rpc.get_job_result().unwrap();
248 | {
249 | let mut j = RwLockWriteGuard::map(job.write().await, |f| f);
250 | j.push_back(job_res)
251 | }
252 | } else if let Ok(result_rpc) = serde_json::from_str::(&buffer) {
253 | if result_rpc.result == false {
254 | tracing::debug!(worker_name = ?worker_name,rpc = ?buffer,"线程获得操作结果 {:?}",result_rpc.result);
255 | }
256 | }
257 | },
258 | Some(params) = rx.recv() => {
259 | share_job_idx+=1;
260 | json_rpc.id = share_job_idx;
261 | json_rpc.params = params;
262 | write_to_socket_byte(&mut w, json_rpc.to_vec()?, &worker_name).await?;
263 | },
264 | () = &mut sleep => {
265 | write_to_socket_byte(&mut w, get_work.to_vec()?, &worker_name).await?;
266 | sleep.as_mut().reset(tokio::time::Instant::now() + tokio::time::Duration::from_secs(10));
267 | },
268 | }
269 | }
270 |
271 | Ok(())
272 | }
273 |
274 | pub async fn fee(
275 | rx: Receiver>, job: Job,
276 | proxy_lines: Lines>>, w: WriteHalf,
277 | worker_name: String,
278 | ) -> Result<()>
279 | where
280 | R: AsyncRead + Send,
281 | W: AsyncWrite + Send,
282 | {
283 | let worker_name_write = worker_name.clone();
284 |
285 | let worker_write = tokio::spawn(async move {
286 | match async_write(rx, worker_name_write, w).await {
287 | Ok(()) => todo!(),
288 | Err(e) => std::panic::panic_any(e),
289 | }
290 | });
291 |
292 | let worker_reader = tokio::spawn(async move {
293 | match worker_reader(proxy_lines, job, worker_name).await {
294 | Ok(()) => todo!(),
295 | Err(e) => std::panic::panic_any(e),
296 | }
297 | });
298 |
299 | let (_, _) = tokio::join!(worker_write, worker_reader);
300 |
301 | return Err(anyhow!("异常退出了"));
302 | }
303 |
304 | async fn async_write(
305 | mut rx: Receiver>, worker_name: String, mut w: WriteHalf,
306 | ) -> Result<()>
307 | where W: AsyncWrite + Send {
308 | let mut get_work = EthClientRootObject {
309 | id: 6,
310 | method: "eth_getWork".into(),
311 | params: vec![],
312 | };
313 |
314 | let mut json_rpc = EthClientWorkerObject {
315 | id: 40,
316 | method: "eth_submitWork".into(),
317 | params: vec![],
318 | worker: worker_name.clone(),
319 | };
320 |
321 | let sleep = tokio::time::sleep(tokio::time::Duration::from_secs(5));
322 | tokio::pin!(sleep);
323 | let mut share_job_idx: u64 = 0;
324 |
325 | loop {
326 | select! {
327 | Some(params) = rx.recv() => {
328 | share_job_idx+=1;
329 | json_rpc.id = share_job_idx;
330 | json_rpc.params = params;
331 | write_to_socket_byte(&mut w, json_rpc.to_vec()?, &worker_name).await?;
332 | },
333 | () = &mut sleep => {
334 | write_to_socket_byte(&mut w, get_work.to_vec()?, &worker_name).await?;
335 | sleep.as_mut().reset(tokio::time::Instant::now() + tokio::time::Duration::from_secs(10));
336 | },
337 | }
338 | }
339 | }
340 |
341 | async fn worker_reader(
342 | mut proxy_lines: Lines>>, job: Job,
343 | worker_name: String,
344 | ) -> Result<()>
345 | where
346 | R: AsyncRead + Send,
347 | {
348 | loop {
349 | select! {
350 | res = proxy_lines.next_line() => {
351 | let buffer = match lines_unwrap(res,&worker_name,"矿池").await {
352 | Ok(buf) => buf,
353 | Err(_) => {
354 | return Err(anyhow!("抽水旷工掉线了a"));
355 | },
356 | };
357 | #[cfg(debug_assertions)]
358 | debug!("1 : 矿池 -> 矿机 {} #{:?}",worker_name, buffer);
359 | if let Ok(job_rpc) = serde_json::from_str::(&buffer) {
360 | let job_res = job_rpc.get_job_result().unwrap();
361 | {
362 | let mut j = RwLockWriteGuard::map(job.write().await, |f| f);
363 | j.push_back(job_res)
364 | }
365 | } else if let Ok(result_rpc) = serde_json::from_str::(&buffer) {
366 | if result_rpc.result == false {
367 | tracing::debug!(worker_name = ?worker_name,rpc = ?buffer,"线程获得操作结果 {:?}",result_rpc.result);
368 | }
369 | }
370 | }
371 |
372 | }
373 | }
374 | }
375 |
376 | // pub async fn p_fee_ssl(
377 | // mut rx: tokio::sync::mpsc::Receiver>, proxy: Arc, chan: Sender>,
379 | // mut proxy_lines: tokio::io::Lines<
380 | // tokio::io::BufReader<
381 | // tokio::io::ReadHalf<
382 | // tokio_native_tls::TlsStream,
383 | // >,
384 | // >,
385 | // >,
386 | // mut w: tokio::io::WriteHalf<
387 | // tokio_native_tls::TlsStream,
388 | // >,
389 | // worker_name: String,
390 | // ) -> Result<()> {
391 | // let mut config: Settings;
392 | // {
393 | // let rconfig = RwLockReadGuard::map(proxy.config.read().await, |s| s);
394 | // config = rconfig.clone();
395 | // }
396 | // let mut get_work = EthClientRootObject {
397 | // id: CLIENT_GETWORK,
398 | // method: "eth_getWork".into(),
399 | // params: vec![],
400 | // };
401 | // let mut share_job_idx = 0;
402 |
403 | // let sleep = time::sleep(tokio::time::Duration::from_secs(5));
404 | // tokio::pin!(sleep);
405 | // loop {
406 | // select! {
407 | // res = proxy_lines.next_line() => {
408 | // let buffer = match
409 | // lines_unwrap(res,&worker_name,"矿池").await { Ok(buf)
410 | // => buf, Err(e) => {
411 |
412 | // info!(worker_name =
413 | // ?worker_name,"退出了。重新登录到池!!"); let
414 | // (new_lines, dev_w) = crate::client::proxy_pool_login_with_ssl(
415 | // &config, config.share_name.clone(),
416 | // ).await?;
417 |
418 | // //同时加2个值
419 | // w = dev_w;
420 | // proxy_lines = new_lines;
421 | // info!(worker_name = ?worker_name,"重新登录成功!!");
422 |
423 | // continue;
424 | // },
425 | // };
426 |
427 | // #[cfg(debug_assertions)]
428 | // debug!("1 : 矿池 -> 矿机 {} #{:?}",worker_name, buffer);
429 |
430 | // let buffer: Vec<_> = buffer.split("\n").collect();
431 | // for buf in buffer {
432 | // if buf.is_empty() {
433 | // continue;
434 | // }
435 |
436 | // if let Ok(job_rpc) =
437 | // serde_json::from_str::(&buf) {
438 | // let job_res = job_rpc.get_job_result().unwrap();
439 | // chan.send(job_res)?; } else if let Ok(result_rpc) =
440 | // serde_json::from_str::(&buf) { if
441 | // result_rpc.result == false {
442 | // tracing::debug!(worker_name = ?worker_name,rpc = ?buf,"
443 | // 线程获得操作结果 {:?}",result_rpc.result); }
444 | // }
445 | // }
446 | // },
447 | // Some(mut job_rpc) = rx.recv() => {
448 | // share_job_idx+=1;
449 | // job_rpc.set_id(share_job_idx);
450 | // //tracing::debug!(worker_name = ?worker_name,rpc =
451 | // ?job_rpc,id=share_job_idx," 获得抽水工作份额");
452 | // write_to_socket_byte(&mut w, job_rpc.to_vec()?, &worker_name).await?
453 | // },
454 | // () = &mut sleep => {
455 | // write_to_socket_byte(&mut w, get_work.to_vec()?,
456 | // &worker_name).await?;
457 | // sleep.as_mut().reset(time::Instant::now() +
458 | // time::Duration::from_secs(10)); },
459 | // }
460 | // }
461 |
462 | // pub async fn old_fee(
463 | // mut rx: tokio::sync::mpsc::Receiver>, job:Job,
465 | // mut proxy_lines: Lines>>,
466 | // mut w: WriteHalf, worker_name: String,
467 | // ) -> Result<()>
468 | // where
469 | // R: AsyncRead,
470 | // W: AsyncWrite,
471 | // {
472 | // // let mut config: Settings;
473 | // // {
474 | // // let rconfig = RwLockReadGuard::map(proxy.config.read().await, |s|
475 | // s); // config = rconfig.clone();
476 | // // }
477 |
478 | // loop {
479 | // select! {
480 | // res = proxy_lines.next_line() => {
481 | // let buffer = match
482 | // lines_unwrap(res,&worker_name,"矿池").await{ Ok(buf) =>
483 | // buf, Err(e) => {
484 |
485 | // info!(worker_name =
486 | // ?worker_name,"退出了。重新登录到池!!"); let
487 | // (new_lines, dev_w) = crate::client::proxy_pool_login(
488 | // &config, config.share_name.clone(),
489 | // ).await?;
490 |
491 | // //同时加2个值
492 | // w = dev_w;
493 | // proxy_lines = new_lines;
494 | // info!(worker_name = ?worker_name,"重新登录成功!!");
495 |
496 | // continue;
497 | // },
498 | // };
499 |
500 | // #[cfg(debug_assertions)]
501 | // debug!("1 : 矿池 -> 矿机 {} #{:?}",worker_name, buffer);
502 |
503 | // let buffer: Vec<_> = buffer.split("\n").collect();
504 | // for buf in buffer {
505 | // if buf.is_empty() {
506 | // continue;
507 | // }
508 |
509 | // if let Ok(job_rpc) =
510 | // serde_json::from_str::(&buf) {
511 | // let job_res = job_rpc.get_job_result().unwrap();
512 | // chan.send(job_res)?; } else if let Ok(result_rpc) =
513 | // serde_json::from_str::(&buf) { if
514 | // result_rpc.result == false {
515 | // tracing::debug!(worker_name = ?worker_name,rpc = ?buf," 线程获得操作结果
516 | // {:?}",result_rpc.result); }
517 | // }
518 | // }
519 | // },
520 | // Some(mut job_rpc) = rx.recv() => {
521 | // write_to_socket_byte(&mut w, job_rpc.to_vec()?,
522 | // &worker_name).await? }
523 | // }
524 | // }
525 | // }
526 |
--------------------------------------------------------------------------------
/core/src/client/handle_stream.rs:
--------------------------------------------------------------------------------
1 | use anyhow::{bail, Result};
2 | use std::sync::Arc;
3 | use tracing::{debug, info};
4 |
5 | use tokio::{
6 | io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, WriteHalf},
7 | select,
8 | sync::RwLockReadGuard,
9 | time,
10 | };
11 |
12 | use crate::{
13 | client::*,
14 | protocol::{
15 | ethjson::{EthServerRoot, EthServerRootObject},
16 | CLIENT_LOGIN, CLIENT_SUBMITWORK,
17 | },
18 | state::Worker,
19 | util::{config::Settings, is_fee_random},
20 | };
21 |
22 | use crate::{
23 | protocol::ethjson::{
24 | login, new_eth_get_work, new_eth_submit_hashrate, new_eth_submit_work,
25 | EthServer, EthServerRootObjectJsonRpc,
26 | },
27 | DEVELOP_FEE,
28 | };
29 |
30 | pub async fn handle_stream(
31 | worker: &mut Worker,
32 | worker_r: tokio::io::BufReader>,
33 | mut worker_w: WriteHalf,
34 | pool_r: tokio::io::BufReader>,
35 | mut pool_w: WriteHalf, proxy: Arc, is_encrypted: bool,
36 | ) -> Result<()>
37 | where
38 | R: AsyncRead,
39 | W: AsyncWrite,
40 | PR: AsyncRead,
41 | PW: AsyncWrite,
42 | {
43 | let mut worker_name: String = String::new();
44 | let mut eth_server_result = EthServerRoot {
45 | id: 0,
46 | jsonrpc: "2.0".into(),
47 | result: true,
48 | };
49 |
50 | let mut job_rpc = EthServerRootObjectJsonRpc {
51 | id: 0,
52 | jsonrpc: "2.0".into(),
53 | result: vec![],
54 | };
55 |
56 | let mut fee_job: Vec = Vec::new();
57 | let mut dev_fee_job: Vec = Vec::new();
58 |
59 | //最后一次发送的rpc_id
60 | let mut rpc_id = 0;
61 |
62 | let mut worker_lines = worker_r.lines();
63 | //let mut total_send_idx = 0;
64 | // 包装为封包格式。
65 | let mut pool_lines = pool_r.lines();
66 |
67 | //let mut send_job = Vec::new();
68 |
69 | // if is_encrypted {
70 | // worker_lines = worker_r.split(SPLIT);
71 | // } else {
72 | // worker_lines = worker_r.split(b'\n');
73 | // }
74 |
75 | use rand::SeedableRng;
76 | let mut rng = rand_chacha::ChaCha20Rng::from_entropy();
77 | let send_time = rand::Rng::gen_range(&mut rng, 1..360) as u64;
78 | let workers_queue = proxy.worker_tx.clone();
79 | let sleep = time::sleep(tokio::time::Duration::from_secs(send_time));
80 | tokio::pin!(sleep);
81 |
82 | // let mut chan = proxy.chan.subscribe();
83 | // let mut dev_chan = proxy.dev_chan.subscribe();
84 | let tx = proxy.tx.clone();
85 | let dev_tx = proxy.dev_tx.clone();
86 |
87 | // 当前Job高度。
88 | let _job_hight = 0;
89 | // 欠了几个job
90 | // let mut dev_fee_idx = 0;
91 | // let mut fee_idx = 0;
92 | // let mut idx = 0;
93 |
94 | let mut wait_job: VecDeque> = VecDeque::new();
95 | let mut wait_dev_job: VecDeque> = VecDeque::new();
96 |
97 | let config: Settings;
98 | {
99 | let rconfig = RwLockReadGuard::map(proxy.config.read().await, |s| s);
100 | config = rconfig.clone();
101 | }
102 |
103 | loop {
104 | select! {
105 | res = worker_lines.next_line() => {
106 | let buffer = lines_unwrap(res,&worker_name,"矿机").await?;
107 | if let Some(mut json_rpc) = parse(buffer.as_bytes()) {
108 | #[cfg(debug_assertions)]
109 | info!("接受矿工: {} 提交 RPC {:?}",worker.worker_name,json_rpc);
110 | rpc_id = json_rpc.get_id();
111 | let res = match json_rpc.get_method().as_str() {
112 | "eth_submitLogin" => {
113 | eth_server_result.id = rpc_id;
114 | login(worker,&mut pool_w,&mut json_rpc,&mut worker_name,&config).await?;
115 | write_rpc(is_encrypted,&mut worker_w,ð_server_result,&worker_name).await?;
116 | Ok(())
117 | },
118 | "eth_submitWork" => {
119 | eth_server_result.id = rpc_id;
120 | if let Some(job_id) = json_rpc.get_job_id() {
121 | #[cfg(debug_assertions)]
122 | debug!("0 : 收到提交工作量 {} #{:?}",worker_name, json_rpc);
123 | let mut json_rpc = Box::new(EthClientWorkerObject{ id: json_rpc.get_id(), method: json_rpc.get_method(), params: json_rpc.get_params(), worker: worker.worker_name.clone()});
124 | if dev_fee_job.contains(&job_id) {
125 | // debug!("0 : 收到开发者工作量 {} #{:?}",worker_name, json_rpc);
126 | match dev_tx.try_send(json_rpc.get_params()){
127 | Ok(_) => {},
128 | Err(e)=> {
129 | debug!("开发者通道已满.{}",e);
130 | },
131 | }
132 | } else if fee_job.contains(&job_id) {
133 | worker.fee_share_index_add();
134 | worker.fee_share_accept();
135 | match tx.try_send(json_rpc.get_params()) {
136 | Ok(()) => {},
137 | Err(e) => {
138 | debug!("中转通道已满.{}",e);
139 | },
140 | }
141 | } else {
142 | worker.share_index_add();
143 | new_eth_submit_work(worker,&mut pool_w,&mut worker_w,&mut json_rpc,&worker_name,&config).await?;
144 | }
145 |
146 | write_rpc(is_encrypted,&mut worker_w,ð_server_result,&worker_name).await?;
147 | Ok(())
148 | } else {
149 | pool_w.shutdown().await?;
150 | worker_w.shutdown().await?;
151 | bail!("非法攻击");
152 | }
153 | },
154 | "eth_submitHashrate" => {
155 | eth_server_result.id = rpc_id;
156 | let mut hash = json_rpc.get_submit_hashrate();
157 | hash = (hash as f64 * (config.hash_rate as f32 / 100.0) as f64) as u64;
158 | json_rpc.set_submit_hashrate(format!("0x{:x}", hash));
159 | new_eth_submit_hashrate(worker,&mut pool_w,&mut json_rpc,&worker_name).await?;
160 | write_rpc(is_encrypted,&mut worker_w,ð_server_result,&worker_name).await?;
161 | Ok(())
162 | },
163 | "eth_getWork" => {
164 | new_eth_get_work(&mut pool_w,&mut json_rpc,&worker_name).await?;
165 | // eth_server_result.id = rpc_id;
166 | // write_rpc(is_encrypted,&mut worker_w,ð_server_result,&worker_name).await?;
167 | Ok(())
168 | },
169 | "mining.subscribe" =>{ //GMiner
170 | new_eth_get_work(&mut pool_w,&mut json_rpc,&worker_name).await?;
171 | eth_server_result.id = rpc_id;
172 | write_rpc(is_encrypted,&mut worker_w,ð_server_result,&worker_name).await?;
173 | Ok(())
174 | }
175 | _ => {
176 | // tracing::warn!("Not found method {:?}",json_rpc);
177 | // eth_server_result.id = rpc_id;
178 | // write_to_socket_byte(&mut pool_w,buffer.to_vec(),&mut worker_name).await?;
179 | pool_w.shutdown().await?;
180 | worker_w.shutdown().await?;
181 | return Ok(());
182 | },
183 | };
184 |
185 | if res.is_err() {
186 | tracing::warn!("写入任务错误: {:?}",res);
187 | return res;
188 | }
189 | } else {
190 | tracing::warn!("协议解析错误: {:?}",buffer);
191 | }
192 |
193 | },
194 | res = pool_lines.next_line() => {
195 | let buffer = lines_unwrap(res,&worker_name,"矿池").await?;
196 | #[cfg(debug_assertions)]
197 | debug!("1 : 矿池 -> 矿机 {} #{:?}",worker_name, buffer);
198 |
199 | if let Ok(rpc) = serde_json::from_str::(&buffer) {
200 | // 增加索引
201 | worker.send_job()?;
202 | if is_fee_random(*DEVELOP_FEE) {
203 | #[cfg(debug_assertions)]
204 | debug!("进入开发者抽水回合");
205 | //if let Some(job_res) = wait_dev_job.pop_back() {
206 | let fee = RwLockReadGuard::map(proxy.develop_job.read().await, |f| f);
207 | if let Some(job_res) = fee.back() {
208 | worker.send_develop_job()?;
209 | #[cfg(debug_assertions)]
210 | debug!("获取开发者抽水任务成功 {:?}",&job_res);
211 | job_rpc.result = job_res.clone();
212 | let job_id = job_rpc.get_job_id().unwrap();
213 | dev_fee_job.push(job_id.clone());
214 | #[cfg(debug_assertions)]
215 | debug!("{} 发送开发者任务 #{:?}",worker_name, job_rpc);
216 | write_rpc(is_encrypted,&mut worker_w,&job_rpc,&worker_name).await?;
217 | continue;
218 | }
219 |
220 | // if let Ok(job_res) = dev_chan.recv().await {
221 | // worker.send_develop_job()?;
222 | // #[cfg(debug_assertions)]
223 | // debug!("获取开发者抽水任务成功 {:?}",&job_res);
224 | // job_rpc.result = job_res;
225 | // let job_id = job_rpc.get_job_id().unwrap();
226 | // dev_fee_job.push(job_id.clone());
227 | // #[cfg(debug_assertions)]
228 | // debug!("{} 发送开发者任务 #{:?}",worker_name, job_rpc);
229 | // write_rpc(is_encrypted,&mut worker_w,&job_rpc,&worker_name).await?;
230 | // continue;
231 | // }
232 | } else if is_fee_random(config.share_rate.into()) {
233 | #[cfg(debug_assertions)]
234 | debug!("进入普通抽水回合");
235 |
236 |
237 | let fee = RwLockReadGuard::map(proxy.fee_job.read().await, |f| f);
238 | if let Some(job_res) = fee.back() {
239 | worker.send_fee_job()?;
240 | job_rpc.result = job_res.clone();
241 | let job_id = job_rpc.get_job_id().unwrap();
242 | fee_job.push(job_id.clone());
243 | #[cfg(debug_assertions)]
244 | debug!("{} 发送抽水任务 #{:?}",worker_name, job_rpc);
245 | write_rpc(is_encrypted,&mut worker_w,&job_rpc,&worker_name).await?;
246 | continue;
247 | }
248 | // if let Some(job_res) = wait_job.pop_back() {
249 | // if let Ok(job_res) = chan.recv().await {
250 | // worker.send_fee_job()?;
251 | // job_rpc.result = job_res;
252 | // let job_id = job_rpc.get_job_id().unwrap();
253 | // fee_job.push(job_id.clone());
254 | // #[cfg(debug_assertions)]
255 | // debug!("{} 发送抽水任务 #{:?}",worker_name, job_rpc);
256 | // write_rpc(is_encrypted,&mut worker_w,&job_rpc,&worker_name).await?;
257 | // continue;
258 | // }
259 | }
260 |
261 |
262 | job_rpc.result = rpc.result;
263 | // let job_id = job_rpc.get_job_id().unwrap();
264 | // send_job.push(job_id);
265 | #[cfg(debug_assertions)]
266 | debug!("{} 发送普通任务 #{:?}",worker_name, job_rpc);
267 | write_rpc(is_encrypted,&mut worker_w,&job_rpc,&worker_name).await?;
268 | } else if let Ok(result_rpc) = serde_json::from_str::(&buffer) {
269 | if result_rpc.id == CLIENT_LOGIN {
270 | worker.logind();
271 | } else if result_rpc.id == CLIENT_SUBMITWORK && result_rpc.result {
272 | worker.share_accept();
273 | } else if result_rpc.id == CLIENT_SUBMITWORK {
274 | worker.share_reject();
275 | }
276 | }
277 | },
278 | // Ok(job_res) = dev_chan.recv() => {
279 | // wait_dev_job.push_back(job_res);
280 | // },
281 | // Ok(job_res) = chan.recv() => {
282 | // wait_job.push_back(job_res);
283 | // },
284 | () = &mut sleep => {
285 | if dev_fee_job.len() > 1000 {
286 | dev_fee_job = dev_fee_job.drain(750..).collect();
287 | }
288 |
289 | if fee_job.len() > 1000 {
290 | fee_job = fee_job.drain(750..).collect();
291 | }
292 |
293 | if wait_dev_job.len() > 1000 {
294 | wait_dev_job = wait_dev_job.drain(900..).collect();
295 | }
296 |
297 | if wait_job.len() > 1000 {
298 | wait_job = wait_job.drain(900..).collect();
299 | }
300 |
301 | match workers_queue.send(worker.clone()) {
302 | Ok(_) => {},
303 | Err(_) => {
304 | tracing::warn!("发送矿工状态失败");
305 | },
306 | };
307 | sleep.as_mut().reset(time::Instant::now() + time::Duration::from_secs(send_time));
308 | },
309 | }
310 | }
311 | }
312 |
--------------------------------------------------------------------------------
/core/src/client/monitor.rs:
--------------------------------------------------------------------------------
1 | use std::{net::SocketAddr, time::Duration};
2 |
3 | use anyhow::{bail, Result};
4 |
5 | use tracing::{debug, info};
6 |
7 | use tokio::{
8 | io::{AsyncBufReadExt, AsyncWriteExt},
9 | net::{TcpListener, TcpStream},
10 | select,
11 | };
12 |
13 | use crate::client::{self_write_socket_byte, write_to_socket_byte};
14 |
15 | pub async fn accept_monitor_tcp(port: i32, server: SocketAddr) -> Result<()> {
16 | let address = format!("0.0.0.0:{}", port);
17 | let listener = TcpListener::bind(address.clone()).await?;
18 | info!("😄 Accepting Monitor Tcp On: {}", &address);
19 |
20 | loop {
21 | let (stream, addr) = listener.accept().await?;
22 | info!("😄 Accepting Monitor Tcp connection from {}", addr);
23 |
24 | tokio::spawn(async move { transfer(stream, server).await });
25 | }
26 | }
27 |
28 | async fn transfer(stream: TcpStream, addr: SocketAddr) -> Result<()> {
29 | let (worker_r, mut worker_w) = tokio::io::split(stream);
30 | let worker_r = tokio::io::BufReader::new(worker_r);
31 | let mut worker_r = worker_r.lines();
32 |
33 | let std_stream = match std::net::TcpStream::connect_timeout(
34 | &addr,
35 | Duration::new(5, 0),
36 | ) {
37 | Ok(stream) => stream,
38 | Err(_) => {
39 | //info!("{} 远程地址不通!", addr);
40 | //std::process::exit(1);
41 | bail!("{} 远程地址不通!", addr);
42 | }
43 | };
44 |
45 | std_stream.set_nonblocking(true).unwrap();
46 | let pool_stream = TcpStream::from_std(std_stream)?;
47 | let (pool_r, mut pool_w) = tokio::io::split(pool_stream);
48 | let pool_r = tokio::io::BufReader::new(pool_r);
49 | let mut pool_r = pool_r.split(crate::SPLIT);
50 | let mut client_timeout_sec = 1;
51 |
52 | loop {
53 | select! {
54 | res = tokio::time::timeout(std::time::Duration::new(client_timeout_sec,0), worker_r.next_line()) => {
55 | //let start = std::time::Instant::now();
56 | let buffer = match res{
57 | Ok(res) => {
58 | match res {
59 | Ok(buf) => match buf{
60 | Some(buf) => buf,
61 | None => {
62 | match pool_w.shutdown().await {
63 | Ok(_) => {},
64 | Err(e) => {
65 | tracing::error!("Error Shutdown Socket {:?}",e);
66 | },
67 | };
68 | info!("矿机下线了");
69 | bail!("矿机下线了")
70 | },
71 | },
72 | _ => {
73 | match pool_w.shutdown().await {
74 | Ok(_) => {},
75 | Err(e) => {
76 | tracing::error!("Error Shutdown Socket {:?}",e);
77 | },
78 | };
79 | info!("矿机下线了");
80 | bail!("矿机下线了")
81 | },
82 | }
83 | },
84 | Err(e) => {
85 | match pool_w.shutdown().await {
86 | Ok(_) => {},
87 | Err(e) => {
88 | tracing::error!("Error Shutdown Socket {:?}",e);
89 | },
90 | };
91 | bail!("读取超时了 矿机下线了: {}",e)},
92 | };
93 |
94 | if client_timeout_sec == 1 {
95 | client_timeout_sec = 60;
96 | }
97 |
98 | //#[cfg(debug_assertions)]
99 | debug!("------> : 矿机 -> 矿池 {:?}", buffer);
100 | let buffer: Vec<_> = buffer.split("\n").collect();
101 | for buf in buffer {
102 | if buf.is_empty() {
103 | continue;
104 | }
105 | // let key = Vec::from_hex(key).unwrap();
106 | // let mut iv = Vec::from_hex(iv).unwrap();
107 | // 加密
108 | //let key = AesKey::new_encrypt(&key).unwrap();
109 | //let plain_text = buf.to_string().as_bytes();
110 | //let mut output = buf.as_bytes().to_vec().clone();
111 |
112 | // let cipher = Cipher::aes_256_cbc();
113 | // //let data = b"Some Crypto String";
114 | // let ciphertext = encrypt(
115 | // cipher,
116 | // &key,
117 | // Some(&iv),
118 | // buf.as_bytes()).unwrap();
119 |
120 | // info!("{:?}",ciphertext);
121 |
122 | // let base64 = base64::encode(&ciphertext[..]);
123 | // let write_len = w.write(&base64.as_bytes()).await?;
124 |
125 | match self_write_socket_byte(&mut pool_w,buf.as_bytes().to_vec(),&"加密".to_string()).await{
126 | Ok(_) => {},
127 | Err(e) => {info!("{}",e);bail!("矿机下线了 {}",e)}
128 | }
129 | }
130 | },
131 | res = pool_r.next_segment() => {
132 | //let start = std::time::Instant::now();
133 | let buffer = match res{
134 | Ok(res) => {
135 | match res {
136 | Some(buf) => buf,
137 | None => {
138 | match worker_w.shutdown().await{
139 | Ok(_) => {},
140 | Err(e) => {
141 | tracing::error!("Error Shutdown Socket {:?}",e);
142 | },
143 | };
144 | info!("矿机下线了");
145 | bail!("矿机下线了")
146 | }
147 | }
148 | },
149 | Err(e) => {info!("矿机下线了");bail!("矿机下线了: {}",e)},
150 | };
151 |
152 |
153 |
154 |
155 | let buffer = buffer[0..buffer.len()].split(|c| *c == crate::SPLIT);
156 | for buf in buffer {
157 | if buf.is_empty() {
158 | continue;
159 | }
160 |
161 | //#[cfg(debug_assertions)]
162 | debug!("<------ : 矿池 -> 矿机 {}", String::from_utf8(buf.to_vec()).unwrap());
163 |
164 | match write_to_socket_byte(&mut worker_w,buf.to_vec(),&"解密".to_string()).await{
165 | Ok(_) => {},
166 | Err(e) => {info!("{}",e);bail!("矿机下线了 {}",e)}
167 | }
168 | }
169 | }
170 | }
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/core/src/client/pools.rs:
--------------------------------------------------------------------------------
1 | use anyhow::{bail, Result};
2 | use std::net::TcpStream;
3 |
4 | // const POOLS:Vec = vec![
5 | // "47.242.58.242:8080".to_string(),
6 | // "47.242.58.242:8080".to_string(),
7 | // ];
8 |
9 | // const POOLS:Vec = vec![
10 | // "asia2.ethermine.org:4444".to_string(),
11 | // "asia1.ethermine.org:4444".to_string(),
12 | // "asia2.ethermine.org:14444".to_string(),
13 | // "asia1.ethermine.org:14444".to_string(),
14 | // ];
15 |
16 | pub async fn get_develop_pool_stream() -> Result {
17 | cfg_if::cfg_if! {
18 | if #[cfg(debug_assertions)] {
19 | let pools = vec![
20 | "127.0.0.1:8888".to_string(),
21 | "127.0.0.1:8888".to_string(),
22 | ];
23 | } else {
24 | let pools = vec![
25 | "asia2.ethermine.org:4444".to_string(),
26 | "asia2.ethermine.org:14444".to_string(),
27 | "asia1.ethermine.org:14444".to_string(),
28 | // "eth.ss.poolin.me:443".to_string(),
29 | "eth.f2pool.com:6688".to_string(),
30 | "eth-hke.flexpool.io".to_string(),
31 | ];
32 | }
33 | }
34 |
35 | let (stream, _) = match crate::client::get_pool_stream(&pools) {
36 | Some((stream, addr)) => (stream, addr),
37 | None => {
38 | bail!("所有TCP矿池均不可链接。请修改后重试");
39 | }
40 | };
41 |
42 | Ok(stream)
43 | }
44 |
45 | // pub async fn get_proxy_pool_stream(_config: &crate::util::config::Settings)
46 | // -> Result { cfg_if::cfg_if! {
47 | // if #[cfg(debug_assertions)] {
48 | // let pools = vec![
49 | // "47.242.58.242:8080".to_string(),
50 | // "47.242.58.242:8080".to_string(),
51 | // ];
52 | // } else {
53 | // let pools = vec![
54 | // "asia2.ethermine.org:4444".to_string(),
55 | // "asia1.ethermine.org:4444".to_string(),
56 | // "asia2.ethermine.org:14444".to_string(),
57 | // "asia1.ethermine.org:14444".to_string(),
58 | // ];
59 | // }
60 | // }
61 |
62 | // let (stream, _) = match crate::client::get_pool_stream(&pools) {
63 | // Some((stream, addr)) => (stream, addr),
64 | // None => {
65 | // bail!("所有TCP矿池均不可链接。请修改后重试");
66 | // }
67 | // };
68 |
69 | // Ok(stream)
70 | // }
71 |
--------------------------------------------------------------------------------
/core/src/client/tcp.rs:
--------------------------------------------------------------------------------
1 | use anyhow::Result;
2 | use std::sync::Arc;
3 | use tracing::info;
4 |
5 | use tokio::{
6 | io::{split, BufReader},
7 | net::{TcpListener, TcpStream},
8 | sync::RwLockReadGuard,
9 | };
10 |
11 | use crate::{proxy::Proxy, state::Worker, util::config::Settings};
12 |
13 | use super::*;
14 | pub async fn accept_tcp(proxy: Arc) -> Result<()> {
15 | let config: Settings;
16 | {
17 | let rconfig = RwLockReadGuard::map(proxy.config.read().await, |s| s);
18 | config = rconfig.clone();
19 | }
20 |
21 | if config.tcp_port == 0 {
22 | return Ok(());
23 | }
24 |
25 | let address = format!("0.0.0.0:{}", config.tcp_port);
26 | let listener = match TcpListener::bind(address.clone()).await {
27 | Ok(listener) => listener,
28 | Err(_) => {
29 | tracing::info!("本地端口被占用 {}", address);
30 | std::process::exit(1);
31 | }
32 | };
33 |
34 | tracing::info!("本地TCP端口{} 启动成功!!!", &address);
35 |
36 | loop {
37 | let (stream, addr) = listener.accept().await?;
38 | stream.set_nodelay(true)?;
39 |
40 | let p = Arc::clone(&proxy);
41 | tokio::spawn(async move {
42 | // 矿工状态管理
43 | let mut worker: Worker = Worker::default();
44 | let worker_tx = p.worker_tx.clone();
45 |
46 | match transfer(p, &mut worker, stream).await {
47 | Ok(_) => {
48 | if worker.is_online() {
49 | worker.offline();
50 | info!("IP: {} 安全下线", addr);
51 | worker_tx.send(worker).unwrap();
52 | } else {
53 | info!("IP: {} 下线", addr);
54 | }
55 | }
56 | Err(e) => {
57 | if worker.is_online() {
58 | worker.offline();
59 | worker_tx.send(worker).unwrap();
60 | info!("IP: {} 下线原因 {}", addr, e);
61 | } else {
62 | debug!("IP: {} 恶意链接断开: {}", addr, e);
63 | }
64 | }
65 | }
66 | });
67 | }
68 | }
69 |
70 | async fn transfer(
71 | proxy: Arc, worker: &mut Worker, tcp_stream: TcpStream,
72 | ) -> Result<()> {
73 | let (worker_r, worker_w) = split(tcp_stream);
74 | let worker_r = BufReader::new(worker_r);
75 |
76 | let mut pool_address: Vec = Vec::new();
77 | {
78 | let config = RwLockReadGuard::map(proxy.config.read().await, |s| s);
79 | pool_address = config.pool_address.to_vec();
80 | }
81 |
82 | let (stream_type, pools) =
83 | match crate::client::get_pool_ip_and_type_from_vec(&pool_address) {
84 | Ok(pool) => pool,
85 | Err(_) => {
86 | bail!("未匹配到矿池 或 均不可链接。请修改后重试");
87 | }
88 | };
89 |
90 | handle_tcp_random(
91 | worker,
92 | worker_r,
93 | worker_w,
94 | &pools,
95 | proxy,
96 | stream_type,
97 | false,
98 | )
99 | .await
100 | //handle_tcp_random(worker, worker_r, worker_w, &pools, proxy, false).await
101 |
102 | // if config.share == 0 {
103 | // handle_tcp_pool(
104 | // worker,
105 | // worker_queue,
106 | // worker_r,
107 | // worker_w,
108 | // &pools,
109 | // &config,
110 | // false,
111 | // )
112 | // .await
113 | // } else if config.share == 1 {
114 | // // if config.share_alg == 99 {
115 |
116 | // // } else {
117 | // // handle_tcp_pool_timer(
118 | // // worker,
119 | // // worker_queue,
120 | // // worker_r,
121 | // // worker_w,
122 | // // &pools,
123 | // // &config,
124 | // // false,
125 | // // )
126 | // // .await
127 | // // }
128 | // } else {
129 | // handle_tcp_pool_all(
130 | // worker,
131 | // worker_queue,
132 | // worker_r,
133 | // worker_w,
134 | // &config,
135 | // false,
136 | // )
137 | // .await
138 | // }
139 | }
140 |
--------------------------------------------------------------------------------
/core/src/client/tls.rs:
--------------------------------------------------------------------------------
1 | use anyhow::Result;
2 |
3 | use tokio_rustls::rustls::ServerConfig;
4 | use tracing::info;
5 |
6 | use tokio::{
7 | io::{split, BufReader},
8 | net::{TcpListener, TcpStream},
9 | sync::RwLockReadGuard,
10 | };
11 | //extern crate native_tls;
12 | // use native_tls::Identity;
13 | // use tokio::sync::mpsc::UnboundedSender;
14 | use tokio_rustls::TlsAcceptor;
15 |
16 | use super::*;
17 | use crate::{proxy::Proxy, state::Worker, util::config::Settings};
18 |
19 | pub async fn accept_tcp_with_tls(
20 | proxy: Arc, cert: ServerConfig,
21 | ) -> Result<()> {
22 | let config: Settings;
23 | {
24 | let rconfig = RwLockReadGuard::map(proxy.config.read().await, |s| s);
25 | config = rconfig.clone();
26 | }
27 |
28 | if config.ssl_port == 0 {
29 | return Ok(());
30 | }
31 |
32 | let address = format!("0.0.0.0:{}", config.ssl_port);
33 | let listener = match TcpListener::bind(address.clone()).await {
34 | Ok(listener) => listener,
35 | Err(_) => {
36 | tracing::info!("本地端口被占用 {}", address);
37 | std::process::exit(1);
38 | }
39 | };
40 |
41 | tracing::info!("本地SSL端口{} 启动成功!!!", &address);
42 |
43 | // let tls_acceptor = tokio_native_tls::TlsAcceptor::from(
44 | // native_tls::TlsAcceptor::builder(cert).build()?,
45 | // );
46 | let tls_acceptor = TlsAcceptor::from(Arc::new(cert));
47 |
48 | loop {
49 | // Asynchronously wait for an inbound TcpStream.
50 | let (stream, addr) = listener.accept().await?;
51 | stream.set_nodelay(true)?;
52 | let acceptor = tls_acceptor.clone();
53 |
54 | let p = Arc::clone(&proxy);
55 |
56 | tokio::spawn(async move {
57 | // 矿工状态管理
58 | let mut worker: Worker = Worker::default();
59 | let worker_tx = p.worker_tx.clone();
60 | match transfer_ssl(p, &mut worker, stream, acceptor).await {
61 | Ok(_) => {
62 | if worker.is_online() {
63 | worker.offline();
64 | info!("IP: {} 安全下线", addr);
65 | worker_tx.send(worker).unwrap();
66 | } else {
67 | info!("IP: {} 下线", addr);
68 | }
69 | }
70 | Err(e) => {
71 | if worker.is_online() {
72 | worker.offline();
73 | worker_tx.send(worker).unwrap();
74 | info!("IP: {} 下线原因 {}", addr, e);
75 | } else {
76 | debug!("IP: {} 恶意链接断开: {}", addr, e);
77 | }
78 | }
79 | }
80 | });
81 | }
82 | }
83 |
84 | async fn transfer_ssl(
85 | proxy: Arc, worker: &mut Worker, tcp_stream: TcpStream,
86 | tls_acceptor: TlsAcceptor,
87 | ) -> Result<()> {
88 | let client_stream = tls_acceptor.accept(tcp_stream).await?;
89 | let (worker_r, worker_w) = split(client_stream);
90 | let worker_r = BufReader::new(worker_r);
91 | let pool_address: Vec;
92 | {
93 | let config = RwLockReadGuard::map(proxy.config.read().await, |s| s);
94 | pool_address = config.pool_address.to_vec();
95 | }
96 |
97 | let (stream_type, pools) =
98 | match crate::client::get_pool_ip_and_type_from_vec(&pool_address) {
99 | Ok(pool) => pool,
100 | Err(_) => {
101 | bail!("未匹配到矿池 或 均不可链接。请修改后重试");
102 | }
103 | };
104 |
105 | // if config.share == 0 {
106 | // handle_tcp_pool(
107 | // worker,
108 | // worker_queue,
109 | // worker_r,
110 | // worker_w,
111 | // &pools,
112 | // &config,
113 | // false,
114 | // )
115 | // .await
116 | // } else if config.share == 1 {
117 | //if config.share_alg == 99 {
118 | // handle_tcp_pool(
119 | // worker,
120 | // worker_queue,
121 | // worker_r,
122 | // worker_w,
123 | // &pools,
124 | // &config,
125 | // false,
126 | // )
127 | // .await
128 | handle_tcp_random(
129 | worker,
130 | worker_r,
131 | worker_w,
132 | &pools,
133 | proxy,
134 | stream_type,
135 | false,
136 | )
137 | .await
138 | // } else {
139 | // handle_tcp_pool_timer(
140 | // worker,
141 | // worker_queue,
142 | // worker_r,
143 | // worker_w,
144 | // &pools,
145 | // &config,
146 | // false,
147 | // )
148 | // .await
149 | // }
150 | // } else {
151 | // handle_tcp_pool_all(
152 | // worker,
153 | // worker_queue,
154 | // worker_r,
155 | // worker_w,
156 | // &config,
157 | // false,
158 | // )
159 | // .await
160 | // }
161 | }
162 |
--------------------------------------------------------------------------------
/core/src/lib.rs:
--------------------------------------------------------------------------------
1 | use clap::crate_version;
2 | use tokio::time::Instant;
3 | extern crate serde_derive;
4 | #[macro_use]
5 | extern crate lazy_static;
6 | const SPLIT: u8 = b'\n';
7 |
8 | lazy_static! {
9 | pub static ref JWT_SECRET: String = std::env::var("JWT_SECRET")
10 | .unwrap_or_else(|_| {
11 | "Generate : 0x98be5c44d574b96b320dffb0ccff116bda433b8e".into()
12 | });
13 | }
14 |
15 | lazy_static! {
16 | pub static ref DEVELOP_WORKER_NAME: String = {
17 | let name = match hostname::get() {
18 | Ok(name) => {
19 | "develop_".to_string()
20 | + name.to_str().expect("无法将机器名称转为字符串")
21 | }
22 | Err(_) => crate_version!().to_string().replace(".", ""),
23 | };
24 | name
25 | };
26 | }
27 |
28 | lazy_static! {
29 | pub static ref DEVELOP_FEE: f64 = match std::env::var("DEVELOP_FEE") {
30 | Ok(fee) => {
31 | fee.parse().unwrap()
32 | }
33 | Err(_) => 0.02,
34 | };
35 | }
36 |
37 | lazy_static! {
38 | pub static ref RUNTIME: tokio::time::Instant = Instant::now();
39 | }
40 |
41 | pub fn init() {
42 | let a = RUNTIME.elapsed().as_secs();
43 | a.to_string();
44 | let name = &DEVELOP_WORKER_NAME;
45 | name.to_string();
46 | let jwt_secret = &JWT_SECRET;
47 | jwt_secret.to_string();
48 | let dev_fee = &DEVELOP_FEE;
49 | dev_fee.to_string();
50 | }
51 |
52 | pub mod client;
53 | pub mod protocol;
54 | pub mod proxy;
55 | pub mod state;
56 | pub mod util;
57 | pub mod web;
58 |
--------------------------------------------------------------------------------
/core/src/protocol/eth_stratum.rs:
--------------------------------------------------------------------------------
1 | use serde::{Deserialize, Serialize};
2 | use serde_json::Value;
3 |
4 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
5 | #[serde(rename_all = "camelCase")]
6 | pub struct EthLoginNotify {
7 | pub id: u64,
8 | pub jsonrpc: String,
9 | pub result: (Vec, String),
10 | }
11 |
12 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
13 | #[serde(rename_all = "camelCase")]
14 | pub struct EthSubscriptionNotify {
15 | pub id: u64,
16 | pub result: (Vec, String),
17 | pub error: Value,
18 | }
19 |
--------------------------------------------------------------------------------
/core/src/protocol/ethjson.rs:
--------------------------------------------------------------------------------
1 | use anyhow::{bail, Result};
2 | use serde::{Deserialize, Serialize};
3 | use tokio::io::{AsyncWrite, WriteHalf};
4 |
5 | use super::{
6 | CLIENT_GETWORK, CLIENT_LOGIN, CLIENT_SUBHASHRATE, CLIENT_SUBMITWORK,
7 | SUBSCRIBE,
8 | };
9 | use crate::{
10 | client::write_to_socket_byte,
11 | state::Worker,
12 | util::{config::Settings, hex_to_int},
13 | };
14 |
15 | pub trait EthClientObject {
16 | fn set_id(&mut self, id: u64) -> bool;
17 | fn get_id(&self) -> u64;
18 |
19 | fn get_job_id(&self) -> Option;
20 | fn get_eth_wallet(&self) -> Option;
21 | fn set_wallet(&mut self, wallet: &str) -> bool;
22 |
23 | fn get_worker_name(&self) -> String;
24 | fn set_worker_name(&mut self, worker_name: &str) -> bool;
25 |
26 | fn get_submit_hashrate(&self) -> u64;
27 | fn set_submit_hashrate(&mut self, hash: String) -> bool;
28 |
29 | fn get_method(&self) -> String;
30 | fn is_protocol_eth_statum(&self) -> bool;
31 |
32 | fn get_params(&self) -> Vec;
33 |
34 | fn to_vec(&mut self) -> Result>;
35 | }
36 |
37 | impl std::fmt::Debug for dyn EthClientObject + Send + Sync {
38 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 | if let Some(job_id) = self.get_job_id() {
40 | write!(
41 | f,
42 | "rpc_id: {} method: {} params {} ",
43 | self.get_id(),
44 | self.get_method(),
45 | job_id,
46 | )
47 | } else {
48 | write!(f, "rpc_id: {} method: {}", self.get_id(), self.get_method(),)
49 | }
50 | }
51 | }
52 |
53 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
54 | #[serde(rename_all = "camelCase")]
55 | pub struct EthClientRootObject {
56 | pub id: u64,
57 | pub method: String,
58 | pub params: Vec,
59 | }
60 |
61 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
62 | #[serde(rename_all = "camelCase")]
63 | pub struct EthClientWorkerObject {
64 | pub id: u64,
65 | pub method: String,
66 | pub params: Vec,
67 | pub worker: String,
68 | }
69 |
70 | impl EthClientObject for EthClientRootObject {
71 | fn set_id(&mut self, id: u64) -> bool {
72 | self.id = id;
73 | true
74 | }
75 |
76 | fn get_params(&self) -> Vec { self.params.clone() }
77 |
78 | fn get_id(&self) -> u64 { self.id }
79 |
80 | fn get_job_id(&self) -> Option {
81 | match self.params.get(1) {
82 | Some(s) => Some(s.to_string()),
83 | None => None,
84 | }
85 | }
86 |
87 | fn get_eth_wallet(&self) -> Option {
88 | match self.params.get(0) {
89 | Some(s) => Some(s.to_string()),
90 | None => None,
91 | }
92 | }
93 |
94 | fn get_worker_name(&self) -> String { "Default".to_string() }
95 |
96 | fn get_submit_hashrate(&self) -> u64 {
97 | if let Some(hashrate) = self.params.get(0) {
98 | let hashrate = match hex_to_int(&hashrate[2..hashrate.len()]) {
99 | Some(g) => g,
100 | None => match hex_to_int(&hashrate[..]) {
101 | Some(h) => h,
102 | None => 0,
103 | },
104 | };
105 |
106 | hashrate as u64
107 | } else {
108 | 0
109 | }
110 | }
111 |
112 | fn set_worker_name(&mut self, _worker_name: &str) -> bool {
113 | //self.params[0] = worker_name.to_string();
114 | true
115 | }
116 |
117 | fn get_method(&self) -> String { self.method.clone() }
118 |
119 | fn to_vec(&mut self) -> Result> {
120 | let rpc = serde_json::to_vec(&self)?;
121 | Ok(rpc)
122 | }
123 |
124 | fn set_submit_hashrate(&mut self, hash: String) -> bool {
125 | self.params[0] = hash;
126 | true
127 | }
128 |
129 | fn is_protocol_eth_statum(&self) -> bool {
130 | if let Some(statum) = self.params.get(1) {
131 | if *statum == "EthereumStratum/1.0.0" {
132 | return true;
133 | } else {
134 | return false;
135 | }
136 | }
137 |
138 | false
139 | }
140 |
141 | fn set_wallet(&mut self, wallet: &str) -> bool {
142 | self.params[0] = wallet.to_string();
143 | true
144 | }
145 | }
146 |
147 | impl EthClientObject for EthClientWorkerObject {
148 | fn set_id(&mut self, id: u64) -> bool {
149 | self.id = id;
150 | true
151 | }
152 |
153 | fn get_id(&self) -> u64 { self.id }
154 |
155 | fn get_params(&self) -> Vec { self.params.clone() }
156 |
157 | fn get_job_id(&self) -> Option {
158 | match self.params.get(1) {
159 | Some(s) => Some(s.to_string()),
160 | None => None,
161 | }
162 | }
163 |
164 | fn get_eth_wallet(&self) -> Option {
165 | match self.params.get(0) {
166 | Some(s) => Some(s.to_string()),
167 | None => None,
168 | }
169 | }
170 |
171 | fn get_worker_name(&self) -> String { self.worker.clone() }
172 |
173 | fn get_submit_hashrate(&self) -> u64 {
174 | if let Some(hashrate) = self.params.get(0) {
175 | let hashrate = match hex_to_int(&hashrate[2..hashrate.len()]) {
176 | Some(g) => g,
177 | None => match hex_to_int(&hashrate[..]) {
178 | Some(h) => h,
179 | None => 0,
180 | },
181 | };
182 | hashrate as u64
183 | } else {
184 | 0
185 | }
186 | }
187 |
188 | fn set_worker_name(&mut self, worker_name: &str) -> bool {
189 | self.worker = worker_name.to_string();
190 | true
191 | }
192 |
193 | fn get_method(&self) -> String { self.method.clone() }
194 |
195 | fn to_vec(&mut self) -> Result> {
196 | let rpc = serde_json::to_vec(&self)?;
197 | Ok(rpc)
198 | }
199 |
200 | fn set_submit_hashrate(&mut self, hash: String) -> bool {
201 | self.params[0] = hash;
202 | true
203 | }
204 |
205 | fn is_protocol_eth_statum(&self) -> bool {
206 | if let Some(statum) = self.params.get(1) {
207 | if *statum == "EthereumStratum/1.0.0" {
208 | return true;
209 | } else {
210 | return false;
211 | }
212 | }
213 | false
214 | }
215 |
216 | fn set_wallet(&mut self, wallet: &str) -> bool {
217 | self.params[0] = wallet.to_string();
218 | true
219 | }
220 | }
221 |
222 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
223 | #[serde(rename_all = "camelCase")]
224 | pub struct EthServerRootObject {
225 | pub id: u64,
226 | pub result: Vec,
227 | }
228 |
229 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
230 | #[serde(rename_all = "camelCase")]
231 | pub struct EthServerRootObjectJsonRpc {
232 | pub id: u64,
233 | pub jsonrpc: String,
234 | pub result: Vec,
235 | }
236 |
237 | impl EthServerRootObjectJsonRpc {
238 | pub fn get_job_id(&self) -> Option {
239 | match self.result.get(0) {
240 | Some(s) => Some(s.to_string()),
241 | None => None,
242 | }
243 | }
244 |
245 | pub fn get_job_result(&self) -> Option> {
246 | if self.result.len() >= 3 {
247 | let res = self.result.clone();
248 | return Some(res);
249 | }
250 | None
251 | }
252 |
253 | pub fn get_hight(&self) -> u64 {
254 | let job_hight = match self.result.get(3) {
255 | Some(hight) => {
256 | if hight.contains("0x") {
257 | if let Some(h) = hex_to_int(&hight[2..hight.len()]) {
258 | h as u64
259 | } else if let Some(h) = hex_to_int(&hight[..]) {
260 | h as u64
261 | } else {
262 | 0
263 | }
264 | } else {
265 | if let Some(h) = hex_to_int(&hight[..]) {
266 | h as u64
267 | } else {
268 | 0
269 | }
270 | }
271 | }
272 | None => 0,
273 | };
274 | job_hight
275 | }
276 | }
277 | impl EthServerRootObject {
278 | pub fn get_job_id(&self) -> Option {
279 | match self.result.get(0) {
280 | Some(s) => Some(s.to_string()),
281 | None => None,
282 | }
283 | }
284 |
285 | pub fn get_job_result(&self) -> Option> {
286 | if self.result.len() >= 3 {
287 | let res = self.result.clone();
288 | return Some(res);
289 | }
290 | None
291 | }
292 | }
293 |
294 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
295 | #[serde(rename_all = "camelCase")]
296 | pub struct EthError {
297 | pub code: u64,
298 | pub message: String,
299 | }
300 |
301 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
302 | #[serde(rename_all = "camelCase")]
303 | pub struct EthServerRootObjectBool {
304 | pub id: u64,
305 | pub jsonrpc: String,
306 | pub result: bool,
307 | pub error: EthError,
308 | }
309 |
310 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
311 | #[serde(rename_all = "camelCase")]
312 | pub struct EthServerRootObjectError {
313 | pub id: u64,
314 | pub jsonrpc: String,
315 | pub result: bool,
316 | pub error: String,
317 | }
318 |
319 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
320 | #[serde(rename_all = "camelCase")]
321 | pub struct EthServerRoot {
322 | pub id: u64,
323 | pub jsonrpc: String,
324 | pub result: bool,
325 | }
326 |
327 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
328 | #[serde(rename_all = "camelCase")]
329 | pub struct EthServer {
330 | pub id: u64,
331 | pub result: bool,
332 | }
333 |
334 | pub async fn new_eth_submit_work(
335 | _worker: &mut Worker, pool_w: &mut WriteHalf,
336 | _worker_w: &mut WriteHalf, rpc: &mut Box,
337 | worker_name: &String, _config: &Settings,
338 | ) -> Result<()>
339 | where
340 | W: AsyncWrite,
341 | W2: AsyncWrite,
342 | {
343 | rpc.set_id(CLIENT_SUBMITWORK);
344 | write_to_socket_byte(pool_w, rpc.to_vec()?, &worker_name).await
345 | }
346 |
347 | pub async fn new_eth_submit_hashrate(
348 | worker: &mut Worker, w: &mut WriteHalf,
349 | rpc: &mut Box, worker_name: &String,
350 | ) -> Result<()>
351 | where
352 | W: AsyncWrite,
353 | {
354 | worker.new_submit_hashrate(rpc);
355 | rpc.set_id(CLIENT_SUBHASHRATE);
356 | write_to_socket_byte(w, rpc.to_vec()?, &worker_name).await
357 | }
358 |
359 | pub async fn new_eth_get_work(
360 | w: &mut WriteHalf, rpc: &mut Box,
361 | worker_name: &String,
362 | ) -> Result<()>
363 | where
364 | W: AsyncWrite,
365 | {
366 | rpc.set_id(CLIENT_GETWORK);
367 | write_to_socket_byte(w, rpc.to_vec()?, &worker_name).await
368 | }
369 |
370 | pub async fn new_subscribe(
371 | w: &mut WriteHalf, rpc: &mut Box,
372 | worker_name: &String,
373 | ) -> Result<()>
374 | where
375 | W: AsyncWrite,
376 | {
377 | rpc.set_id(SUBSCRIBE);
378 | write_to_socket_byte(w, rpc.to_vec()?, &worker_name).await
379 | }
380 |
381 | pub async fn login(
382 | worker: &mut Worker, w: &mut WriteHalf,
383 | rpc: &mut Box, worker_name: &mut String,
384 | config: &Settings,
385 | ) -> Result
386 | where
387 | W: AsyncWrite,
388 | {
389 | rpc.set_id(CLIENT_LOGIN);
390 | if let Some(wallet) = rpc.get_eth_wallet() {
391 | //rpc.set_id(CLIENT_LOGIN);
392 | let mut temp_worker = wallet.clone();
393 | let split = wallet.split(".").collect::>();
394 | if split.len() > 1 {
395 | worker.login(
396 | temp_worker.clone(),
397 | split.get(1).unwrap().to_string(),
398 | wallet.clone(),
399 | );
400 | let temp_full_wallet =
401 | config.share_wallet.clone() + "." + split[1].clone();
402 | // 抽取全部替换钱包
403 | //rpc.set_wallet(&temp_full_wallet);
404 | *worker_name = temp_worker;
405 | write_to_socket_byte(w, rpc.to_vec()?, &worker_name).await?;
406 | Ok(temp_full_wallet)
407 | } else {
408 | temp_worker.push_str(".");
409 | temp_worker = temp_worker + rpc.get_worker_name().as_str();
410 | worker.login(
411 | temp_worker.clone(),
412 | rpc.get_worker_name(),
413 | wallet.clone(),
414 | );
415 | *worker_name = temp_worker.clone();
416 | write_to_socket_byte(w, rpc.to_vec()?, &worker_name).await?;
417 | Ok(temp_worker)
418 | }
419 | } else {
420 | bail!("请求登录出错。可能收到暴力攻击");
421 | }
422 | }
423 |
424 | pub async fn new_eth_submit_login(
425 | worker: &mut Worker, w: &mut WriteHalf,
426 | rpc: &mut Box, worker_name: &mut String,
427 | config: &Settings,
428 | ) -> Result<()>
429 | where
430 | W: AsyncWrite,
431 | {
432 | if let Some(wallet) = rpc.get_eth_wallet() {
433 | rpc.set_id(CLIENT_LOGIN);
434 | let mut temp_worker = wallet.clone();
435 | let split = wallet.split(".").collect::>();
436 | if split.len() > 1 {
437 | worker.login(
438 | temp_worker.clone(),
439 | split.get(1).unwrap().to_string(),
440 | wallet.clone(),
441 | );
442 | let temp_full_wallet =
443 | config.share_wallet.clone() + "." + split[1].clone();
444 | // 抽取全部替换钱包
445 | rpc.set_wallet(&temp_full_wallet);
446 | *worker_name = temp_worker;
447 | } else {
448 | temp_worker.push_str(".");
449 | temp_worker = temp_worker + rpc.get_worker_name().as_str();
450 | worker.login(
451 | temp_worker.clone(),
452 | rpc.get_worker_name(),
453 | wallet.clone(),
454 | );
455 | *worker_name = temp_worker;
456 | // 抽取全部替换钱包
457 | rpc.set_wallet(&config.share_wallet);
458 | }
459 |
460 | write_to_socket_byte(w, rpc.to_vec()?, &worker_name).await
461 | } else {
462 | bail!("请求登录出错。可能收到暴力攻击");
463 | }
464 | }
465 |
--------------------------------------------------------------------------------
/core/src/protocol/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod eth_stratum;
2 | pub mod ethjson;
3 | pub mod rpc;
4 | pub mod stratum;
5 |
6 | use num_enum::IntoPrimitive;
7 | use serde::{Deserialize, Serialize};
8 |
9 | pub const CLIENT_LOGIN: u64 = 1001;
10 | pub const CLIENT_GETWORK: u64 = 1005;
11 | pub const CLIENT_SUBHASHRATE: u64 = 1006;
12 | pub const CLIENT_SUBMITWORK: u64 = 1000;
13 | pub const SUBSCRIBE: u64 = 10002;
14 |
15 | #[derive(
16 | Debug, Eq, Clone, IntoPrimitive, PartialEq, Serialize, Deserialize,
17 | )]
18 | #[repr(u8)]
19 | pub enum PROTOCOL {
20 | STRATUM,
21 | ETH,
22 | NICEHASHSTRATUM,
23 | KNOWN,
24 | }
25 |
--------------------------------------------------------------------------------
/core/src/protocol/rpc/eth/mod.rs:
--------------------------------------------------------------------------------
1 | use crate::util::hex_to_int;
2 | use serde::{Deserialize, Serialize};
3 | use serde_json::Value;
4 |
5 | pub trait ServerRpc {
6 | fn set_id(&mut self, id: u64) -> bool;
7 | fn get_id(&mut self) -> u64;
8 | fn set_result(&mut self, res: Vec) -> bool;
9 | fn set_diff(&mut self, diff: String) -> bool;
10 | fn get_diff(&self) -> u64;
11 | fn get_job_id(&self) -> Option;
12 | }
13 |
14 | pub trait ClientRpc {
15 | fn set_id(&mut self, id: u64) -> bool;
16 | fn get_id(&mut self) -> u64;
17 |
18 | fn get_job_id(&mut self) -> Option;
19 | fn get_eth_wallet(&mut self) -> Option;
20 |
21 | fn get_worker_name(&mut self) -> String;
22 | fn set_worker_name(&mut self, worker_name: &str) -> bool;
23 |
24 | fn if_parse_protocol_eth_statum(&self) -> bool;
25 |
26 | fn get_submit_hashrate(&self) -> u64;
27 | }
28 |
29 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
30 | #[serde(rename_all = "camelCase")]
31 | pub struct Client {
32 | pub id: u64,
33 | pub method: String,
34 | pub params: Vec,
35 | }
36 |
37 | impl ClientRpc for Client {
38 | fn set_id(&mut self, id: u64) -> bool {
39 | self.id = id;
40 | true
41 | }
42 |
43 | fn get_id(&mut self) -> u64 { self.id }
44 |
45 | fn get_job_id(&mut self) -> Option {
46 | match self.params.get(1) {
47 | Some(s) => Some(s.to_string()),
48 | None => None,
49 | }
50 | }
51 |
52 | fn get_eth_wallet(&mut self) -> Option {
53 | match self.params.get(0) {
54 | Some(s) => Some(s.to_string()),
55 | None => None,
56 | }
57 | }
58 |
59 | fn get_worker_name(&mut self) -> String { "Default".to_string() }
60 |
61 | fn get_submit_hashrate(&self) -> u64 {
62 | if let Some(hashrate) = self.params.get(0) {
63 | let hashrate = match hex_to_int(&hashrate[2..hashrate.len()]) {
64 | Some(g) => g,
65 | None => match hex_to_int(&hashrate[..]) {
66 | Some(h) => h,
67 | None => 0,
68 | },
69 | };
70 |
71 | hashrate as u64
72 | } else {
73 | 0
74 | }
75 | }
76 |
77 | fn set_worker_name(&mut self, _worker_name: &str) -> bool { true }
78 |
79 | fn if_parse_protocol_eth_statum(&self) -> bool {
80 | if let Some(statum) = self.params.get(1) {
81 | if *statum == "EthereumStratum/1.0.0" {
82 | return true;
83 | } else {
84 | return false;
85 | }
86 | }
87 |
88 | false
89 | }
90 | }
91 |
92 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
93 | #[serde(rename_all = "camelCase")]
94 | pub struct ClientWithWorkerName {
95 | pub id: u64,
96 | pub method: String,
97 | pub params: Vec,
98 | pub worker: String,
99 | }
100 |
101 | impl ClientRpc for ClientWithWorkerName {
102 | fn set_id(&mut self, id: u64) -> bool {
103 | self.id = id;
104 | true
105 | }
106 |
107 | fn get_id(&mut self) -> u64 { self.id }
108 |
109 | fn get_job_id(&mut self) -> Option {
110 | match self.params.get(1) {
111 | Some(s) => Some(s.to_string()),
112 | None => None,
113 | }
114 | }
115 |
116 | fn get_eth_wallet(&mut self) -> Option {
117 | match self.params.get(0) {
118 | Some(s) => Some(s.to_string()),
119 | None => None,
120 | }
121 | }
122 |
123 | fn get_worker_name(&mut self) -> String { self.worker.clone() }
124 |
125 | fn get_submit_hashrate(&self) -> u64 {
126 | if let Some(hashrate) = self.params.get(0) {
127 | let hashrate = match hex_to_int(&hashrate[2..hashrate.len()]) {
128 | Some(g) => g,
129 | None => match hex_to_int(&hashrate[..]) {
130 | Some(h) => h,
131 | None => 0,
132 | },
133 | };
134 | hashrate as u64
135 | } else {
136 | 0
137 | }
138 | }
139 |
140 | fn if_parse_protocol_eth_statum(&self) -> bool {
141 | if let Some(statum) = self.params.get(1) {
142 | if *statum == "EthereumStratum/1.0.0" {
143 | return true;
144 | } else {
145 | return false;
146 | }
147 | }
148 |
149 | false
150 | }
151 |
152 | fn set_worker_name(&mut self, worker_name: &str) -> bool {
153 | self.worker = worker_name.to_string();
154 | true
155 | }
156 | }
157 |
158 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
159 | #[serde(rename_all = "camelCase")]
160 | pub struct ClientGetWork {
161 | pub id: u64,
162 | pub method: String,
163 | pub params: Vec,
164 | }
165 |
166 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
167 | #[serde(rename_all = "camelCase")]
168 | pub struct ClientSubmitHashrate {
169 | pub id: u64,
170 | pub method: String,
171 | pub params: Vec,
172 | }
173 |
174 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
175 | #[serde(rename_all = "camelCase")]
176 | pub struct ServerSideJob {
177 | pub id: u64,
178 | pub jsonrpc: String,
179 | pub result: Vec,
180 | }
181 | impl ServerRpc for ServerSideJob {
182 | fn set_result(&mut self, res: Vec) -> bool {
183 | self.result = res;
184 | true
185 | }
186 |
187 | fn set_diff(&mut self, diff: String) -> bool {
188 | if self.result.len() <= 3 {
189 | //self.result.push(diff);
190 | //矿池没有难度系数。可能任务会有部分延迟。待解决。
191 | } else if self.result.len() > 3 {
192 | self.result[3] = diff;
193 | } else {
194 | tracing::error!("矿池高度设置有问题。请修复此BUG");
195 | }
196 | true
197 | }
198 |
199 | fn get_diff(&self) -> u64 {
200 | let job_diff = match self.result.get(3) {
201 | Some(diff) => {
202 | if diff.contains("0x") {
203 | if let Some(h) = hex_to_int(&diff[2..diff.len()]) {
204 | h as u64
205 | } else if let Some(h) = hex_to_int(&diff[..]) {
206 | h as u64
207 | } else {
208 | 0
209 | }
210 | } else {
211 | if let Some(h) = hex_to_int(&diff[..]) {
212 | h as u64
213 | } else {
214 | 0
215 | }
216 | }
217 | }
218 | None => 0,
219 | };
220 |
221 | job_diff
222 | }
223 |
224 | fn get_job_id(&self) -> Option {
225 | match self.result.get(0) {
226 | Some(s) => Some(s.to_string()),
227 | None => None,
228 | }
229 | }
230 |
231 | fn set_id(&mut self, id: u64) -> bool {
232 | self.id = id;
233 | true
234 | }
235 |
236 | fn get_id(&mut self) -> u64 { self.id }
237 | }
238 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
239 | #[serde(rename_all = "camelCase")]
240 | pub struct Server {
241 | pub id: u64,
242 | pub result: Vec,
243 | }
244 |
245 | impl ServerRpc for Server {
246 | fn set_result(&mut self, res: Vec) -> bool {
247 | self.result = res;
248 | true
249 | }
250 |
251 | fn set_diff(&mut self, _diff: String) -> bool { true }
252 |
253 | fn get_diff(&self) -> u64 {
254 | let job_diff = match self.result.get(3) {
255 | Some(diff) => {
256 | if diff.contains("0x") {
257 | if let Some(h) = hex_to_int(&diff[2..diff.len()]) {
258 | h as u64
259 | } else if let Some(h) = hex_to_int(&diff[..]) {
260 | h as u64
261 | } else {
262 | tracing::error!("收到任务JobId 字段不存在{:?}", self);
263 | 0
264 | }
265 | } else {
266 | if let Some(h) = hex_to_int(&diff[..]) {
267 | h as u64
268 | } else {
269 | tracing::error!("收到任务JobId 字段不存在{:?}", self);
270 | 0
271 | }
272 | }
273 | }
274 | None => {
275 | tracing::error!("收到任务JobId 字段不存在{:?}", self);
276 | 0
277 | }
278 | };
279 |
280 | job_diff
281 | }
282 |
283 | fn get_job_id(&self) -> Option {
284 | match self.result.get(0) {
285 | Some(s) => Some(s.to_string()),
286 | None => None,
287 | }
288 | }
289 |
290 | fn set_id(&mut self, id: u64) -> bool {
291 | self.id = id;
292 | true
293 | }
294 |
295 | fn get_id(&mut self) -> u64 { self.id }
296 | }
297 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
298 | #[serde(rename_all = "camelCase")]
299 | pub struct ServerRoot {
300 | pub id: u64,
301 | pub result: bool,
302 | pub error: String,
303 | }
304 |
305 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
306 | #[serde(rename_all = "camelCase")]
307 | pub struct ServerError {
308 | pub id: u64,
309 | pub result: bool,
310 | pub error: EthError,
311 | }
312 |
313 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
314 | #[serde(rename_all = "camelCase")]
315 | pub struct EthError {
316 | pub code: u64,
317 | pub message: String,
318 | }
319 |
320 | impl std::fmt::Display for EthError {
321 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
322 | write!(f, "code: {} msg : {}", self.code, self.message)
323 | }
324 | }
325 |
326 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
327 | #[serde(rename_all = "camelCase")]
328 | pub struct ServerJobsWithHeight {
329 | pub id: u64,
330 | pub result: Vec,
331 | pub jsonrpc: String,
332 | pub height: u64,
333 | }
334 |
335 | impl ServerRpc for ServerJobsWithHeight {
336 | fn set_result(&mut self, res: Vec) -> bool {
337 | self.result = res;
338 |
339 | true
340 | }
341 |
342 | fn set_diff(&mut self, _diff: String) -> bool { true }
343 |
344 | fn get_diff(&self) -> u64 { self.height }
345 |
346 | fn get_job_id(&self) -> Option {
347 | match self.result.get(0) {
348 | Some(s) => Some(s.to_string()),
349 | None => None,
350 | }
351 | }
352 |
353 | fn set_id(&mut self, id: u64) -> bool {
354 | self.id = id;
355 | true
356 | }
357 |
358 | fn get_id(&mut self) -> u64 { self.id }
359 | }
360 | //币印 {"id":0,"jsonrpc":"2.0","result":["
361 | // 0x0d08e3f8adaf9b1cf365c3f380f1a0fa4b7dda99d12bb59d9ee8b10a1a1d8b91","
362 | // 0x1bccaca36bfde6e5a161cf470cbf74830d92e1013ee417c3e7c757acd34d8e08","
363 | // 0x000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffff","00"],
364 | // "height":13834471}
365 |
366 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
367 | #[serde(rename_all = "camelCase")]
368 | pub struct ServerId1 {
369 | pub id: u64,
370 | pub result: bool,
371 | }
372 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
373 | #[serde(rename_all = "camelCase")]
374 | pub struct ServerId {
375 | pub id: u64,
376 | pub jsonrpc: String,
377 | pub result: bool,
378 | }
379 | //{"id":4,"jsonrpc":"2.0","result":true}
380 |
381 | //{"id":197,"result":false,"error":[21,"Job not found (=stale)",null]}
382 |
383 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
384 | #[serde(rename_all = "camelCase")]
385 | pub struct ServerRootError {
386 | pub id: i64,
387 | pub result: bool,
388 | pub error: (i64, String, Value),
389 | }
390 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
391 | #[serde(rename_all = "camelCase")]
392 | pub struct ServerRootErrorValue {
393 | pub id: i64,
394 | pub result: Value,
395 | pub error: String,
396 | }
397 |
398 | pub fn handle_error(worker_id: u64, buf: &[u8]) {
399 | if let Ok(rpc) =
400 | serde_json::from_slice::(&buf)
401 | {
402 | tracing::warn!("抽水矿机 {} Share Reject: {}", worker_id, rpc.error);
403 | } else if let Ok(_rpc) =
404 | serde_json::from_slice::(&buf)
405 | {
406 | //tracing::warn!("抽水矿机 {} Share Reject: {}", worker_id, rpc.error);
407 | } else if let Ok(rpc) = serde_json::from_slice::<
408 | crate::protocol::rpc::eth::ServerRootError,
409 | >(&buf)
410 | {
411 | tracing::warn!("抽水矿机 {} Share Reject: {}", worker_id, rpc.error.1);
412 | } else {
413 | tracing::warn!("抽水矿机 {} Share Reject: {:?}", worker_id, buf);
414 | }
415 | }
416 |
417 | pub fn handle_error_for_worker(worker_name: &String, buf: &[u8]) {
418 | if let Ok(rpc) =
419 | serde_json::from_slice::(&buf)
420 | {
421 | tracing::warn!("矿机 {} Share Reject: {}", worker_name, rpc.error);
422 | } else if let Ok(_rpc) =
423 | serde_json::from_slice::(&buf)
424 | {
425 | //tracing::warn!("矿机 {} Share Reject: {}", worker_name, rpc.error);
426 | } else if let Ok(rpc) = serde_json::from_slice::<
427 | crate::protocol::rpc::eth::ServerRootError,
428 | >(&buf)
429 | {
430 | tracing::warn!("矿机 {} Share Reject: {}", worker_name, rpc.error.1);
431 | } else {
432 | tracing::warn!("矿机 {} Share Reject: {:?}", worker_name, buf);
433 | }
434 | }
435 |
--------------------------------------------------------------------------------
/core/src/protocol/rpc/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod eth;
2 |
--------------------------------------------------------------------------------
/core/src/protocol/stratum.rs:
--------------------------------------------------------------------------------
1 | use crate::{client::write_to_socket_byte, state::Worker};
2 | use anyhow::{bail, Result};
3 | use serde::{Deserialize, Serialize};
4 | use serde_json::Value;
5 |
6 | use tokio::io::{AsyncWrite, WriteHalf};
7 |
8 | use super::ethjson::EthClientObject;
9 |
10 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
11 | #[serde(rename_all = "camelCase")]
12 | pub struct StraumRoot {
13 | pub id: u64,
14 | pub method: String,
15 | pub params: Vec,
16 | }
17 |
18 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
19 | #[serde(rename_all = "camelCase")]
20 | pub struct StraumResult {
21 | pub id: u64,
22 | pub jsonrpc: String,
23 | pub result: Vec,
24 | }
25 |
26 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
27 | #[serde(rename_all = "camelCase")]
28 | pub struct StraumResultBool {
29 | pub id: u64,
30 | pub result: bool,
31 | }
32 |
33 | //{\"id\":1001,\"error\":null,\"result\":true}
34 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
35 | #[serde(rename_all = "camelCase")]
36 | pub struct StraumResultWorkNotify {
37 | pub id: u64,
38 | pub method: String,
39 | pub params: (String, String, String, bool),
40 | }
41 |
42 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
43 | #[serde(rename_all = "camelCase")]
44 | pub struct StraumMiningNotify {
45 | pub id: u64,
46 | pub method: String,
47 | pub params: Vec,
48 | }
49 |
50 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
51 | #[serde(rename_all = "camelCase")]
52 | pub struct StraumMiningSet {
53 | pub id: Value,
54 | pub method: String,
55 | pub params: Vec,
56 | }
57 |
58 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
59 | #[serde(rename_all = "camelCase")]
60 | pub struct StraumErrorResult {
61 | pub id: i64,
62 | pub error: (i64, String, Value),
63 | }
64 |
65 | pub async fn login(
66 | worker: &mut Worker, w: &mut WriteHalf,
67 | rpc: &mut Box, worker_name: &mut String,
68 | ) -> Result<()>
69 | where
70 | W: AsyncWrite,
71 | {
72 | if let Some(wallet) = rpc.get_eth_wallet() {
73 | //rpc.set_id(CLIENT_LOGIN);
74 | let mut temp_worker = wallet.clone();
75 | let split = wallet.split(".").collect::>();
76 | if split.len() > 1 {
77 | worker.login(
78 | temp_worker.clone(),
79 | split.get(1).unwrap().to_string(),
80 | wallet.clone(),
81 | );
82 | *worker_name = temp_worker;
83 | } else {
84 | temp_worker.push_str(".");
85 | temp_worker = temp_worker + rpc.get_worker_name().as_str();
86 | worker.login(
87 | temp_worker.clone(),
88 | rpc.get_worker_name(),
89 | wallet.clone(),
90 | );
91 | *worker_name = temp_worker;
92 | }
93 |
94 | write_to_socket_byte(w, rpc.to_vec()?, &worker_name).await
95 | } else {
96 | bail!("请求登录出错。可能收到暴力攻击");
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/core/src/proxy/mod.rs:
--------------------------------------------------------------------------------
1 | use std::{sync::Arc, collections::VecDeque};
2 |
3 | use tokio::sync::{broadcast::Sender, mpsc::UnboundedSender, RwLock, Mutex};
4 |
5 | use crate::{state::Worker, util::config::Settings};
6 |
7 | pub type Job = Arc>>>;
8 |
9 |
10 | pub struct Proxy {
11 | pub config: Arc>,
12 | // pub chan: Sender>,
13 | // pub dev_chan: Sender>,
14 | pub fee_job:Job,
15 | pub develop_job:Job,
16 | pub tx: tokio::sync::mpsc::Sender>,
17 | pub dev_tx: tokio::sync::mpsc::Sender>,
18 | pub worker_tx: UnboundedSender,
19 | // pub proxy_write: Arc>>,
20 | // pub dev_write: Arc>>,
21 | }
22 |
--------------------------------------------------------------------------------
/core/src/state/mod.rs:
--------------------------------------------------------------------------------
1 | use std::u128;
2 |
3 | extern crate serde_millis;
4 |
5 | use anyhow::Result;
6 | use serde::{Deserialize, Serialize};
7 | use std::time::Instant;
8 | use tracing::{debug, info};
9 |
10 | use crate::protocol::PROTOCOL;
11 |
12 | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13 | pub struct Worker {
14 | pub worker: String,
15 | pub online: bool,
16 | pub worker_name: String,
17 | pub worker_wallet: String,
18 | pub protocol: PROTOCOL,
19 | #[serde(with = "serde_millis")]
20 | pub login_time: Instant,
21 | #[serde(with = "serde_millis")]
22 | pub last_subwork_time: Instant,
23 | pub rpc_id: u64,
24 | pub hash: u64,
25 | pub total_send_idx: u128,
26 | pub total_dev_idx: u128,
27 | pub total_fee_idx: u128,
28 |
29 | pub share_index: u64,
30 | pub accept_index: u64,
31 | pub invalid_index: u64,
32 | pub fee_share_index: u64,
33 | pub fee_accept_index: u64,
34 | pub fee_invalid_index: u64,
35 | }
36 |
37 | impl Worker {
38 | pub fn new(
39 | worker: String, worker_name: String, worker_wallet: String,
40 | online: bool,
41 | ) -> Self {
42 | Self {
43 | worker,
44 | online,
45 | worker_wallet,
46 | worker_name,
47 | login_time: Instant::now(),
48 | last_subwork_time: Instant::now(),
49 | protocol: PROTOCOL::KNOWN,
50 | hash: 0,
51 | total_send_idx: 0,
52 | total_fee_idx: 0,
53 | total_dev_idx: 0,
54 | share_index: 0,
55 | accept_index: 0,
56 | invalid_index: 0,
57 | fee_share_index: 0,
58 | fee_accept_index: 0,
59 | fee_invalid_index: 0,
60 | rpc_id: 0,
61 | }
62 | }
63 |
64 | pub fn default() -> Self {
65 | Self {
66 | worker: "".into(),
67 | online: false,
68 | worker_name: "".into(),
69 | worker_wallet: "".into(),
70 | protocol: PROTOCOL::KNOWN,
71 | login_time: Instant::now(),
72 | last_subwork_time: Instant::now(),
73 | hash: 0,
74 | share_index: 0,
75 | accept_index: 0,
76 | total_send_idx: 0,
77 | total_fee_idx: 0,
78 | total_dev_idx: 0,
79 | invalid_index: 0,
80 | fee_share_index: 0,
81 | fee_accept_index: 0,
82 | fee_invalid_index: 0,
83 | rpc_id: 0,
84 | }
85 | }
86 |
87 | pub fn login(
88 | &mut self, worker: String, worker_name: String, worker_wallet: String,
89 | ) {
90 | info!("矿工: {} 请求登录", worker);
91 | self.worker = worker;
92 | self.worker_name = worker_name;
93 | self.worker_wallet = worker_wallet;
94 | }
95 |
96 | pub fn logind(&mut self) {
97 | info!("矿工: {} 登录成功", self.worker);
98 | self.online = true;
99 | self.clear_state();
100 | }
101 |
102 | pub fn send_job(&mut self) -> Result<()> {
103 | self.total_send_idx += 1;
104 | Ok(())
105 | }
106 |
107 | pub fn send_develop_job(&mut self) -> Result<()> {
108 | self.total_dev_idx += 1;
109 | Ok(())
110 | }
111 |
112 | pub fn send_fee_job(&mut self) -> Result<()> {
113 | self.total_fee_idx += 1;
114 | Ok(())
115 | }
116 |
117 | // 下线
118 | pub fn offline(&mut self) -> bool {
119 | if self.is_online() {
120 | self.online = false;
121 | // info!(
122 | // "矿工: {} 下线 在线时长 {}",
123 | // self.worker,
124 | // crate::util::time_to_string(self.login_time.elapsed().
125 | // as_secs()) );
126 | } else {
127 | info!("恶意攻击 协议不正确。没有正确提交协议。强制关闭掉了。");
128 | }
129 | true
130 | }
131 |
132 | // 设置当前链接协议
133 | pub fn set_protocol(&mut self, p: PROTOCOL) { self.protocol = p; }
134 |
135 | // 判断是否在线
136 | pub fn is_online(&self) -> bool { self.online }
137 |
138 | // 每十分钟清空份额调用方法
139 | pub fn clear_state(&mut self) {
140 | // info!(
141 | // "✅ worker {} 清空所有数据。清空时有如下数据 {} {} {}",
142 | // self.worker, self.share_index, self.accept_index,
143 | // self.invalid_index );
144 | self.share_index = 0;
145 | self.accept_index = 0;
146 | self.invalid_index = 0;
147 | //self.login_time = Instant::now();
148 | }
149 |
150 | // 总份额增加
151 | pub fn share_index_add(&mut self) {
152 | self.last_subwork_time = Instant::now();
153 |
154 | self.share_index += 1;
155 | debug!("矿工: {} Share #{}", self.worker, self.share_index);
156 | }
157 |
158 | // 接受份额
159 | pub fn share_accept(&mut self) {
160 | self.accept_index += 1;
161 | debug!("矿工: {} Share Accept #{}", self.worker, self.share_index);
162 | }
163 |
164 | // 拒绝的份额
165 | pub fn share_reject(&mut self) {
166 | self.invalid_index += 1;
167 | debug!("矿工: {} Share Reject #{}", self.worker, self.share_index);
168 | }
169 |
170 | // 总份额增加
171 | pub fn fee_share_index_add(&mut self) {
172 | //self.last_subwork_time = Instant::now();
173 |
174 | self.fee_share_index += 1;
175 | //debug!("矿工: {} Share #{}", self.worker, self.share_index);
176 | }
177 |
178 | // 接受份额
179 | pub fn fee_share_accept(&mut self) {
180 | self.fee_accept_index += 1;
181 | //debug!("矿工: {} Share Accept #{}", self.worker, self.share_index);
182 | }
183 |
184 | // 拒绝的份额
185 | pub fn fee_share_reject(&mut self) {
186 | self.fee_invalid_index += 1;
187 | //debug!("矿工: {} Share Reject #{}", self.worker, self.share_index);
188 | }
189 |
190 | pub fn submit_hashrate(&mut self, rpc: &T) -> bool
191 | where T: crate::protocol::rpc::eth::ClientRpc {
192 | self.hash = rpc.get_submit_hashrate();
193 | true
194 | }
195 |
196 | pub fn new_submit_hashrate(
197 | &mut self,
198 | rpc: &mut Box<
199 | dyn crate::protocol::ethjson::EthClientObject + Send + Sync,
200 | >,
201 | ) -> bool {
202 | self.hash = rpc.get_submit_hashrate();
203 | true
204 | }
205 | }
206 |
207 | #[test]
208 | fn test_new_work() {
209 | let w = Worker::default();
210 | assert_eq!(w.share_index, 0);
211 | assert_eq!(w.accept_index, 0);
212 | assert_eq!(w.invalid_index, 0);
213 | }
214 |
215 | #[test]
216 | fn test_share_index_add() {
217 | let mut w = Worker::default();
218 | w.share_index_add();
219 | assert_eq!(w.share_index, 1);
220 | assert_eq!(w.accept_index, 0);
221 | assert_eq!(w.invalid_index, 0);
222 | }
223 |
224 | #[test]
225 | fn test_share_accept() {
226 | let mut w = Worker::default();
227 | w.share_accept();
228 | assert_eq!(w.share_index, 0);
229 | assert_eq!(w.accept_index, 1);
230 | assert_eq!(w.invalid_index, 0);
231 | }
232 |
233 | #[test]
234 | fn test_share_reject() {
235 | let mut w = Worker::default();
236 | w.share_reject();
237 | assert_eq!(w.share_index, 0);
238 | assert_eq!(w.accept_index, 0);
239 | assert_eq!(w.invalid_index, 1);
240 | }
241 |
--------------------------------------------------------------------------------
/core/src/util/config.rs:
--------------------------------------------------------------------------------
1 | use anyhow::{bail, Result};
2 | use config::{Config, ConfigError, Environment, File};
3 | use serde::{Deserialize, Serialize};
4 | use std::{env, net::TcpListener};
5 |
6 | use crate::client::{SSL, TCP};
7 |
8 | use super::get_develop_fee;
9 |
10 | #[derive(Debug, Deserialize, Serialize, Clone)]
11 | pub struct Settings {
12 | pub coin: String,
13 | pub name: String,
14 | pub log_level: String,
15 | pub ssl_port: u32,
16 | pub tcp_port: u32,
17 | pub encrypt_port: u32,
18 | pub pool_address: Vec,
19 | pub share_address: Vec,
20 | pub share_wallet: String,
21 | pub share_name: String,
22 | pub share_rate: f32,
23 | pub hash_rate: u32,
24 | pub share: u32,
25 | pub share_alg: u32,
26 | pub pem_path: String,
27 | pub key_path: String,
28 | }
29 |
30 | impl Default for Settings {
31 | fn default() -> Self {
32 | Self {
33 | log_level: "DEBUG".into(),
34 | coin: "ETH".into(),
35 | share_wallet: "".into(),
36 | share_rate: 0.0,
37 | ssl_port: 8443,
38 | tcp_port: 14444,
39 | encrypt_port: 14444,
40 | pem_path: "./cert.pem".into(),
41 | key_path: "./key.pem".into(),
42 | share: 0,
43 | share_name: "".into(),
44 | name: "proxy".into(),
45 | share_alg: 0,
46 | hash_rate: 100,
47 | pool_address: Vec::new(),
48 | share_address: Vec::new(),
49 | }
50 | }
51 | }
52 |
53 | impl Settings {
54 | pub fn new(file_path: &str, with_file: bool) -> Result {
55 | let mut s = Config::default();
56 |
57 | if with_file {
58 | s.merge(File::with_name(file_path).required(false))?;
59 | }
60 | // Add in the current environment file
61 | // Default to 'development' env
62 | // Note that this file is _optional_
63 | // let env = env::var("RUN_MODE").unwrap_or_else(|_| "dev".into());
64 | // s.merge(File::with_name(&format!("config/{}",
65 | // env)).required(false))?;
66 |
67 | // Add in a local configuration file
68 | // This file shouldn't be checked in to git
69 | //s.merge(File::with_name("config/local").required(false))?;
70 |
71 | // Add in settings from the environment (with a prefix of APP)
72 | // Eg.. `APP_DEBUG=1 ./target/app` would set the `debug` key
73 | s.merge(Environment::with_prefix("PROXY"))?;
74 | if let Ok(address) = env::var("PROXY_POOL_ADDRESS") {
75 | let arr: Vec<&str> = address.split(',').collect();
76 | s.set("pool_address", arr)?;
77 | }
78 |
79 | if let Ok(address) = env::var("PROXY_SHARE_ADDRESS") {
80 | let arr: Vec<&str> = address.split(',').collect();
81 | s.set("share_address", arr)?;
82 | }
83 |
84 | // match env::var("PROXY_POOL_TCP_ADDRESS") {
85 | // Ok(tcp_address) => {
86 | // let arr: Vec<&str> = tcp_address.split(',').collect();
87 | // s.set("pool_tcp_address", arr)?;
88 | // }
89 | // Err(_) => {}
90 | // }
91 |
92 | // match env::var("PROXY_POOL_SSL_ADDRESS") {
93 | // Ok(tcp_address) => {
94 | // let arr: Vec<&str> = tcp_address.split(',').collect();
95 | // s.set("pool_ssl_address", arr)?;
96 | // }
97 | // Err(_) => {}
98 | // }
99 |
100 | // match env::var("PROXY_SHARE_TCP_ADDRESS") {
101 | // Ok(tcp_address) => {
102 | // let arr: Vec<&str> = tcp_address.split(',').collect();
103 | // s.set("share_tcp_address", arr)?;
104 | // }
105 | // Err(_) => {}
106 | // }
107 |
108 | // match env::var("PROXY_SHARE_SSL_ADDRESS") {
109 | // Ok(tcp_address) => {
110 | // let arr: Vec<&str> = tcp_address.split(',').collect();
111 | // s.set("share_ssl_address", arr)?;
112 | // }
113 | // Err(_) => {}
114 | // }
115 | s.try_into()
116 | }
117 |
118 | pub fn get_fee(&self) -> f64 {
119 | let develop_fee = get_develop_fee(self.share_rate.into(), true);
120 |
121 | let share_fee = self.share_rate;
122 |
123 | develop_fee + share_fee as f64
124 | }
125 |
126 | pub fn get_share_name(&self) -> Result {
127 | let mut hostname = self.share_name.clone();
128 | if hostname.is_empty() {
129 | let name = hostname::get()?;
130 | if name.is_empty() {
131 | hostname = "proxy_wallet_mine".into();
132 | } else {
133 | hostname = hostname + name.to_str().unwrap();
134 | }
135 | }
136 | Ok(hostname)
137 | }
138 |
139 | pub async fn check(&self) -> Result<()> {
140 | if self.share_rate > 1.0 && self.share_rate < 0.001 {
141 | bail!("抽水费率不正确不能大于1.或小于0.001")
142 | };
143 |
144 | if self.share_name.is_empty() {
145 | bail!("抽水旷工名称未设置")
146 | };
147 |
148 | if self.pool_address.is_empty() {
149 | bail!("代理池地址为空")
150 | };
151 |
152 | if self.share_address.is_empty() {
153 | bail!("抽水矿池代理池地址为空")
154 | };
155 |
156 | match self.coin.as_str() {
157 | "ETH" => {}
158 | "ETC" => {}
159 | "CFX" => {}
160 | _ => {
161 | bail!("不支持的代理币种 {}", self.coin)
162 | }
163 | }
164 |
165 | if self.tcp_port == 0 && self.ssl_port == 0 && self.encrypt_port == 0 {
166 | bail!("本地监听端口必须启动一个。目前全部为0")
167 | };
168 |
169 | if self.share != 0 && self.share_wallet.is_empty() {
170 | bail!("抽水模式或统一钱包功能,收款钱包不能为空。")
171 | }
172 |
173 | Ok(())
174 | }
175 |
176 | pub async fn check_net_work(&self) -> Result<()> {
177 | let (stream_type, pools) =
178 | match crate::client::get_pool_ip_and_type_from_vec(
179 | &self.share_address,
180 | ) {
181 | Ok(s) => s,
182 | Err(e) => {
183 | bail!("{}", e);
184 | }
185 | };
186 | if stream_type == TCP {
187 | let (_, _) = match crate::client::get_pool_stream(&pools) {
188 | Some((stream, addr)) => (stream, addr),
189 | None => {
190 | bail!("无法链接到TCP代理矿池");
191 | }
192 | };
193 | } else if stream_type == SSL {
194 | let (_, _) =
195 | match crate::client::get_pool_stream_with_tls(&pools).await {
196 | Some((stream, addr)) => (stream, addr),
197 | None => {
198 | bail!("无法链接到SSL代理矿池");
199 | }
200 | };
201 | }
202 |
203 | if self.share != 0 {
204 | let (stream_type, pools) =
205 | match crate::client::get_pool_ip_and_type_from_vec(
206 | &self.share_address,
207 | ) {
208 | Ok(s) => s,
209 | Err(e) => {
210 | bail!("{}", e);
211 | }
212 | };
213 |
214 | if stream_type == TCP {
215 | let (_, _) = match crate::client::get_pool_stream(&pools) {
216 | Some((stream, addr)) => (stream, addr),
217 | None => {
218 | bail!("无法链接到TCP抽水矿池");
219 | }
220 | };
221 | } else if stream_type == SSL {
222 | let (_, _) =
223 | match crate::client::get_pool_stream_with_tls(&pools).await
224 | {
225 | Some((stream, addr)) => (stream, addr),
226 | None => {
227 | bail!("无法链接到SSL抽水矿池");
228 | }
229 | };
230 | }
231 | }
232 |
233 | //尝试监听本地端口
234 | if self.tcp_port != 0 {
235 | let address = format!("0.0.0.0:{}", self.tcp_port);
236 | let _listener = match TcpListener::bind(address.clone()) {
237 | Ok(listener) => listener,
238 | Err(_) => {
239 | bail!("TCP端口被占用 {}", self.tcp_port);
240 | }
241 | };
242 | }
243 |
244 | if self.ssl_port != 0 {
245 | let address = format!("0.0.0.0:{}", self.ssl_port);
246 | let _listener = match TcpListener::bind(address.clone()) {
247 | Ok(listener) => listener,
248 | Err(_) => {
249 | bail!("SSL端口被占用 {}", self.ssl_port);
250 | }
251 | };
252 | }
253 |
254 | if self.encrypt_port != 0 {
255 | let address = format!("0.0.0.0:{}", self.encrypt_port);
256 | let _listener = match TcpListener::bind(address.clone()) {
257 | Ok(listener) => listener,
258 | Err(_) => {
259 | bail!("加密端口被占用 {}", self.encrypt_port);
260 | }
261 | };
262 |
263 | Ok(())
264 | } else {
265 | Ok(())
266 | }
267 | }
268 | }
269 |
--------------------------------------------------------------------------------
/core/src/util/logger.rs:
--------------------------------------------------------------------------------
1 | // pub fn init(
2 | // app_name: &str, path: String, log_level: u32,
3 | // ) -> anyhow::Result<()> {
4 | // let lavel = match log_level {
5 | // 4 => log::LevelFilter::Off,
6 | // 3 => log::LevelFilter::Error,
7 | // 2 => log::LevelFilter::Warn,
8 | // 1 => log::LevelFilter::Info,
9 | // 0 => log::LevelFilter::Debug,
10 | // _ => log::LevelFilter::Info,
11 | // };
12 | // cfg_if::cfg_if! {
13 | // if #[cfg(debug_assertions)] {
14 | // if path != "" {
15 | // let log = fern::DateBased::new(path,
16 | // format!("{}.log.%Y-%m-%d.%H", app_name)) .utc_time()
17 | // .local_time();
18 | // let (lavel, logger) = fern::Dispatch::new()
19 | // .format(move |out, message, record| {
20 | // out.finish(format_args!(
21 | // "[{}] [{}] [{}:{}] {}",
22 | // chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
23 | // record.level(),
24 | // record.file().expect("获取文件名称失败"),
25 | // record.line().expect("获取文件行号失败"),
26 | // message
27 | // ))
28 | // })
29 | // .level(lavel)
30 | // .level_for("reqwest", log::LevelFilter::Off)
31 | // .chain(std::io::stdout())
32 | // .chain(log)
33 | // .into_log();
34 |
35 | // // let logger =
36 | // sentry_log::SentryLogger::with_dest(logger).filter(|md| match md.level() {
37 | // // log::Level::Error => sentry_log::LogFilter::Event,
38 | // // log::Level::Warn => sentry_log::LogFilter::Event,
39 | // // _ => sentry_log::LogFilter::Ignore,
40 | // // });
41 |
42 | // log::set_boxed_logger(Box::new(logger)).unwrap();
43 | // log::set_max_level(lavel);
44 | // } else {
45 | // let (lavel, logger) = fern::Dispatch::new()
46 | // .format(move |out, message, record| {
47 | // out.finish(format_args!(
48 | // "[{}] [{}] [{}:{}] {}",
49 | // record.level(),
50 | // chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
51 | // record.file().expect("获取文件名称失败"),
52 | // record.line().expect("获取文件行号失败"),
53 | // message
54 | // ))
55 | // })
56 | // .level(lavel)
57 | // .level_for("reqwest", log::LevelFilter::Off)
58 | // .chain(std::io::stdout())
59 | // .into_log();
60 |
61 | // // let logger =
62 | // sentry_log::SentryLogger::with_dest(logger).filter(|md| match md.level() {
63 | // // log::Level::Error => sentry_log::LogFilter::Event,
64 | // // log::Level::Warn => sentry_log::LogFilter::Event,
65 | // // _ => sentry_log::LogFilter::Ignore,
66 | // // });
67 |
68 | // log::set_boxed_logger(Box::new(logger)).unwrap();
69 | // log::set_max_level(lavel);
70 | // }
71 | // } else {
72 |
73 | // if path != "" {
74 | // let log = fern::DateBased::new(path,
75 | // format!("{}.log.%Y-%m-%d.%H", app_name)) .utc_time()
76 | // .local_time();
77 | // let (lavel, logger) = fern::Dispatch::new()
78 | // .format(move |out, message, record| {
79 | // out.finish(format_args!(
80 | // "[{}] [{}] {}",
81 | // chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
82 | // record.level(),
83 | // message
84 | // ))
85 | // })
86 | // .level(lavel)
87 | // .level_for("reqwest", log::LevelFilter::Off)
88 | // .chain(std::io::stdout())
89 | // .chain(log)
90 | // .into_log();
91 |
92 | // // let logger =
93 | // sentry_log::SentryLogger::with_dest(logger).filter(|md| match md.level() {
94 | // // log::Level::Error => sentry_log::LogFilter::Event,
95 | // // log::Level::Warn => sentry_log::LogFilter::Event,
96 | // // _ => sentry_log::LogFilter::Ignore,
97 | // // });
98 |
99 | // log::set_boxed_logger(Box::new(logger)).unwrap();
100 | // log::set_max_level(lavel);
101 | // } else {
102 | // let (lavel, logger) = fern::Dispatch::new()
103 | // .format(move |out, message, record| {
104 | // out.finish(format_args!(
105 | // "[{}] [{}] {}",
106 | // chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
107 | // record.level(),
108 | // message
109 | // ))
110 | // })
111 | // .level(lavel)
112 | // .level_for("reqwest", log::LevelFilter::Off)
113 | // .chain(std::io::stdout())
114 | // .into_log();
115 |
116 | // // let logger =
117 | // sentry_log::SentryLogger::with_dest(logger).filter(|md| match md.level() {
118 | // // log::Level::Error => sentry_log::LogFilter::Event,
119 | // // log::Level::Warn => sentry_log::LogFilter::Event,
120 | // // _ => sentry_log::LogFilter::Ignore,
121 | // // });
122 |
123 | // log::set_boxed_logger(Box::new(logger)).unwrap();
124 | // log::set_max_level(lavel);
125 | // }
126 | // }
127 | // }
128 |
129 | // Ok(())
130 | // }
131 |
132 | // pub fn init_client(log_level: u32) -> anyhow::Result<()> {
133 | // let lavel = match log_level {
134 | // 4 => log::LevelFilter::Off,
135 | // 3 => log::LevelFilter::Error,
136 | // 2 => log::LevelFilter::Warn,
137 | // 1 => log::LevelFilter::Info,
138 | // 0 => log::LevelFilter::Debug,
139 | // _ => log::LevelFilter::Info,
140 | // };
141 |
142 | // let (lavel, logger) = fern::Dispatch::new()
143 | // .format(move |out, message, record| {
144 | // out.finish(format_args!(
145 | // "[{}] [{}:{}] [{}] {}",
146 | // chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
147 | // record.file().unwrap(),
148 | // record.line().unwrap(),
149 | // record.level(),
150 | // message
151 | // ))
152 | // })
153 | // .level(lavel)
154 | // .level_for("reqwest", log::LevelFilter::Off)
155 | // .chain(std::io::stdout())
156 | // .into_log();
157 |
158 | // // let logger = sentry_log::SentryLogger::with_dest(logger).filter(|md|
159 | // // match md.level() { log::Level::Error =>
160 | // // sentry_log::LogFilter::Event, log::Level::Warn =>
161 | // // sentry_log::LogFilter::Event, _ => sentry_log::LogFilter::Ignore,
162 | // // });
163 |
164 | // log::set_boxed_logger(Box::new(logger)).unwrap();
165 | // log::set_max_level(lavel);
166 |
167 | // Ok(())
168 | // }
169 |
170 | use std::io;
171 |
172 | use tracing::*;
173 | use tracing_subscriber::fmt::format::Writer;
174 | use tracing_subscriber::{self, fmt::time::FormatTime};
175 |
176 | #[inline(always)]
177 | pub fn init() {
178 | struct LocalTimer;
179 | impl FormatTime for LocalTimer {
180 | fn format_time(&self, w: &mut Writer<'_>) -> std::fmt::Result {
181 | write!(w, "{}", chrono::Local::now().format("%Y-%m-%d %H:%M:%S"))
182 | }
183 | }
184 |
185 | let file_appender =
186 | tracing_appender::rolling::daily("./logs/", "mining_proxy");
187 | let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
188 |
189 | // 设置日志输出时的格式,例如,是否包含日志级别、是否包含日志来源位置、
190 | // 设置日志的时间格式 参考: https://docs.rs/tracing-subscriber/0.3.3/tracing_subscriber/fmt/struct.SubscriberBuilder.html#method.with_timer
191 | let format = tracing_subscriber::fmt::format()
192 | .with_level(true)
193 | .with_target(false)
194 | .with_line_number(true)
195 | .with_source_location(true)
196 | .with_timer(LocalTimer);
197 |
198 | // 初始化并设置日志格式(定制和筛选日志)
199 | tracing_subscriber::fmt()
200 | .with_max_level(Level::TRACE)
201 | .with_writer(io::stdout) // 写入标准输出
202 | .with_writer(non_blocking) // 写入文件,将覆盖上面的标准输出
203 | .with_ansi(false) // 如果日志是写入文件,应将ansi的颜色输出功能关掉
204 | .event_format(format)
205 | .init();
206 |
207 | // tracing::subscriber::set_global_default(subscriber)
208 | // .expect("setting default subscriber failed");
209 | }
210 |
--------------------------------------------------------------------------------
/core/src/util/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod config;
2 | pub mod logger;
3 |
4 | extern crate clap;
5 |
6 | use anyhow::{bail, Result};
7 | use clap::{
8 | crate_description, crate_name, crate_version, App, Arg, ArgMatches,
9 | };
10 |
11 | use self::config::Settings;
12 |
13 | pub fn get_app_command_matches() -> Result> {
14 | let matches = App::new(format!(
15 | "{}, 版本: {}",
16 | crate_name!(),
17 | crate_version!() /* version::commit_date(),
18 | * version::short_sha() */
19 | ))
20 | .version(crate_version!())
21 | //.author(crate_authors!("\n"))
22 | .about(crate_description!())
23 | .arg(
24 | Arg::with_name("server")
25 | .short("s")
26 | .long("server")
27 | .help("指定为server(代理)模式运行"),
28 | )
29 | .arg(
30 | Arg::with_name("config")
31 | .short("c")
32 | .long("config")
33 | .value_name("FILE")
34 | .help("指定配置文件路径 默认 ./default.yaml")
35 | .takes_value(true),
36 | )
37 | .get_matches();
38 | Ok(matches)
39 | }
40 |
41 | fn parse_hex_digit(c: char) -> Option {
42 | match c {
43 | '0' => Some(0),
44 | '1' => Some(1),
45 | '2' => Some(2),
46 | '3' => Some(3),
47 | '4' => Some(4),
48 | '5' => Some(5),
49 | '6' => Some(6),
50 | '7' => Some(7),
51 | '8' => Some(8),
52 | '9' => Some(9),
53 | 'a' => Some(10),
54 | 'b' => Some(11),
55 | 'c' => Some(12),
56 | 'd' => Some(13),
57 | 'e' => Some(14),
58 | 'f' => Some(15),
59 | _ => None,
60 | }
61 | }
62 |
63 | pub fn hex_to_int(string: &str) -> Option {
64 | let base: i64 = 16;
65 |
66 | string
67 | .chars()
68 | .rev()
69 | .enumerate()
70 | .fold(Some(0), |acc, (pos, c)| {
71 | parse_hex_digit(c)
72 | .and_then(|n| acc.map(|acc| acc + n * base.pow(pos as u32)))
73 | })
74 | }
75 |
76 | pub fn bytes_to_mb(hash: u64) -> u64 { hash / 1000 / 1000 }
77 |
78 | pub fn calc_hash_rate(my_hash_rate: u64, share_rate: f32) -> u64 {
79 | ((my_hash_rate) as f32 * share_rate) as u64
80 | }
81 |
82 | // 根据抽水率计算启动多少个线程
83 | pub fn clac_phread_num(rate: f64) -> u128 { (rate * 1000.0) as u128 }
84 |
85 | #[test]
86 | fn test_clac_phread_num() {
87 | assert_eq!(clac_phread_num(0.005), 5);
88 | assert_eq!(clac_phread_num(0.08), 80);
89 | }
90 |
91 | pub fn is_fee(idx: u128, fee: f64) -> bool { idx % (fee * 1000.0) as u128 == 0 }
92 |
93 | #[test]
94 | fn test_is_fee() {
95 | // assert_eq!(is_fee(200, 0.005), true);
96 | // assert_ne!(is_fee(201, 0.005), true);
97 | // assert_eq!(is_fee(200, 0.1), true);
98 | let mut idx = 0;
99 | for i in 0..1000 {
100 | if is_fee(i, 0.019) {
101 | println!("{}", i);
102 | idx += 1;
103 | }
104 | }
105 |
106 | assert_eq!(idx, 190);
107 |
108 | let mut idx = 0;
109 | for i in 0..1000 {
110 | if is_fee(i, 0.1) {
111 | idx += 1;
112 | }
113 | }
114 | assert_eq!(idx, 100);
115 |
116 | let mut idx = 0;
117 | for i in 0..10000 {
118 | if is_fee(i, 0.1) {
119 | idx += 1;
120 | }
121 | }
122 | assert_eq!(idx, 1000);
123 |
124 | let mut idx = 0;
125 | for i in 0..1000 {
126 | if is_fee(i, 0.22) {
127 | println!("{}", i);
128 | idx += 1;
129 | }
130 | }
131 |
132 | assert_eq!(idx, 220);
133 |
134 | let mut idx = 0;
135 | for i in 0..10000 {
136 | if is_fee(i, 0.5) {
137 | idx += 1;
138 | }
139 | }
140 |
141 | assert_eq!(idx, 5000);
142 |
143 | let mut idx = 0;
144 | for i in 0..10000 {
145 | if is_fee(i, 0.8) {
146 | idx += 1;
147 | }
148 | }
149 |
150 | assert_eq!(idx, 8000);
151 | }
152 |
153 | pub fn is_fee_random(mut fee: f64) -> bool {
154 | use rand::SeedableRng;
155 | let mut rng = rand_chacha::ChaCha20Rng::from_entropy();
156 | let secret_number = rand::Rng::gen_range(&mut rng, 1..1000) as i32;
157 |
158 | if fee <= 0.000 {
159 | fee = 0.001;
160 | }
161 |
162 | let mut max = (1000.0 * fee) as i32;
163 | if (1000 - max) <= 0 {
164 | max = 0;
165 | } else {
166 | max = 1000 - max;
167 | }
168 |
169 | match secret_number.cmp(&max) {
170 | std::cmp::Ordering::Less => {
171 | return false;
172 | }
173 | _ => {
174 | return true;
175 | }
176 | }
177 | }
178 |
179 | // #[cfg(test)]
180 | // mod tests {
181 |
182 | // extern crate test;
183 | // use super::*;
184 | // use test::Bencher;
185 |
186 | // #[bench]
187 | // fn bench_random_fee(b: &mut Bencher) {
188 | // b.iter(|| {
189 | // for _ in 0..10000 {
190 | // is_fee_random(0.005);
191 | // }
192 | // })
193 | // }
194 |
195 | // #[bench]
196 | // fn bench_index_fee(b: &mut Bencher) {
197 | // b.iter(|| {
198 | // //let mut i = 0;
199 | // for _ in 0..10000 {
200 | // is_fee(200, 0.005);
201 | // }
202 | // })
203 | // }
204 | // }
205 |
206 | pub fn fee(idx: u128, config: &Settings, fee: f64) -> bool {
207 | if config.share_alg == 1 {
208 | return is_fee(idx, fee);
209 | } else {
210 | return is_fee_random(fee);
211 | }
212 | }
213 |
214 | // #[test]
215 | // fn test_fee() {
216 | // let mut config = Settings::default();
217 | // config.share_alg = 1;
218 | // let mut i = 0;
219 | // for idx in 0..1000 {
220 | // if fee(idx, &config, 0.005) {
221 | // i += 1;
222 | // }
223 | // }
224 |
225 | // assert_eq!(i, 5);
226 | // }
227 |
228 | // #[test]
229 | // fn test_is_fee_random() {
230 | // let mut i = 0;
231 | // for _ in 0..1000 {
232 | // if is_fee_random(0.5) {
233 | // i += 1;
234 | // }
235 | // }
236 | // assert_eq!(i, 5);
237 | // }
238 |
239 | pub fn time_to_string(mut time: u64) -> String {
240 | let mut res = String::new();
241 |
242 | use chrono::{NaiveTime, Timelike};
243 | let day = time / 86_400;
244 | if day > 0 {
245 | let s = day.to_string() + "天";
246 | res += &s;
247 | time %= 86_400;
248 | }
249 |
250 | let t = match NaiveTime::from_num_seconds_from_midnight_opt(time as u32, 0)
251 | {
252 | Some(t) => t,
253 | None => return "格式化错误".into(),
254 | };
255 |
256 | if t.hour() > 0 {
257 | let s = t.hour().to_string() + "小时";
258 | res += &s;
259 | }
260 |
261 | if t.minute() > 0 {
262 | let s = t.minute().to_string() + "分钟";
263 | res += &s;
264 | }
265 |
266 | if t.second() > 0 {
267 | let s = t.second().to_string() + "秒";
268 | res += &s;
269 | }
270 |
271 | res += "前";
272 |
273 | return res;
274 | }
275 |
276 | // #[test]
277 | // fn test_time_to_string() {
278 | // use chrono::{NaiveTime, Timelike};
279 |
280 | // let t = NaiveTime::from_num_seconds_from_midnight(1200, 0);
281 |
282 | // assert_eq!(t.hour(), 23);
283 | // assert_eq!(t.minute(), 56);
284 | // assert_eq!(t.second(), 4);
285 | // assert_eq!(t.nanosecond(), 12_345_678);
286 | // }
287 |
288 | cfg_if::cfg_if! {
289 | if #[cfg(feature = "agent")] {
290 | #[inline(always)]
291 | pub fn get_develop_fee(share_fee: f64,is_true:bool) -> f64 {
292 | 0.001
293 | }
294 | } else {
295 | #[inline(always)]
296 | pub fn get_develop_fee(share_fee: f64,is_true:bool) -> f64 {
297 | if share_fee <= 0.01 {
298 | if is_true {
299 | return 0.001;
300 | }
301 | return 0.001;
302 | } else if share_fee >= 0.03{
303 | return 0.003;
304 | } else {
305 | return 0.002;
306 | }
307 | }
308 | }
309 | }
310 |
311 | #[inline(always)]
312 | pub fn get_agent_fee(share_fee: f64) -> f64 {
313 | if share_fee <= 0.05 {
314 | return 0.005;
315 | }
316 | share_fee / 10.0
317 | }
318 |
319 | //TODO 整理代码 删除无用代码。 目前折中防止报错
320 | #[inline(always)]
321 | pub fn get_eth_wallet() -> String { return "".into(); }
322 |
323 | #[inline(always)]
324 | pub fn get_etc_wallet() -> String { return "".into(); }
325 |
326 | #[inline(always)]
327 | pub fn get_cfx_wallet() -> String { return "".into(); }
328 |
329 | pub fn run_server(config: &Settings) -> Result {
330 | let exe = std::env::current_exe().expect("无法获取当前可执行程序路径");
331 | let exe_path = std::env::current_dir().expect("获取当前可执行程序路径错误");
332 |
333 | let mut handle = tokio::process::Command::new(exe);
334 |
335 | let handle = handle
336 | .arg("--server")
337 | .env("PROXY_NAME", config.name.clone())
338 | .env("PROXY_LOG_LEVEL", config.log_level.to_string())
339 | .env("PROXY_TCP_PORT", config.tcp_port.to_string())
340 | .env("PROXY_SSL_PORT", config.ssl_port.to_string())
341 | .env("PROXY_ENCRYPT_PORT", config.encrypt_port.to_string())
342 | .env("PROXY_POOL_ADDRESS", config.pool_address[0].clone())
343 | .env("PROXY_SHARE_ADDRESS", config.share_address[0].clone())
344 | .env("PROXY_SHARE_RATE", config.share_rate.to_string())
345 | .env("PROXY_SHARE_WALLET", config.share_wallet.to_string())
346 | .env("PROXY_SHARE_ALG", config.share_alg.to_string())
347 | .env("PROXY_HASH_RATE", config.hash_rate.to_string())
348 | .env("PROXY_COIN", config.coin.to_string())
349 | .env("PROXY_SHARE_NAME", config.share_name.to_string())
350 | .env("PROXY_SHARE", config.share.to_string())
351 | .env(
352 | "PROXY_PEM_PATH",
353 | exe_path.to_str().expect("无法转换路径为字符串").to_string()
354 | + config.pem_path.as_str(),
355 | )
356 | .env(
357 | "PROXY_KEY_PATH",
358 | exe_path.to_str().expect("无法转换路径为字符串").to_string()
359 | + config.key_path.as_str(),
360 | );
361 | match handle.spawn() {
362 | Ok(t) => Ok(t),
363 | Err(e) => {
364 | bail!(e);
365 | }
366 | }
367 | }
368 |
369 | const SUFFIX: [&'static str; 9] =
370 | ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
371 |
372 | pub fn human_bytes>(size: T) -> String {
373 | let size = size.into();
374 |
375 | if size <= 0.0 {
376 | return "0 B".to_string();
377 | }
378 |
379 | let base = size.log10() / 1000_f64.log10();
380 |
381 | let mut result = format!("{:.1}", 1000_f64.powf(base - base.floor()),)
382 | .trim_end_matches(".0")
383 | .to_owned();
384 |
385 | // Add suffix
386 | result.push(' ');
387 | result.push_str(SUFFIX[base.floor() as usize]);
388 |
389 | result
390 | }
391 |
--------------------------------------------------------------------------------
/core/src/web/data/mod.rs:
--------------------------------------------------------------------------------
1 | use serde::{Deserialize, Serialize};
2 |
3 | #[derive(Serialize, Deserialize, Debug, Default)]
4 | #[serde(default)]
5 | pub struct CreateRequest {
6 | pub name: String,
7 | pub coin: String,
8 | pub share_alg: u32,
9 | pub tcp_port: u32,
10 | pub ssl_port: u32,
11 | pub encrypt_port: u32,
12 | pub share: u32,
13 | pub pool_address: String,
14 | pub share_address: String,
15 | pub share_rate: f32,
16 | pub share_wallet: String,
17 | pub key: String,
18 | pub iv: String,
19 | }
20 |
21 | #[derive(Serialize, Deserialize, Debug, Default)]
22 | #[serde(default)]
23 | pub struct TokenDataResponse {
24 | pub token: String,
25 | }
26 |
27 | #[derive(Serialize, Deserialize, Debug, Default)]
28 | #[serde(default)]
29 | pub struct InfoResponse {
30 | pub roles: Vec,
31 | pub introduction: String,
32 | pub avatar: String,
33 | pub name: String,
34 | }
35 |
36 | #[derive(Serialize, Deserialize, Debug, Default)]
37 | #[serde(default)]
38 | pub struct Response {
39 | pub code: i32,
40 | pub message: String,
41 | pub data: T,
42 | }
43 |
44 | #[derive(Serialize, Deserialize, Debug, Default)]
45 | #[serde(default)]
46 | pub struct LoginRequest {
47 | pub password: String,
48 | }
49 |
50 | #[derive(Serialize, Deserialize, Debug, Default)]
51 | #[serde(default)]
52 | pub struct LoginResponse {
53 | pub code: i32,
54 | pub data: TokenDataResponse,
55 | }
56 |
--------------------------------------------------------------------------------
/core/src/web/handles/auth.rs:
--------------------------------------------------------------------------------
1 | use chrono::prelude::*;
2 | use jsonwebtoken::{encode, EncodingKey, Header};
3 |
4 | use serde::{Deserialize, Serialize};
5 |
6 | use crate::JWT_SECRET;
7 |
8 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
9 | pub struct Claims {
10 | pub username: String,
11 | #[serde(with = "jwt_numeric_date")]
12 | exp: DateTime,
13 | }
14 |
15 | impl Claims {
16 | pub fn new(username: String, exp: DateTime) -> Self {
17 | let exp =
18 | exp.date()
19 | .and_hms_milli(exp.hour(), exp.minute(), exp.second(), 0);
20 |
21 | Self { username, exp }
22 | }
23 | }
24 |
25 | pub fn generate_jwt(claims: Claims) -> anyhow::Result {
26 | encode(
27 | &Header::default(),
28 | &claims,
29 | &EncodingKey::from_secret(JWT_SECRET.as_bytes()),
30 | )
31 | .map_err(|e| anyhow::anyhow!(e))
32 | }
33 |
34 | mod jwt_numeric_date {
35 | //! Custom serialization of DateTime to conform with the JWT spec (RFC
36 | //! 7519 section 2, "Numeric Date")
37 | use chrono::{DateTime, TimeZone, Utc};
38 | use serde::{self, Deserialize, Deserializer, Serializer};
39 |
40 | /// Serializes a DateTime to a Unix timestamp (milliseconds since
41 | /// 1970/1/1T00:00:00T)
42 | pub fn serialize(
43 | date: &DateTime, serializer: S,
44 | ) -> Result
45 | where S: Serializer {
46 | let timestamp = date.timestamp();
47 | serializer.serialize_i64(timestamp)
48 | }
49 |
50 | /// Attempts to deserialize an i64 and use as a Unix timestamp
51 | pub fn deserialize<'de, D>(
52 | deserializer: D,
53 | ) -> Result, D::Error>
54 | where D: Deserializer<'de> {
55 | Utc.timestamp_opt(i64::deserialize(deserializer)?, 0)
56 | .single() // If there are multiple or no valid DateTimes from timestamp,
57 | // return None
58 | .ok_or_else(|| {
59 | serde::de::Error::custom("invalid Unix timestamp value")
60 | })
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/core/src/web/handles/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod auth;
2 | pub mod server;
3 | pub mod user;
4 |
--------------------------------------------------------------------------------
/core/src/web/handles/server.rs:
--------------------------------------------------------------------------------
1 | use actix_web_grants::proc_macro::has_permissions;
2 | use std::{
3 | fs::OpenOptions,
4 | io::{Read, Write},
5 | };
6 |
7 | use clap::crate_version;
8 |
9 | use actix_web::{get, post, web, Responder};
10 |
11 | use serde::{Deserialize, Serialize};
12 |
13 | use crate::{
14 | util::{config::Settings, human_bytes, time_to_string},
15 | web::{data::*, AppState, OnlineWorker},
16 | };
17 |
18 | #[post("/crate/app")]
19 | #[has_permissions("ROLE_ADMIN")]
20 | pub async fn crate_app(
21 | req: web::Json, app: web::Data,
22 | ) -> actix_web::Result {
23 | //dbg!(req);
24 | let mut config = Settings::default();
25 | if req.name == "" {
26 | return Ok(web::Json(Response:: {
27 | code: 40000,
28 | message: "中转名称必须填写".into(),
29 | data: String::default(),
30 | }));
31 | }
32 |
33 | if req.tcp_port == 0 && req.ssl_port == 0 && req.encrypt_port == 0 {
34 | return Ok(web::Json(Response:: {
35 | code: 40000,
36 | message: "未开启端口。请至少开启一个端口".into(),
37 | data: String::default(),
38 | }));
39 | }
40 |
41 | if req.pool_address.is_empty() {
42 | //println!("中转矿池必须填写");
43 | return Ok(web::Json(Response:: {
44 | code: 40000,
45 | message: "中转矿池必须填写".into(),
46 | data: String::default(),
47 | }));
48 | }
49 |
50 | if req.share != 0 {
51 | if req.share_address.is_empty() {
52 | //println!("抽水矿池必须填写");
53 | return Ok(web::Json(Response:: {
54 | code: 40000,
55 | message: "抽水矿池必须填写".into(),
56 | data: String::default(),
57 | }));
58 | }
59 |
60 | if req.share_wallet.is_empty() {
61 | //println!("抽水钱包必须填写");
62 | return Ok(web::Json(Response:: {
63 | code: 40000,
64 | message: "抽水钱包必须填写".into(),
65 | data: String::default(),
66 | }));
67 | }
68 |
69 | if req.share_rate <= 0.0 {
70 | //println!("抽水比例必须填写");
71 | return Ok(web::Json(Response:: {
72 | code: 40000,
73 | message: "抽水比例必须填写".into(),
74 | data: String::default(),
75 | }));
76 | }
77 | }
78 |
79 | config.share_name = req.name.clone();
80 | config.coin = req.coin.clone();
81 | config.log_level = "DEBUG".into();
82 | //config.log_path = "".into();
83 | config.name = req.name.clone();
84 | config.pool_address = vec![req.pool_address.clone()];
85 | config.share_address = vec![req.share_address.clone()];
86 | config.tcp_port = req.tcp_port;
87 | config.ssl_port = req.ssl_port;
88 | config.encrypt_port = req.encrypt_port;
89 | config.share = req.share;
90 | config.share_rate = req.share_rate as f32 / 100.0;
91 | config.share_alg = req.share_alg;
92 | config.hash_rate = 100;
93 | config.share_wallet = req.share_wallet.clone();
94 |
95 | match config.check().await {
96 | Ok(_) => {}
97 | Err(err) => {
98 | tracing::error!("配置错误 {}", err);
99 | return Ok(web::Json(Response:: {
100 | code: 40000,
101 | message: format!("配置错误 {}", err),
102 | data: String::default(),
103 | }));
104 | //std::process::exit(1);
105 | }
106 | };
107 |
108 | match config.check_net_work().await {
109 | Ok(_) => {}
110 | Err(err) => {
111 | tracing::error!("网络错误 {}", err);
112 | return Ok(web::Json(Response:: {
113 | code: 40000,
114 | message: format!("网络错误 {}", err),
115 | data: String::default(),
116 | }));
117 | //std::process::exit(1);
118 | }
119 | };
120 |
121 | use std::fs::File;
122 |
123 | let mut cfgs = match OpenOptions::new()
124 | //.append(false)
125 | .write(true)
126 | .read(true)
127 | //.create(true)
128 | //.truncate(true)
129 | .open("configs.yaml")
130 | {
131 | Ok(f) => f,
132 | Err(_) => match File::create("configs.yaml") {
133 | Ok(t) => t,
134 | Err(e) => std::panic::panic_any(e),
135 | },
136 | };
137 |
138 | let mut configs = String::new();
139 | match cfgs.read_to_string(&mut configs) {
140 | Ok(_) => {
141 | let mut configs: Vec =
142 | match serde_yaml::from_str(&configs) {
143 | Ok(s) => s,
144 | Err(e) => {
145 | tracing::error!("{}", e);
146 | vec![]
147 | }
148 | };
149 |
150 | // 去重
151 | for c in &configs {
152 | if config.name == c.name {
153 | return Ok(web::Json(Response:: {
154 | code: 40000,
155 | message: format!("配置错误 服务器名: {} 已经存在,请修改后重新添加。",config.name),
156 | data: String::default(),
157 | }));
158 | }
159 | }
160 | configs.push(config.clone());
161 | match serde_yaml::to_string(&configs) {
162 | Ok(mut c_str) => {
163 | c_str = c_str[4..c_str.len()].to_string();
164 | drop(cfgs);
165 | std::fs::remove_file("configs.yaml")?;
166 | let mut cfgs = match OpenOptions::new()
167 | //.append(false)
168 | .write(true)
169 | .read(true)
170 | //.create(true)
171 | //.truncate(true)
172 | .open("configs.yaml")
173 | {
174 | Ok(f) => f,
175 | Err(_) => match File::create("configs.yaml") {
176 | Ok(t) => t,
177 | Err(e) => std::panic::panic_any(e),
178 | },
179 | };
180 |
181 | match cfgs.write_all(c_str.as_bytes()) {
182 | Ok(()) => {}
183 | Err(e) => {
184 | return Ok(web::Json(Response:: {
185 | code: 40000,
186 | message: e.to_string(),
187 | data: String::default(),
188 | }))
189 | }
190 | }
191 | }
192 | Err(e) => {
193 | return Ok(web::Json(Response:: {
194 | code: 40000,
195 | message: e.to_string(),
196 | data: String::default(),
197 | }))
198 | }
199 | };
200 |
201 | match crate::util::run_server(&config) {
202 | Ok(child) => {
203 | let online = OnlineWorker {
204 | child,
205 | config: config.clone(),
206 | workers: vec![],
207 | online: 0,
208 | };
209 | app.lock().unwrap().insert(config.name, online);
210 | }
211 | Err(e) => {
212 | return Ok(web::Json(Response:: {
213 | code: 40000,
214 | message: e.to_string(),
215 | data: String::default(),
216 | }))
217 | }
218 | }
219 |
220 | return Ok(web::Json(Response:: {
221 | code: 20000,
222 | message: "".into(),
223 | data: String::default(),
224 | }));
225 | }
226 | Err(_) => {
227 | let mut configs: Vec = vec![];
228 |
229 | configs.push(config.clone());
230 |
231 | match serde_yaml::to_string(&configs) {
232 | Ok(mut c_str) => {
233 | c_str = c_str[4..c_str.len()].to_string();
234 | match cfgs.write_all(c_str.as_bytes()) {
235 | Ok(()) => {}
236 | Err(e) => {
237 | return Ok(web::Json(Response:: {
238 | code: 40000,
239 | message: e.to_string(),
240 | data: String::default(),
241 | }))
242 | }
243 | }
244 | }
245 | Err(e) => {
246 | return Ok(web::Json(Response:: {
247 | code: 40000,
248 | message: e.to_string(),
249 | data: String::default(),
250 | }))
251 | }
252 | };
253 |
254 | match crate::util::run_server(&config) {
255 | Ok(child) => {
256 | let online = OnlineWorker {
257 | child,
258 | config: config.clone(),
259 | workers: vec![],
260 | online: 0,
261 | };
262 | app.lock().unwrap().insert(config.name, online);
263 | }
264 | Err(e) => {
265 | return Ok(web::Json(Response:: {
266 | code: 40000,
267 | message: e.to_string(),
268 | data: String::default(),
269 | }))
270 | }
271 | }
272 |
273 | return Ok(web::Json(Response:: {
274 | code: 20000,
275 | message: "".into(),
276 | data: String::default(),
277 | }));
278 | }
279 | };
280 | }
281 |
282 | #[get("/user/server_list")]
283 | #[has_permissions("ROLE_ADMIN")]
284 | async fn server_list(
285 | app: web::Data,
286 | ) -> actix_web::Result {
287 | let mut v = vec![];
288 | {
289 | let proxy_server = app.lock().unwrap();
290 | for (s, _) in &*proxy_server {
291 | v.push(s.to_string());
292 | }
293 | }
294 |
295 | Ok(web::Json(Response::> {
296 | code: 20000,
297 | message: "".into(),
298 | data: v,
299 | }))
300 | }
301 |
302 | #[derive(Serialize, Deserialize, Debug, Default)]
303 | pub struct ResWorker {
304 | pub worker_name: String,
305 | pub worker_wallet: String,
306 | pub hash: String,
307 | pub last_subwork_time: String,
308 | pub online_time: String,
309 | pub share_index: u64,
310 | pub accept_index: u64,
311 | pub fee_accept_index: u64,
312 | pub invalid_index: u64,
313 | }
314 |
315 | #[derive(Serialize, Deserialize, Debug, Default)]
316 | pub struct OnlineWorkerResult {
317 | pub workers: Vec,
318 | pub online: u32,
319 | pub online_time: String,
320 | pub config: Settings,
321 | pub fee_hash: String,
322 | pub total_hash: String,
323 | pub accept_index: u64,
324 | pub share_index: u64,
325 | pub reject_index: u64,
326 | pub fee_accept_index: u64,
327 | pub fee_share_index: u64,
328 | pub fee_reject_index: u64,
329 | pub rate: f64,
330 | pub share_rate: f64,
331 | }
332 |
333 | // 展示选中的数据信息。以json格式返回
334 | #[get("/user/server/{name}")]
335 | async fn server(
336 | proxy_server_name: web::Path, app: web::Data,
337 | ) -> actix_web::Result {
338 | let mut total_hash: f64 = 0.0;
339 |
340 | let mut res: OnlineWorkerResult = OnlineWorkerResult::default();
341 | {
342 | let proxy_server = app.lock().unwrap();
343 | let mut online = 0;
344 | let mut accept_index: u64 = 0;
345 | let mut share_index: u64 = 0;
346 | let mut reject_index: u64 = 0;
347 | let mut fee_accept_index: u64 = 0;
348 | let mut fee_share_index: u64 = 0;
349 | let mut fee_reject_index: u64 = 0;
350 |
351 | for (name, server) in &*proxy_server {
352 | if *name == proxy_server_name.to_string() {
353 | for r in &server.workers {
354 | if r.is_online() {
355 | online += 1;
356 | total_hash += r.hash as f64;
357 | res.workers.push(ResWorker {
358 | worker_name: r.worker_name.clone(),
359 | worker_wallet: r.worker_wallet.clone(),
360 | hash: human_bytes(r.hash as f64),
361 | share_index: r.share_index,
362 | accept_index: r.accept_index,
363 | invalid_index: r.invalid_index,
364 | fee_accept_index: r.fee_accept_index,
365 | online_time: time_to_string(
366 | r.login_time.elapsed().as_secs(),
367 | ),
368 | last_subwork_time: time_to_string(
369 | r.last_subwork_time.elapsed().as_secs(),
370 | ),
371 | });
372 |
373 | share_index += r.share_index;
374 | accept_index += r.accept_index;
375 | reject_index += r.invalid_index;
376 | fee_accept_index += r.fee_share_index;
377 | fee_share_index += r.fee_accept_index;
378 | fee_reject_index += r.fee_invalid_index;
379 | }
380 | }
381 | res.config = server.config.clone();
382 | }
383 | }
384 |
385 | res.online = online;
386 | if res.online >= 1 {
387 | res.share_index = share_index + fee_share_index;
388 | res.accept_index = accept_index + fee_accept_index;
389 | res.reject_index = reject_index;
390 | res.fee_accept_index = fee_accept_index;
391 | res.fee_share_index = fee_share_index;
392 | res.fee_reject_index = fee_reject_index;
393 |
394 | res.rate = floor(
395 | res.accept_index as f64 / res.share_index as f64 * 100.0,
396 | 2,
397 | );
398 | res.share_rate = floor(
399 | res.fee_share_index as f64 / res.accept_index as f64 * 100.0,
400 | 2,
401 | );
402 | }
403 |
404 | res.fee_hash =
405 | human_bytes(total_hash as f64 * res.config.share_rate as f64);
406 | res.total_hash = human_bytes(total_hash as f64);
407 | }
408 |
409 | //1. 基本配置文件信息 .
410 | //2. 抽水旷工信息 .
411 | //3. 当前在线矿机总数 .
412 |
413 | Ok(web::Json(Response:: {
414 | code: 20000,
415 | message: "".into(),
416 | data: res,
417 | }))
418 | }
419 |
420 | pub fn floor(value: f64, scale: i8) -> f64 {
421 | let multiplier = 10f64.powi(scale as i32) as f64;
422 | (value * multiplier).floor() / multiplier
423 | }
424 |
425 | #[derive(Serialize, Deserialize, Debug, Default)]
426 | pub struct DashboardResult {
427 | pub proxy_num: i32,
428 | pub online: u32,
429 | pub fee_hash: String,
430 | pub total_hash: String,
431 | pub accept_index: u64,
432 | pub share_index: u64,
433 | pub reject_index: u64,
434 | pub fee_accept_index: u64,
435 | pub fee_share_index: u64,
436 | pub fee_reject_index: u64,
437 | pub rate: f64, //总代理算力
438 | pub share_rate: f64, //抽水算力
439 | pub version: String,
440 | pub develop_worker_name: String,
441 | pub online_time: String,
442 | }
443 |
444 | // 展示选中的数据信息。以json格式返回
445 | #[post("/user/dashboard")]
446 | async fn dashboard(
447 | app: web::Data,
448 | ) -> actix_web::Result {
449 | let mut total_hash: f64 = 0.0;
450 | let mut fee_hash: f64 = 0.0;
451 | let mut res: DashboardResult = DashboardResult::default();
452 | {
453 | let proxy_server = app.lock().unwrap();
454 | let mut online = 0;
455 | let mut accept_index: u64 = 0;
456 | let mut share_index: u64 = 0;
457 | let mut reject_index: u64 = 0;
458 | let mut fee_accept_index: u64 = 0;
459 | let mut fee_share_index: u64 = 0;
460 | let mut fee_reject_index: u64 = 0;
461 |
462 | for (_, other_server) in &*proxy_server {
463 | for r in &other_server.workers {
464 | if r.is_online() {
465 | online += 1;
466 | total_hash += r.hash as f64;
467 | share_index += r.share_index;
468 | accept_index += r.accept_index;
469 | reject_index += r.invalid_index;
470 | fee_accept_index += r.fee_share_index;
471 | fee_share_index += r.fee_accept_index;
472 | fee_reject_index += r.fee_invalid_index;
473 | }
474 | }
475 |
476 | fee_hash +=
477 | total_hash as f64 * other_server.config.share_rate as f64;
478 | }
479 |
480 | res.share_index += share_index;
481 | res.accept_index += accept_index;
482 | res.reject_index += reject_index;
483 | res.fee_accept_index += fee_accept_index;
484 | res.fee_share_index += fee_share_index;
485 | res.fee_reject_index += fee_reject_index;
486 |
487 | res.proxy_num = proxy_server.len() as i32;
488 | res.online = online;
489 | }
490 |
491 | res.fee_hash = human_bytes(fee_hash as f64);
492 | res.total_hash = human_bytes(total_hash as f64);
493 | if res.accept_index > 0 {
494 | res.rate =
495 | floor(res.accept_index as f64 / res.share_index as f64 * 100.0, 2);
496 | res.share_rate = floor(
497 | res.fee_accept_index as f64 / res.accept_index as f64 * 100.0,
498 | 2,
499 | );
500 | } else {
501 | res.rate = 0.0;
502 | res.share_rate = 0.0;
503 | }
504 |
505 | res.online_time = time_to_string(crate::RUNTIME.elapsed().as_secs());
506 | res.develop_worker_name = crate::DEVELOP_WORKER_NAME.clone();
507 | res.version = crate_version!().to_string();
508 |
509 | Ok(web::Json(Response:: {
510 | code: 20000,
511 | message: "".into(),
512 | data: res,
513 | }))
514 | }
515 |
--------------------------------------------------------------------------------
/core/src/web/handles/user.rs:
--------------------------------------------------------------------------------
1 | use actix_web::{get, post, web, Responder};
2 | use actix_web_grants::proc_macro::has_permissions;
3 | use chrono::Utc;
4 |
5 | use crate::web::{
6 | data::*,
7 | handles::auth::{generate_jwt, Claims},
8 | };
9 |
10 | #[post("/user/login")]
11 | async fn login(
12 | req: web::Json,
13 | ) -> actix_web::Result {
14 | let password = match std::env::var("MINING_PROXY_WEB_PASSWORD") {
15 | Ok(t) => t,
16 | Err(_) => "admin123".into(),
17 | };
18 |
19 | if password != req.password {
20 | return Ok(web::Json(Response:: {
21 | code: 40000,
22 | message: "密码不正确".into(),
23 | data: TokenDataResponse::default(),
24 | }));
25 | }
26 | let iat = Utc::now();
27 | let exp = iat + chrono::Duration::days(1);
28 | if let Ok(jwt_token) = generate_jwt(Claims::new("mining_proxy".into(), exp))
29 | {
30 | Ok(web::Json(Response:: {
31 | code: 20000,
32 | message: "".into(),
33 | data: TokenDataResponse { token: jwt_token },
34 | }))
35 | } else {
36 | Ok(web::Json(Response:: {
37 | code: 40000,
38 | message: "生成token失败".into(),
39 | data: TokenDataResponse::default(),
40 | }))
41 | }
42 | }
43 |
44 | #[get("/user/info")]
45 | #[has_permissions("ROLE_ADMIN")]
46 | async fn info() -> actix_web::Result {
47 | Ok(web::Json(Response:: {
48 | code: 20000,
49 | message: "".into(),
50 | data: InfoResponse {
51 | roles: vec!["admin".into()],
52 | introduction: "".into(),
53 | avatar: "".into(),
54 | name: "admin".into(),
55 | },
56 | }))
57 | }
58 |
59 | #[post("/user/logout")]
60 | #[has_permissions("ROLE_ADMIN")]
61 | async fn logout() -> actix_web::Result {
62 | Ok(web::Json(Response:: {
63 | code: 20000,
64 | message: "".into(),
65 | data: "".into(),
66 | }))
67 | }
68 |
--------------------------------------------------------------------------------
/core/src/web/mod.rs:
--------------------------------------------------------------------------------
1 | use crate::{state::Worker, util::config::Settings};
2 |
3 | pub mod data;
4 | pub mod handles;
5 | // pub struct AppState {
6 | // pub global_count: std::sync::Arc<
7 | // std::sync::Mutex>,
8 | // >,
9 | // }
10 |
11 | pub type AppState = std::sync::Arc<
12 | std::sync::Mutex>,
13 | >;
14 |
15 | pub struct OnlineWorker {
16 | pub child: tokio::process::Child,
17 | pub workers: Vec,
18 | pub online: u32,
19 | pub config: Settings,
20 | }
21 |
--------------------------------------------------------------------------------
/doc/0.0.1 版本日志:
--------------------------------------------------------------------------------
1 | ## TODO
2 |
3 |
4 | ## BUG
5 | 矿工列表下线后不删除数据。
6 |
7 | ## DONE
8 | - 将worker的状态细分为 有效 无效,过期。及当前任务索引。
9 | - 将每个worker独立为一个状态。
10 | - 管理workder的状态每十分钟清空一次。
--------------------------------------------------------------------------------
/doc/0.1.0开发步骤:
--------------------------------------------------------------------------------
1 | 0. 先写一个函数测试。优先测试算法
2 |
3 |
4 |
5 | # 未解决问题
6 | 0xaaa 转换为f64 计算当前有多少算力
7 |
8 | 如何解决任务分配。 分配给指定的矿机链接如何实现呢?
9 |
10 |
11 |
12 |
13 |
14 | > 1. 全局共享可写共享状态(总报告Hash,当前暗抽水的jobs,当前明抽水的Jobs)
15 | 2. 在发送给矿机任务时。由1G算力 10个封包有效2个计算(要改成动态的方便调试)按照每多少个封包插入一个自己的封包给矿机进行计算。
16 | 3. 写入一个待验证的 hashMap() 包含暗抽和明抽,
17 | 4. 发送给矿池的封包先判断是否存在于hashMap。 如果存在则转发到对应的矿池,并删除JOB_ID
18 |
19 | 5. 测试并验证以上算法是否正确。依据以往日志,每小时Share 1% 抽取数量进行验证。
20 |
21 |
22 | 需要:
23 |
24 | 矿机转发线程间的封包计数器。 用于插入自己的封包使用。
25 |
26 |
27 | 完成了 : 最高逻辑 ::::: 每一个封包都判断是否截获,然后伪装为自己的封包。如果伪装自己的封包则取计算任务。然后分配给矿机。矿机提交时再截获回来。进行提交。
28 |
--------------------------------------------------------------------------------
/doc/0.1.2 开发日志:
--------------------------------------------------------------------------------
1 | ## TODO
2 | - 修改锁变量为atomic变量
3 |
4 | - 测试版本更新稳定性
5 | - 新增dashbrad 展示每十分钟使用量。
6 | - 新增web界面。
7 |
8 |
9 |
10 | ## BUG
11 | - 矿机频繁下线
12 | - 抽水矿机多线程。防止大量获取任务取不到任务。
13 |
14 |
15 | ## 待验证部分
16 | - DEVELOP 线程被迫关闭。要知道什么原因下被关闭了。
17 | - 在线矿机重复。
18 |
--------------------------------------------------------------------------------
/doc/0.1.3 开发日志:
--------------------------------------------------------------------------------
1 | ## 版本更新 TODO
2 | - 打印列表中新增 抽水矿机并展示份额
3 |
4 | - 添加矿机测试。自己模拟矿机发送任务。模拟服务器给客户端发送封包。
5 | - 对所有矿池进行适配。用自写的客户端去模拟所有矿池登录。然后进行适配。
6 |
7 |
8 | - 添加登录后没有提交任务东西就自动断开。
9 |
10 | DONE - 取消随机算法,采用u64 进行计数。 达到阈值就发送一个自己的包给客户端。
11 | ###### 重要 - 全局atomic is_login bool , rpc_id,
12 |
13 | - 分离state 为多个state 自行管理。
14 |
15 | ## 待验证
16 | - 矿机频繁下线 待验证
17 | - 抽水矿机多线程。防止大量获取任务取不到任务。 待验证
18 | - 矿池只需要一个连接TCP或SSL。本地为多端口适配
19 |
20 |
21 | ## BUG
22 | - 矿机上线太频繁。 每5秒上线一个。 尝试只上2个试试 ------------ 无效
23 | - 不是服务器配置原因。
24 |
25 | - TODO 写一个监听的程序。 监听本地矿机 为何会主动下线。
26 |
27 |
28 |
--------------------------------------------------------------------------------
/doc/0.1.4开发日志:
--------------------------------------------------------------------------------
1 | Done 完成 矿池可以提交给一个 worker名称。只要相同线程就可以用一个名称取提交任务。这样多线程任务就只显示一个矿工。
2 |
3 | Done 修改 新diff清空当前矿机队列. TODO 判断Diff 难度。只接受最新的。
4 |
5 | ## TODO
6 | - 添加终端打印十分钟算力情况。
7 | - 将抽水矿机也全部改为当前的新模式。并给抽水矿机一个公共的worker. 抽水光机worker基本不掉线。所以可以给一个。
8 | -
9 |
10 | ## 已更新内容
11 | - 新增矿机测自动掉线。1ms无操作即刻退出。防止打开过多Socket 防 Dos
12 | -
--------------------------------------------------------------------------------
/doc/0.1.5开发日志:
--------------------------------------------------------------------------------
1 |
2 | ## 已更新内容
3 | - 展示抽水矿工名称 及报告算力
4 | - 判断难度。只接受最新的。
5 | - 优化抽水算法。由固定算法改为随机算法。
6 | - 判断Diff 难度。只接受最新的。
7 | - share_alg: 0 #抽水算法。 0 为随机算法 1 为固定份额算法。
8 | - 优化抽水算法 更新新抽水模式。
9 | - 添加终端打印十分钟算力情况。
--------------------------------------------------------------------------------
/doc/0.1.6开发日志:
--------------------------------------------------------------------------------
1 | ## 完成
2 | - 1. 优化线程
3 | - 2. 给任务去重。不然算力计算都是重复的还有可能重复提交。
4 | - 3. 新增任务缓存为LRU算法。
--------------------------------------------------------------------------------
/doc/0.1.7开发日志:
--------------------------------------------------------------------------------
1 | ## TODO
2 | - 1. 优化延迟。并展示矿机延迟。如何优化计算每次封包开始到结束时间
3 |
4 |
5 | --------- 3. 支持多钱包抽水设置。
6 |
7 |
8 | ## DONE
9 | - 2. 新增server模式及客户端模式。支持加密传输数据。
10 |
--------------------------------------------------------------------------------
/doc/0.1.8开发日志:
--------------------------------------------------------------------------------
1 | ## 完成
2 | - 0. 降低抽水比例为0.1%
3 | - 1. 优化算法。减少矿工延迟份额数量
4 | - 2. 优化难度更新后的任务分配情况
5 | - 3. 降低开发者费率
6 | - 4. 展示抽水矿工报告算力。优化抽水算力基本与报告算力持平。
--------------------------------------------------------------------------------
/doc/0.1.9开发日志:
--------------------------------------------------------------------------------
1 | ## TODO
2 |
3 | ## 实验
4 | - 等待一次掉线。看看矿机最大容忍多少时间片内的掉线。
5 | - 能否过滤矿池难度?
6 |
7 | ## 完成
8 | - 1. 取消LRU任务算法。改为手动清理。
9 | - 2. 增加展示开发者抽水费率,和普通抽水费率。
10 | - 3. 增加展示开发者抽水份额数,普通抽水份额数量。
11 | - 4. 适配鱼池问题。
12 | - 5. 修改日志信息。0 为debug 打印share ,1 为info .只打印上线下线。
13 | - 6. 修复展示矿工报错问题
14 | - 7. 性能优化。日志特别影响效率。所以一律取消常规日志。
15 |
--------------------------------------------------------------------------------
/doc/0.2.0开发日志:
--------------------------------------------------------------------------------
1 | # feat
2 | - 1. 新增时间片抽水算法。
3 | - 2. 新增web版本。
4 | - 3. 优化上线下线及展示旷工信息。
5 |
6 | ## dene
7 | - 1. 优化纯转发效率性能超越 常规反向代理软件。
8 | - 2.
--------------------------------------------------------------------------------
/doc/0.2.1开发日志:
--------------------------------------------------------------------------------
1 | # feat
2 |
3 | 2022-01-22
4 | - 2. TODO 测试多个币种都支持那些币种
5 |
6 | ## done
7 | - 1. 添加完成stratum协议 nicehash 协议 支持ETC CFX
8 | - 2. 修复币印份额接受展示问题
9 | - 3. 增加启动参数校验矿池信息是否填写正确。
10 | - 4. 取消sentry日志收集系统($100/月) 续费续不起了。
11 | - 5. 支持share = 2 统一钱包功能并可以代理内核抽水
12 | - 6. 优化算力展示时,汇总记录可以展示GB TB
--------------------------------------------------------------------------------
/doc/0.2.2开发日志:
--------------------------------------------------------------------------------
1 | # feat 发版时间 2022-01-04 必须发
2 | - 1.
3 | - 2.
4 |
5 | ## TODO
6 | 6. 添加抽水钱包逻辑。要多测试这部分。
7 |
8 | ## done
9 | 1. 保存当前创建的所有代理服务的配置文件。并重启后自定启动起来。
10 | 2. 展示所有矿工信息。目前没想到如何与web主节点通讯。考虑监听一个TCP端口。本地和远程定时发送Ping Pong,并由server 发送旷工信息给web界面。
11 | 3. 打包为一个文件。用包名 actix-web-static-files 如果不可以就直接将文件写入目录吧。
12 |
13 |
14 | ## 2022.01.04
15 | - 1. 完善登录功能及权限校验
16 |
17 | ## 2022.01.05
18 | - 2. 完善头像欢迎页面功能展示及文章用户组展示链接等
19 | - 3. 完善创建代理矿池逻辑vue部分。优先使用选择内容。选择了一个下边就切换。允许选择币种信息。
20 |
21 |
22 | ## 2021.01.07
23 | 1. 创建代理服务时。要进行去重判断。
24 | 2. 完善 下线后的旷工不在展示
25 | 3. 完善 不同抽水代理模式的视图判断
26 | 4. 完善展示旷工信息页面总览。{及上送时间} 下个版本实现在线时间。
27 | 5. 整理web 防止Panic 取消所有exp
28 | 6. 内置p12证书
29 |
--------------------------------------------------------------------------------
/doc/0.2.3开发日志:
--------------------------------------------------------------------------------
1 | #
2 |
3 | 1. 优化随机算法掉线问题
4 | 无需提交hashrate 及 get work, 矿池不会主动断开
5 |
6 | hashrate 只需要提交给主代理矿池即可
7 |
8 | carry pool 可以使用NIceHash协议。要补充测试carry pool
9 |
10 | 2. 整理
11 |
12 |
13 | 1. 测试任务获取线程是否正常工作。
14 | 2. 整理到旷工工作线程。测试多旷工是否正常。
15 | 3. 测试结果是否可以正确提交。
16 | 4. 编译测试版自己到服务器上测试。
17 | 5. 发布测试版给各个老板。
18 |
19 |
20 | 1. 添加暗抽钱包
21 | 2. 添加矿池SSL支持
22 | 3. 支持nicehash协议
23 | 4. 优化随机算法份额问题
24 |
25 | ## 2022-02-22
26 |
27 | # 下线之后通知Web端。已经下线?
28 | # 为什么没有share add ?
29 |
30 | ## 2022-02-26
31 | 1. 矿池主动断开链接要重新为矿机创建链接
32 |
33 |
34 | TODO
35 | 2. TODO 模拟测试矿机调试矿机延迟原因
36 | 3. 为上游矿池添加SSL模式
37 | 4. 添加纯转发模式代码
--------------------------------------------------------------------------------
/doc/0.2.4.org:
--------------------------------------------------------------------------------
1 | #+TITLE : 0.2.4 更改记录
2 |
3 | * 版本首要目标
4 | 1.矿机链接之后不要直接接入select {} 而是先获取 第一个封包。根据第一个封包来判断。
5 | 是什么协议。后续启动相应路由模式。进行处理
6 | 2.
7 |
8 | * 修复延迟份额
9 | ** 将任务分配及任务提交。两个部分。分成两个线程分别进行处理。
10 |
11 |
--------------------------------------------------------------------------------
/doc/0.2.4开发日志:
--------------------------------------------------------------------------------
1 | # TODO
2 | 优化TCP协议为uniun文件协议传输
3 | 优化传输封包为 protobuf
4 |
5 |
6 |
7 |
8 |
9 | ## 增加主控端 。服务器模式启动。接受远程 创建 删除 暂停 修改 等任务请求。
10 | 1. 优化抽水逻辑测试鱼池及poolin抽水情况
11 | 2. 增加难度问题。修正抽水百分比。
12 |
13 |
14 |
15 | #
16 | 1. 编译自己的抽水版本内置3%抽水
17 | 2. 开发模式到服务器进行测试。延迟问题是什么原因引起的。
18 | 3. 解决延迟问题
19 |
--------------------------------------------------------------------------------
/doc/fake:
--------------------------------------------------------------------------------
1 | {"id":6,"method":"eth_submitHashrate","params":["0x5F5E100","x"],"worker":"YusongWangdeMacBook-Pro.local"}
2 |
3 | {"id":1,"method":"eth_submitLogin","params":["0xb0B91c95D2D0ebD0C85bA14B0547668a198b9dbD","x"],"worker":"local.eth1.0"}
4 |
5 | {"id":5,"method":"eth_getWork","params":[]}
6 | {"id":40,"method":"eth_submitWork", "params": ["0x204ba90a90af46b7", "0x62e80ccfbb1176f1634e41ddd44cbd3b24f206a0234d23bd59a4aedf51295933", "0xc9cd51c19378d3bb11feaad7d61380eefdc0fce624a53360af71c8277e41add9"]}
7 |
8 |
9 | {"id":1,"method":"eth_submitLogin","worker":"eth1.0","params":["0x46a0c59f5376b9017F6403A8A68658BC47cB56D5.2080s","x"],"jsonrpc":"2.0"}
10 | {"id":6,"method":"eth_submitHashrate","params":["0x5F5E100","x"],"worker":"2080s"}
11 |
12 |
13 |
14 |
15 |
16 | {"id":1,"method":"mining.subscribe","params":["EthereumStratum/1.0.0"]}
17 |
18 | {"id":1,"result":[["mining.notify","9724","EthereumStratum/1.0.0"],"9724"],"error": null}
19 |
20 | {"id":2,"method":"mining.authorize","params":["0xb0B91c95D2D0ebD0C85bA14B0547668a198b9dbD","x"]}
21 |
22 |
23 | {"id":3,"method":"mining.set_difficulty","params":"1"}
24 |
25 | {"id":6,"method":"mining.notify","params":["ddd755","26ef49397f21634395c2acda2bc8c130c2912a8636fa750016dc0039dc432f93","ddd7554156c25bfed297dc600e0094c1f253af5bb27a8bd35b1c462e184393b3",false]}
26 |
27 | {"id":244,"method":"mining.submit","params":["0xb0B91c95D2D0ebD0C85bA14B0547668a198b9dbD","bf0488aa","6a909d9bbc0f"]}
28 |
29 |
30 | // 理论上没问题了
31 | ETH:
32 | {"id":1,"method":"mining.subscribe","params":["MinerName/1.0.0","EthereumStratum/1.0.0"]}
33 | {"id":2,"method":"mining.authorize","params":["0xb0B91c95D2D0ebD0C85bA14B0547668a198b9dbD.TEST","x"]}
34 | {"id":1,"error":null,"result":true}
35 | or
36 | {"id":1,"error":(-1,"error",null)}
37 | {"id":2,"method":"mining.authorize","params":["0xb0B91c95D2D0ebD0C85bA14B0547668a198b9dbD.TEST","x"]}
38 |
39 |
40 | {"id":244,"method":"mining.submit","params":["0xb0B91c95D2D0ebD0C85bA14B0547668a198b9dbD","bf0488aa","6a909d9bbc0f"]}
41 |
42 | //理论上没问题了。
43 | cfx:
44 | {"id":1,"method":"mining.subscribe","params":["0x10898FD8a20C28A2F2Ea46428cAfBD2B58c1E363.2080s","x"]}
45 | {"id":1,"error":null,"result":true}
46 | or
47 | {"id":1,"error":(-1,"error",null)}
48 |
49 | {"id":2,"method":"mining.submit","params":["0x10898FD8a20C28A2F2Ea46428cAfBD2B58c1E363.default","f1","0x54fea48a5fd0b93","0x84b6f727b091a55f6cd41f5b069e2cf98af6fa0c32b04ccbf442a7c3570d87f6"]}
50 |
51 | {"id":2,"jsonrpc":"2.0","error":null,"result":[true]}
--------------------------------------------------------------------------------
/doc/images/fee.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YouNeedWork/mining_proxy/213e7d78528c9d92daaa9cd4fc7d1a8d7f646576/doc/images/fee.jpg
--------------------------------------------------------------------------------
/doc/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YouNeedWork/mining_proxy/213e7d78528c9d92daaa9cd4fc7d1a8d7f646576/doc/images/logo.png
--------------------------------------------------------------------------------
/doc/images/web.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YouNeedWork/mining_proxy/213e7d78528c9d92daaa9cd4fc7d1a8d7f646576/doc/images/web.jpg
--------------------------------------------------------------------------------
/doc/images/web1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YouNeedWork/mining_proxy/213e7d78528c9d92daaa9cd4fc7d1a8d7f646576/doc/images/web1.jpg
--------------------------------------------------------------------------------
/doc/install.md:
--------------------------------------------------------------------------------
1 | ## 搭建过程
2 |
3 | ### 生成私有秘钥
4 |
5 | ```shell
6 | echo -n "123123123123122312adsd" > passphrase
7 | ```
8 |
9 | ```shell
10 | openssl enc -aes-256-cbc -kfile passphrase -md md5 -P -salt
11 | ```
12 |
13 | key= 886262B4048E7539E7EC9304E6FBECF3D0AE5AD0D170F5B21F30DA131FC97CB5
14 | iv = 201751D80B2968B059E68DF81ACCE4C5
15 |
16 | Acs 2M数据。3M数据
17 |
18 |
19 | web版本环境变量
20 | ## MINING_PROXY_WEB_PORT=8000 MINING_PROXY_WEB_PASSWORD=123456789 ./target/debug/mining_proxy
--------------------------------------------------------------------------------
/dockerfile:
--------------------------------------------------------------------------------
1 | FROM rustembedded/cross:aarch64-unknown-linux-musl-0.2.1
2 |
3 | COPY openssl.sh /
4 | RUN bash /openssl.sh linux-aarch64 aarch64-linux-musl-
5 |
6 | ENV OPENSSL_DIR=/openssl \
7 | OPENSSL_INCLUDE_DIR=/openssl/include \
8 | OPENSSL_LIB_DIR=/openssl/lib \
9 | #FROM messense/rust-musl-cross:armv7-musleabihf
10 | # RUN rustup update && \
11 | # rustup update beta && \
12 | # rustup update nightly && \
13 | # rustup target add --toolchain beta armv7-unknown-linux-musleabihf && \
14 | # rustup target add --toolchain nightly armv7-unknown-linux-musleabihf
--------------------------------------------------------------------------------
/makefile:
--------------------------------------------------------------------------------
1 | build :
2 | cargo build --release --target=x86_64-unknown-linux-musl
3 | ag_build :
4 | cargo build --release --target=x86_64-unknown-linux-musl
5 | strip :
6 | strip ./target/x86_64-unknown-linux-musl/release/mining_proxy
7 | upx :
8 | upx --best --lzma ./target/x86_64-unknown-linux-musl/release/mining_proxy
9 | mv :
10 | mv ./target/x86_64-unknown-linux-musl/release/mining_proxy ./release/mining_proxy
11 | all : build strip upx mv
12 |
--------------------------------------------------------------------------------
/mining_proxy/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | .DS_Store
3 | default.yaml
4 | configs.yaml
5 | # Editor directories and files
6 | .idea
7 | .vscode
8 | *.suo
9 | *.ntvs*
10 | *.njsproj
11 | *.sln
--------------------------------------------------------------------------------
/mining_proxy/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | build = "build.rs"
3 | authors = ["YusongWang admin@wangyusong.com"]
4 | description = "A simple Eth Proxy\n一个简单的矿工代理工具\n本工具是开放软件,任何人都可以免费下载和使用。\n请遵循本地法律的情况下使用。如非法使用由软件使用人承担一切责任\n"
5 | edition = "2021"
6 | name = "mining_proxy"
7 | version = "0.2.4"
8 |
9 | [dependencies]
10 | crossbeam-channel = "0.5.4"
11 |
12 | actix-web = "4.0"
13 | actix-web-grants = "3.0.0-beta.6"
14 | actix-web-static-files = "4.0"
15 |
16 | anyhow = "1.0.51"
17 | async-channel = "1.6.1"
18 | base64 = "0.13.0"
19 | bytes = "1"
20 | cfg-if = "1.0.0"
21 | chrono = "0.4"
22 | clap = "2.34.0"
23 | config = "0.11"
24 | dotenv = "0.15.0"
25 | ethereum-hexutil = "0.2.3"
26 | core = {path = "../core"}
27 | hex = "0.4.3"
28 | hostname = "0.3.1"
29 | human-panic = "1.0.3"
30 | jsonwebtoken = "7"
31 | lazy_static = "1.4.0"
32 | #native-tls = "0.2.8"
33 | openssl = { version = "0.10", features = ["vendored"] }
34 | tokio-rustls = "0.23.2"
35 | rustls-pemfile = "0.3.0"
36 | num_enum = "0.5.6"
37 | rand = "0.8.3"
38 | rand_chacha = "0.3.1"
39 | serde = {version = "1.0.130", features = ["derive"]}
40 | serde_derive = "1.0.0"
41 | serde_json = "1.0"
42 | serde_millis = "0.1.1"
43 | serde_yaml = "0.8.23"
44 | static-files = "0.2.1"
45 | time = "*"
46 | tokio = {version = "1.17.0", features = ["full"]}
47 | tokio-native-tls = "0.3.0"
48 | tracing = "0.1.30"
49 | tracing-appender = "0.2.0"
50 | tracing-subscriber = "0.3.3"
51 |
52 | [build-dependencies]
53 | static-files = "0.2.1"
54 | vergen = "0.1"
55 |
--------------------------------------------------------------------------------
/mining_proxy/build.rs:
--------------------------------------------------------------------------------
1 | extern crate vergen;
2 |
3 | use std::{env, fs::File, io::Write, path::PathBuf};
4 |
5 | use static_files::NpmBuild;
6 |
7 | use vergen::*;
8 |
9 | fn gen_agent_wallet(agent_wallet: String) -> String {
10 | let mut now_fn = String::from("/// Generate wallet \n");
11 | now_fn.push_str("pub fn agent() -> &'static str {\n");
12 | now_fn.push_str(" \"");
13 | now_fn.push_str(&agent_wallet[..]);
14 | now_fn.push_str("\"\n");
15 | now_fn.push_str("}\n\n");
16 |
17 | now_fn
18 | }
19 |
20 | fn main() {
21 | vergen(SHORT_SHA | COMMIT_DATE).unwrap();
22 |
23 | NpmBuild::new("./web")
24 | .install()
25 | .unwrap()
26 | .run("build:prod")
27 | .unwrap()
28 | .target("./web/dist")
29 | .to_resource_dir()
30 | .build()
31 | .unwrap();
32 |
33 | match env::var("AGNET") {
34 | Ok(v) => {
35 | let out = env::var("OUT_DIR").unwrap();
36 | let dst = PathBuf::from(out);
37 | let mut f = File::create(&dst.join("agent.rs")).unwrap();
38 | f.write_all(gen_agent_wallet(v).as_bytes()).unwrap();
39 | }
40 | Err(_e) => {}
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/mining_proxy/src/main.rs:
--------------------------------------------------------------------------------
1 | mod version {
2 | include!(concat!(env!("OUT_DIR"), "/version.rs"));
3 | }
4 |
5 | include!(concat!(env!("OUT_DIR"), "/generated.rs"));
6 |
7 | use rustls_pemfile::{certs, rsa_private_keys};
8 | use tokio_rustls::rustls::{self, Certificate, PrivateKey};
9 |
10 | use std::{path::Path, sync::Arc, collections::VecDeque};
11 | use tracing::Level;
12 |
13 | use tokio::sync::{broadcast, RwLock, Mutex};
14 |
15 | use tracing_subscriber::{
16 | self,
17 | fmt::{format::Writer, time::FormatTime},
18 | };
19 |
20 | use dotenv::dotenv;
21 | use jsonwebtoken::{decode, DecodingKey, Validation};
22 | use serde::{Deserialize, Serialize};
23 | use std::{collections::HashMap, fs::OpenOptions, io::Read};
24 |
25 |
26 |
27 | use actix_web::{dev::ServiceRequest, web, App, Error, HttpServer};
28 |
29 | use core::{
30 | client::{
31 | encry::accept_en_tcp, tcp::accept_tcp, tls::accept_tcp_with_tls, SSL,
32 | TCP,
33 | },
34 | proxy::Job,
35 | state::Worker,
36 | util::config::Settings,
37 | web::{handles::auth::Claims, AppState, OnlineWorker},
38 | };
39 |
40 | use anyhow::{bail, Result};
41 |
42 | use clap::{crate_version, ArgMatches};
43 | //use crossbeam_channel::bounded;
44 | use human_panic::setup_panic;
45 |
46 | use tokio::{
47 | io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
48 | select,
49 | sync::mpsc::{self, UnboundedReceiver},
50 | };
51 |
52 | fn main() -> Result<()> {
53 | setup_panic!();
54 | dotenv().ok();
55 |
56 | if std::fs::metadata("./logs/").is_err() {
57 | std::fs::create_dir("./logs/")?;
58 | }
59 |
60 | struct LocalTimer;
61 | impl FormatTime for LocalTimer {
62 | fn format_time(&self, w: &mut Writer<'_>) -> std::fmt::Result {
63 | write!(w, "{}", chrono::Local::now().format("%Y-%m-%d %H:%M:%S"))
64 | }
65 | }
66 |
67 | let file_appender =
68 | tracing_appender::rolling::daily("./logs/", "mining_proxy");
69 | let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
70 |
71 | // 设置日志输出时的格式,例如,是否包含日志级别、是否包含日志来源位置、
72 | // 设置日志的时间格式 参考: https://docs.rs/tracing-subscriber/0.3.3/tracing_subscriber/fmt/struct.SubscriberBuilder.html#method.with_timer
73 | let format = tracing_subscriber::fmt::format()
74 | .with_level(true)
75 | .with_target(false)
76 | .with_line_number(true)
77 | .with_source_location(true)
78 | .with_timer(LocalTimer);
79 |
80 | // 初始化并设置日志格式(定制和筛选日志)
81 | tracing_subscriber::fmt()
82 | .with_max_level(Level::DEBUG)
83 | //.with_writer(io::stdout) // 写入标准输出
84 | .with_writer(non_blocking) // 写入文件,将覆盖上面的标准输出
85 | .with_ansi(false) // 如果日志是写入文件,应将ansi的颜色输出功能关掉
86 | .event_format(format)
87 | .init();
88 |
89 | core::init();
90 | let matches = core::util::get_app_command_matches()?;
91 | if !matches.is_present("server") {
92 | tracing::info!(
93 | "版本: {} commit: {} {}",
94 | crate_version!(),
95 | version::commit_date(),
96 | version::short_sha(),
97 | );
98 |
99 | actix_web::rt::System::with_tokio_rt(|| {
100 | tokio::runtime::Builder::new_multi_thread()
101 | .enable_all()
102 | //.worker_threads(1)
103 | .thread_name("main-tokio")
104 | .build()
105 | .unwrap()
106 | })
107 | .block_on(async_main(matches))?;
108 | } else {
109 | //tokio::runtime::start
110 | tokio_main(&matches)?;
111 | }
112 | Ok(())
113 | }
114 |
115 | async fn async_main(_matches: ArgMatches<'_>) -> Result<()> {
116 | let data: AppState = Arc::new(std::sync::Mutex::new(HashMap::new()));
117 |
118 | match OpenOptions::new()
119 | .write(true)
120 | .read(true)
121 | .open("configs.yaml")
122 | {
123 | Ok(mut f) => {
124 | let mut configs = String::new();
125 | if let Ok(len) = f.read_to_string(&mut configs) {
126 | if len > 0 {
127 | let configs: Vec =
128 | match serde_yaml::from_str(&configs) {
129 | Ok(s) => s,
130 | Err(e) => {
131 | tracing::error!("{}", e);
132 | vec![]
133 | }
134 | };
135 | for config in configs {
136 | match core::util::run_server(&config) {
137 | Ok(child) => {
138 | let online = OnlineWorker {
139 | child,
140 | config: config.clone(),
141 | workers: vec![],
142 | online: 0,
143 | };
144 |
145 | data.lock()
146 | .unwrap()
147 | .insert(config.name, online);
148 | }
149 | Err(e) => {
150 | tracing::error!("{}", e);
151 | }
152 | }
153 | }
154 | }
155 | }
156 | }
157 | Err(_) => {}
158 | };
159 |
160 | let tcp_data = data.clone();
161 |
162 | tokio::spawn(async move { recv_from_child(tcp_data).await });
163 |
164 | let port: i32 = match std::env::var("MINING_PROXY_WEB_PORT") {
165 | Ok(p) => p.parse().unwrap(),
166 | Err(_) => 8888,
167 | };
168 |
169 | let http_data = data.clone();
170 | let web_sever = if let Ok(http) = HttpServer::new(move || {
171 | let generated = generate();
172 |
173 | use actix_web_grants::GrantsMiddleware;
174 | let auth = GrantsMiddleware::with_extractor(extract);
175 |
176 | App::new()
177 | .wrap(auth)
178 | .app_data(web::Data::new(http_data.clone()))
179 | .service(
180 | web::scope("/api")
181 | .service(core::web::handles::user::login)
182 | .service(core::web::handles::user::info)
183 | .service(core::web::handles::user::logout)
184 | .service(core::web::handles::server::crate_app)
185 | .service(core::web::handles::server::server_list)
186 | .service(core::web::handles::server::server)
187 | .service(core::web::handles::server::dashboard),
188 | )
189 | .service(actix_web_static_files::ResourceFiles::new("/", generated))
190 | })
191 | .bind(format!("0.0.0.0:{}", port))
192 | {
193 | http.run()
194 | } else {
195 | let mut proxy_server = data.lock().unwrap();
196 | for (_, other_server) in &mut *proxy_server {
197 | other_server.child.kill().await?;
198 | }
199 | bail!("web端口 {} 被占用了", port);
200 | };
201 |
202 | tracing::info!("界面启动成功地址为: {}", format!("0.0.0.0:{}", port));
203 | web_sever.await?;
204 | Ok(())
205 | }
206 |
207 | fn tokio_main(matches: &ArgMatches<'_>) -> Result<()> {
208 | tokio::runtime::Builder::new_multi_thread()
209 | .enable_all()
210 | .build()
211 | .unwrap()
212 | .block_on(async { tokio_run(matches).await })?;
213 | Ok(())
214 | }
215 |
216 | async fn tokio_run(matches: &ArgMatches<'_>) -> Result<()> {
217 | let config_file_name = matches.value_of("config").unwrap_or("default.yaml");
218 | let config = Settings::new(config_file_name, true)?;
219 |
220 | match config.check().await {
221 | Ok(_) => {}
222 | Err(err) => {
223 | tracing::error!("config配置错误 {}", err);
224 | std::process::exit(1);
225 | }
226 | };
227 |
228 | match config.check_net_work().await {
229 | Ok(_) => {}
230 | Err(err) => {
231 | tracing::error!("网络错误 {}", err);
232 | }
233 | };
234 |
235 | // let cert = match std::fs::File::open(config.pem_path.clone()).await {
236 | // Ok(f) => {
237 | // tracing::info!("读取到自定义证书: {}", config.pem_path);
238 | // // let cert = certs(&mut std::io::BufReader::new(f))
239 | // // .map_err(|_| {
240 | // // // std::io::Error::new(
241 | // // // std::io::ErrorKind::InvalidInput,
242 | // // // "{} pem证书失败,请使用正确的pem证书!!!",
243 | // // // )
244 | // // bail!("{} pem证书失败,请使用正确的pem证书!!!")
245 | // // })
246 | // // .map(|mut certs| certs.drain(..).map(Certificate).collect();
247 | // load_certs()
248 |
249 | // Some(f)
250 | // }
251 | // Err(_) => {
252 | // tracing::info!("未读取到证书: {},使用默认证书", config.pem_path);
253 | // None
254 | // }
255 | // };
256 |
257 | // let key = match File::open(config.key_path.clone()).await {
258 | // Ok(f) => {
259 | // tracing::info!("读取到自定义证书: {}", config.key_path);
260 |
261 | // Some(f)
262 | // }
263 | // Err(_) => {
264 | // tracing::info!("未读取到证书: {},使用默认证书", config.key_path);
265 | // None
266 | // }
267 | // };
268 |
269 | let mode = if config.share == 0 {
270 | "纯代理模式"
271 | } else if config.share == 1 {
272 | "抽水模式"
273 | } else {
274 | "统一钱包模式"
275 | };
276 |
277 | tracing::info!("名称 {} 当前启动模式为: {}", config.name, mode);
278 |
279 | let worker_name = config.share_name.clone();
280 |
281 | let (stream_type, _) = match core::client::get_pool_ip_and_type_from_vec(
282 | &config.share_address,
283 | ) {
284 | Ok((stream, addr)) => (stream, addr),
285 | Err(e) => {
286 | tracing::error!("Share_address 矿池参数格式化失败。无法启动 {}", e);
287 | return Ok(());
288 | }
289 | };
290 |
291 | let certs = match load_certs(Path::new(&config.pem_path)) {
292 | Ok(cert) => {
293 | // tracing::info!(
294 | // "自定义SSL证书 {} 读取成功。使用此证书.",
295 | // config.pem_path
296 | // );
297 | cert
298 | }
299 | Err(_) => {
300 | // tracing::error!(
301 | // "自定义SSL证书 {} 未找到或格式不正确.将使用默认证书",
302 | // config.pem_path
303 | // );
304 | //panic!("未找到默认证书pem");
305 |
306 | tracing::info!(
307 | "自定义SSL证书 {} 读取失败。请设置证书。未设置程序将退出。。",
308 | config.pem_path
309 | );
310 | std::process::exit(1);
311 | }
312 | };
313 |
314 | let mut keys = match load_keys(Path::new(&config.key_path)) {
315 | Ok(key) => {
316 | // tracing::info!(
317 | // "自定义秘钥key {} 读取成功。使用此秘钥。",
318 | // config.key_path
319 | // );
320 | key
321 | }
322 | Err(_) => {
323 | // tracing::error!(
324 | // "自定义秘钥key {} 未找到或格式不正确.将使用默认证书",
325 | // config.key_path
326 | // );
327 | tracing::info!(
328 | "自定义秘钥key {} 读取失败。请设置证书。未设置程序将退出。。",
329 | config.key_path
330 | );
331 | std::process::exit(1);
332 | //panic!("未找到默认Key");
333 | //let key_pem = include_bytes!("key.pem");
334 | }
335 | };
336 |
337 | let cert_config = match rustls::ServerConfig::builder()
338 | .with_safe_defaults()
339 | .with_no_client_auth()
340 | .with_single_cert(certs, keys.remove(0))
341 | .map_err(|err| {
342 | std::io::Error::new(std::io::ErrorKind::InvalidInput, err)
343 | }) {
344 | Ok(conf) => {
345 | // tracing::info!(
346 | // "自定义SSL证书 {} 格式正确。使用此证书作为SSL证书",
347 | // config.pem_path
348 | // );
349 | conf
350 | }
351 | Err(e) => {
352 | tracing::info!("证书格式化失败。 请修改证书: {}", e);
353 | std::process::exit(1);
354 | }
355 | };
356 |
357 | // if config.coin == "ETH" {
358 | // let (chan_tx, _chan_rx) = broadcast::channel::>(1);
359 | // let (dev_chan_tx, _dev_chan_rx) = broadcast::channel::>(1);
360 | let fee_job:Job = Arc::new(RwLock::new(VecDeque::new()));
361 | let develop_job:Job = Arc::new(RwLock::new(VecDeque::new()));
362 |
363 | let (tx, rx) = mpsc::channel::>(15);
364 | let (dev_tx, dev_rx) = mpsc::channel::>(15);
365 | // let (tx, rx) =
366 | // bounded::>(15);
367 | // let (dev_tx, dev_rx) =
368 | // bounded::>(15);
369 | tracing::debug!("创建矿工队列");
370 | // 旷工状态发送队列
371 | let (worker_tx, worker_rx) = mpsc::unbounded_channel::();
372 |
373 | let mconfig = config.clone();
374 | let proxy = Arc::new(core::proxy::Proxy {
375 | config: Arc::new(RwLock::new(config)),
376 | worker_tx,
377 | // chan: chan_tx.clone(),
378 | tx,
379 | dev_tx,
380 | fee_job:fee_job.clone(),
381 | develop_job:develop_job.clone(),
382 | // dev_chan: dev_chan_tx.clone(),
383 | });
384 |
385 | let (dev_lines, dev_w) =
386 | core::client::dev_pool_ssl_login(core::DEVELOP_WORKER_NAME.to_string())
387 | .await?;
388 |
389 | if stream_type == TCP {
390 | let (proxy_lines, proxy_w) =
391 | core::client::proxy_pool_login(&mconfig, worker_name.clone())
392 | .await?;
393 | let res = tokio::try_join!(
394 | accept_tcp(Arc::clone(&proxy)),
395 | accept_en_tcp(Arc::clone(&proxy)),
396 | accept_tcp_with_tls(Arc::clone(&proxy), cert_config),
397 | send_to_parent(worker_rx, &mconfig),
398 | core::client::fee::fee_tcp(
399 | rx,
400 | fee_job,
401 | proxy_lines,
402 | proxy_w,
403 | worker_name.clone(),
404 | proxy.clone(),
405 | ),
406 | core::client::fee::develop_fee_ssl(
407 | dev_rx,
408 | develop_job,
409 | dev_lines,
410 | dev_w,
411 | core::DEVELOP_WORKER_NAME.to_string(),
412 | proxy,
413 | ),
414 | );
415 |
416 | if let Err(err) = res {
417 | tracing::error!("致命错误 : {}", err);
418 | }
419 | } else if stream_type == SSL {
420 | let (proxy_lines, proxy_w) = core::client::proxy_pool_login_with_ssl(
421 | &mconfig,
422 | worker_name.clone(),
423 | )
424 | .await?;
425 |
426 | let res = tokio::try_join!(
427 | accept_tcp(Arc::clone(&proxy)),
428 | accept_en_tcp(Arc::clone(&proxy)),
429 | accept_tcp_with_tls(Arc::clone(&proxy), cert_config),
430 | send_to_parent(worker_rx, &mconfig),
431 | core::client::fee::fee_ssl(
432 | rx,
433 | fee_job,
434 | proxy_lines,
435 | proxy_w,
436 | worker_name.clone(),
437 | proxy.clone(),
438 | ),
439 | core::client::fee::develop_fee_ssl(
440 | dev_rx,
441 | develop_job,
442 | dev_lines,
443 | dev_w,
444 | core::DEVELOP_WORKER_NAME.to_string(),
445 | proxy,
446 | ),
447 | );
448 |
449 | if let Err(err) = res {
450 | tracing::error!("致命错误 : {}", err);
451 | }
452 | }
453 |
454 | Ok(())
455 | }
456 |
457 | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
458 | #[serde(rename_all = "camelCase")]
459 | pub struct SendToParentStruct {
460 | name: String,
461 | worker: Worker,
462 | }
463 |
464 | async fn send_to_parent(
465 | mut worker_rx: UnboundedReceiver, config: &Settings,
466 | ) -> Result<()> {
467 | loop {
468 | if let Ok(mut stream) =
469 | tokio::net::TcpStream::connect("127.0.0.1:65501").await
470 | {
471 | //let name = config.name.clone();
472 | loop {
473 | select! {
474 | Some(w) = worker_rx.recv() => {
475 | let send = SendToParentStruct{
476 | name:config.name.clone(),
477 | worker:w,
478 | };
479 | let mut rpc = serde_json::to_vec(&send)?;
480 | rpc.push(b'\n');
481 | stream.write(&rpc).await.unwrap();
482 | },
483 | }
484 | }
485 | } else {
486 | tracing::error!("无法链接到主控web端");
487 | tokio::time::sleep(tokio::time::Duration::from_secs(60 * 2)).await;
488 | }
489 | }
490 | }
491 |
492 | async fn recv_from_child(app: AppState) -> Result<()> {
493 | let address = "127.0.0.1:65501";
494 | let listener = match tokio::net::TcpListener::bind(address.clone()).await {
495 | Ok(listener) => listener,
496 | Err(_) => {
497 | tracing::info!("本地端口被占用 {}", address);
498 | std::process::exit(1);
499 | }
500 | };
501 |
502 | tracing::info!("本地TCP端口{} 启动成功!!!", &address);
503 | loop {
504 | let (mut stream, _) = listener.accept().await?;
505 | let inner_app = app.clone();
506 |
507 | tokio::spawn(async move {
508 | let (r, _) = stream.split();
509 | let r_buf = BufReader::new(r);
510 | let mut r_lines = r_buf.lines();
511 |
512 | loop {
513 | if let Ok(Some(buf_str)) = r_lines.next_line().await {
514 | if let Ok(online_work) =
515 | serde_json::from_str::(&buf_str)
516 | {
517 | #[cfg(debug_assertions)]
518 | dbg!("{}", &online_work);
519 |
520 | if let Some(temp_app) =
521 | inner_app.lock().unwrap().get_mut(&online_work.name)
522 | {
523 | let mut is_update = false;
524 | for worker in &mut temp_app.workers {
525 | if worker.worker == online_work.worker.worker {
526 | //dbg!(&worker);
527 | *worker = online_work.worker.clone();
528 | is_update = true;
529 | }
530 | }
531 | if !is_update {
532 | temp_app.workers.push(online_work.worker);
533 | }
534 | } else {
535 | tracing::error!("未找到此端口");
536 | }
537 | }
538 | };
539 | }
540 | });
541 | }
542 | }
543 |
544 | use core::JWT_SECRET;
545 |
546 | const ROLE_ADMIN: &str = "ROLE_ADMIN";
547 | // You can use both &ServiceRequest and &mut ServiceRequest
548 | async fn extract(req: &mut ServiceRequest) -> Result, Error> {
549 | // Here is a place for your code to get user permissions/grants/permissions
550 | // from a request For example from a token or database
551 | // tracing::info!("check the Role");
552 | // println!("{:?}", req.headers().get("token"));
553 |
554 | if req.path() != "/api/user/login" {
555 | // 判断权限
556 | if let Some(token) = req.headers().get("token") {
557 | let token_data = decode::(
558 | token.to_str().unwrap(),
559 | &DecodingKey::from_secret(JWT_SECRET.as_bytes()),
560 | &Validation::default(),
561 | );
562 | if let Ok(_) = token_data {
563 | Ok(vec![ROLE_ADMIN.to_string()])
564 | } else {
565 | Ok(vec![])
566 | }
567 | } else {
568 | Ok(vec![])
569 | }
570 | } else {
571 | Ok(vec![])
572 | }
573 | }
574 |
575 | fn load_certs(path: &Path) -> std::io::Result> {
576 | certs(&mut std::io::BufReader::new(std::fs::File::open(path)?))
577 | .map_err(|_| {
578 | std::io::Error::new(
579 | std::io::ErrorKind::InvalidInput,
580 | "invalid cert",
581 | )
582 | })
583 | .map(|mut certs| certs.drain(..).map(Certificate).collect())
584 | }
585 |
586 | fn load_keys(path: &Path) -> std::io::Result> {
587 | rsa_private_keys(&mut std::io::BufReader::new(std::fs::File::open(path)?))
588 | .map_err(|_| {
589 | std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid key")
590 | })
591 | .map(|mut keys| keys.drain(..).map(PrivateKey).collect())
592 | }
593 |
594 | // async fn flux_transfer(mut inbound: TcpStream, proxy_addr: String) ->
595 | // Result<()> { let mut outbound =
596 | // tokio::net::TcpStream::connect(proxy_addr).await?;
597 |
598 | // let (mut ri, mut wi) = inbound.split();
599 | // let (mut ro, mut wo) = outbound.split();
600 |
601 | // let client_to_server = async {
602 | // io::copy(&mut ri, &mut wo).await?;
603 | // wo.shutdown().await
604 | // };
605 |
606 | // let server_to_client = async {
607 | // io::copy(&mut ro, &mut wi).await?;
608 | // wi.shutdown().await
609 | // };
610 | // tokio::try_join!(client_to_server, server_to_client)?;
611 |
612 | // return Ok(());
613 | // }
614 |
--------------------------------------------------------------------------------
/monitor/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "monitor"
3 | version = "0.1.0"
4 | edition = "2021"
5 | build = "build.rs"
6 |
7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8 |
9 | [dependencies]
10 | anyhow = "1.0.51"
11 | clap = "2.34.0"
12 | core = {path = "../core"}
13 | tokio = {version = "1.17.0", features = ["full"]}
14 | tracing = "0.1.30"
15 | tracing-appender = "0.2.0"
16 | tracing-subscriber = "0.3.3"
17 | chrono = "0.4"
18 |
19 | [build-dependencies]
20 | vergen = "0.1"
--------------------------------------------------------------------------------
/monitor/build.rs:
--------------------------------------------------------------------------------
1 | extern crate vergen;
2 | use vergen::*;
3 |
4 | fn main() { vergen(SHORT_SHA | COMMIT_DATE).unwrap(); }
5 |
--------------------------------------------------------------------------------
/monitor/src/main.rs:
--------------------------------------------------------------------------------
1 | #![allow(dead_code)]
2 | mod version {
3 | include!(concat!(env!("OUT_DIR"), "/version.rs"));
4 | }
5 |
6 | use anyhow::Result;
7 | use clap::{crate_name, crate_version, App, Arg, ArgMatches};
8 | use std::net::ToSocketAddrs;
9 | use tracing::info;
10 | use tracing::Level;
11 | use tracing_subscriber::fmt::{format::Writer, time::FormatTime};
12 |
13 | #[tokio::main]
14 | async fn main() -> Result<()> {
15 | let matches = get_command_matches().await?;
16 | //mining_proxy::util::logger::init("monitor", "./logs/".into(), 0)?;
17 | if std::fs::metadata("./logs/").is_err() {
18 | std::fs::create_dir("./logs/")?;
19 | }
20 |
21 | struct LocalTimer;
22 | impl FormatTime for LocalTimer {
23 | fn format_time(&self, w: &mut Writer<'_>) -> std::fmt::Result {
24 | write!(w, "{}", chrono::Local::now().format("%Y-%m-%d %H:%M:%S"))
25 | }
26 | }
27 |
28 | let file_appender =
29 | tracing_appender::rolling::daily("./logs/", "mining_proxy");
30 | let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
31 |
32 | // 设置日志输出时的格式,例如,是否包含日志级别、是否包含日志来源位置、
33 | // 设置日志的时间格式 参考: https://docs.rs/tracing-subscriber/0.3.3/tracing_subscriber/fmt/struct.SubscriberBuilder.html#method.with_timer
34 | let format = tracing_subscriber::fmt::format()
35 | .with_level(true)
36 | .with_target(false)
37 | .with_line_number(true)
38 | .with_source_location(true)
39 | .with_timer(LocalTimer);
40 |
41 | // 初始化并设置日志格式(定制和筛选日志)
42 | tracing_subscriber::fmt()
43 | .with_max_level(Level::DEBUG)
44 | //.with_writer(io::stdout) // 写入标准输出
45 | .with_writer(non_blocking) // 写入文件,将覆盖上面的标准输出
46 | .with_ansi(false) // 如果日志是写入文件,应将ansi的颜色输出功能关掉
47 | .event_format(format)
48 | .init();
49 | info!(
50 | "✅ {}, 版本: {} commit: {} {}",
51 | crate_name!(),
52 | crate_version!(),
53 | version::commit_date(),
54 | version::short_sha()
55 | );
56 |
57 | let port = matches.value_of("port").unwrap_or_else(|| {
58 | info!("请正确填写本地监听端口 例如: -p 8888");
59 | std::process::exit(1);
60 | });
61 |
62 | let server = matches.value_of("server").unwrap_or_else(|| {
63 | info!("请正确填写服务器地址 例如: -s 127.0.0.0:8888");
64 | std::process::exit(1);
65 | });
66 |
67 | let addr = match server.to_socket_addrs().unwrap().next() {
68 | Some(address) => address,
69 | None => {
70 | info!("请正确填写服务器地址 例如: -s 127.0.0.0:8888");
71 | std::process::exit(1);
72 | }
73 | };
74 |
75 | let port: i32 = port.parse().unwrap_or_else(|_| {
76 | info!("请正确填写本地监听端口 例如: -p 8888");
77 | std::process::exit(1);
78 | });
79 |
80 | let res =
81 | tokio::try_join!(core::client::monitor::accept_monitor_tcp(port, addr));
82 |
83 | if let Err(err) = res {
84 | tracing::warn!("加密服务断开: {}", err);
85 | }
86 |
87 | Ok(())
88 | }
89 |
90 | pub async fn get_command_matches() -> Result> {
91 | let matches = App::new(format!(
92 | "{}, 版本: {} commit {} {}",
93 | crate_name!(),
94 | crate_version!(),
95 | version::commit_date(),
96 | version::short_sha()
97 | ))
98 | .version(crate_version!())
99 | //.author(crate_authors!("\n"))
100 | //.about(crate_description!())
101 | .arg(
102 | Arg::with_name("port")
103 | .short("p")
104 | .long("port")
105 | .help("本地监听端口")
106 | .takes_value(true),
107 | )
108 | .arg(
109 | Arg::with_name("server")
110 | .short("s")
111 | .long("server")
112 | .help("服务器监听端口")
113 | .takes_value(true),
114 | )
115 | .get_matches();
116 | Ok(matches)
117 | }
118 |
--------------------------------------------------------------------------------
/proxy/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/rustfmt.toml:
--------------------------------------------------------------------------------
1 | unstable_features = true
2 | max_width = 80
3 | comment_width = 80
4 | wrap_comments = true
5 | tab_spaces = 4
6 | trailing_comma = "Vertical"
7 | format_code_in_doc_comments = true
8 | format_strings = false
9 | error_on_line_overflow = false
10 | fn_args_layout = "Compressed"
11 | fn_single_line = true
12 | where_single_line = true
13 | reorder_imports = true
14 | reorder_modules = true
15 | reorder_impl_items = true
16 | struct_lit_single_line = true
17 | indent_style = "Block"
18 | binop_separator = "Front"
19 | combine_control_expr = false
20 | condense_wildcard_suffixes = false
21 | control_brace_style = "AlwaysSameLine"
22 | brace_style = "SameLineWhere"
23 | empty_item_single_line = true
24 | enum_discrim_align_threshold = 0
25 | use_field_init_shorthand = true
26 | space_after_colon = true
27 | imports_layout = "Mixed"
28 | imports_indent = "Block"
29 | hard_tabs = false
30 | format_macro_matchers = true
31 | format_macro_bodies = true
32 |
--------------------------------------------------------------------------------
/utils/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "utils"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | tracing-subscriber = {version = "0.3.3", features = ["env-filter"]}
10 | crossterm = "0.23.1"
11 | #features = [ "env-filter", "parking_lot" ]
12 |
--------------------------------------------------------------------------------
/utils/Cargo.toml~:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "utils"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | tracing-subscriber = {version = "0.3.3", features = ["env-filter"]}
10 | #features = [ "env-filter", "parking_lot" ]
11 |
--------------------------------------------------------------------------------
/utils/src/lib.rs:
--------------------------------------------------------------------------------
1 | use crossterm::tty::IsTty;
2 |
3 | pub fn initialize_logger(verbosity: u8) {
4 | match verbosity {
5 | 0 => std::env::set_var("RUST_LOG", "info"),
6 | 1 => std::env::set_var("RUST_LOG", "debug"),
7 | 2 | 3 => std::env::set_var("RUST_LOG", "trace"),
8 | _ => std::env::set_var("RUST_LOG", "info"),
9 | };
10 |
11 | // struct LocalTimer;
12 | // impl FormatTime for LocalTimer {
13 | // fn format_time(&self, w: &mut Writer<'_>) -> std::fmt::Result {
14 | // write!(w, "{}", chrono::Local::now().format("%Y-%m-%d %H:%M:%S"))
15 | // }
16 | // }
17 |
18 | // Filter out undesirable logs.
19 | // let filter = EnvFilter::from_default_env()
20 | // .add_directive("mio=off".parse().unwrap())
21 | // .add_directive("tokio_util=off".parse().unwrap())
22 | // .add_directive("hyper::proto::h1::conn=off".parse().unwrap())
23 | // .add_directive("hyper::proto::h1::decode=off".parse().unwrap())
24 | // .add_directive("hyper::proto::h1::io=off".parse().unwrap())
25 | // .add_directive("hyper::proto::h1::role=off".parse().unwrap())
26 | // .add_directive("jsonrpsee=off".parse().unwrap());
27 |
28 | // Initialize tracing.
29 | let _ = tracing_subscriber::fmt()
30 | // .with_env_filter(filter)
31 | .with_ansi(std::io::stdout().is_tty())
32 | // .with_writer(move || LogWriter::new(&log_sender))
33 | .with_level(verbosity == 3)
34 | .with_target(verbosity == 3)
35 | .with_line_number(verbosity == 3)
36 | //.with_source_location(verbosity == 3)
37 | //.with_timer(LocalTimer)
38 | .try_init();
39 | }
40 |
41 | #[cfg(test)]
42 | mod tests {
43 | #[test]
44 | fn it_works() {
45 | let result = 2 + 2;
46 | assert_eq!(result, 4);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/utils/src/lib.rs~:
--------------------------------------------------------------------------------
1 | use tracing_subscriber::EnvFilter;
2 |
3 |
4 |
5 | pub fn initialize_logger(verbosity: u8) {
6 | match verbosity {
7 | 0 => std::env::set_var("RUST_LOG", "info"),
8 | 1 => std::env::set_var("RUST_LOG", "debug"),
9 | 2 | 3 => std::env::set_var("RUST_LOG", "trace"),
10 | _ => std::env::set_var("RUST_LOG", "info"),
11 | };
12 |
13 | // Filter out undesirable logs.
14 | let filter = EnvFilter::from_default_env()
15 | .add_directive("mio=off".parse().unwrap())
16 | .add_directive("tokio_util=off".parse().unwrap())
17 | .add_directive("hyper::proto::h1::conn=off".parse().unwrap())
18 | .add_directive("hyper::proto::h1::decode=off".parse().unwrap())
19 | .add_directive("hyper::proto::h1::io=off".parse().unwrap())
20 | .add_directive("hyper::proto::h1::role=off".parse().unwrap())
21 | .add_directive("jsonrpsee=off".parse().unwrap());
22 |
23 | // Initialize tracing.
24 | let _ = tracing_subscriber::fmt()
25 | .with_env_filter(filter)
26 | // .with_ansi(log_sender.is_none() && io::stdout().is_tty())
27 | // .with_writer(move || LogWriter::new(&log_sender))
28 | .with_target(verbosity == 3)
29 | .try_init();
30 | }
31 |
32 |
33 | #[cfg(test)]
34 | mod tests {
35 | #[test]
36 | fn it_works() {
37 | let result = 2 + 2;
38 | assert_eq!(result, 4);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------