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

Neptune-Crypto / neptune-core / 16544362308

26 Jul 2025 10:07PM UTC coverage: 74.138% (+1.7%) from 72.422%
16544362308

push

github

Sword-Smith
Merge branch 'reboot'

3111 of 3416 new or added lines in 72 files covered. (91.07%)

28 existing lines in 14 files now uncovered.

22913 of 30906 relevant lines covered (74.14%)

618079.03 hits per line

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

71.03
/src/api/regtest/regtest_impl.rs
1
// private module.  no need for module docs.
2

3
use tasm_lib::prelude::Digest;
4

5
use super::error::RegTestError;
6
use crate::api::export::Timestamp;
7
use crate::models::blockchain::block::mock_block_generator::MockBlockGenerator;
8
use crate::models::shared::SIZE_20MB_IN_BYTES;
9
use crate::GlobalStateLock;
10
use crate::RPCServerToMain;
11

12
/// provides an API for interacting with regtest mode
13
#[derive(Debug)]
14
pub struct RegTest {
15
    worker: RegTestPrivate,
16
}
17

18
impl From<GlobalStateLock> for RegTest {
19
    fn from(gsl: GlobalStateLock) -> Self {
15✔
20
        Self {
15✔
21
            worker: RegTestPrivate::new(gsl),
15✔
22
        }
15✔
23
    }
15✔
24
}
25

26
// these methods just call a worker method, so the public API
27
// is easy to read and digest.  Please keep it that way.
28
//
29
// future methods are planned. see:
30
//
31
// https://github.com/Neptune-Crypto/neptune-core/issues/539
32
//
33
// they will provide functionality similar to bitcoin-core, eg:
34
//
35
// generatetoaddress: This RPC creates a specified number of blocks and sends the block rewards to a provided address, enabling rapid chain advancement for testing.
36
// generate: This RPC mines a specified number of blocks, but offers less control over the recipient address compared to generatetoaddress.
37
// generateblock: This RPC mines a block and allows the caller to specify the block template.
38
// setmocktime: This RPC allows manual manipulation of the blockchain's apparent timestamp, facilitating testing of time-sensitive consensus rules.
39
// invalidateblock: This RPC removes a block from the current best chain, enabling the simulation of blockchain reorganizations.
40
// reconsiderblock: This RPC reconsiders whether a block should be part of the best chain, often used in conjunction with invalidateblock to test chain selection logic.
41
//
42
impl RegTest {
43
    /// mine a series of blocks to the node's wallet. (regtest network only)
44
    ///
45
    /// These blocks can be generated quickly because they do not have
46
    /// a real ZK proof.  they have a mock "proof" that is simply trusted
47
    /// by recipients and will validate without error.
48
    ///
49
    /// Mock proofs are allowed only on the regtest network, for development purposes.
50
    ///
51
    /// The timestamp of each block will be the current system time, meaning
52
    /// that they will be temporally very close to eachother.
UNCOV
53
    pub async fn mine_blocks_to_wallet(
×
UNCOV
54
        &mut self,
×
UNCOV
55
        n_blocks: u32,
×
UNCOV
56
        mine_mempool_txs: bool,
×
57
    ) -> Result<(), RegTestError> {
15✔
58
        self.worker
15✔
59
            .mine_blocks_to_wallet(n_blocks, mine_mempool_txs)
15✔
60
            .await
15✔
61
    }
15✔
62

63
    /// mine a single block to the node's wallet with a custom timestamp
64
    ///
65
    /// note: the timestamp must be within the allowed range for new blocks
66
    /// as compared to the current tip block.
67
    ///
68
    /// These blocks can be generated quickly because they do not have
69
    /// a real ZK proof.  they have a mock "proof" that is simply trusted
70
    /// by recipients and will validate without error.
71
    ///
72
    /// Mock proofs are allowed only on the regtest network, for development purposes.
73
    pub async fn mine_block_to_wallet(
×
74
        &mut self,
×
75
        timestamp: Timestamp,
×
76
        mine_mempool_sp_txs: bool,
×
77
    ) -> Result<Digest, RegTestError> {
×
78
        self.worker
×
79
            .mine_block_to_wallet(timestamp, mine_mempool_sp_txs)
×
80
            .await
×
81
    }
×
82
}
83

84
#[derive(Debug)]
85
struct RegTestPrivate {
86
    global_state_lock: GlobalStateLock,
87
}
88

89
impl RegTestPrivate {
90
    fn new(global_state_lock: GlobalStateLock) -> Self {
15✔
91
        Self { global_state_lock }
15✔
92
    }
15✔
93

94
    // see description in [RegTest]
95
    async fn mine_blocks_to_wallet(
15✔
96
        &mut self,
15✔
97
        n_blocks: u32,
15✔
98
        mine_mempool_sp_txs: bool,
15✔
99
    ) -> Result<(), RegTestError> {
15✔
100
        for _ in 0..n_blocks {
15✔
101
            self.mine_block_to_wallet(Timestamp::now(), mine_mempool_sp_txs)
28✔
102
                .await?;
28✔
103
        }
104
        Ok(())
15✔
105
    }
15✔
106

107
    // see description in [RegTest]
108
    async fn mine_block_to_wallet(
28✔
109
        &mut self,
28✔
110
        timestamp: Timestamp,
28✔
111
        include_mempool_txs: bool,
28✔
112
    ) -> Result<Digest, RegTestError> {
28✔
113
        let gsl = &mut self.global_state_lock;
28✔
114

115
        if !gsl.cli().network.use_mock_proof() {
28✔
116
            return Err(RegTestError::WrongNetwork);
×
117
        }
28✔
118

119
        let gs = gsl.lock_guard().await;
28✔
120

121
        let tip_block = gs.chain.light_state_clone();
28✔
122

123
        let next_block_height = tip_block.header().height + 1;
28✔
124
        let guesser_fraction = 0.5;
28✔
125
        let fee_notification_policy = Default::default();
28✔
126
        let composer_parameters = gs.wallet_state.composer_parameters(
28✔
127
            next_block_height,
28✔
128
            guesser_fraction,
28✔
129
            fee_notification_policy,
28✔
130
        );
131

132
        let guesser_key = gs.wallet_state.wallet_entropy.guesser_fee_key();
28✔
133

134
        // retrieve selected tx from mempool for block inclusion.
135
        let txs_from_mempool = if include_mempool_txs {
28✔
136
            gs.mempool.get_transactions_for_block_composition(
4✔
137
                SIZE_20MB_IN_BYTES,
138
                Some(gsl.cli().max_num_compose_mergers.get()),
4✔
139
            )
140
        } else {
141
            vec![]
24✔
142
        };
143

144
        drop(gs);
28✔
145

146
        let (block, composer_tx_outputs) = MockBlockGenerator::mock_successor_with_pow(
28✔
147
            tip_block,
28✔
148
            composer_parameters.clone(),
28✔
149
            guesser_key.to_address().into(),
28✔
150
            timestamp,
28✔
151
            rand::random(), // seed.
28✔
152
            txs_from_mempool,
28✔
153
            gsl.cli().network,
28✔
154
        );
28✔
155

156
        // obtain utxos destined for our wallet from composer rewards.
157
        let expected_utxos = composer_parameters.extract_expected_utxos(composer_tx_outputs);
28✔
158

159
        // note: guesser utxos will be found by
160
        // WalletState::update_wallet_state_with_new_block() inside this call.
161
        //
162
        // gsl.set_new_self_composed_tip(block.clone(), expected_utxos)
163
        //     .await?;
164
        gsl.lock_guard_mut()
28✔
165
            .await
28✔
166
            .wallet_state
167
            .add_expected_utxos(expected_utxos)
28✔
168
            .await;
28✔
169

170
        let block_hash = block.hash();
28✔
171

172
        // inform main-loop.  to add to mempool and broadcast.
173
        //
174
        // todo: ideally we would pass a listener here to wait on, so that
175
        // once the block is added we get notified, rather than polling.
176
        gsl.rpc_server_to_main_tx()
28✔
177
            .send(RPCServerToMain::ProofOfWorkSolution(Box::new(block)))
28✔
178
            .await
28✔
179
            .map_err(|_| {
28✔
180
                tracing::warn!("channel send failed. channel 'rpc_server_to_main' closed unexpectedly. main_loop may have terminated prematurely.");
×
181
                RegTestError::Failed("internal error. block not added to blockchain".into())
×
182
            })?;
×
183

184
        // wait until the main-loop has actually added the block to the canonical chain
185
        // or 5 second timeout happens.
186
        //
187
        // otherwise, wallet balance might not (yet) see coinbase funds, etc.
188
        //
189
        // note: temporary until listener approach is implemented.
190
        Self::wait_until_block_in_chain(&self.global_state_lock, block_hash).await?;
28✔
191

192
        Ok(block_hash)
28✔
193
    }
28✔
194

195
    // waits (polls) until block is found in canonical chain or 5 second timeout occurs.
196
    //
197
    // note: temporary until listener approach is implemented.
198
    async fn wait_until_block_in_chain(
×
199
        gsl: &GlobalStateLock,
×
200
        block_hash: Digest,
×
201
    ) -> Result<(), RegTestError> {
28✔
202
        let start = std::time::Instant::now();
28✔
203
        while gsl.lock_guard().await.chain.light_state().hash() != block_hash {
49✔
204
            if start.elapsed() > std::time::Duration::from_secs(5) {
21✔
205
                // last chance.  maybe another block buried ours.  we will do an expensive check.
206
                if gsl
×
207
                    .lock_guard()
×
208
                    .await
×
209
                    .chain
210
                    .archival_state()
×
211
                    .block_belongs_to_canonical_chain(block_hash)
×
212
                    .await
×
213
                {
214
                    return Ok(());
×
215
                }
×
216
                return Err(RegTestError::Failed(
×
217
                    "block not in blockchain after 5 seconds".into(),
×
218
                ));
×
219
            }
21✔
220
            tokio::time::sleep(std::time::Duration::from_millis(10)).await;
21✔
221
        }
222
        Ok(())
28✔
223
    }
28✔
224
}
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