@@ -3,6 +3,15 @@ pub mod job_declarator;
3
3
pub mod mempool;
4
4
pub mod status;
5
5
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
+
6
15
use codec_sv2:: { StandardEitherFrame , StandardSv2Frame } ;
7
16
use key_utils:: { Secp256k1PublicKey , Secp256k1SecretKey } ;
8
17
use roles_logic_sv2:: {
@@ -19,6 +28,177 @@ pub type Message = JdsMessages<'static>;
19
28
pub type StdFrame = StandardSv2Frame < Message > ;
20
29
pub type EitherFrame = StandardEitherFrame < Message > ;
21
30
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: {}\n Try to restart the downstream listener" ,
184
+ err
185
+ ) ;
186
+ }
187
+ status:: State :: TemplateProviderShutdown ( err) => {
188
+ error ! ( "SHUTDOWN from Upstream: {}\n Try 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
+
22
202
pub fn get_coinbase_output ( config : & Configuration ) -> Result < Vec < TxOut > , Error > {
23
203
let mut result = Vec :: new ( ) ;
24
204
for coinbase_output_pool in & config. coinbase_outputs {
@@ -55,6 +235,15 @@ pub struct CoinbaseOutput {
55
235
output_script_value : String ,
56
236
}
57
237
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
+
58
247
#[ derive( Debug , Deserialize , Clone ) ]
59
248
pub struct Configuration {
60
249
#[ serde( default = "default_true" ) ]
@@ -72,6 +261,51 @@ pub struct Configuration {
72
261
pub mempool_update_interval : Duration ,
73
262
}
74
263
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
+
75
309
fn default_true ( ) -> bool {
76
310
true
77
311
}
0 commit comments