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

Neptune-Crypto / neptune-core / 14743851146

30 Apr 2025 12:15AM UTC coverage: 74.966% (-4.7%) from 79.634%
14743851146

Pull #573

github

web-flow
Merge 66b171787 into 0112461e0
Pull Request #573: docs: document release candidate testnet policy

26065 of 34769 relevant lines covered (74.97%)

295956.79 hits per line

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

77.68
/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::MAX_NUM_TXS_TO_MERGE;
9
use crate::models::shared::SIZE_20MB_IN_BYTES;
10
use crate::GlobalStateLock;
11
use crate::RPCServerToMain;
12

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

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

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

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

76
#[derive(Debug)]
77
struct RegTestPrivate {
78
    global_state_lock: GlobalStateLock,
79
}
80

81
impl RegTestPrivate {
82
    fn new(global_state_lock: GlobalStateLock) -> Self {
9✔
83
        Self { global_state_lock }
9✔
84
    }
9✔
85

86
    // see description in [RegTest]
87
    async fn mine_blocks_to_wallet(&mut self, n_blocks: u32) -> Result<(), RegTestError> {
9✔
88
        for _ in 0..n_blocks {
9✔
89
            self.mine_block_to_wallet(Timestamp::now()).await?;
19✔
90
        }
91
        Ok(())
9✔
92
    }
9✔
93

94
    // see description in [RegTest]
95
    async fn mine_block_to_wallet(&mut self, timestamp: Timestamp) -> Result<Digest, RegTestError> {
19✔
96
        let gsl = &mut self.global_state_lock;
19✔
97

19✔
98
        if !gsl.cli().network.use_mock_proof() {
19✔
99
            return Err(RegTestError::WrongNetwork);
×
100
        }
19✔
101

102
        let gs = gsl.lock_guard().await;
19✔
103

104
        let tip_block = gs.chain.light_state_clone();
19✔
105

19✔
106
        let next_block_height = tip_block.header().height + 1;
19✔
107
        let guesser_fraction = 0.5;
19✔
108
        let fee_notification_policy = Default::default();
19✔
109
        let composer_parameters = gs.wallet_state.composer_parameters(
19✔
110
            next_block_height,
19✔
111
            guesser_fraction,
19✔
112
            fee_notification_policy,
19✔
113
        );
19✔
114

19✔
115
        let guesser_key = gs
19✔
116
            .wallet_state
19✔
117
            .wallet_entropy
19✔
118
            .guesser_spending_key(tip_block.hash());
19✔
119

19✔
120
        // retrieve selected tx from mempool for block inclusion.
19✔
121
        let txs_from_mempool = gs.mempool.get_transactions_for_block(
19✔
122
            SIZE_20MB_IN_BYTES,
19✔
123
            Some(MAX_NUM_TXS_TO_MERGE),
19✔
124
            true, //only_merge_single_proofs
19✔
125
            tip_block.mutator_set_accumulator_after().hash(),
19✔
126
        );
19✔
127

19✔
128
        drop(gs);
19✔
129

130
        let (block, composer_tx_outputs) = MockBlockGenerator::mock_successor_with_pow(
19✔
131
            tip_block,
19✔
132
            composer_parameters.clone(),
19✔
133
            guesser_key,
19✔
134
            timestamp,
19✔
135
            rand::random(), // seed.
19✔
136
            txs_from_mempool,
19✔
137
            gsl.cli().network,
19✔
138
        )?;
×
139

140
        // obtain utxos destined for our wallet from composer rewards.
141
        let expected_utxos = composer_parameters.extract_expected_utxos(composer_tx_outputs);
19✔
142

19✔
143
        // note: guesser utxos will be found by
19✔
144
        // WalletState::update_wallet_state_with_new_block() inside this call.
19✔
145
        //
19✔
146
        // gsl.set_new_self_composed_tip(block.clone(), expected_utxos)
19✔
147
        //     .await?;
19✔
148
        gsl.lock_guard_mut()
19✔
149
            .await
19✔
150
            .wallet_state
151
            .add_expected_utxos(expected_utxos)
19✔
152
            .await;
19✔
153

154
        let block_hash = block.hash();
19✔
155

19✔
156
        // inform main-loop.  to add to mempool and broadcast.
19✔
157
        //
19✔
158
        // todo: ideally we would pass a listener here to wait on, so that
19✔
159
        // once the block is added we get notified, rather than polling.
19✔
160
        gsl.rpc_server_to_main_tx()
19✔
161
            .send(RPCServerToMain::ProofOfWorkSolution(Box::new(block)))
19✔
162
            .await
19✔
163
            .map_err(|_| {
19✔
164
                tracing::warn!("channel send failed. channel 'rpc_server_to_main' closed unexpectedly. main_loop may have terminated prematurely.");
×
165
                RegTestError::Failed("internal error. block not added to blockchain".into())
×
166
            })?;
×
167

168
        // wait until the main-loop has actually added the block to the canonical chain
169
        // or 5 second timeout happens.
170
        //
171
        // otherwise, wallet balance might not (yet) see coinbase funds, etc.
172
        //
173
        // note: temporary until listener approach is implemented.
174
        Self::wait_until_block_in_chain(&self.global_state_lock, block_hash).await?;
19✔
175

176
        Ok(block_hash)
19✔
177
    }
19✔
178

179
    // waits (polls) until block is found in canonical chain or 5 second timeout occurs.
180
    //
181
    // note: temporary until listener approach is implemented.
182
    async fn wait_until_block_in_chain(
×
183
        gsl: &GlobalStateLock,
×
184
        block_hash: Digest,
×
185
    ) -> Result<(), RegTestError> {
19✔
186
        let start = std::time::Instant::now();
19✔
187
        while gsl.lock_guard().await.chain.light_state().hash() != block_hash {
33✔
188
            if start.elapsed() > std::time::Duration::from_secs(5) {
14✔
189
                // last chance.  maybe another block buried ours.  we will do an expensive check.
190
                if gsl
×
191
                    .lock_guard()
×
192
                    .await
×
193
                    .chain
194
                    .archival_state()
×
195
                    .block_belongs_to_canonical_chain(block_hash)
×
196
                    .await
×
197
                {
198
                    return Ok(());
×
199
                }
×
200
                return Err(RegTestError::Failed(
×
201
                    "block not in blockchain after 5 seconds".into(),
×
202
                ));
×
203
            }
14✔
204
            tokio::time::sleep(std::time::Duration::from_millis(10)).await;
14✔
205
        }
206
        Ok(())
19✔
207
    }
19✔
208
}
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