• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tari-project / tari / 17275382059

27 Aug 2025 06:28PM UTC coverage: 60.14% (-0.1%) from 60.274%
17275382059

push

github

web-flow
chore: new release v5.0.0-pre.8 (#7446)

Description
---
new release

71505 of 118897 relevant lines covered (60.14%)

536444.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

6.36
/base_layer/core/src/base_node/comms_interface/local_interface.rs
1
// Copyright 2019. The Tari Project
2
//
3
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
4
// following conditions are met:
5
//
6
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
7
// disclaimer.
8
//
9
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
10
// following disclaimer in the documentation and/or other materials provided with the distribution.
11
//
12
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
13
// products derived from this software without specific prior written permission.
14
//
15
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
16
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
21
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22

23
use std::{ops::RangeInclusive, sync::Arc};
24

25
use tari_common_types::{
26
    chain_metadata::ChainMetadata,
27
    epoch::VnEpoch,
28
    types::{BlockHash, CompressedCommitment, CompressedPublicKey, CompressedSignature, FixedHash, HashOutput},
29
};
30
use tari_node_components::blocks::{Block, NewBlockTemplate};
31
use tari_service_framework::{reply_channel::SenderService, Service};
32
use tari_transaction_components::{
33
    tari_proof_of_work::{Difficulty, PowAlgorithm},
34
    transaction_components::{TransactionKernel, TransactionOutput},
35
};
36
use tokio::sync::broadcast;
37

38
use crate::{
39
    base_node::comms_interface::{
40
        comms_request::GetNewBlockTemplateRequest,
41
        comms_response::ValidatorNodeChange,
42
        error::CommsInterfaceError,
43
        BlockEvent,
44
        NodeCommsRequest,
45
        NodeCommsResponse,
46
    },
47
    blocks::{ChainHeader, HistoricalBlock},
48
    chain_storage::{
49
        InputMinedInfo,
50
        MinedInfo,
51
        OutputMinedInfo,
52
        TemplateRegistrationEntry,
53
        ValidatorNodeRegistrationInfo,
54
    },
55
};
56
pub type BlockEventSender = broadcast::Sender<Arc<BlockEvent>>;
57
pub type BlockEventReceiver = broadcast::Receiver<Arc<BlockEvent>>;
58

59
/// The InboundNodeCommsInterface provides an interface to request information from the current local node by other
60
/// internal services.
61
#[derive(Clone)]
62
pub struct LocalNodeCommsInterface {
63
    request_sender: SenderService<NodeCommsRequest, Result<NodeCommsResponse, CommsInterfaceError>>,
64
    block_sender: SenderService<Block, Result<BlockHash, CommsInterfaceError>>,
65
    block_event_sender: BlockEventSender,
66
}
67

68
impl LocalNodeCommsInterface {
69
    /// Construct a new LocalNodeCommsInterface with the specified SenderService.
70
    pub fn new(
9✔
71
        request_sender: SenderService<NodeCommsRequest, Result<NodeCommsResponse, CommsInterfaceError>>,
9✔
72
        block_sender: SenderService<Block, Result<BlockHash, CommsInterfaceError>>,
9✔
73
        block_event_sender: BlockEventSender,
9✔
74
    ) -> Self {
9✔
75
        Self {
9✔
76
            request_sender,
9✔
77
            block_sender,
9✔
78
            block_event_sender,
9✔
79
        }
9✔
80
    }
9✔
81

82
    pub fn get_block_event_stream(&self) -> BlockEventReceiver {
1✔
83
        self.block_event_sender.subscribe()
1✔
84
    }
1✔
85

86
    /// Request metadata from the current local node.
87
    pub async fn get_metadata(&mut self) -> Result<ChainMetadata, CommsInterfaceError> {
1✔
88
        match self.request_sender.call(NodeCommsRequest::GetChainMetadata).await?? {
1✔
89
            NodeCommsResponse::ChainMetadata(metadata) => Ok(metadata),
1✔
90
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
91
        }
92
    }
1✔
93

94
    pub async fn get_target_difficulty_for_next_block(
×
95
        &mut self,
×
96
        algo: PowAlgorithm,
×
97
    ) -> Result<Difficulty, CommsInterfaceError> {
×
98
        match self
×
99
            .request_sender
×
100
            .call(NodeCommsRequest::GetTargetDifficultyNextBlock(algo))
×
101
            .await??
×
102
        {
103
            NodeCommsResponse::TargetDifficulty(target_difficulty) => Ok(target_difficulty),
×
104
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
105
        }
106
    }
×
107

108
    /// Request the block headers within the given range
109
    pub async fn get_blocks(
×
110
        &mut self,
×
111
        range: RangeInclusive<u64>,
×
112
        compact: bool,
×
113
    ) -> Result<Vec<HistoricalBlock>, CommsInterfaceError> {
×
114
        match self
×
115
            .request_sender
×
116
            .call(NodeCommsRequest::FetchMatchingBlocks { range, compact })
×
117
            .await??
×
118
        {
119
            NodeCommsResponse::HistoricalBlocks(blocks) => Ok(blocks),
×
120
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
121
        }
122
    }
×
123

124
    /// Request the block header at the given height
125
    pub async fn get_block(
×
126
        &mut self,
×
127
        height: u64,
×
128
        compact: bool,
×
129
    ) -> Result<Option<HistoricalBlock>, CommsInterfaceError> {
×
130
        match self
×
131
            .request_sender
×
132
            .call(NodeCommsRequest::FetchMatchingBlocks {
×
133
                range: height..=height,
×
134
                compact,
×
135
            })
×
136
            .await??
×
137
        {
138
            NodeCommsResponse::HistoricalBlocks(mut blocks) => Ok(blocks.pop()),
×
139
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
140
        }
141
    }
×
142

143
    /// Request the block headers with the given range of heights. The returned headers are ordered from lowest to
144
    /// highest block height
145
    pub async fn get_headers(&mut self, range: RangeInclusive<u64>) -> Result<Vec<ChainHeader>, CommsInterfaceError> {
×
146
        match self
×
147
            .request_sender
×
148
            .call(NodeCommsRequest::FetchHeaders(range))
×
149
            .await??
×
150
        {
151
            NodeCommsResponse::BlockHeaders(headers) => Ok(headers),
×
152
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
153
        }
154
    }
×
155

156
    /// Request the block header with the height.
157
    pub async fn get_header(&mut self, height: u64) -> Result<Option<ChainHeader>, CommsInterfaceError> {
×
158
        match self
×
159
            .request_sender
×
160
            .call(NodeCommsRequest::FetchHeaders(height..=height))
×
161
            .await??
×
162
        {
163
            NodeCommsResponse::BlockHeaders(mut headers) => Ok(headers.pop()),
×
164
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
165
        }
166
    }
×
167

168
    /// Request the construction of a new mineable block template from the base node service.
169
    pub async fn get_new_block_template(
×
170
        &mut self,
×
171
        pow_algorithm: PowAlgorithm,
×
172
        max_weight: u64,
×
173
    ) -> Result<NewBlockTemplate, CommsInterfaceError> {
×
174
        let request = GetNewBlockTemplateRequest {
×
175
            algo: pow_algorithm,
×
176
            max_weight,
×
177
        };
×
178
        match self
×
179
            .request_sender
×
180
            .call(NodeCommsRequest::GetNewBlockTemplate(request))
×
181
            .await??
×
182
        {
183
            NodeCommsResponse::NewBlockTemplate(new_block_template) => Ok(new_block_template),
×
184
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
185
        }
186
    }
×
187

188
    /// Request from base node service the construction of a block from a block template.
189
    pub async fn get_new_block(&mut self, block_template: NewBlockTemplate) -> Result<Block, CommsInterfaceError> {
×
190
        match self
×
191
            .request_sender
×
192
            .call(NodeCommsRequest::GetNewBlock(block_template))
×
193
            .await??
×
194
        {
195
            NodeCommsResponse::NewBlock { success, error, block } => {
×
196
                if success {
×
197
                    if let Some(block) = block {
×
198
                        Ok(block)
×
199
                    } else {
200
                        Err(CommsInterfaceError::UnexpectedApiResponse)
×
201
                    }
202
                } else {
203
                    Err(CommsInterfaceError::ApiError(
×
204
                        error.unwrap_or_else(|| "Unspecified error".to_string()),
×
205
                    ))
×
206
                }
207
            },
208
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
209
        }
210
    }
×
211

212
    /// Submit a block to the base node service.
213
    pub async fn submit_block(&mut self, block: Block) -> Result<BlockHash, CommsInterfaceError> {
×
214
        self.block_sender.call(block).await?
×
215
    }
×
216

217
    pub fn publish_block_event(&self, event: BlockEvent) -> usize {
×
218
        // If event send fails, that means that there are no receivers (i.e. it was sent to zero receivers)
×
219
        self.block_event_sender.send(Arc::new(event)).unwrap_or(0)
×
220
    }
×
221

222
    pub async fn fetch_matching_utxos(
×
223
        &mut self,
×
224
        hashes: Vec<HashOutput>,
×
225
    ) -> Result<Vec<TransactionOutput>, CommsInterfaceError> {
×
226
        match self
×
227
            .request_sender
×
228
            .call(NodeCommsRequest::FetchMatchingUtxos(hashes))
×
229
            .await??
×
230
        {
231
            NodeCommsResponse::TransactionOutputs(outputs) => Ok(outputs),
×
232
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
233
        }
234
    }
×
235

236
    /// Fetches the blocks with the specified utxo commitments
237
    pub async fn fetch_blocks_with_utxos(
×
238
        &mut self,
×
239
        commitments: Vec<CompressedCommitment>,
×
240
    ) -> Result<Vec<HistoricalBlock>, CommsInterfaceError> {
×
241
        match self
×
242
            .request_sender
×
243
            .call(NodeCommsRequest::FetchBlocksByUtxos(commitments))
×
244
            .await??
×
245
        {
246
            NodeCommsResponse::HistoricalBlocks(blocks) => Ok(blocks),
×
247
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
248
        }
249
    }
×
250

251
    /// Fetches the blocks with the specified kernel signatures commitments
252
    pub async fn get_blocks_with_kernels(
×
253
        &mut self,
×
254
        kernels: Vec<CompressedSignature>,
×
255
    ) -> Result<Vec<HistoricalBlock>, CommsInterfaceError> {
×
256
        match self
×
257
            .request_sender
×
258
            .call(NodeCommsRequest::FetchBlocksByKernelExcessSigs(kernels))
×
259
            .await??
×
260
        {
261
            NodeCommsResponse::HistoricalBlocks(blocks) => Ok(blocks),
×
262
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
263
        }
264
    }
×
265

266
    /// Return header matching the given hash. If the header cannot be found `Ok(None)` is returned.
267
    pub async fn get_header_by_hash(&mut self, hash: HashOutput) -> Result<Option<ChainHeader>, CommsInterfaceError> {
×
268
        match self
×
269
            .request_sender
×
270
            .call(NodeCommsRequest::GetHeaderByHash(hash))
×
271
            .await??
×
272
        {
273
            NodeCommsResponse::BlockHeader(header) => Ok(header),
×
274
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
275
        }
276
    }
×
277

278
    /// Return block matching the given hash. If the block cannot be found `Ok(None)` is returned.
279
    pub async fn get_block_by_hash(
×
280
        &mut self,
×
281
        hash: HashOutput,
×
282
    ) -> Result<Option<HistoricalBlock>, CommsInterfaceError> {
×
283
        match self
×
284
            .request_sender
×
285
            .call(NodeCommsRequest::GetBlockByHash(hash))
×
286
            .await??
×
287
        {
288
            NodeCommsResponse::HistoricalBlock(block) => Ok(*block),
×
289
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
290
        }
291
    }
×
292

293
    /// Searches for a kernel via the excess sig
294
    pub async fn get_kernel_by_excess_sig(
×
295
        &mut self,
×
296
        kernel: CompressedSignature,
×
297
    ) -> Result<Vec<TransactionKernel>, CommsInterfaceError> {
×
298
        match self
×
299
            .request_sender
×
300
            .call(NodeCommsRequest::FetchKernelByExcessSig(kernel))
×
301
            .await??
×
302
        {
303
            NodeCommsResponse::TransactionKernels(kernels) => Ok(kernels),
×
304
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
305
        }
306
    }
×
307

308
    pub async fn get_active_validator_nodes(
×
309
        &mut self,
×
310
        height: u64,
×
311
        validator_network: Option<CompressedPublicKey>,
×
312
    ) -> Result<Vec<ValidatorNodeRegistrationInfo>, CommsInterfaceError> {
×
313
        match self
×
314
            .request_sender
×
315
            .call(NodeCommsRequest::FetchValidatorNodesKeys {
×
316
                height,
×
317
                validator_network,
×
318
            })
×
319
            .await??
×
320
        {
321
            NodeCommsResponse::FetchValidatorNodesKeysResponse(validator_node) => Ok(validator_node),
×
322
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
323
        }
324
    }
×
325

326
    pub async fn get_validator_node_changes(
×
327
        &mut self,
×
328
        sidechain_id: Option<CompressedPublicKey>,
×
329
        epoch: VnEpoch,
×
330
    ) -> Result<Vec<ValidatorNodeChange>, CommsInterfaceError> {
×
331
        match self
×
332
            .request_sender
×
333
            .call(NodeCommsRequest::FetchValidatorNodeChanges { epoch, sidechain_id })
×
334
            .await??
×
335
        {
336
            NodeCommsResponse::FetchValidatorNodeChangesResponse(validator_node_change) => Ok(validator_node_change),
×
337
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
338
        }
339
    }
×
340

341
    pub async fn get_validator_node(
×
342
        &mut self,
×
343
        sidechain_id: Option<CompressedPublicKey>,
×
344
        public_key: CompressedPublicKey,
×
345
    ) -> Result<Option<ValidatorNodeRegistrationInfo>, CommsInterfaceError> {
×
346
        match self
×
347
            .request_sender
×
348
            .call(NodeCommsRequest::GetValidatorNode {
×
349
                sidechain_id,
×
350
                public_key,
×
351
            })
×
352
            .await??
×
353
        {
354
            NodeCommsResponse::GetValidatorNode(vn) => Ok(vn),
×
355
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
356
        }
357
    }
×
358

359
    pub async fn get_template_registrations(
×
360
        &mut self,
×
361
        start_height: u64,
×
362
        end_height: u64,
×
363
    ) -> Result<Vec<TemplateRegistrationEntry>, CommsInterfaceError> {
×
364
        match self
×
365
            .request_sender
×
366
            .call(NodeCommsRequest::FetchTemplateRegistrations {
×
367
                start_height,
×
368
                end_height,
×
369
            })
×
370
            .await??
×
371
        {
372
            NodeCommsResponse::FetchTemplateRegistrationsResponse(template_registrations) => Ok(template_registrations),
×
373
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
374
        }
375
    }
×
376

377
    /// Fetches UTXOs that are not spent for the given block hash up to the current chain tip.
378
    pub async fn fetch_unspent_utxos_in_block(
×
379
        &mut self,
×
380
        block_hash: BlockHash,
×
381
    ) -> Result<Vec<TransactionOutput>, CommsInterfaceError> {
×
382
        match self
×
383
            .request_sender
×
384
            .call(NodeCommsRequest::FetchUnspentUtxosInBlock { block_hash })
×
385
            .await??
×
386
        {
387
            NodeCommsResponse::TransactionOutputs(outputs) => Ok(outputs),
×
388
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
389
        }
390
    }
×
391

392
    /// Fetch mined info by PayRef (Payment Reference)
393
    pub async fn fetch_mined_info_by_payref(&mut self, payref: &FixedHash) -> Result<MinedInfo, CommsInterfaceError> {
×
394
        match self
×
395
            .request_sender
×
396
            .call(NodeCommsRequest::FetchMinedInfoByPayRef(*payref))
×
397
            .await??
×
398
        {
399
            NodeCommsResponse::MinedInfo(mined_info) => Ok(mined_info),
×
400
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
401
        }
402
    }
×
403

404
    /// Fetch mined info by output hash
405
    pub async fn fetch_mined_info_by_output_hash(
×
406
        &mut self,
×
407
        output_hash: &HashOutput,
×
408
    ) -> Result<MinedInfo, CommsInterfaceError> {
×
409
        match self
×
410
            .request_sender
×
411
            .call(NodeCommsRequest::FetchMinedInfoByOutputHash(*output_hash))
×
412
            .await??
×
413
        {
414
            NodeCommsResponse::MinedInfo(mined_info) => Ok(mined_info),
×
415
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
416
        }
417
    }
×
418

419
    /// Fetch output mined info by output hash
420
    pub async fn fetch_output_mined_info(
×
421
        &mut self,
×
422
        output_hash: &HashOutput,
×
423
    ) -> Result<Option<OutputMinedInfo>, CommsInterfaceError> {
×
424
        match self
×
425
            .request_sender
×
426
            .call(NodeCommsRequest::FetchOutputMinedInfo(*output_hash))
×
427
            .await??
×
428
        {
429
            NodeCommsResponse::OutputMinedInfo(output_info) => Ok(output_info),
×
430
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
431
        }
432
    }
×
433

434
    /// Check if an output is spent and return spent information
435
    pub async fn check_output_spent_status(
×
436
        &mut self,
×
437
        output_hash: HashOutput,
×
438
    ) -> Result<Option<InputMinedInfo>, CommsInterfaceError> {
×
439
        match self
×
440
            .request_sender
×
441
            .call(NodeCommsRequest::CheckOutputSpentStatus(output_hash))
×
442
            .await??
×
443
        {
444
            NodeCommsResponse::InputMinedInfo(input_info) => Ok(input_info),
×
445
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
446
        }
447
    }
×
448
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc