From 2e8072edbeb20a8c05c0dbd06ca105bc4dd07b96 Mon Sep 17 00:00:00 2001 From: Sebastian Falbesoner Date: Tue, 24 Dec 2024 01:31:55 +0100 Subject: [PATCH 1/3] rpc: support writing UTXO set dump (`dumptxoutset`) to a named pipe This allows external tooling (e.g. converters) to consume the output directly, rather than having to write the dump to disk first and then read it from there again. Co-authored-by: Luke Dashjr --- src/rpc/blockchain.cpp | 11 +++++++---- src/util/fs.h | 4 ++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 475c5ca41c9..3beea79f419 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -3109,11 +3109,12 @@ static RPCHelpMan dumptxoutset() const ArgsManager& args{EnsureAnyArgsman(request.context)}; const fs::path path = fsbridge::AbsPathJoin(args.GetDataDirNet(), fs::u8path(self.Arg("path"))); + const auto path_info{fs::status(path)}; // Write to a temporary path and then move into `path` on completion - // to avoid confusion due to an interruption. - const fs::path temppath = path + ".incomplete"; + // to avoid confusion due to an interruption. If a named pipe passed, write directly to it. + const fs::path temppath = fs::is_fifo(path_info) ? path : path + ".incomplete"; - if (fs::exists(path)) { + if (fs::exists(path_info) && !fs::is_fifo(path_info)) { throw JSONRPCError( RPC_INVALID_PARAMETER, path.utf8string() + " already exists. If you are sure this is what you want, " @@ -3197,7 +3198,9 @@ static RPCHelpMan dumptxoutset() path, temppath, node.rpc_interruption_point); - fs::rename(temppath, path); + if (!fs::is_fifo(path_info)) { + fs::rename(temppath, path); + } result.pushKV("path", path.utf8string()); return result; diff --git a/src/util/fs.h b/src/util/fs.h index 147904d030a..dce371cc5ef 100644 --- a/src/util/fs.h +++ b/src/util/fs.h @@ -96,6 +96,10 @@ static inline bool exists(const path& p) { return std::filesystem::exists(p); } +static inline bool exists(const std::filesystem::file_status& s) +{ + return std::filesystem::exists(s); +} // Allow explicit quoted stream I/O. static inline auto quoted(const std::string& s) From 61a5460d0d6cd174d395c51333def798fe7442fe Mon Sep 17 00:00:00 2001 From: Sebastian Falbesoner Date: Tue, 24 Dec 2024 01:39:19 +0100 Subject: [PATCH 2/3] test: add test for utxo-to-sqlite conversion using named pipe --- test/functional/tool_utxo_to_sqlite.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/test/functional/tool_utxo_to_sqlite.py b/test/functional/tool_utxo_to_sqlite.py index d1f3e7e1934..d3b3fc43439 100755 --- a/test/functional/tool_utxo_to_sqlite.py +++ b/test/functional/tool_utxo_to_sqlite.py @@ -4,7 +4,8 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test utxo-to-sqlite conversion tool""" from itertools import product -import os.path +import os +import platform try: import sqlite3 except ImportError: @@ -135,6 +136,19 @@ class UtxoToSqliteTest(BitcoinTestFramework): assert_equal(muhash_sqlite, muhash_compact_serialized) self.log.info('') + if platform.system() != "Windows": # FIFOs are not available on Windows + self.log.info('Convert UTXO set directly (without intermediate dump) via named pipe') + fifo_filename = os.path.join(self.options.tmpdir, "utxos.fifo") + os.mkfifo(fifo_filename) + output_direct_filename = os.path.join(self.options.tmpdir, "utxos_direct.sqlite") + p = subprocess.Popen([sys.executable, utxo_to_sqlite_path, fifo_filename, output_direct_filename], + stderr=subprocess.STDOUT) + node.dumptxoutset(fifo_filename, "latest") + p.wait(timeout=10) + muhash_direct_sqlite = calculate_muhash_from_sqlite_utxos(output_direct_filename, "hex", "hex") + assert_equal(muhash_sqlite, muhash_direct_sqlite) + os.remove(fifo_filename) + if __name__ == "__main__": UtxoToSqliteTest(__file__).main() From b19caeea098f92a7f72aaeee49573358f4b153a3 Mon Sep 17 00:00:00 2001 From: Sebastian Falbesoner Date: Wed, 11 Mar 2026 16:40:02 +0100 Subject: [PATCH 3/3] doc: add release note for #31560 (named pipe support for `dumptxoutset` RPC) --- doc/release-notes-31560.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 doc/release-notes-31560.md diff --git a/doc/release-notes-31560.md b/doc/release-notes-31560.md new file mode 100644 index 00000000000..07a8f7798ed --- /dev/null +++ b/doc/release-notes-31560.md @@ -0,0 +1,8 @@ +Updated RPCs +------------ + +- The `dumptxoutset` RPC now supports writing to a named pipe + on UNIX-like systems (see mkfifo(1) and mkfifo(3) man pages). + This allows the raw UTXO set data to be consumed directly by + another process (e.g., the `contrib/utxo-tools/utxo_to_sqlite.py` + conversion script) without first writing it to disk. (#31560)