diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index 26c261eba27..de142cd2c91 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -542,3 +542,33 @@ std::unique_ptr CChainParams::TestNet() { return std::make_unique(); } + +std::vector CChainParams::GetAvailableSnapshotHeights() const +{ + std::vector heights; + heights.reserve(m_assumeutxo_data.size()); + + for (const auto& data : m_assumeutxo_data) { + heights.emplace_back(data.height); + } + return heights; +} + +std::optional GetNetworkForMagic(MessageStartChars& message) +{ + const auto mainnet_msg = CChainParams::Main()->MessageStart(); + const auto testnet_msg = CChainParams::TestNet()->MessageStart(); + const auto regtest_msg = CChainParams::RegTest({})->MessageStart(); + const auto signet_msg = CChainParams::SigNet({})->MessageStart(); + + if (std::equal(message.begin(), message.end(), mainnet_msg.data())) { + return ChainType::MAIN; + } else if (std::equal(message.begin(), message.end(), testnet_msg.data())) { + return ChainType::TESTNET; + } else if (std::equal(message.begin(), message.end(), regtest_msg.data())) { + return ChainType::REGTEST; + } else if (std::equal(message.begin(), message.end(), signet_msg.data())) { + return ChainType::SIGNET; + } + return std::nullopt; +} diff --git a/src/kernel/chainparams.h b/src/kernel/chainparams.h index 7a5539bc71c..f396c1b42ca 100644 --- a/src/kernel/chainparams.h +++ b/src/kernel/chainparams.h @@ -93,6 +93,7 @@ public: const Consensus::Params& GetConsensus() const { return consensus; } const MessageStartChars& MessageStart() const { return pchMessageStart; } uint16_t GetDefaultPort() const { return nDefaultPort; } + std::vector GetAvailableSnapshotHeights() const; const CBlock& GenesisBlock() const { return genesis; } /** Default value for -checkmempool and -checkblockindex argument */ @@ -183,4 +184,6 @@ protected: ChainTxData chainTxData; }; +std::optional GetNetworkForMagic(MessageStartChars& pchMessageStart); + #endif // BITCOIN_KERNEL_CHAINPARAMS_H diff --git a/src/node/utxo_snapshot.h b/src/node/utxo_snapshot.h index 1160bb55f07..256a4a601d3 100644 --- a/src/node/utxo_snapshot.h +++ b/src/node/utxo_snapshot.h @@ -6,16 +6,22 @@ #ifndef BITCOIN_NODE_UTXO_SNAPSHOT_H #define BITCOIN_NODE_UTXO_SNAPSHOT_H +#include +#include #include #include #include #include +#include #include #include #include #include +// UTXO set snapshot magic bytes +static constexpr std::array SNAPSHOT_MAGIC_BYTES = {'u', 't', 'x', 'o', 0xff}; + class Chainstate; namespace node { @@ -23,10 +29,14 @@ namespace node { //! assumeutxo Chainstate can be constructed. class SnapshotMetadata { + const uint16_t m_version{1}; + const std::set m_supported_versions{1}; public: //! The hash of the block that reflects the tip of the chain for the //! UTXO set contained in this snapshot. uint256 m_base_blockhash; + uint32_t m_base_blockheight; + //! The number of coins in the UTXO set contained in this snapshot. Used //! during snapshot load to estimate progress of UTXO set reconstruction. @@ -35,11 +45,55 @@ public: SnapshotMetadata() { } SnapshotMetadata( const uint256& base_blockhash, + const int base_blockheight, uint64_t coins_count) : m_base_blockhash(base_blockhash), + m_base_blockheight(base_blockheight), m_coins_count(coins_count) { } - SERIALIZE_METHODS(SnapshotMetadata, obj) { READWRITE(obj.m_base_blockhash, obj.m_coins_count); } + template + inline void Serialize(Stream& s) const { + s << SNAPSHOT_MAGIC_BYTES; + s << m_version; + s << Params().MessageStart(); + s << m_base_blockheight; + s << m_base_blockhash; + s << m_coins_count; + } + + template + inline void Unserialize(Stream& s) { + // Read the snapshot magic bytes + std::array snapshot_magic; + s >> snapshot_magic; + if (snapshot_magic != SNAPSHOT_MAGIC_BYTES) { + throw std::ios_base::failure("Invalid UTXO set snapshot magic bytes. Please check if this is indeed a snapshot file or if you are using an outdated snapshot format."); + } + + // Read the version + uint16_t version; + s >> version; + if (m_supported_versions.find(version) == m_supported_versions.end()) { + throw std::ios_base::failure(strprintf("Version of snapshot %s does not match any of the supported versions.", version)); + } + + // Read the network magic (pchMessageStart) + MessageStartChars message; + s >> message; + if (!std::equal(message.begin(), message.end(), Params().MessageStart().data())) { + auto metadata_network = GetNetworkForMagic(message); + if (metadata_network) { + std::string network_string{ChainTypeToString(metadata_network.value())}; + throw std::ios_base::failure(strprintf("The network of the snapshot (%s) does not match the network of this node (%s).", network_string, Params().GetChainTypeString())); + } else { + throw std::ios_base::failure("This snapshot has been created for an unrecognized network. This could be a custom signet, a new testnet or possibly caused by data corruption."); + } + } + + s >> m_base_blockheight; + s >> m_base_blockhash; + s >> m_coins_count; + } }; //! The file in the snapshot chainstate dir which stores the base blockhash. This is diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 263d9f1e0ab..a6c959797ab 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -34,6 +34,7 @@ #include #include #include