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

Neptune-Crypto / neptune-core / 15778331933

20 Jun 2025 11:52AM UTC coverage: 72.25% (+0.4%) from 71.873%
15778331933

Pull #610

github

web-flow
Merge 0253eb5b0 into 3b7351ed6
Pull Request #610: Refactor mempool: Preserve PW over block updates

651 of 753 new or added lines in 19 files covered. (86.45%)

37 existing lines in 9 files now uncovered.

20610 of 28526 relevant lines covered (72.25%)

483914.76 hits per line

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

70.0
/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 {
15✔
21
        Self {
15✔
22
            worker: RegTestPrivate::new(gsl),
15✔
23
        }
15✔
24
    }
15✔
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.
NEW
54
    pub async fn mine_blocks_to_wallet(
×
NEW
55
        &mut self,
×
NEW
56
        n_blocks: u32,
×
NEW
57
        mine_mempool_txs: bool,
×
58
    ) -> Result<(), RegTestError> {
15✔
59
        self.worker
15✔
60
            .mine_blocks_to_wallet(n_blocks, mine_mempool_txs)
15✔
61
            .await
15✔
62
    }
15✔
63

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

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

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

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

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

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

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

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

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

133
        let guesser_key = gs
28✔
134
            .wallet_state
28✔
135
            .wallet_entropy
28✔
136
            .guesser_spending_key(tip_block.hash());
28✔
137

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

148
        drop(gs);
28✔
149

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

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

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

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

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

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

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

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