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

Neptune-Crypto / neptune-core / 15778825012

20 Jun 2025 12:17PM UTC coverage: 72.299% (+0.4%) from 71.873%
15778825012

push

github

Sword-Smith
Merge branch `jfs/separate_upgrader`

This merge works towards two goals:
- Improve the likelihood that weak computers have their transactions mined, and
- move closer to the vision of three-step mining as outlined here:
  https://talk.neptune.cash/t/thoughts-on-the-mempool-issue-and-upgrading/52/26

Three main changes:

    - Refactor mempool to never throw away PrimitiveWitness data.
    - Don't automatically run SingleProof's "update" branch when receiving a new
      block, but let "update" be handled by proof upgraders.
    - Add new task for upgraders: The updating of SingleProof backed transactions.

    Since ProofCollection-backed transactions cannot be updated to be valid under a new mutator set, we instead update them with their PrimitiveWitness if one such is present. A PrimitiveWitness will be present in the mempool if the transaction was initiated locally. Previously, a ProofCollection backed transaction would always throw out the PrimitiveWitness but with this change, the PrimitiveWitness is always preserved in the mempool, if it was ever present there (i.e. if the transaction was initiated locally).
    Replace TransactionOrigin in the mempool with an ordered UpgradePriority that can be used by proof upgraders to pick the most favorable transaction for proof upgrading -- this especially helps pick the best transaction for "update".
    Remove execution of the "update" branch from being done automatically by any composer when a new block is received to being a task that only the proof upgrader performs.
    Only locally initiated transactions (as checked by the presence of a primitive witness) are updated immediately after the receival of a new block.

The central part of the code to understand is, in my opinion, this loop in mempool's update_with_block, as it defined the rules for what to do with transactions that did not get mined in the new block.

Two questions are relevant for each transaction:
 - Should the transaction be updated ... (continued)

650 of 753 new or added lines in 19 files covered. (86.32%)

21 existing lines in 5 files now uncovered.

20624 of 28526 relevant lines covered (72.3%)

505014.56 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 {
49✔
208
            if start.elapsed() > std::time::Duration::from_secs(5) {
21✔
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
            }
21✔
224
            tokio::time::sleep(std::time::Duration::from_millis(10)).await;
21✔
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