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

tari-project / tari / 16123384529

07 Jul 2025 05:11PM UTC coverage: 64.327% (-7.6%) from 71.89%
16123384529

push

github

web-flow
chore: new release v4.9.0-pre.0 (#7289)

Description
---
new release esmeralda

77151 of 119935 relevant lines covered (64.33%)

227108.34 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, FixedHash, HashOutput, Signature},
29
};
30
use tari_service_framework::{reply_channel::SenderService, Service};
31
use tokio::sync::broadcast;
32

33
use crate::{
34
    base_node::comms_interface::{
35
        comms_request::GetNewBlockTemplateRequest,
36
        comms_response::ValidatorNodeChange,
37
        error::CommsInterfaceError,
38
        BlockEvent,
39
        NodeCommsRequest,
40
        NodeCommsResponse,
41
    },
42
    blocks::{Block, ChainHeader, HistoricalBlock, NewBlockTemplate},
43
    chain_storage::{
44
        InputMinedInfo,
45
        MinedInfo,
46
        OutputMinedInfo,
47
        TemplateRegistrationEntry,
48
        ValidatorNodeRegistrationInfo,
49
    },
50
    proof_of_work::{Difficulty, PowAlgorithm},
51
    transactions::transaction_components::{TransactionKernel, TransactionOutput},
52
};
53

54
pub type BlockEventSender = broadcast::Sender<Arc<BlockEvent>>;
55
pub type BlockEventReceiver = broadcast::Receiver<Arc<BlockEvent>>;
56

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

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

80
    pub fn get_block_event_stream(&self) -> BlockEventReceiver {
3✔
81
        self.block_event_sender.subscribe()
3✔
82
    }
3✔
83

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

432
    /// Check if an output is spent and return spent information
433
    pub async fn check_output_spent_status(
×
434
        &mut self,
×
435
        output_hash: HashOutput,
×
436
    ) -> Result<Option<InputMinedInfo>, CommsInterfaceError> {
×
437
        match self
×
438
            .request_sender
×
439
            .call(NodeCommsRequest::CheckOutputSpentStatus(output_hash))
×
440
            .await??
×
441
        {
442
            NodeCommsResponse::InputMinedInfo(input_info) => Ok(input_info),
×
443
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
444
        }
445
    }
×
446
}
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