|
42 | 42 | #include <core/log.hpp> |
43 | 43 | #include <core/target_utils.hpp> // chain::bits_to_target (parent-PoW, step 4) |
44 | 44 | #include <core/leveldb_store.hpp> // P1f: core::LevelDBStore persistence |
| 45 | +#include <core/coin/utxo.hpp> // P1 PC: core::coin::DEFAULT_MAX_TIP_AGE (is_synced) |
45 | 46 |
|
46 | 47 | #include <algorithm> |
47 | 48 | #include <atomic> |
48 | 49 | #include <cstdint> |
49 | 50 | #include <cstring> |
| 51 | +#include <ctime> |
50 | 52 | #include <functional> |
51 | 53 | #include <mutex> |
52 | 54 | #include <memory> |
@@ -688,6 +690,116 @@ inline uint256 get_block_proof(uint32_t bits) { |
688 | 690 | return (~target / (target + uint256::ONE)) + uint256::ONE; |
689 | 691 | } |
690 | 692 |
|
| 693 | +// NMC Difficulty Retarget |
| 694 | +// Mirror of btc::coin::calculate_next_work_required / get_next_work_required |
| 695 | +// (src/impl/btc/coin/header_chain.hpp). Namecoin is a SHA256d Bitcoin fork and |
| 696 | +// shares Bitcoin's 2016-block retarget window / 10-minute spacing, so the |
| 697 | +// retarget algorithm is identical. Kept local to the NMC lane (the btc tree is |
| 698 | +// read-only; core/ is the exceptional SSOT) rather than cross-including the btc |
| 699 | +// header. Adapted from Bitcoin Core pow.cpp (MIT license). |
| 700 | + |
| 701 | +/// Core retarget calculation: adjust difficulty based on actual vs target |
| 702 | +/// timespan. |
| 703 | +inline uint32_t calculate_next_work_required( |
| 704 | + uint32_t tip_bits, |
| 705 | + int64_t tip_time, |
| 706 | + int64_t first_block_time, |
| 707 | + const NMCChainParams& params) |
| 708 | +{ |
| 709 | + if (params.no_retargeting) |
| 710 | + return tip_bits; |
| 711 | + |
| 712 | + int64_t actual_timespan = tip_time - first_block_time; |
| 713 | + |
| 714 | + // Clamp to [timespan/4, timespan*4]. |
| 715 | + if (actual_timespan < params.target_timespan / 4) |
| 716 | + actual_timespan = params.target_timespan / 4; |
| 717 | + if (actual_timespan > params.target_timespan * 4) |
| 718 | + actual_timespan = params.target_timespan * 4; |
| 719 | + |
| 720 | + // Retarget. |
| 721 | + uint256 bn_new; |
| 722 | + bn_new.SetCompact(tip_bits); |
| 723 | + const uint256 bn_pow_limit = params.pow_limit; |
| 724 | + |
| 725 | + // Intermediate uint256 can overflow by 1 bit (same guard as btc/ltc). |
| 726 | + bool shift = bn_new.bits() > bn_pow_limit.bits() - 1; |
| 727 | + if (shift) |
| 728 | + bn_new >>= 1; |
| 729 | + bn_new *= static_cast<uint32_t>(actual_timespan); |
| 730 | + bn_new /= uint256(static_cast<uint64_t>(params.target_timespan)); |
| 731 | + if (shift) |
| 732 | + bn_new <<= 1; |
| 733 | + |
| 734 | + if (bn_new > bn_pow_limit) |
| 735 | + bn_new = bn_pow_limit; |
| 736 | + |
| 737 | + return bn_new.GetCompact(); |
| 738 | +} |
| 739 | + |
| 740 | +/// Calculate next work required at a given height. |
| 741 | +/// @param get_ancestor Function to look up ancestor header by height. |
| 742 | +/// @param tip_height Height of the current tip (the block we're building on). |
| 743 | +/// @param tip_bits nBits of the current tip. |
| 744 | +/// @param tip_time Timestamp of the current tip. |
| 745 | +/// @param new_time Timestamp of the new block being validated. |
| 746 | +/// @param params Chain parameters. |
| 747 | +inline uint32_t get_next_work_required( |
| 748 | + std::function<std::optional<IndexEntry>(uint32_t)> get_ancestor, |
| 749 | + uint32_t tip_height, |
| 750 | + uint32_t tip_bits, |
| 751 | + uint32_t tip_time, |
| 752 | + uint32_t new_time, |
| 753 | + const NMCChainParams& params) |
| 754 | +{ |
| 755 | + uint256 pow_limit_compact; |
| 756 | + pow_limit_compact = params.pow_limit; |
| 757 | + uint32_t pow_limit_bits = pow_limit_compact.GetCompact(); |
| 758 | + |
| 759 | + // Next block height. |
| 760 | + uint32_t new_height = tip_height + 1; |
| 761 | + int64_t interval = params.difficulty_adjustment_interval(); |
| 762 | + |
| 763 | + // Only change once per difficulty adjustment interval. |
| 764 | + if (new_height % interval != 0) { |
| 765 | + if (params.allow_min_difficulty) { |
| 766 | + // Testnet special rule: if >2x target spacing since last block, |
| 767 | + // allow a min-difficulty block. |
| 768 | + if (static_cast<int64_t>(new_time) > static_cast<int64_t>(tip_time) + params.target_spacing * 2) |
| 769 | + return pow_limit_bits; |
| 770 | + |
| 771 | + // Return the last non-special-min-difficulty-rules block. |
| 772 | + uint32_t h = tip_height; |
| 773 | + uint32_t last_bits = tip_bits; |
| 774 | + while (h > 0 && (h % interval) != 0 && last_bits == pow_limit_bits) { |
| 775 | + auto ancestor = get_ancestor(h - 1); |
| 776 | + if (!ancestor) break; |
| 777 | + last_bits = ancestor->header.m_bits; |
| 778 | + h--; |
| 779 | + } |
| 780 | + return last_bits; |
| 781 | + } |
| 782 | + return tip_bits; |
| 783 | + } |
| 784 | + |
| 785 | + if (params.no_retargeting) |
| 786 | + return tip_bits; |
| 787 | + |
| 788 | + // Bitcoin (and Namecoin) always go back `interval - 1` blocks (= 2015 for |
| 789 | + // the 2016-block retarget window). The Litecoin "Art Forz" off-by-one is |
| 790 | + // not part of Namecoin's history. |
| 791 | + int64_t blocks_to_go_back = interval - 1; |
| 792 | + |
| 793 | + uint32_t first_height = static_cast<uint32_t>(tip_height - blocks_to_go_back); |
| 794 | + auto first_entry = get_ancestor(first_height); |
| 795 | + if (!first_entry) |
| 796 | + return tip_bits; // shouldn't happen for a connected chain |
| 797 | + |
| 798 | + int64_t first_time = first_entry->header.m_timestamp; |
| 799 | + |
| 800 | + return calculate_next_work_required(tip_bits, tip_time, first_time, params); |
| 801 | +} |
| 802 | + |
691 | 803 | // ─── HeaderChain ───────────────────────────────────────────────────────────── |
692 | 804 |
|
693 | 805 | /// Header-only chain skeleton for embedded NMC. Mirrors the public surface of |
@@ -894,6 +1006,44 @@ class HeaderChain { |
894 | 1006 | return 0; |
895 | 1007 | } |
896 | 1008 |
|
| 1009 | + /// P1 PC: look up a header by absolute height by walking the tip's ancestry |
| 1010 | + /// through m_previous_block. NMC keeps NO height index (mirror of |
| 1011 | + /// get_locator()'s note) so a height lookup walks parent links down the |
| 1012 | + /// BEST chain. Returns nullopt when the height is above the tip or the walk |
| 1013 | + /// hits a dangling parent. The TemplateBuilder's difficulty retarget needs |
| 1014 | + /// at most `interval` ancestors, so this O(depth) walk is bounded in |
| 1015 | + /// practice. Caller must NOT hold m_mutex (this acquires it). |
| 1016 | + std::optional<IndexEntry> get_header_by_height(uint32_t h) const { |
| 1017 | + std::lock_guard<std::mutex> lock(m_mutex); |
| 1018 | + if (m_tip.IsNull()) return std::nullopt; |
| 1019 | + auto it = m_index.find(m_tip); |
| 1020 | + if (it == m_index.end()) return std::nullopt; |
| 1021 | + if (h > it->second.height) return std::nullopt; // above the tip |
| 1022 | + // Walk parent links from the tip down to height h. |
| 1023 | + uint256 cursor = m_tip; |
| 1024 | + while (true) { |
| 1025 | + auto cit = m_index.find(cursor); |
| 1026 | + if (cit == m_index.end()) return std::nullopt; // dangling link |
| 1027 | + if (cit->second.height == h) return cit->second; |
| 1028 | + if (cit->second.height == 0) return std::nullopt; // reached root, not found |
| 1029 | + cursor = cit->second.header.m_previous_block; |
| 1030 | + } |
| 1031 | + } |
| 1032 | + |
| 1033 | + /// P1 PC: whether the chain tip is recent enough to be considered synced |
| 1034 | + /// with the network (mirror of btc::coin::HeaderChain::is_synced). Uses the |
| 1035 | + /// shared core::coin::DEFAULT_MAX_TIP_AGE (24h) gate: a chain whose tip is |
| 1036 | + /// older than that is still in IBD. An empty chain is never synced. |
| 1037 | + bool is_synced() const { |
| 1038 | + std::lock_guard<std::mutex> lock(m_mutex); |
| 1039 | + if (m_tip.IsNull()) return false; |
| 1040 | + auto it = m_index.find(m_tip); |
| 1041 | + if (it == m_index.end()) return false; |
| 1042 | + auto now = static_cast<uint32_t>(std::time(nullptr)); |
| 1043 | + uint32_t age = now - it->second.header.m_timestamp; |
| 1044 | + return age < core::coin::DEFAULT_MAX_TIP_AGE; |
| 1045 | + } |
| 1046 | + |
897 | 1047 | const NMCChainParams& params() const { return m_params; } |
898 | 1048 |
|
899 | 1049 | private: |
|
0 commit comments