rustdcr 0.1.0

Decred daemon infrastructure in Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
//! Chain Notification Commands.
//! Contains all chain non-wallet notification commands to RPC server.

use super::connection::RPCConn;

use {
    super::{check_config, error::RpcClientError, future_type::NotificationsFuture},
    crate::{
        chaincfg::chainhash::Hash,
        dcrjson::{commands, parse_hex_parameters, result_types},
        rpcclient::client::Client,
    },
    log::{trace, warn},
};

macro_rules! notification_generator {
    ($doc: tt, $name: ident, $return_type: ty, $command: expr, $param: expr, all_defined($($callback_name: tt),*), ($($fn_params:ident : $fn_type: ty),*)) => {
        #[doc = $doc]
        pub async fn $name(&mut self, $($fn_params : $fn_type),*) -> Result<$return_type, RpcClientError> {
            check_config!(self);
            callback_check!(self, $command, all_defined($($callback_name),*));
            create_notif_future!(self, $command, $param)
        }
    };

    ($doc: tt, $name: ident, $return_type: ty, $command: expr, $param: expr, either_defined($($callback_name: tt),*), ($($fn_params:ident : $fn_type: ty),*)) => {
        #[doc = $doc]
        pub async fn $name(&mut self, $($fn_params : $fn_type),*) -> Result<$return_type, RpcClientError> {
            check_config!(self);
            callback_check!(self, $command, either_defined($($callback_name),*));
            create_notif_future!(self, $command, $param)
        }
    };
}

macro_rules! callback_check {
    ($self: ident, $name: expr, either_defined($($callback_name: tt), *)) => {
        $(
            let $callback_name = $self.notification_handler.$callback_name;
        )*
        if $(
            $callback_name.is_none()
        ) && * {
            return Err(RpcClientError::UnregisteredNotification(
                $name.to_string(),
            ));
        }
    };

    ($self: ident, $name: expr, all_defined($($callback_name: tt), *)) => {
        $(
            let $callback_name = $self.notification_handler.$callback_name;
        )*
       if $(
            $callback_name.is_none()
        ) || * {
            return Err(RpcClientError::UnregisteredNotification(
                $name.to_string(),
            ));
        }
    };
}

macro_rules! create_notif_future {
    ($self: ident, $command: expr, $param: expr) => {{
        let notif_future = $self.create_notification($command, $param).await;

        notif_future
    }};
}

impl<C: 'static + RPCConn> Client<C> {
    notification_generator!(
        "notify_blocks registers the client to receive notifications when blocks are connected
        and disconnected from the main chain.  The notifications are delivered to the 
        notification handlers associated with the client.  Calling this function has no effect
        if there are no notification handlers and will result in an error if the client is configured
        to run in HTTP POST mode.
        \nThe notifications delivered as a result of this call will be via one of OnBlockConnected or 
        OnBlockDisconnected.
        \n**NOTE: This is a non-wallet extension and requires a websocket connection.**",
        notify_blocks,
        NotificationsFuture,
        commands::METHOD_NOTIFY_BLOCKS.to_string(),
        &[],
        all_defined(on_block_connected, on_block_disconnected),
        ()
    );

    notification_generator!(
        "notify_new_tickets registers the client to receive notifications when blocks are connected to the main
        chain and new tickets have matured. The notifications are delivered to the notification handlers
        associated with the client. Calling this function has no effect if there are no notification handlers
        and will result in an error if the client is configured to run in HTTP POST mode.
        \nThe notifications delivered as a result of this call will be via OnNewTickets.
        \n**NOTE: This is a chain extension and requires a websocket connection.**",
        notify_new_tickets,
        NotificationsFuture,
        commands::METHOD_NOTIFY_NEW_TICKETS.to_string(),
        &[],
        all_defined(on_new_tickets),
        ()
    );

    notification_generator!(
        "notify_work registers the client to receive notifications when a new block
        template has been generated.
        \nThe notifications delivered as a result of this call will be via on_work.
        \n**NOTE: This is a dcrd extension and requires a websocket connection**",
        notify_work,
        NotificationsFuture,
        commands::METHOD_NOTIFIY_NEW_WORK.to_string(),
        &[],
        all_defined(on_work),
        ()
    );

    notification_generator!(
        "notify_new_transactions registers the client to receive notifications every
        time a new transaction is accepted to the memory pool.  The notifications are
        delivered to the notification handlers associated with the client.  Calling
        this function has no effect if there are no notification handlers and will
        result in an error if the client is configured to run in HTTP POST mode.
        \nThe notifications delivered as a result of this call will be via one of
        on_tx_accepted (when verbose is false) or on_tx_accepted_verbose (when verbose is
        true).
        \n**NOTE: This is a dcrd extension and requires a websocket connection.**",
        notify_new_transactions,
        NotificationsFuture,
        commands::METHOD_NEW_TX.to_string(),
        &[serde_json::json!(verbose)],
        either_defined(on_tx_accepted, on_tx_accepted_verbose),
        (verbose: bool)
    );

    async fn create_notification(
        &mut self,
        method: String,
        params: &[serde_json::Value],
    ) -> Result<NotificationsFuture, RpcClientError> {
        let (id, result_receiver) = match self.send_custom_command(&method, params).await {
            Ok(e) => e,

            Err(e) => return Err(e),
        };

        // Register notification command to active notifications for reconnection.
        let mut notification_state = self.notification_state.write().await;
        notification_state.insert(method, id);

        Ok(NotificationsFuture {
            message: result_receiver,
        })
    }
}

pub(super) fn on_block_connected(
    params: &[serde_json::Value],
    on_block_connected: fn(block_header: Vec<u8>, transactions: Vec<Vec<u8>>),
) {
    trace!("Received on block connected notification");

    if params.len() != 2 {
        warn!("Server sent wrong number of parameters on block connected notification handler");
        return;
    }

    let block_header = match parse_hex_parameters(&params[0]) {
        Some(e) => e,

        None => {
            warn!("Error parsing hex value on block connected notification.");
            return;
        }
    };

    let hex_transactions = if params[1].is_null() {
        Vec::new()
    } else {
        let hex_transactions: Vec<String> = match serde_json::from_value(params[1].clone()) {
            Ok(e) => e,

            Err(e) => {
                warn!(
                    "Error marshalling on block transaction hex transaction values, error: {}",
                    e
                );

                return;
            }
        };

        hex_transactions
    };

    let mut transactions = Vec::new();

    for hex_transaction in hex_transactions {
        match hex::decode(hex_transaction) {
            Ok(v) => transactions.push(v),

            Err(e) => {
                warn!(
                    "Error getting hex value transaction on block connected notifier, error: {}",
                    e
                );
                return;
            }
        };
    }

    on_block_connected(block_header, transactions);
}

pub(super) fn on_block_disconnected(
    params: &[serde_json::Value],
    on_block_disconnected: fn(block_header: Vec<u8>),
) {
    trace!("Received on block disconnected notification");

    if params.len() != 1 {
        warn!("Server sent wrong number of parameters on block disconnected notification handler");
        return;
    }

    let block_header = match parse_hex_parameters(&params[0]) {
        Some(e) => e,

        None => {
            warn!("Error parsing hex value on block disconnection notification");
            return;
        }
    };

    on_block_disconnected(block_header);
}

pub(super) fn on_reorganization(
    params: &[serde_json::Value],
    on_reorganization_callback: fn(
        old_hash: Hash,
        old_height: i32,
        new_hash: Hash,
        new_height: i32,
    ),
) {
    trace!("Received on reorganization notification");

    if params.len() != 4 {
        warn!("Server sent wrong number of parameters on reorganization notification handler");
        return;
    }

    let old_hash = match crate::dcrjson::marshal_to_hash(params[0].clone()) {
        Some(e) => e,

        None => {
            warn!("Error marshalling old_hash params in on reorganization notifiaction.");
            return;
        }
    };

    let old_height: i32 = match serde_json::from_value(params[1].clone()) {
        Ok(e) => e,

        Err(e) => {
            warn!(
                "Error unmarshalling old height params in reorganization notification, error: {}",
                e
            );
            return;
        }
    };

    let new_hash = match crate::dcrjson::marshal_to_hash(params[2].clone()) {
        Some(e) => e,

        None => {
            warn!("Error marshalling new_hash params in on reorganization notifiaction.");
            return;
        }
    };

    let new_height: i32 = match serde_json::from_value(params[3].clone()) {
        Ok(e) => e,

        Err(e) => {
            warn!(
                "Error unmarshalling new height params in reorganization notification, error: {}",
                e
            );
            return;
        }
    };

    on_reorganization_callback(old_hash, old_height, new_hash, new_height)
}

pub(super) fn on_new_tickets(
    params: &[serde_json::Value],
    new_tickets_callback: fn(hash: Hash, height: i64, stake_diff: i64, tickets: Vec<Hash>),
) {
    trace!("Received on new ticket notification");

    if params.len() != 4 {
        warn!("Server sent wrong number of parameters on new tickets notification handler");
        return;
    }

    let hash = match crate::dcrjson::marshal_to_hash(params[0].clone()) {
        Some(e) => e,

        None => {
            warn!("Error marshalling to hash in on new ticket notification.");
            return;
        }
    };

    let block_height: i64 = match serde_json::from_value(params[1].clone()) {
        Ok(e) => e,

        Err(e) => {
            warn!(
                "Error marshalling block height in on new tickets notification, error: {}",
                e
            );
            return;
        }
    };

    let stake_diff: i64 = match serde_json::from_value(params[2].clone()) {
        Ok(e) => e,

        Err(e) => {
            warn!(
                "Error marshalling stake diff in on new tickets notification, error: {}",
                e
            );
            return;
        }
    };

    let tickets_str: Vec<String> = match serde_json::from_value(params[3].clone()) {
        Ok(e) => e,

        Err(e) => {
            warn!(
                "Error marshalling tickets in on new tickets notification, error: {}",
                e
            );
            return;
        }
    };

    let mut tickets: Vec<Hash> = Vec::with_capacity(tickets_str.len());

    for ticket_str in tickets_str.iter() {
        match Hash::new_from_str(ticket_str) {
            Ok(e) => {
                tickets.push(e);
            }

            Err(e) => {
                warn!("Error converting tickets string to hash, error: {}", e);
                return;
            }
        }
    }

    new_tickets_callback(hash, block_height, stake_diff, tickets)
}

pub(super) fn on_work(
    params: &[serde_json::Value],
    on_work_callback: fn(data: Vec<u8>, target: Vec<u8>, reason: String),
) {
    trace!("Received on work notification");

    if params.len() != 3 {
        warn!("Server sent wrong number of parameters on new work notification handler");
        return;
    }

    let data = match parse_hex_parameters(&params[0]) {
        Some(e) => e,

        None => {
            warn!("Error getting hex DATA on work notification handler.");
            return;
        }
    };

    let target = match parse_hex_parameters(&params[1]) {
        Some(e) => e,

        None => {
            warn!("Error getting hex TARGET on work notification handler.");
            return;
        }
    };

    let reason: String = match serde_json::from_value(params[2].clone()) {
        Ok(e) => e,

        Err(e) => {
            warn!(
                "Error getting on work REASON parmeter on work notification handler, error: {}.",
                e
            );
            return;
        }
    };

    on_work_callback(data, target, reason);
}

pub(super) fn on_tx_accepted(
    params: &[serde_json::Value],
    on_tx_callback: fn(hash: Hash, amount: crate::dcrutil::amount::Amount),
) {
    trace!("Received transaction accepted notification");

    if params.len() != 2 {
        warn!(
            "Server sent wrong number of parameters on transaction accepted notification handler"
        );
        return;
    }

    let hash = match crate::dcrjson::marshal_to_hash(params[0].clone()) {
        Some(e) => e,

        None => {
            warn!("Error marshalling value to hash in on transaction accepted");
            return;
        }
    };

    let amount_unit: f64 = match serde_json::from_value(params[1].clone()) {
        Ok(e) => e,

        Err(e) => {
            warn!(
                "Error marshalling amount unit in on transaction accepted notification, error: {}",
                e
            );
            return;
        }
    };

    let amount = match crate::dcrutil::amount::new(amount_unit) {
        Ok(e) => e,

        Err(e) => {
            warn!(
                "Error converting marshalled number to Amount type, error: {}",
                e
            );
            return;
        }
    };

    on_tx_callback(hash, amount)
}

pub(super) fn on_tx_accepted_verbose(
    params: &[serde_json::Value],
    on_tx_verbose_callback: fn(tx_details: result_types::TxRawResult),
) {
    trace!("Received transaction accepted verbose notification");

    if params.len() != 1 {
        warn!(
            "Server sent wrong number of parameters on transaction accepted verbsose notification handler"
        );
        return;
    }

    let tx_details: result_types::TxRawResult = match serde_json::from_value(params[0].clone()) {
        Ok(e) => e,

        Err(e) => {
            warn!(
                "Error marshalling transaction accepted verbose notification, error: {}",
                e
            );
            return;
        }
    };

    on_tx_verbose_callback(tx_details);
}