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

Neptune-Crypto / neptune-core / 16021970764

02 Jul 2025 09:54AM UTC coverage: 72.481% (+0.1%) from 72.358%
16021970764

push

github

Sword-Smith
chore!: Upgrade dependency “Triton VM”

20678 of 28529 relevant lines covered (72.48%)

491826.19 hits per line

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

74.55
/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.
53
    pub async fn mine_blocks_to_wallet(
15✔
54
        &mut self,
15✔
55
        n_blocks: u32,
15✔
56
        mine_mempool_txs: bool,
15✔
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
28✔
133
            .wallet_state
28✔
134
            .wallet_entropy
28✔
135
            .guesser_spending_key(tip_block.hash());
28✔
136

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

147
        drop(gs);
28✔
148

149
        let (block, composer_tx_outputs) = MockBlockGenerator::mock_successor_with_pow(
28✔
150
            tip_block,
28✔
151
            composer_parameters.clone(),
28✔
152
            guesser_key,
28✔
153
            timestamp,
28✔
154
            rand::random(), // seed.
28✔
155
            txs_from_mempool,
28✔
156
            gsl.cli().network,
28✔
157
        )?;
×
158

159
        // obtain utxos destined for our wallet from composer rewards.
160
        let expected_utxos = composer_parameters.extract_expected_utxos(composer_tx_outputs);
28✔
161

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

173
        let block_hash = block.hash();
28✔
174

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

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

195
        Ok(block_hash)
28✔
196
    }
28✔
197

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