Repository: bitcoin/bitcoin (language: cpp)

Issue:
Should we check free disk size before syncing blocks
This happens to me that my computer forces qt to exit because my disk is running out of memory

Should we skip syncing or check and show warning beforehand  when disk size available is enough for syncing all blocks?

Also wondering how large free disks should I prepare for syncing all the blocks?


Discussion / hints:
As for how much free disk space you need, my datadir folder currently it taking up about ~300gb, this however has fully syned mainnet and testnet.  Without testnet it is about 30gb less.
I believe the gui intro does this, but not the bitcoind
I guess it could print a warning or error on init
I started looking into this and it is already done at the eleventh step in init : https://github.com/bitcoin/bitcoin/blob/dae72998e8576439254ee497e82e2b9ce4917977/src/init.cpp#L1681-L1688
But it uses the default minimum space for `CheckDiskSpace` of 50MiB
https://github.com/bitcoin/bitcoin/blob/2d46f1be0c3c8b7287aa1f62bb1f5b4a8d00ff6e/src/util/system.cpp#L138-L144
we could estimate a better `additional_bytes` value depending on initial sync, pruning, etc ? But can we hardcode it ? We cannot know each block size before starting downloading them
Yeah, but we can assume the accumulative block size of the currently most-work valid chain. See `m_assumed_blockchain_size` (It is used that way in the gui intro)

Fix patch:
diff --git a/src/init.cpp b/src/init.cpp
index 25b40c6c6eb95..2dc7ff4254016 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1569,6 +1569,24 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
         return false;
     }
 
+    int chain_active_height = WITH_LOCK(cs_main, return chainman.ActiveChain().Height());
+
+    // On first startup, warn on low block storage space
+    if (!fReindex && !fReindexChainState && chain_active_height <= 1) {
+        uint64_t additional_bytes_needed = fPruneMode ? nPruneTarget
+            : chainparams.AssumedBlockchainSize() * 1024 * 1024 * 1024;
+
+        if (!CheckDiskSpace(args.GetBlocksDirPath(), additional_bytes_needed)) {
+            InitWarning(strprintf(_(
+                    "Disk space for %s may not accommodate the block files. " \
+                    "Approximately %u GB of data will be stored in this directory."
+                ),
+                fs::quoted(fs::PathToString(args.GetBlocksDirPath())),
+                chainparams.AssumedBlockchainSize()
+            ));
+        }
+    }
+
     // Either install a handler to notify us when genesis activates, or set fHaveGenesis directly.
     // No locking, as this happens before any background thread is started.
     boost::signals2::connection block_notify_genesis_wait_connection;
@@ -1618,8 +1636,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
 
     // ********************************************************* Step 12: start node
 
-    int chain_active_height;
-
     //// debug print
     {
         LOCK(cs_main);


Test patch:
diff --git a/test/functional/p2p_dos_header_tree.py b/test/functional/p2p_dos_header_tree.py
index 7e26994511838..1f904644fc640 100755
--- a/test/functional/p2p_dos_header_tree.py
+++ b/test/functional/p2p_dos_header_tree.py
@@ -22,7 +22,7 @@ def set_test_params(self):
         self.setup_clean_chain = True
         self.chain = 'testnet3'  # Use testnet chain because it has an early checkpoint
         self.num_nodes = 2
-        self.extra_args = [["-minimumchainwork=0x0"], ["-minimumchainwork=0x0"]]
+        self.extra_args = [["-minimumchainwork=0x0", '-prune=550']] * self.num_nodes
 
     def add_options(self, parser):
         parser.add_argument(
@@ -63,7 +63,7 @@ def run_test(self):
 
         self.log.info("Feed all fork headers (succeeds without checkpoint)")
         # On node 0 it succeeds because checkpoints are disabled
-        self.restart_node(0, extra_args=['-nocheckpoints', "-minimumchainwork=0x0"])
+        self.restart_node(0, extra_args=['-nocheckpoints', "-minimumchainwork=0x0", '-prune=550'])
         peer_no_checkpoint = self.nodes[0].add_p2p_connection(P2PInterface())
         peer_no_checkpoint.send_and_ping(msg_headers(self.headers_fork))
         assert {
diff --git a/test/functional/wallet_crosschain.py b/test/functional/wallet_crosschain.py
index b6d0c87985fa0..c0047542ed34c 100755
--- a/test/functional/wallet_crosschain.py
+++ b/test/functional/wallet_crosschain.py
@@ -21,7 +21,7 @@ def setup_network(self):
 
         # Switch node 1 to testnet before starting it.
         self.nodes[1].chain = 'testnet3'
-        self.nodes[1].extra_args = ['-maxconnections=0'] # disable testnet sync
+        self.nodes[1].extra_args = ['-maxconnections=0', '-prune=550'] # disable testnet sync
         with open(self.nodes[1].bitcoinconf, 'r', encoding='utf8') as conf:
             conf_data = conf.read()
         with open (self.nodes[1].bitcoinconf, 'w', encoding='utf8') as conf:
@@ -51,7 +51,7 @@ def run_test(self):
         if not self.options.descriptors:
             self.log.info("Override cross-chain wallet load protection")
             self.stop_nodes()
-            self.start_nodes([['-walletcrosschain']] * self.num_nodes)
+            self.start_nodes([['-walletcrosschain', '-prune=550']] * self.num_nodes)
             self.nodes[0].loadwallet(node1_wallet)
             self.nodes[1].loadwallet(node0_wallet)