Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2024-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <compat/byteswap.h>6#include <crypto/common.h>7#include <streams.h>8#include <util/log.h>9#include <util/translation.h>10#include <wallet/migrate.h>12#include <array>13#include <cstddef>14#include <optional>15#include <stdexcept>16#include <variant>17#include <vector>19namespace wallet {20// Magic bytes in both endianness's21constexpr uint32_t BTREE_MAGIC = 0x00053162; // If the file endianness matches our system, we see this magic22constexpr uint32_t BTREE_MAGIC_OE = 0x62310500; // If the file endianness is the other one, we will see this magic24// Subdatabase name25static const std::vector<std::byte> SUBDATABASE_NAME = {std::byte{'m'}, std::byte{'a'}, std::byte{'i'}, std::byte{'n'}};27enum class PageType : uint8_t {28/*29* BDB has several page types, most of which we do not use30* They are listed here for completeness, but commented out31* to avoid opening something unintended.32INVALID = 0, // Invalid page type33DUPLICATE = 1, // Duplicate. Deprecated and no longer used34HASH_UNSORTED = 2, // Hash pages. Deprecated.35RECNO_INTERNAL = 4, // Recno internal36RECNO_LEAF = 6, // Recno leaf37HASH_META = 8, // Hash metadata38QUEUE_META = 10, // Queue Metadata39QUEUE_DATA = 11, // Queue Data40DUPLICATE_LEAF = 12, // Off-page duplicate leaf41HASH_SORTED = 13, // Sorted hash page42*/43BTREE_INTERNAL = 3, // BTree internal44BTREE_LEAF = 5, // BTree leaf45OVERFLOW_DATA = 7, // Overflow46BTREE_META = 9, // BTree metadata47};49enum class RecordType : uint8_t {50KEYDATA = 1,51// DUPLICATE = 2, Unused as our databases do not support duplicate records52OVERFLOW_DATA = 3,53DELETE_FLAG = 0x80, // Indicate this record is deleted. This is OR'd with the real type.54};56enum class BTreeFlags : uint32_t {57/*58* BTree databases have feature flags, but we do not use them except for59* subdatabases. The unused flags are included for completeness, but commented out60* to avoid accidental use.61DUP = 1, // Duplicates62RECNO = 2, // Recno tree63RECNUM = 4, // BTree: Maintain record counts64FIXEDLEN = 8, // Recno: fixed length records65RENUMBER = 0x10, // Recno: renumber on insert/delete66DUPSORT = 0x40, // Duplicates are sorted67COMPRESS = 0x80, // Compressed68*/69SUBDB = 0x20, // Subdatabases70};72/** Berkeley DB BTree metadata page layout */73class MetaPage74{75public:76uint32_t lsn_file; // Log Sequence Number file77uint32_t lsn_offset; // Log Sequence Number offset78uint32_t page_num; // Current page number79uint32_t magic; // Magic number80uint32_t version; // Version81uint32_t pagesize; // Page size82uint8_t encrypt_algo; // Encryption algorithm83PageType type; // Page type84uint8_t metaflags; // Meta-only flags85uint8_t unused1; // Unused86uint32_t free_list; // Free list page number87uint32_t last_page; // Page number of last page in db88uint32_t partitions; // Number of partitions89uint32_t key_count; // Cached key count90uint32_t record_count; // Cached record count91BTreeFlags flags; // Flags92std::array<std::byte, 20> uid; // 20 byte unique file ID93uint32_t unused2; // Unused94uint32_t minkey; // Minimum key95uint32_t re_len; // Recno: fixed length record length96uint32_t re_pad; // Recno: fixed length record pad97uint32_t root; // Root page number98char unused3[368]; // 92 * 4 bytes of unused space99uint32_t crypto_magic; // Crypto magic number100char trash[12]; // 3 * 4 bytes of trash space101unsigned char iv[20]; // Crypto IV102unsigned char chksum[16]; // Checksum104bool other_endian;105uint32_t expected_page_num;107MetaPage(uint32_t expected_page_num) : expected_page_num(expected_page_num) {}108MetaPage() = delete;110template <typename Stream>111void Unserialize(Stream& s)112{113s >> lsn_file;114s >> lsn_offset;115s >> page_num;116s >> magic;117s >> version;118s >> pagesize;119s >> encrypt_algo;121other_endian = magic == BTREE_MAGIC_OE;123uint8_t uint8_type;124s >> uint8_type;125type = static_cast<PageType>(uint8_type);127s >> metaflags;128s >> unused1;129s >> free_list;130s >> last_page;131s >> partitions;132s >> key_count;133s >> record_count;135uint32_t uint32_flags;136s >> uint32_flags;137if (other_endian) {138uint32_flags = internal_bswap_32(uint32_flags);139}140flags = static_cast<BTreeFlags>(uint32_flags);142s >> uid;143s >> unused2;144s >> minkey;145s >> re_len;146s >> re_pad;147s >> root;148s >> unused3;149s >> crypto_magic;150s >> trash;151s >> iv;152s >> chksum;154if (other_endian) {155lsn_file = internal_bswap_32(lsn_file);156lsn_offset = internal_bswap_32(lsn_offset);157page_num = internal_bswap_32(page_num);158magic = internal_bswap_32(magic);159version = internal_bswap_32(version);160pagesize = internal_bswap_32(pagesize);161free_list = internal_bswap_32(free_list);162last_page = internal_bswap_32(last_page);163partitions = internal_bswap_32(partitions);164key_count = internal_bswap_32(key_count);165record_count = internal_bswap_32(record_count);166unused2 = internal_bswap_32(unused2);167minkey = internal_bswap_32(minkey);168re_len = internal_bswap_32(re_len);169re_pad = internal_bswap_32(re_pad);170root = internal_bswap_32(root);171crypto_magic = internal_bswap_32(crypto_magic);172}174// Page number must match175if (page_num != expected_page_num) {176throw std::runtime_error("Meta page number mismatch");177}179// Check magic180if (magic != BTREE_MAGIC) {181throw std::runtime_error("Not a BDB file");182}184// Only version 9 is supported185if (version != 9) {186throw std::runtime_error("Unsupported BDB data file version number");187}189// Page size must be 512 <= pagesize <= 64k, and be a power of 2190if (pagesize < 512 || pagesize > 65536 || (pagesize & (pagesize - 1)) != 0) {191throw std::runtime_error("Bad page size");192}194// Page type must be the btree type195if (type != PageType::BTREE_META) {196throw std::runtime_error("Unexpected page type, should be 9 (BTree Metadata)");197}199// Only supported meta-flag is subdatabase200if (flags != BTreeFlags::SUBDB) {201throw std::runtime_error("Unexpected database flags, should only be 0x20 (subdatabases)");202}203}204};206/** General class for records in a BDB BTree database. Contains common fields. */207class RecordHeader208{209public:210uint16_t len; // Key/data item length211RecordType type; // Page type (BDB has this; includes a DELETE_FLAG that we track separately)212bool deleted; // Whether the DELETE_FLAG was set on type214static constexpr size_t SIZE = 3; // The record header is 3 bytes216bool other_endian;218RecordHeader(bool other_endian) : other_endian(other_endian) {}219RecordHeader() = delete;221template <typename Stream>222void Unserialize(Stream& s)223{224s >> len;226uint8_t uint8_type;227s >> uint8_type;228type = static_cast<RecordType>(uint8_type & ~static_cast<uint8_t>(RecordType::DELETE_FLAG));229deleted = uint8_type & static_cast<uint8_t>(RecordType::DELETE_FLAG);231if (other_endian) {232len = internal_bswap_16(len);233}234}235};237/** Class for data in the record directly */238class DataRecord239{240public:241DataRecord(const RecordHeader& header) : m_header(header) {}242DataRecord() = delete;244RecordHeader m_header;246std::vector<std::byte> data; // Variable length key/data item248template <typename Stream>249void Unserialize(Stream& s)250{