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

tari-project / tari / 15582316596

11 Jun 2025 10:21AM UTC coverage: 72.172% (-0.1%) from 72.299%
15582316596

push

github

web-flow
chore: new release v4.4.0-pre.0 (#7202)

Description
---
new release for esmeralda

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Documentation**
- Updated README and changelogs to reflect new recommended and released
versions for all networks, highlighting new features and bug fixes in
version 4.4.0.
- **Chores**
- Bumped version numbers across multiple packages and configuration
files from 4.3.1-pre.0 to 4.4.0-pre.0.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

81639 of 113117 relevant lines covered (72.17%)

241334.47 hits per line

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

18.78
/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
    types::{BlockHash, CompressedCommitment, CompressedPublicKey, FixedHash, HashOutput, Signature},
28
};
29
use tari_service_framework::{reply_channel::SenderService, Service};
30
use tokio::sync::broadcast;
31

32
use crate::{
33
    base_node::comms_interface::{
34
        comms_request::GetNewBlockTemplateRequest,
35
        error::CommsInterfaceError,
36
        BlockEvent,
37
        NodeCommsRequest,
38
        NodeCommsResponse,
39
    },
40
    blocks::{Block, ChainHeader, HistoricalBlock, NewBlockTemplate},
41
    chain_storage::{InputMinedInfo, OutputMinedInfo, TemplateRegistrationEntry},
42
    proof_of_work::{Difficulty, PowAlgorithm},
43
    transactions::transaction_components::{TransactionKernel, TransactionOutput},
44
};
45

46
pub type BlockEventSender = broadcast::Sender<Arc<BlockEvent>>;
47
pub type BlockEventReceiver = broadcast::Receiver<Arc<BlockEvent>>;
48

49
/// The InboundNodeCommsInterface provides an interface to request information from the current local node by other
50
/// internal services.
51
#[derive(Clone)]
52
pub struct LocalNodeCommsInterface {
53
    request_sender: SenderService<NodeCommsRequest, Result<NodeCommsResponse, CommsInterfaceError>>,
54
    block_sender: SenderService<Block, Result<BlockHash, CommsInterfaceError>>,
55
    block_event_sender: BlockEventSender,
56
}
57

58
impl LocalNodeCommsInterface {
59
    /// Construct a new LocalNodeCommsInterface with the specified SenderService.
60
    pub fn new(
62✔
61
        request_sender: SenderService<NodeCommsRequest, Result<NodeCommsResponse, CommsInterfaceError>>,
62✔
62
        block_sender: SenderService<Block, Result<BlockHash, CommsInterfaceError>>,
62✔
63
        block_event_sender: BlockEventSender,
62✔
64
    ) -> Self {
62✔
65
        Self {
62✔
66
            request_sender,
62✔
67
            block_sender,
62✔
68
            block_event_sender,
62✔
69
        }
62✔
70
    }
62✔
71

72
    pub fn get_block_event_stream(&self) -> BlockEventReceiver {
107✔
73
        self.block_event_sender.subscribe()
107✔
74
    }
107✔
75

76
    /// Request metadata from the current local node.
77
    pub async fn get_metadata(&mut self) -> Result<ChainMetadata, CommsInterfaceError> {
82✔
78
        match self.request_sender.call(NodeCommsRequest::GetChainMetadata).await?? {
82✔
79
            NodeCommsResponse::ChainMetadata(metadata) => Ok(metadata),
82✔
80
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
81
        }
82
    }
82✔
83

84
    pub async fn get_target_difficulty_for_next_block(
×
85
        &mut self,
×
86
        algo: PowAlgorithm,
×
87
    ) -> Result<Difficulty, CommsInterfaceError> {
×
88
        match self
×
89
            .request_sender
×
90
            .call(NodeCommsRequest::GetTargetDifficultyNextBlock(algo))
×
91
            .await??
×
92
        {
93
            NodeCommsResponse::TargetDifficulty(target_difficulty) => Ok(target_difficulty),
×
94
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
95
        }
96
    }
×
97

98
    /// Request the block headers within the given range
99
    pub async fn get_blocks(
×
100
        &mut self,
×
101
        range: RangeInclusive<u64>,
×
102
        compact: bool,
×
103
    ) -> Result<Vec<HistoricalBlock>, CommsInterfaceError> {
×
104
        match self
×
105
            .request_sender
×
106
            .call(NodeCommsRequest::FetchMatchingBlocks { range, compact })
×
107
            .await??
×
108
        {
109
            NodeCommsResponse::HistoricalBlocks(blocks) => Ok(blocks),
×
110
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
111
        }
112
    }
×
113

114
    /// Request the block header at the given height
115
    pub async fn get_block(
×
116
        &mut self,
×
117
        height: u64,
×
118
        compact: bool,
×
119
    ) -> Result<Option<HistoricalBlock>, CommsInterfaceError> {
×
120
        match self
×
121
            .request_sender
×
122
            .call(NodeCommsRequest::FetchMatchingBlocks {
×
123
                range: height..=height,
×
124
                compact,
×
125
            })
×
126
            .await??
×
127
        {
128
            NodeCommsResponse::HistoricalBlocks(mut blocks) => Ok(blocks.pop()),
×
129
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
130
        }
131
    }
×
132

133
    /// Request the block headers with the given range of heights. The returned headers are ordered from lowest to
134
    /// highest block height
135
    pub async fn get_headers(&mut self, range: RangeInclusive<u64>) -> Result<Vec<ChainHeader>, CommsInterfaceError> {
×
136
        match self
×
137
            .request_sender
×
138
            .call(NodeCommsRequest::FetchHeaders(range))
×
139
            .await??
×
140
        {
141
            NodeCommsResponse::BlockHeaders(headers) => Ok(headers),
×
142
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
143
        }
144
    }
×
145

146
    /// Request the block header with the height.
147
    pub async fn get_header(&mut self, height: u64) -> Result<Option<ChainHeader>, CommsInterfaceError> {
×
148
        match self
×
149
            .request_sender
×
150
            .call(NodeCommsRequest::FetchHeaders(height..=height))
×
151
            .await??
×
152
        {
153
            NodeCommsResponse::BlockHeaders(mut headers) => Ok(headers.pop()),
×
154
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
155
        }
156
    }
×
157

158
    /// Request the construction of a new mineable block template from the base node service.
159
    pub async fn get_new_block_template(
3✔
160
        &mut self,
3✔
161
        pow_algorithm: PowAlgorithm,
3✔
162
        max_weight: u64,
3✔
163
    ) -> Result<NewBlockTemplate, CommsInterfaceError> {
3✔
164
        let request = GetNewBlockTemplateRequest {
3✔
165
            algo: pow_algorithm,
3✔
166
            max_weight,
3✔
167
        };
3✔
168
        match self
3✔
169
            .request_sender
3✔
170
            .call(NodeCommsRequest::GetNewBlockTemplate(request))
3✔
171
            .await??
3✔
172
        {
173
            NodeCommsResponse::NewBlockTemplate(new_block_template) => Ok(new_block_template),
3✔
174
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
175
        }
176
    }
3✔
177

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

202
    /// Submit a block to the base node service.
203
    pub async fn submit_block(&mut self, block: Block) -> Result<BlockHash, CommsInterfaceError> {
12✔
204
        self.block_sender.call(block).await?
12✔
205
    }
12✔
206

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

212
    pub async fn fetch_matching_utxos(
×
213
        &mut self,
×
214
        hashes: Vec<HashOutput>,
×
215
    ) -> Result<Vec<TransactionOutput>, CommsInterfaceError> {
×
216
        match self
×
217
            .request_sender
×
218
            .call(NodeCommsRequest::FetchMatchingUtxos(hashes))
×
219
            .await??
×
220
        {
221
            NodeCommsResponse::TransactionOutputs(outputs) => Ok(outputs),
×
222
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
223
        }
224
    }
×
225

226
    /// Fetches the blocks with the specified utxo commitments
227
    pub async fn fetch_blocks_with_utxos(
×
228
        &mut self,
×
229
        commitments: Vec<CompressedCommitment>,
×
230
    ) -> Result<Vec<HistoricalBlock>, CommsInterfaceError> {
×
231
        match self
×
232
            .request_sender
×
233
            .call(NodeCommsRequest::FetchBlocksByUtxos(commitments))
×
234
            .await??
×
235
        {
236
            NodeCommsResponse::HistoricalBlocks(blocks) => Ok(blocks),
×
237
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
238
        }
239
    }
×
240

241
    /// Fetches the blocks with the specified kernel signatures commitments
242
    pub async fn get_blocks_with_kernels(
×
243
        &mut self,
×
244
        kernels: Vec<Signature>,
×
245
    ) -> Result<Vec<HistoricalBlock>, CommsInterfaceError> {
×
246
        match self
×
247
            .request_sender
×
248
            .call(NodeCommsRequest::FetchBlocksByKernelExcessSigs(kernels))
×
249
            .await??
×
250
        {
251
            NodeCommsResponse::HistoricalBlocks(blocks) => Ok(blocks),
×
252
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
253
        }
254
    }
×
255

256
    /// Return header matching the given hash. If the header cannot be found `Ok(None)` is returned.
257
    pub async fn get_header_by_hash(&mut self, hash: HashOutput) -> Result<Option<ChainHeader>, CommsInterfaceError> {
×
258
        match self
×
259
            .request_sender
×
260
            .call(NodeCommsRequest::GetHeaderByHash(hash))
×
261
            .await??
×
262
        {
263
            NodeCommsResponse::BlockHeader(header) => Ok(header),
×
264
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
265
        }
266
    }
×
267

268
    /// Return block matching the given hash. If the block cannot be found `Ok(None)` is returned.
269
    pub async fn get_block_by_hash(
×
270
        &mut self,
×
271
        hash: HashOutput,
×
272
    ) -> Result<Option<HistoricalBlock>, CommsInterfaceError> {
×
273
        match self
×
274
            .request_sender
×
275
            .call(NodeCommsRequest::GetBlockByHash(hash))
×
276
            .await??
×
277
        {
278
            NodeCommsResponse::HistoricalBlock(block) => Ok(*block),
×
279
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
280
        }
281
    }
×
282

283
    /// Searches for a kernel via the excess sig
284
    pub async fn get_kernel_by_excess_sig(
×
285
        &mut self,
×
286
        kernel: Signature,
×
287
    ) -> Result<Vec<TransactionKernel>, CommsInterfaceError> {
×
288
        match self
×
289
            .request_sender
×
290
            .call(NodeCommsRequest::FetchKernelByExcessSig(kernel))
×
291
            .await??
×
292
        {
293
            NodeCommsResponse::TransactionKernels(kernels) => Ok(kernels),
×
294
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
295
        }
296
    }
×
297

298
    pub async fn get_active_validator_nodes(
×
299
        &mut self,
×
300
        height: u64,
×
301
    ) -> Result<Vec<(CompressedPublicKey, [u8; 32])>, CommsInterfaceError> {
×
302
        match self
×
303
            .request_sender
×
304
            .call(NodeCommsRequest::FetchValidatorNodesKeys { height })
×
305
            .await??
×
306
        {
307
            NodeCommsResponse::FetchValidatorNodesKeysResponse(validator_node) => Ok(validator_node),
×
308
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
309
        }
310
    }
×
311

312
    pub async fn get_shard_key(
×
313
        &mut self,
×
314
        height: u64,
×
315
        public_key: CompressedPublicKey,
×
316
    ) -> Result<Option<[u8; 32]>, CommsInterfaceError> {
×
317
        match self
×
318
            .request_sender
×
319
            .call(NodeCommsRequest::GetShardKey { height, public_key })
×
320
            .await??
×
321
        {
322
            NodeCommsResponse::GetShardKeyResponse(shard_key) => Ok(shard_key),
×
323
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
324
        }
325
    }
×
326

327
    pub async fn get_template_registrations(
×
328
        &mut self,
×
329
        start_height: u64,
×
330
        end_height: u64,
×
331
    ) -> Result<Vec<TemplateRegistrationEntry>, CommsInterfaceError> {
×
332
        match self
×
333
            .request_sender
×
334
            .call(NodeCommsRequest::FetchTemplateRegistrations {
×
335
                start_height,
×
336
                end_height,
×
337
            })
×
338
            .await??
×
339
        {
340
            NodeCommsResponse::FetchTemplateRegistrationsResponse(template_registrations) => Ok(template_registrations),
×
341
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
342
        }
343
    }
×
344

345
    /// Fetches UTXOs that are not spent for the given block hash up to the current chain tip.
346
    pub async fn fetch_unspent_utxos_in_block(
×
347
        &mut self,
×
348
        block_hash: BlockHash,
×
349
    ) -> Result<Vec<TransactionOutput>, CommsInterfaceError> {
×
350
        match self
×
351
            .request_sender
×
352
            .call(NodeCommsRequest::FetchUnspentUtxosInBlock { block_hash })
×
353
            .await??
×
354
        {
355
            NodeCommsResponse::TransactionOutputs(outputs) => Ok(outputs),
×
356
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
357
        }
358
    }
×
359

360
    /// Fetch output by PayRef (Payment Reference)
361
    pub async fn fetch_output_by_payref(
×
362
        &mut self,
×
363
        payref: &FixedHash,
×
364
    ) -> Result<Option<OutputMinedInfo>, CommsInterfaceError> {
×
365
        match self
×
366
            .request_sender
×
367
            .call(NodeCommsRequest::FetchOutputByPayRef(*payref))
×
368
            .await??
×
369
        {
370
            NodeCommsResponse::OutputMinedInfo(output_info) => Ok(output_info),
×
371
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
372
        }
373
    }
×
374

375
    /// Check if an output is spent and return spent information
376
    pub async fn check_output_spent_status(
×
377
        &mut self,
×
378
        output_hash: HashOutput,
×
379
    ) -> Result<Option<InputMinedInfo>, CommsInterfaceError> {
×
380
        match self
×
381
            .request_sender
×
382
            .call(NodeCommsRequest::CheckOutputSpentStatus(output_hash))
×
383
            .await??
×
384
        {
385
            NodeCommsResponse::InputMinedInfo(input_info) => Ok(input_info),
×
386
            _ => Err(CommsInterfaceError::UnexpectedApiResponse),
×
387
        }
388
    }
×
389
}
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