Skip to content

Commit 5347351

Browse files
authored
Merge pull request #1095 from jbesraa/2024-08-13-refactor-jdserver
Move `JDServer` lib code out of `main.rs`
2 parents 4173c4d + ef254e1 commit 5347351

File tree

2 files changed

+238
-168
lines changed

2 files changed

+238
-168
lines changed

roles/jd-server/src/lib/mod.rs

+234
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ pub mod job_declarator;
33
pub mod mempool;
44
pub mod status;
55

6+
use async_channel::{bounded, unbounded, Receiver, Sender};
7+
use error_handling::handle_result;
8+
use job_declarator::JobDeclarator;
9+
use mempool::error::JdsMempoolError;
10+
use roles_logic_sv2::utils::Mutex;
11+
use std::{ops::Sub, sync::Arc};
12+
use tokio::{select, task};
13+
use tracing::{error, info, warn};
14+
615
use codec_sv2::{StandardEitherFrame, StandardSv2Frame};
716
use key_utils::{Secp256k1PublicKey, Secp256k1SecretKey};
817
use roles_logic_sv2::{
@@ -19,6 +28,177 @@ pub type Message = JdsMessages<'static>;
1928
pub type StdFrame = StandardSv2Frame<Message>;
2029
pub type EitherFrame = StandardEitherFrame<Message>;
2130

31+
pub struct JobDeclaratorServer {
32+
config: Configuration,
33+
}
34+
35+
impl JobDeclaratorServer {
36+
pub fn new(config: Configuration) -> Self {
37+
Self { config }
38+
}
39+
pub async fn start(&self) {
40+
let config = self.config.clone();
41+
let url = config.core_rpc_url.clone() + ":" + &config.core_rpc_port.clone().to_string();
42+
let username = config.core_rpc_user.clone();
43+
let password = config.core_rpc_pass.clone();
44+
// TODO should we manage what to do when the limit is reaced?
45+
let (new_block_sender, new_block_receiver): (Sender<String>, Receiver<String>) =
46+
bounded(10);
47+
let mempool = Arc::new(Mutex::new(mempool::JDsMempool::new(
48+
url.clone(),
49+
username,
50+
password,
51+
new_block_receiver,
52+
)));
53+
let mempool_update_interval = config.mempool_update_interval;
54+
let mempool_cloned_ = mempool.clone();
55+
let (status_tx, status_rx) = unbounded();
56+
let sender = status::Sender::Downstream(status_tx.clone());
57+
let mut last_empty_mempool_warning =
58+
std::time::Instant::now().sub(std::time::Duration::from_secs(60));
59+
60+
// TODO if the jd-server is launched with core_rpc_url empty, the following flow is never
61+
// taken. Consequentally new_block_receiver in JDsMempool::on_submit is never read, possibly
62+
// reaching the channel bound. The new_block_sender is given as input to JobDeclarator::start()
63+
if url.contains("http") {
64+
let sender_update_mempool = sender.clone();
65+
task::spawn(async move {
66+
loop {
67+
let update_mempool_result: Result<(), mempool::error::JdsMempoolError> =
68+
mempool::JDsMempool::update_mempool(mempool_cloned_.clone()).await;
69+
if let Err(err) = update_mempool_result {
70+
match err {
71+
JdsMempoolError::EmptyMempool => {
72+
if last_empty_mempool_warning.elapsed().as_secs() >= 60 {
73+
warn!("{:?}", err);
74+
warn!("Template Provider is running, but its mempool is empty (possible reasons: you're testing in testnet, signet, or regtest)");
75+
last_empty_mempool_warning = std::time::Instant::now();
76+
}
77+
}
78+
JdsMempoolError::NoClient => {
79+
mempool::error::handle_error(&err);
80+
handle_result!(sender_update_mempool, Err(err));
81+
}
82+
JdsMempoolError::Rpc(_) => {
83+
mempool::error::handle_error(&err);
84+
handle_result!(sender_update_mempool, Err(err));
85+
}
86+
JdsMempoolError::PoisonLock(_) => {
87+
mempool::error::handle_error(&err);
88+
handle_result!(sender_update_mempool, Err(err));
89+
}
90+
}
91+
}
92+
tokio::time::sleep(mempool_update_interval).await;
93+
// DO NOT REMOVE THIS LINE
94+
//let _transactions = mempool::JDsMempool::_get_transaction_list(mempool_cloned_.clone());
95+
}
96+
});
97+
98+
let mempool_cloned = mempool.clone();
99+
let sender_submit_solution = sender.clone();
100+
task::spawn(async move {
101+
loop {
102+
let result = mempool::JDsMempool::on_submit(mempool_cloned.clone()).await;
103+
if let Err(err) = result {
104+
match err {
105+
JdsMempoolError::EmptyMempool => {
106+
if last_empty_mempool_warning.elapsed().as_secs() >= 60 {
107+
warn!("{:?}", err);
108+
warn!("Template Provider is running, but its mempool is empty (possible reasons: you're testing in testnet, signet, or regtest)");
109+
last_empty_mempool_warning = std::time::Instant::now();
110+
}
111+
}
112+
_ => {
113+
// TODO here there should be a better error managmenet
114+
mempool::error::handle_error(&err);
115+
handle_result!(sender_submit_solution, Err(err));
116+
}
117+
}
118+
}
119+
}
120+
});
121+
};
122+
123+
let cloned = config.clone();
124+
let mempool_cloned = mempool.clone();
125+
let (sender_add_txs_to_mempool, receiver_add_txs_to_mempool) = unbounded();
126+
task::spawn(async move {
127+
JobDeclarator::start(
128+
cloned,
129+
sender,
130+
mempool_cloned,
131+
new_block_sender,
132+
sender_add_txs_to_mempool,
133+
)
134+
.await
135+
});
136+
task::spawn(async move {
137+
loop {
138+
if let Ok(add_transactions_to_mempool) = receiver_add_txs_to_mempool.recv().await {
139+
let mempool_cloned = mempool.clone();
140+
task::spawn(async move {
141+
match mempool::JDsMempool::add_tx_data_to_mempool(
142+
mempool_cloned,
143+
add_transactions_to_mempool,
144+
)
145+
.await
146+
{
147+
Ok(_) => (),
148+
Err(err) => {
149+
// TODO
150+
// here there should be a better error management
151+
mempool::error::handle_error(&err);
152+
}
153+
}
154+
});
155+
}
156+
}
157+
});
158+
159+
// Start the error handling loop
160+
// See `./status.rs` and `utils/error_handling` for information on how this operates
161+
loop {
162+
let task_status = select! {
163+
task_status = status_rx.recv() => task_status,
164+
interrupt_signal = tokio::signal::ctrl_c() => {
165+
match interrupt_signal {
166+
Ok(()) => {
167+
info!("Interrupt received");
168+
},
169+
Err(err) => {
170+
error!("Unable to listen for interrupt signal: {}", err);
171+
// we also shut down in case of error
172+
},
173+
}
174+
break;
175+
}
176+
};
177+
let task_status: status::Status = task_status.unwrap();
178+
179+
match task_status.state {
180+
// Should only be sent by the downstream listener
181+
status::State::DownstreamShutdown(err) => {
182+
error!(
183+
"SHUTDOWN from Downstream: {}\nTry to restart the downstream listener",
184+
err
185+
);
186+
}
187+
status::State::TemplateProviderShutdown(err) => {
188+
error!("SHUTDOWN from Upstream: {}\nTry to reconnecting or connecting to a new upstream", err);
189+
break;
190+
}
191+
status::State::Healthy(msg) => {
192+
info!("HEALTHY message: {}", msg);
193+
}
194+
status::State::DownstreamInstanceDropped(downstream_id) => {
195+
warn!("Dropping downstream instance {} from jds", downstream_id);
196+
}
197+
}
198+
}
199+
}
200+
}
201+
22202
pub fn get_coinbase_output(config: &Configuration) -> Result<Vec<TxOut>, Error> {
23203
let mut result = Vec::new();
24204
for coinbase_output_pool in &config.coinbase_outputs {
@@ -55,6 +235,15 @@ pub struct CoinbaseOutput {
55235
output_script_value: String,
56236
}
57237

238+
impl CoinbaseOutput {
239+
pub fn new(output_script_type: String, output_script_value: String) -> Self {
240+
Self {
241+
output_script_type,
242+
output_script_value,
243+
}
244+
}
245+
}
246+
58247
#[derive(Debug, Deserialize, Clone)]
59248
pub struct Configuration {
60249
#[serde(default = "default_true")]
@@ -72,6 +261,51 @@ pub struct Configuration {
72261
pub mempool_update_interval: Duration,
73262
}
74263

264+
#[derive(Debug, Deserialize, Clone)]
265+
pub struct CoreRpc {
266+
url: String,
267+
port: u16,
268+
user: String,
269+
pass: String,
270+
}
271+
272+
impl CoreRpc {
273+
pub fn new(url: String, port: u16, user: String, pass: String) -> Self {
274+
Self {
275+
url,
276+
port,
277+
user,
278+
pass,
279+
}
280+
}
281+
}
282+
283+
impl Configuration {
284+
pub fn new(
285+
listen_jd_address: String,
286+
authority_public_key: Secp256k1PublicKey,
287+
authority_secret_key: Secp256k1SecretKey,
288+
cert_validity_sec: u64,
289+
coinbase_outputs: Vec<CoinbaseOutput>,
290+
core_rpc: CoreRpc,
291+
mempool_update_interval: Duration,
292+
) -> Self {
293+
Self {
294+
async_mining_allowed: true,
295+
listen_jd_address,
296+
authority_public_key,
297+
authority_secret_key,
298+
cert_validity_sec,
299+
coinbase_outputs,
300+
core_rpc_url: core_rpc.url,
301+
core_rpc_port: core_rpc.port,
302+
core_rpc_user: core_rpc.user,
303+
core_rpc_pass: core_rpc.pass,
304+
mempool_update_interval,
305+
}
306+
}
307+
}
308+
75309
fn default_true() -> bool {
76310
true
77311
}

0 commit comments

Comments
 (0)