bitcoinkernel_wrapper.h
1 // Copyright (c) 2024-present The Bitcoin Core developers 2 // Distributed under the MIT software license, see the accompanying 3 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 5 #ifndef BITCOIN_KERNEL_BITCOINKERNEL_WRAPPER_H 6 #define BITCOIN_KERNEL_BITCOINKERNEL_WRAPPER_H 7 8 #include <kernel/bitcoinkernel.h> 9 10 #include <array> 11 #include <exception> 12 #include <functional> 13 #include <memory> 14 #include <optional> 15 #include <span> 16 #include <stdexcept> 17 #include <string> 18 #include <string_view> 19 #include <type_traits> 20 #include <utility> 21 #include <vector> 22 23 namespace btck { 24 25 enum class LogCategory : btck_LogCategory { 26 ALL = btck_LogCategory_ALL, 27 BENCH = btck_LogCategory_BENCH, 28 BLOCKSTORAGE = btck_LogCategory_BLOCKSTORAGE, 29 COINDB = btck_LogCategory_COINDB, 30 LEVELDB = btck_LogCategory_LEVELDB, 31 MEMPOOL = btck_LogCategory_MEMPOOL, 32 PRUNE = btck_LogCategory_PRUNE, 33 RAND = btck_LogCategory_RAND, 34 REINDEX = btck_LogCategory_REINDEX, 35 VALIDATION = btck_LogCategory_VALIDATION, 36 KERNEL = btck_LogCategory_KERNEL 37 }; 38 39 enum class LogLevel : btck_LogLevel { 40 TRACE_LEVEL = btck_LogLevel_TRACE, 41 DEBUG_LEVEL = btck_LogLevel_DEBUG, 42 INFO_LEVEL = btck_LogLevel_INFO 43 }; 44 45 enum class ChainType : btck_ChainType { 46 MAINNET = btck_ChainType_MAINNET, 47 TESTNET = btck_ChainType_TESTNET, 48 TESTNET_4 = btck_ChainType_TESTNET_4, 49 SIGNET = btck_ChainType_SIGNET, 50 REGTEST = btck_ChainType_REGTEST 51 }; 52 53 enum class SynchronizationState : btck_SynchronizationState { 54 INIT_REINDEX = btck_SynchronizationState_INIT_REINDEX, 55 INIT_DOWNLOAD = btck_SynchronizationState_INIT_DOWNLOAD, 56 POST_INIT = btck_SynchronizationState_POST_INIT 57 }; 58 59 enum class Warning : btck_Warning { 60 UNKNOWN_NEW_RULES_ACTIVATED = btck_Warning_UNKNOWN_NEW_RULES_ACTIVATED, 61 LARGE_WORK_INVALID_CHAIN = btck_Warning_LARGE_WORK_INVALID_CHAIN 62 }; 63 64 enum class ValidationMode : btck_ValidationMode { 65 VALID = btck_ValidationMode_VALID, 66 INVALID = btck_ValidationMode_INVALID, 67 INTERNAL_ERROR = btck_ValidationMode_INTERNAL_ERROR 68 }; 69 70 enum class BlockValidationResult : btck_BlockValidationResult { 71 UNSET = btck_BlockValidationResult_UNSET, 72 CONSENSUS = btck_BlockValidationResult_CONSENSUS, 73 CACHED_INVALID = btck_BlockValidationResult_CACHED_INVALID, 74 INVALID_HEADER = btck_BlockValidationResult_INVALID_HEADER, 75 MUTATED = btck_BlockValidationResult_MUTATED, 76 MISSING_PREV = btck_BlockValidationResult_MISSING_PREV, 77 INVALID_PREV = btck_BlockValidationResult_INVALID_PREV, 78 TIME_FUTURE = btck_BlockValidationResult_TIME_FUTURE, 79 HEADER_LOW_WORK = btck_BlockValidationResult_HEADER_LOW_WORK 80 }; 81 82 enum class ScriptVerifyStatus : btck_ScriptVerifyStatus { 83 OK = btck_ScriptVerifyStatus_OK, 84 ERROR_INVALID_FLAGS_COMBINATION = btck_ScriptVerifyStatus_ERROR_INVALID_FLAGS_COMBINATION, 85 ERROR_SPENT_OUTPUTS_REQUIRED = btck_ScriptVerifyStatus_ERROR_SPENT_OUTPUTS_REQUIRED, 86 }; 87 88 enum class ScriptVerificationFlags : btck_ScriptVerificationFlags { 89 NONE = btck_ScriptVerificationFlags_NONE, 90 P2SH = btck_ScriptVerificationFlags_P2SH, 91 DERSIG = btck_ScriptVerificationFlags_DERSIG, 92 NULLDUMMY = btck_ScriptVerificationFlags_NULLDUMMY, 93 CHECKLOCKTIMEVERIFY = btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY, 94 CHECKSEQUENCEVERIFY = btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY, 95 WITNESS = btck_ScriptVerificationFlags_WITNESS, 96 TAPROOT = btck_ScriptVerificationFlags_TAPROOT, 97 ALL = btck_ScriptVerificationFlags_ALL 98 }; 99 100 template <typename T> 101 struct is_bitmask_enum : std::false_type { 102 }; 103 104 template <> 105 struct is_bitmask_enum<ScriptVerificationFlags> : std::true_type { 106 }; 107 108 template <typename T> 109 concept BitmaskEnum = is_bitmask_enum<T>::value; 110 111 template <BitmaskEnum T> 112 constexpr T operator|(T lhs, T rhs) 113 { 114 return static_cast<T>( 115 static_cast<std::underlying_type_t<T>>(lhs) | static_cast<std::underlying_type_t<T>>(rhs)); 116 } 117 118 template <BitmaskEnum T> 119 constexpr T operator&(T lhs, T rhs) 120 { 121 return static_cast<T>( 122 static_cast<std::underlying_type_t<T>>(lhs) & static_cast<std::underlying_type_t<T>>(rhs)); 123 } 124 125 template <BitmaskEnum T> 126 constexpr T operator^(T lhs, T rhs) 127 { 128 return static_cast<T>( 129 static_cast<std::underlying_type_t<T>>(lhs) ^ static_cast<std::underlying_type_t<T>>(rhs)); 130 } 131 132 template <BitmaskEnum T> 133 constexpr T operator~(T value) 134 { 135 return static_cast<T>(~static_cast<std::underlying_type_t<T>>(value)); 136 } 137 138 template <BitmaskEnum T> 139 constexpr T& operator|=(T& lhs, T rhs) 140 { 141 return lhs = lhs | rhs; 142 } 143 144 template <BitmaskEnum T> 145 constexpr T& operator&=(T& lhs, T rhs) 146 { 147 return lhs = lhs & rhs; 148 } 149 150 template <BitmaskEnum T> 151 constexpr T& operator^=(T& lhs, T rhs) 152 { 153 return lhs = lhs ^ rhs; 154 } 155 156 template <typename T> 157 T check(T ptr) 158 { 159 if (ptr == nullptr) { 160 throw std::runtime_error("failed to instantiate btck object"); 161 } 162 return ptr; 163 } 164 165 template <typename Collection, typename ValueType> 166 class Iterator 167 { 168 public: 169 using iterator_category = std::random_access_iterator_tag; 170 using iterator_concept = std::random_access_iterator_tag; 171 using difference_type = std::ptrdiff_t; 172 using value_type = ValueType; 173 174 private: 175 const Collection* m_collection; 176 size_t m_idx; 177 178 public: 179 Iterator() = default; 180 Iterator(const Collection* ptr) : m_collection{ptr}, m_idx{0} {} 181 Iterator(const Collection* ptr, size_t idx) : m_collection{ptr}, m_idx{idx} {} 182 183 // This is just a view, so return a copy. 184 auto operator*() const { return (*m_collection)[m_idx]; } 185 auto operator->() const { return (*m_collection)[m_idx]; } 186 187 auto& operator++() { m_idx++; return *this; } 188 auto operator++(int) { Iterator tmp = *this; ++(*this); return tmp; } 189 190 auto& operator--() { m_idx--; return *this; } 191 auto operator--(int) { auto temp = *this; --m_idx; return temp; } 192 193 auto& operator+=(difference_type n) { m_idx += n; return *this; } 194 auto& operator-=(difference_type n) { m_idx -= n; return *this; } 195 196 auto operator+(difference_type n) const { return Iterator(m_collection, m_idx + n); } 197 auto operator-(difference_type n) const { return Iterator(m_collection, m_idx - n); } 198 199 auto operator-(const Iterator& other) const { return static_cast<difference_type>(m_idx) - static_cast<difference_type>(other.m_idx); } 200 201 ValueType operator[](difference_type n) const { return (*m_collection)[m_idx + n]; } 202 203 auto operator<=>(const Iterator& other) const { return m_idx <=> other.m_idx; } 204 205 bool operator==(const Iterator& other) const { return m_collection == other.m_collection && m_idx == other.m_idx; } 206 207 private: 208 friend Iterator operator+(difference_type n, const Iterator& it) { return it + n; } 209 }; 210 211 template <typename Container, typename SizeFunc, typename GetFunc> 212 concept IndexedContainer = requires(const Container& c, SizeFunc size_func, GetFunc get_func, std::size_t i) { 213 { std::invoke(size_func, c) } -> std::convertible_to<std::size_t>; 214 { std::invoke(get_func, c, i) }; // Return type is deduced 215 }; 216 217 template <typename Container, auto SizeFunc, auto GetFunc> 218 requires IndexedContainer<Container, decltype(SizeFunc), decltype(GetFunc)> 219 class Range 220 { 221 public: 222 using value_type = std::invoke_result_t<decltype(GetFunc), const Container&, size_t>; 223 using difference_type = std::ptrdiff_t; 224 using iterator = Iterator<Range, value_type>; 225 using const_iterator = iterator; 226 227 private: 228 const Container* m_container; 229 230 public: 231 explicit Range(const Container& container) : m_container(&container) 232 { 233 static_assert(std::ranges::random_access_range<Range>); 234 } 235 236 iterator begin() const { return iterator(this, 0); } 237 iterator end() const { return iterator(this, size()); } 238 239 const_iterator cbegin() const { return begin(); } 240 const_iterator cend() const { return end(); } 241 242 size_t size() const { return std::invoke(SizeFunc, *m_container); } 243 244 bool empty() const { return size() == 0; } 245 246 value_type operator[](size_t index) const { return std::invoke(GetFunc, *m_container, index); } 247 248 value_type at(size_t index) const 249 { 250 if (index >= size()) { 251 throw std::out_of_range("Index out of range"); 252 } 253 return (*this)[index]; 254 } 255 256 value_type front() const { return (*this)[0]; } 257 value_type back() const { return (*this)[size() - 1]; } 258 }; 259 260 #define MAKE_RANGE_METHOD(method_name, ContainerType, SizeFunc, GetFunc, container_expr) \ 261 auto method_name() const & { \ 262 return Range<ContainerType, SizeFunc, GetFunc>{container_expr}; \ 263 } \ 264 auto method_name() const && = delete; 265 266 template <typename T> 267 std::vector<std::byte> write_bytes(const T* object, int (*to_bytes)(const T*, btck_WriteBytes, void*)) 268 { 269 std::vector<std::byte> bytes; 270 struct UserData { 271 std::vector<std::byte>* bytes; 272 std::exception_ptr exception; 273 }; 274 UserData user_data = UserData{.bytes = &bytes, .exception = nullptr}; 275 276 constexpr auto const write = +[](const void* buffer, size_t len, void* user_data) -> int { 277 auto& data = *reinterpret_cast<UserData*>(user_data); 278 auto& bytes = *data.bytes; 279 try { 280 auto const* first = static_cast<const std::byte*>(buffer); 281 auto const* last = first + len; 282 bytes.insert(bytes.end(), first, last); 283 return 0; 284 } catch (...) { 285 data.exception = std::current_exception(); 286 return -1; 287 } 288 }; 289 290 if (to_bytes(object, write, &user_data) != 0) { 291 std::rethrow_exception(user_data.exception); 292 } 293 return bytes; 294 } 295 296 template <typename CType> 297 class View 298 { 299 protected: 300 const CType* m_ptr; 301 302 public: 303 explicit View(const CType* ptr) : m_ptr{check(ptr)} {} 304 305 const CType* get() const { return m_ptr; } 306 }; 307 308 template <typename CType, CType* (*CopyFunc)(const CType*), void (*DestroyFunc)(CType*)> 309 class Handle 310 { 311 protected: 312 CType* m_ptr; 313 314 public: 315 explicit Handle(CType* ptr) : m_ptr{check(ptr)} {} 316 317 // Copy constructors 318 Handle(const Handle& other) 319 : m_ptr{check(CopyFunc(other.m_ptr))} {} 320 Handle& operator=(const Handle& other) 321 { 322 if (this != &other) { 323 Handle temp(other); 324 std::swap(m_ptr, temp.m_ptr); 325 } 326 return *this; 327 } 328 329 // Move constructors 330 Handle(Handle&& other) noexcept : m_ptr(other.m_ptr) { other.m_ptr = nullptr; } 331 Handle& operator=(Handle&& other) noexcept 332 { 333 DestroyFunc(m_ptr); 334 m_ptr = std::exchange(other.m_ptr, nullptr); 335 return *this; 336 } 337 338 template <typename ViewType> 339 requires std::derived_from<ViewType, View<CType>> 340 Handle(const ViewType& view) 341 : Handle{CopyFunc(view.get())} 342 { 343 } 344 345 ~Handle() { DestroyFunc(m_ptr); } 346 347 CType* get() { return m_ptr; } 348 const CType* get() const { return m_ptr; } 349 }; 350 351 template <typename CType, void (*DestroyFunc)(CType*)> 352 class UniqueHandle 353 { 354 protected: 355 struct Deleter { 356 void operator()(CType* ptr) const noexcept 357 { 358 if (ptr) DestroyFunc(ptr); 359 } 360 }; 361 std::unique_ptr<CType, Deleter> m_ptr; 362 363 public: 364 explicit UniqueHandle(CType* ptr) : m_ptr{check(ptr)} {} 365 366 CType* get() { return m_ptr.get(); } 367 const CType* get() const { return m_ptr.get(); } 368 }; 369 370 class PrecomputedTransactionData; 371 class Transaction; 372 class TransactionOutput; 373 374 template <typename Derived> 375 class ScriptPubkeyApi 376 { 377 private: 378 auto impl() const 379 { 380 return static_cast<const Derived*>(this)->get(); 381 } 382 383 friend Derived; 384 ScriptPubkeyApi() = default; 385 386 public: 387 bool Verify(int64_t amount, 388 const Transaction& tx_to, 389 const PrecomputedTransactionData* precomputed_txdata, 390 unsigned int input_index, 391 ScriptVerificationFlags flags, 392 ScriptVerifyStatus& status) const; 393 394 std::vector<std::byte> ToBytes() const 395 { 396 return write_bytes(impl(), btck_script_pubkey_to_bytes); 397 } 398 }; 399 400 class ScriptPubkeyView : public View<btck_ScriptPubkey>, public ScriptPubkeyApi<ScriptPubkeyView> 401 { 402 public: 403 explicit ScriptPubkeyView(const btck_ScriptPubkey* ptr) : View{ptr} {} 404 }; 405 406 class ScriptPubkey : public Handle<btck_ScriptPubkey, btck_script_pubkey_copy, btck_script_pubkey_destroy>, public ScriptPubkeyApi<ScriptPubkey> 407 { 408 public: 409 explicit ScriptPubkey(std::span<const std::byte> raw) 410 : Handle{btck_script_pubkey_create(raw.data(), raw.size())} {} 411 412 ScriptPubkey(const ScriptPubkeyView& view) 413 : Handle(view) {} 414 }; 415 416 template <typename Derived> 417 class TransactionOutputApi 418 { 419 private: 420 auto impl() const 421 { 422 return static_cast<const Derived*>(this)->get(); 423 } 424 425 friend Derived; 426 TransactionOutputApi() = default; 427 428 public: 429 int64_t Amount() const 430 { 431 return btck_transaction_output_get_amount(impl()); 432 } 433 434 ScriptPubkeyView GetScriptPubkey() const 435 { 436 return ScriptPubkeyView{btck_transaction_output_get_script_pubkey(impl())}; 437 } 438 }; 439 440 class TransactionOutputView : public View<btck_TransactionOutput>, public TransactionOutputApi<TransactionOutputView> 441 { 442 public: 443 explicit TransactionOutputView(const btck_TransactionOutput* ptr) : View{ptr} {} 444 }; 445 446 class TransactionOutput : public Handle<btck_TransactionOutput, btck_transaction_output_copy, btck_transaction_output_destroy>, public TransactionOutputApi<TransactionOutput> 447 { 448 public: 449 explicit TransactionOutput(const ScriptPubkey& script_pubkey, int64_t amount) 450 : Handle{btck_transaction_output_create(script_pubkey.get(), amount)} {} 451 452 TransactionOutput(const TransactionOutputView& view) 453 : Handle(view) {} 454 }; 455 456 template <typename Derived> 457 class TxidApi 458 { 459 private: 460 auto impl() const 461 { 462 return static_cast<const Derived*>(this)->get(); 463 } 464 465 friend Derived; 466 TxidApi() = default; 467 468 public: 469 bool operator==(const TxidApi& other) const 470 { 471 return btck_txid_equals(impl(), other.impl()) != 0; 472 } 473 474 bool operator!=(const TxidApi& other) const 475 { 476 return btck_txid_equals(impl(), other.impl()) == 0; 477 } 478 479 std::array<std::byte, 32> ToBytes() const 480 { 481 std::array<std::byte, 32> hash; 482 btck_txid_to_bytes(impl(), reinterpret_cast<unsigned char*>(hash.data())); 483 return hash; 484 } 485 }; 486 487 class TxidView : public View<btck_Txid>, public TxidApi<TxidView> 488 { 489 public: 490 explicit TxidView(const btck_Txid* ptr) : View{ptr} {} 491 }; 492 493 class Txid : public Handle<btck_Txid, btck_txid_copy, btck_txid_destroy>, public TxidApi<Txid> 494 { 495 public: 496 Txid(const TxidView& view) 497 : Handle(view) {} 498 }; 499 500 template <typename Derived> 501 class OutPointApi 502 { 503 private: 504 auto impl() const 505 { 506 return static_cast<const Derived*>(this)->get(); 507 } 508 509 friend Derived; 510 OutPointApi() = default; 511 512 public: 513 uint32_t index() const 514 { 515 return btck_transaction_out_point_get_index(impl()); 516 } 517 518 TxidView Txid() const 519 { 520 return TxidView{btck_transaction_out_point_get_txid(impl())}; 521 } 522 }; 523 524 class OutPointView : public View<btck_TransactionOutPoint>, public OutPointApi<OutPointView> 525 { 526 public: 527 explicit OutPointView(const btck_TransactionOutPoint* ptr) : View{ptr} {} 528 }; 529 530 class OutPoint : public Handle<btck_TransactionOutPoint, btck_transaction_out_point_copy, btck_transaction_out_point_destroy>, public OutPointApi<OutPoint> 531 { 532 public: 533 OutPoint(const OutPointView& view) 534 : Handle(view) {} 535 }; 536 537 template <typename Derived> 538 class TransactionInputApi 539 { 540 private: 541 auto impl() const 542 { 543 return static_cast<const Derived*>(this)->get(); 544 } 545 546 friend Derived; 547 TransactionInputApi() = default; 548 549 public: 550 OutPointView OutPoint() const 551 { 552 return OutPointView{btck_transaction_input_get_out_point(impl())}; 553 } 554 }; 555 556 class TransactionInputView : public View<btck_TransactionInput>, public TransactionInputApi<TransactionInputView> 557 { 558 public: 559 explicit TransactionInputView(const btck_TransactionInput* ptr) : View{ptr} {} 560 }; 561 562 class TransactionInput : public Handle<btck_TransactionInput, btck_transaction_input_copy, btck_transaction_input_destroy>, public TransactionInputApi<TransactionInput> 563 { 564 public: 565 TransactionInput(const TransactionInputView& view) 566 : Handle(view) {} 567 }; 568 569 template <typename Derived> 570 class TransactionApi 571 { 572 private: 573 auto impl() const 574 { 575 return static_cast<const Derived*>(this)->get(); 576 } 577 578 public: 579 size_t CountOutputs() const 580 { 581 return btck_transaction_count_outputs(impl()); 582 } 583 584 size_t CountInputs() const 585 { 586 return btck_transaction_count_inputs(impl()); 587 } 588 589 TransactionOutputView GetOutput(size_t index) const 590 { 591 return TransactionOutputView{btck_transaction_get_output_at(impl(), index)}; 592 } 593 594 TransactionInputView GetInput(size_t index) const 595 { 596 return TransactionInputView{btck_transaction_get_input_at(impl(), index)}; 597 } 598 599 TxidView Txid() const 600 { 601 return TxidView{btck_transaction_get_txid(impl())}; 602 } 603 604 MAKE_RANGE_METHOD(Outputs, Derived, &TransactionApi<Derived>::CountOutputs, &TransactionApi<Derived>::GetOutput, *static_cast<const Derived*>(this)) 605 606 MAKE_RANGE_METHOD(Inputs, Derived, &TransactionApi<Derived>::CountInputs, &TransactionApi<Derived>::GetInput, *static_cast<const Derived*>(this)) 607 608 std::vector<std::byte> ToBytes() const 609 { 610 return write_bytes(impl(), btck_transaction_to_bytes); 611 } 612 }; 613 614 class TransactionView : public View<btck_Transaction>, public TransactionApi<TransactionView> 615 { 616 public: 617 explicit TransactionView(const btck_Transaction* ptr) : View{ptr} {} 618 }; 619 620 class Transaction : public Handle<btck_Transaction, btck_transaction_copy, btck_transaction_destroy>, public TransactionApi<Transaction> 621 { 622 public: 623 explicit Transaction(std::span<const std::byte> raw_transaction) 624 : Handle{btck_transaction_create(raw_transaction.data(), raw_transaction.size())} {} 625 626 Transaction(const TransactionView& view) 627 : Handle{view} {} 628 }; 629 630 class PrecomputedTransactionData : public Handle<btck_PrecomputedTransactionData, btck_precomputed_transaction_data_copy, btck_precomputed_transaction_data_destroy> 631 { 632 public: 633 explicit PrecomputedTransactionData(const Transaction& tx_to, std::span<const TransactionOutput> spent_outputs) 634 : Handle{btck_precomputed_transaction_data_create( 635 tx_to.get(), 636 reinterpret_cast<const btck_TransactionOutput**>( 637 const_cast<TransactionOutput*>(spent_outputs.data())), 638 spent_outputs.size())} {} 639 }; 640 641 template <typename Derived> 642 bool ScriptPubkeyApi<Derived>::Verify(int64_t amount, 643 const Transaction& tx_to, 644 const PrecomputedTransactionData* precomputed_txdata, 645 unsigned int input_index, 646 ScriptVerificationFlags flags, 647 ScriptVerifyStatus& status) const 648 { 649 auto result = btck_script_pubkey_verify( 650 impl(), 651 amount, 652 tx_to.get(), 653 precomputed_txdata ? precomputed_txdata->get() : nullptr, 654 input_index, 655 static_cast<btck_ScriptVerificationFlags>(flags), 656 reinterpret_cast<btck_ScriptVerifyStatus*>(&status)); 657 return result == 1; 658 } 659 660 template <typename Derived> 661 class BlockHashApi 662 { 663 private: 664 auto impl() const 665 { 666 return static_cast<const Derived*>(this)->get(); 667 } 668 669 public: 670 bool operator==(const Derived& other) const 671 { 672 return btck_block_hash_equals(impl(), other.get()) != 0; 673 } 674 675 bool operator!=(const Derived& other) const 676 { 677 return btck_block_hash_equals(impl(), other.get()) == 0; 678 } 679 680 std::array<std::byte, 32> ToBytes() const 681 { 682 std::array<std::byte, 32> hash; 683 btck_block_hash_to_bytes(impl(), reinterpret_cast<unsigned char*>(hash.data())); 684 return hash; 685 } 686 }; 687 688 class BlockHashView : public View<btck_BlockHash>, public BlockHashApi<BlockHashView> 689 { 690 public: 691 explicit BlockHashView(const btck_BlockHash* ptr) : View{ptr} {} 692 }; 693 694 class BlockHash : public Handle<btck_BlockHash, btck_block_hash_copy, btck_block_hash_destroy>, public BlockHashApi<BlockHash> 695 { 696 public: 697 explicit BlockHash(const std::array<std::byte, 32>& hash) 698 : Handle{btck_block_hash_create(reinterpret_cast<const unsigned char*>(hash.data()))} {} 699 700 explicit BlockHash(btck_BlockHash* hash) 701 : Handle{hash} {} 702 703 BlockHash(const BlockHashView& view) 704 : Handle{view} {} 705 }; 706 707 template <typename Derived> 708 class BlockHeaderApi 709 { 710 private: 711 auto impl() const 712 { 713 return static_cast<const Derived*>(this)->get(); 714 } 715 716 friend Derived; 717 BlockHeaderApi() = default; 718 719 public: 720 BlockHash Hash() const 721 { 722 return BlockHash{btck_block_header_get_hash(impl())}; 723 } 724 725 BlockHashView PrevHash() const 726 { 727 return BlockHashView{btck_block_header_get_prev_hash(impl())}; 728 } 729 730 uint32_t Timestamp() const 731 { 732 return btck_block_header_get_timestamp(impl()); 733 } 734 735 uint32_t Bits() const 736 { 737 return btck_block_header_get_bits(impl()); 738 } 739 740 int32_t Version() const 741 { 742 return btck_block_header_get_version(impl()); 743 } 744 745 uint32_t Nonce() const 746 { 747 return btck_block_header_get_nonce(impl()); 748 } 749 }; 750 751 class BlockHeaderView : public View<btck_BlockHeader>, public BlockHeaderApi<BlockHeaderView> 752 { 753 public: 754 explicit BlockHeaderView(const btck_BlockHeader* ptr) : View{ptr} {} 755 }; 756 757 class BlockHeader : public Handle<btck_BlockHeader, btck_block_header_copy, btck_block_header_destroy>, public BlockHeaderApi<BlockHeader> 758 { 759 public: 760 explicit BlockHeader(std::span<const std::byte> raw_header) 761 : Handle{btck_block_header_create(reinterpret_cast<const unsigned char*>(raw_header.data()), raw_header.size())} {} 762 763 BlockHeader(const BlockHeaderView& view) 764 : Handle{view} {} 765 766 BlockHeader(btck_BlockHeader* header) 767 : Handle{header} {} 768 }; 769 770 class Block : public Handle<btck_Block, btck_block_copy, btck_block_destroy> 771 { 772 public: 773 Block(const std::span<const std::byte> raw_block) 774 : Handle{btck_block_create(raw_block.data(), raw_block.size())} 775 { 776 } 777 778 Block(btck_Block* block) : Handle{block} {} 779 780 size_t CountTransactions() const 781 { 782 return btck_block_count_transactions(get()); 783 } 784 785 TransactionView GetTransaction(size_t index) const 786 { 787 return TransactionView{btck_block_get_transaction_at(get(), index)}; 788 } 789 790 MAKE_RANGE_METHOD(Transactions, Block, &Block::CountTransactions, &Block::GetTransaction, *this) 791 792 BlockHash GetHash() const 793 { 794 return BlockHash{btck_block_get_hash(get())}; 795 } 796 797 BlockHeader GetHeader() const 798 { 799 return BlockHeader{btck_block_get_header(get())}; 800 } 801 802 std::vector<std::byte> ToBytes() const 803 { 804 return write_bytes(get(), btck_block_to_bytes); 805 } 806 }; 807 808 inline void logging_disable() 809 { 810 btck_logging_disable(); 811 } 812 813 inline void logging_set_options(const btck_LoggingOptions& logging_options) 814 { 815 btck_logging_set_options(logging_options); 816 } 817 818 inline void logging_set_level_category(LogCategory category, LogLevel level) 819 { 820 btck_logging_set_level_category(static_cast<btck_LogCategory>(category), static_cast<btck_LogLevel>(level)); 821 } 822 823 inline void logging_enable_category(LogCategory category) 824 { 825 btck_logging_enable_category(static_cast<btck_LogCategory>(category)); 826 } 827 828 inline void logging_disable_category(LogCategory category) 829 { 830 btck_logging_disable_category(static_cast<btck_LogCategory>(category)); 831 } 832 833 template <typename T> 834 concept Log = requires(T a, std::string_view message) { 835 { a.LogMessage(message) } -> std::same_as<void>; 836 }; 837 838 template <Log T> 839 class Logger : UniqueHandle<btck_LoggingConnection, btck_logging_connection_destroy> 840 { 841 public: 842 Logger(std::unique_ptr<T> log) 843 : UniqueHandle{btck_logging_connection_create( 844 +[](void* user_data, const char* message, size_t message_len) { static_cast<T*>(user_data)->LogMessage({message, message_len}); }, 845 log.release(), 846 +[](void* user_data) { delete static_cast<T*>(user_data); })} 847 { 848 } 849 }; 850 851 class BlockTreeEntry : public View<btck_BlockTreeEntry> 852 { 853 public: 854 BlockTreeEntry(const btck_BlockTreeEntry* entry) 855 : View{entry} 856 { 857 } 858 859 bool operator==(const BlockTreeEntry& other) const 860 { 861 return btck_block_tree_entry_equals(get(), other.get()) != 0; 862 } 863 864 std::optional<BlockTreeEntry> GetPrevious() const 865 { 866 auto entry{btck_block_tree_entry_get_previous(get())}; 867 if (!entry) return std::nullopt; 868 return entry; 869 } 870 871 int32_t GetHeight() const 872 { 873 return btck_block_tree_entry_get_height(get()); 874 } 875 876 BlockHashView GetHash() const 877 { 878 return BlockHashView{btck_block_tree_entry_get_block_hash(get())}; 879 } 880 881 BlockHeader GetHeader() const 882 { 883 return BlockHeader{btck_block_tree_entry_get_block_header(get())}; 884 } 885 }; 886 887 class KernelNotifications 888 { 889 public: 890 virtual ~KernelNotifications() = default; 891 892 virtual void BlockTipHandler(SynchronizationState state, BlockTreeEntry entry, double verification_progress) {} 893 894 virtual void HeaderTipHandler(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) {} 895 896 virtual void ProgressHandler(std::string_view title, int progress_percent, bool resume_possible) {} 897 898 virtual void WarningSetHandler(Warning warning, std::string_view message) {} 899 900 virtual void WarningUnsetHandler(Warning warning) {} 901 902 virtual void FlushErrorHandler(std::string_view error) {} 903 904 virtual void FatalErrorHandler(std::string_view error) {} 905 }; 906 907 template <typename Derived> 908 class BlockValidationStateApi 909 { 910 private: 911 auto impl() const 912 { 913 return static_cast<const Derived*>(this)->get(); 914 } 915 916 friend Derived; 917 BlockValidationStateApi() = default; 918 919 public: 920 ValidationMode GetValidationMode() const 921 { 922 return static_cast<ValidationMode>(btck_block_validation_state_get_validation_mode(impl())); 923 } 924 925 BlockValidationResult GetBlockValidationResult() const 926 { 927 return static_cast<BlockValidationResult>(btck_block_validation_state_get_block_validation_result(impl())); 928 } 929 }; 930 931 class BlockValidationStateView : public View<btck_BlockValidationState>, public BlockValidationStateApi<BlockValidationStateView> 932 { 933 public: 934 explicit BlockValidationStateView(const btck_BlockValidationState* ptr) : View{ptr} {} 935 }; 936 937 class BlockValidationState : public Handle<btck_BlockValidationState, btck_block_validation_state_copy, btck_block_validation_state_destroy>, public BlockValidationStateApi<BlockValidationState> 938 { 939 public: 940 explicit BlockValidationState() : Handle{btck_block_validation_state_create()} {} 941 942 BlockValidationState(const BlockValidationStateView& view) : Handle{view} {} 943 }; 944 945 class ValidationInterface 946 { 947 public: 948 virtual ~ValidationInterface() = default; 949 950 virtual void BlockChecked(Block block, BlockValidationStateView state) {} 951 952 virtual void PowValidBlock(BlockTreeEntry entry, Block block) {} 953 954 virtual void BlockConnected(Block block, BlockTreeEntry entry) {} 955 956 virtual void BlockDisconnected(Block block, BlockTreeEntry entry) {} 957 }; 958 959 class ChainParams : public Handle<btck_ChainParameters, btck_chain_parameters_copy, btck_chain_parameters_destroy> 960 { 961 public: 962 ChainParams(ChainType chain_type) 963 : Handle{btck_chain_parameters_create(static_cast<btck_ChainType>(chain_type))} {} 964 }; 965 966 class ContextOptions : public UniqueHandle<btck_ContextOptions, btck_context_options_destroy> 967 { 968 public: 969 ContextOptions() : UniqueHandle{btck_context_options_create()} {} 970 971 void SetChainParams(ChainParams& chain_params) 972 { 973 btck_context_options_set_chainparams(get(), chain_params.get()); 974 } 975 976 template <typename T> 977 void SetNotifications(std::shared_ptr<T> notifications) 978 { 979 static_assert(std::is_base_of_v<KernelNotifications, T>); 980 auto heap_notifications = std::make_unique<std::shared_ptr<T>>(std::move(notifications)); 981 using user_type = std::shared_ptr<T>*; 982 btck_context_options_set_notifications( 983 get(), 984 btck_NotificationInterfaceCallbacks{ 985 .user_data = heap_notifications.release(), 986 .user_data_destroy = +[](void* user_data) { delete static_cast<user_type>(user_data); }, 987 .block_tip = +[](void* user_data, btck_SynchronizationState state, const btck_BlockTreeEntry* entry, double verification_progress) { (*static_cast<user_type>(user_data))->BlockTipHandler(static_cast<SynchronizationState>(state), BlockTreeEntry{entry}, verification_progress); }, 988 .header_tip = +[](void* user_data, btck_SynchronizationState state, int64_t height, int64_t timestamp, int presync) { (*static_cast<user_type>(user_data))->HeaderTipHandler(static_cast<SynchronizationState>(state), height, timestamp, presync == 1); }, 989 .progress = +[](void* user_data, const char* title, size_t title_len, int progress_percent, int resume_possible) { (*static_cast<user_type>(user_data))->ProgressHandler({title, title_len}, progress_percent, resume_possible == 1); }, 990 .warning_set = +[](void* user_data, btck_Warning warning, const char* message, size_t message_len) { (*static_cast<user_type>(user_data))->WarningSetHandler(static_cast<Warning>(warning), {message, message_len}); }, 991 .warning_unset = +[](void* user_data, btck_Warning warning) { (*static_cast<user_type>(user_data))->WarningUnsetHandler(static_cast<Warning>(warning)); }, 992 .flush_error = +[](void* user_data, const char* error, size_t error_len) { (*static_cast<user_type>(user_data))->FlushErrorHandler({error, error_len}); }, 993 .fatal_error = +[](void* user_data, const char* error, size_t error_len) { (*static_cast<user_type>(user_data))->FatalErrorHandler({error, error_len}); }, 994 }); 995 } 996 997 template <typename T> 998 void SetValidationInterface(std::shared_ptr<T> validation_interface) 999 { 1000 static_assert(std::is_base_of_v<ValidationInterface, T>); 1001 auto heap_vi = std::make_unique<std::shared_ptr<T>>(std::move(validation_interface)); 1002 using user_type = std::shared_ptr<T>*; 1003 btck_context_options_set_validation_interface( 1004 get(), 1005 btck_ValidationInterfaceCallbacks{ 1006 .user_data = heap_vi.release(), 1007 .user_data_destroy = +[](void* user_data) { delete static_cast<user_type>(user_data); }, 1008 .block_checked = +[](void* user_data, btck_Block* block, const btck_BlockValidationState* state) { (*static_cast<user_type>(user_data))->BlockChecked(Block{block}, BlockValidationStateView{state}); }, 1009 .pow_valid_block = +[](void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry) { (*static_cast<user_type>(user_data))->PowValidBlock(BlockTreeEntry{entry}, Block{block}); }, 1010 .block_connected = +[](void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry) { (*static_cast<user_type>(user_data))->BlockConnected(Block{block}, BlockTreeEntry{entry}); }, 1011 .block_disconnected = +[](void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry) { (*static_cast<user_type>(user_data))->BlockDisconnected(Block{block}, BlockTreeEntry{entry}); }, 1012 }); 1013 } 1014 }; 1015 1016 class Context : public Handle<btck_Context, btck_context_copy, btck_context_destroy> 1017 { 1018 public: 1019 Context(ContextOptions& opts) 1020 : Handle{btck_context_create(opts.get())} {} 1021 1022 Context() 1023 : Handle{btck_context_create(ContextOptions{}.get())} {} 1024 1025 bool interrupt() 1026 { 1027 return btck_context_interrupt(get()) == 0; 1028 } 1029 }; 1030 1031 class ChainstateManagerOptions : public UniqueHandle<btck_ChainstateManagerOptions, btck_chainstate_manager_options_destroy> 1032 { 1033 public: 1034 ChainstateManagerOptions(const Context& context, std::string_view data_dir, std::string_view blocks_dir) 1035 : UniqueHandle{btck_chainstate_manager_options_create( 1036 context.get(), data_dir.data(), data_dir.length(), blocks_dir.data(), blocks_dir.length())} 1037 { 1038 } 1039 1040 void SetWorkerThreads(int worker_threads) 1041 { 1042 btck_chainstate_manager_options_set_worker_threads_num(get(), worker_threads); 1043 } 1044 1045 bool SetWipeDbs(bool wipe_block_tree, bool wipe_chainstate) 1046 { 1047 return btck_chainstate_manager_options_set_wipe_dbs(get(), wipe_block_tree, wipe_chainstate) == 0; 1048 } 1049 1050 void UpdateBlockTreeDbInMemory(bool block_tree_db_in_memory) 1051 { 1052 btck_chainstate_manager_options_update_block_tree_db_in_memory(get(), block_tree_db_in_memory); 1053 } 1054 1055 void UpdateChainstateDbInMemory(bool chainstate_db_in_memory) 1056 { 1057 btck_chainstate_manager_options_update_chainstate_db_in_memory(get(), chainstate_db_in_memory); 1058 } 1059 }; 1060 1061 class ChainView : public View<btck_Chain> 1062 { 1063 public: 1064 explicit ChainView(const btck_Chain* ptr) : View{ptr} {} 1065 1066 int32_t Height() const 1067 { 1068 return btck_chain_get_height(get()); 1069 } 1070 1071 int CountEntries() const 1072 { 1073 return btck_chain_get_height(get()) + 1; 1074 } 1075 1076 BlockTreeEntry GetByHeight(int height) const 1077 { 1078 auto index{btck_chain_get_by_height(get(), height)}; 1079 if (!index) throw std::runtime_error("No entry in the chain at the provided height"); 1080 return index; 1081 } 1082 1083 bool Contains(BlockTreeEntry& entry) const 1084 { 1085 return btck_chain_contains(get(), entry.get()); 1086 } 1087 1088 MAKE_RANGE_METHOD(Entries, ChainView, &ChainView::CountEntries, &ChainView::GetByHeight, *this) 1089 }; 1090 1091 template <typename Derived> 1092 class CoinApi 1093 { 1094 private: 1095 auto impl() const 1096 { 1097 return static_cast<const Derived*>(this)->get(); 1098 } 1099 1100 friend Derived; 1101 CoinApi() = default; 1102 1103 public: 1104 uint32_t GetConfirmationHeight() const { return btck_coin_confirmation_height(impl()); } 1105 1106 bool IsCoinbase() const { return btck_coin_is_coinbase(impl()) == 1; } 1107 1108 TransactionOutputView GetOutput() const 1109 { 1110 return TransactionOutputView{btck_coin_get_output(impl())}; 1111 } 1112 }; 1113 1114 class CoinView : public View<btck_Coin>, public CoinApi<CoinView> 1115 { 1116 public: 1117 explicit CoinView(const btck_Coin* ptr) : View{ptr} {} 1118 }; 1119 1120 class Coin : public Handle<btck_Coin, btck_coin_copy, btck_coin_destroy>, public CoinApi<Coin> 1121 { 1122 public: 1123 Coin(btck_Coin* coin) : Handle{coin} {} 1124 1125 Coin(const CoinView& view) : Handle{view} {} 1126 }; 1127 1128 template <typename Derived> 1129 class TransactionSpentOutputsApi 1130 { 1131 private: 1132 auto impl() const 1133 { 1134 return static_cast<const Derived*>(this)->get(); 1135 } 1136 1137 friend Derived; 1138 TransactionSpentOutputsApi() = default; 1139 1140 public: 1141 size_t Count() const 1142 { 1143 return btck_transaction_spent_outputs_count(impl()); 1144 } 1145 1146 CoinView GetCoin(size_t index) const 1147 { 1148 return CoinView{btck_transaction_spent_outputs_get_coin_at(impl(), index)}; 1149 } 1150 1151 MAKE_RANGE_METHOD(Coins, Derived, &TransactionSpentOutputsApi<Derived>::Count, &TransactionSpentOutputsApi<Derived>::GetCoin, *static_cast<const Derived*>(this)) 1152 }; 1153 1154 class TransactionSpentOutputsView : public View<btck_TransactionSpentOutputs>, public TransactionSpentOutputsApi<TransactionSpentOutputsView> 1155 { 1156 public: 1157 explicit TransactionSpentOutputsView(const btck_TransactionSpentOutputs* ptr) : View{ptr} {} 1158 }; 1159 1160 class TransactionSpentOutputs : public Handle<btck_TransactionSpentOutputs, btck_transaction_spent_outputs_copy, btck_transaction_spent_outputs_destroy>, 1161 public TransactionSpentOutputsApi<TransactionSpentOutputs> 1162 { 1163 public: 1164 TransactionSpentOutputs(btck_TransactionSpentOutputs* transaction_spent_outputs) : Handle{transaction_spent_outputs} {} 1165 1166 TransactionSpentOutputs(const TransactionSpentOutputsView& view) : Handle{view} {} 1167 }; 1168 1169 class BlockSpentOutputs : public Handle<btck_BlockSpentOutputs, btck_block_spent_outputs_copy, btck_block_spent_outputs_destroy> 1170 { 1171 public: 1172 BlockSpentOutputs(btck_BlockSpentOutputs* block_spent_outputs) 1173 : Handle{block_spent_outputs} 1174 { 1175 } 1176 1177 size_t Count() const 1178 { 1179 return btck_block_spent_outputs_count(get()); 1180 } 1181 1182 TransactionSpentOutputsView GetTxSpentOutputs(size_t tx_undo_index) const 1183 { 1184 return TransactionSpentOutputsView{btck_block_spent_outputs_get_transaction_spent_outputs_at(get(), tx_undo_index)}; 1185 } 1186 1187 MAKE_RANGE_METHOD(TxsSpentOutputs, BlockSpentOutputs, &BlockSpentOutputs::Count, &BlockSpentOutputs::GetTxSpentOutputs, *this) 1188 }; 1189 1190 class ChainMan : UniqueHandle<btck_ChainstateManager, btck_chainstate_manager_destroy> 1191 { 1192 public: 1193 ChainMan(const Context& context, const ChainstateManagerOptions& chainman_opts) 1194 : UniqueHandle{btck_chainstate_manager_create(chainman_opts.get())} 1195 { 1196 } 1197 1198 bool ImportBlocks(const std::span<const std::string> paths) 1199 { 1200 std::vector<const char*> c_paths; 1201 std::vector<size_t> c_paths_lens; 1202 c_paths.reserve(paths.size()); 1203 c_paths_lens.reserve(paths.size()); 1204 for (const auto& path : paths) { 1205 c_paths.push_back(path.c_str()); 1206 c_paths_lens.push_back(path.length()); 1207 } 1208 1209 return btck_chainstate_manager_import_blocks(get(), c_paths.data(), c_paths_lens.data(), c_paths.size()) == 0; 1210 } 1211 1212 bool ProcessBlock(const Block& block, bool* new_block) 1213 { 1214 int _new_block; 1215 int res = btck_chainstate_manager_process_block(get(), block.get(), &_new_block); 1216 if (new_block) *new_block = _new_block == 1; 1217 return res == 0; 1218 } 1219 1220 bool ProcessBlockHeader(const BlockHeader& header, BlockValidationState& state) 1221 { 1222 return btck_chainstate_manager_process_block_header(get(), header.get(), state.get()) == 0; 1223 } 1224 1225 ChainView GetChain() const 1226 { 1227 return ChainView{btck_chainstate_manager_get_active_chain(get())}; 1228 } 1229 1230 std::optional<BlockTreeEntry> GetBlockTreeEntry(const BlockHash& block_hash) const 1231 { 1232 auto entry{btck_chainstate_manager_get_block_tree_entry_by_hash(get(), block_hash.get())}; 1233 if (!entry) return std::nullopt; 1234 return entry; 1235 } 1236 1237 BlockTreeEntry GetBestEntry() const 1238 { 1239 return btck_chainstate_manager_get_best_entry(get()); 1240 } 1241 1242 std::optional<Block> ReadBlock(const BlockTreeEntry& entry) const 1243 { 1244 auto block{btck_block_read(get(), entry.get())}; 1245 if (!block) return std::nullopt; 1246 return block; 1247 } 1248 1249 BlockSpentOutputs ReadBlockSpentOutputs(const BlockTreeEntry& entry) const 1250 { 1251 return btck_block_spent_outputs_read(get(), entry.get()); 1252 } 1253 }; 1254 1255 } // namespace btck 1256 1257 #endif // BITCOIN_KERNEL_BITCOINKERNEL_WRAPPER_H