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 Transaction; 371 class TransactionOutput; 372 373 template <typename Derived> 374 class ScriptPubkeyApi 375 { 376 private: 377 auto impl() const 378 { 379 return static_cast<const Derived*>(this)->get(); 380 } 381 382 friend Derived; 383 ScriptPubkeyApi() = default; 384 385 public: 386 bool Verify(int64_t amount, 387 const Transaction& tx_to, 388 std::span<const TransactionOutput> spent_outputs, 389 unsigned int input_index, 390 ScriptVerificationFlags flags, 391 ScriptVerifyStatus& status) const; 392 393 std::vector<std::byte> ToBytes() const 394 { 395 return write_bytes(impl(), btck_script_pubkey_to_bytes); 396 } 397 }; 398 399 class ScriptPubkeyView : public View<btck_ScriptPubkey>, public ScriptPubkeyApi<ScriptPubkeyView> 400 { 401 public: 402 explicit ScriptPubkeyView(const btck_ScriptPubkey* ptr) : View{ptr} {} 403 }; 404 405 class ScriptPubkey : public Handle<btck_ScriptPubkey, btck_script_pubkey_copy, btck_script_pubkey_destroy>, public ScriptPubkeyApi<ScriptPubkey> 406 { 407 public: 408 explicit ScriptPubkey(std::span<const std::byte> raw) 409 : Handle{btck_script_pubkey_create(raw.data(), raw.size())} {} 410 411 ScriptPubkey(const ScriptPubkeyView& view) 412 : Handle(view) {} 413 }; 414 415 template <typename Derived> 416 class TransactionOutputApi 417 { 418 private: 419 auto impl() const 420 { 421 return static_cast<const Derived*>(this)->get(); 422 } 423 424 friend Derived; 425 TransactionOutputApi() = default; 426 427 public: 428 int64_t Amount() const 429 { 430 return btck_transaction_output_get_amount(impl()); 431 } 432 433 ScriptPubkeyView GetScriptPubkey() const 434 { 435 return ScriptPubkeyView{btck_transaction_output_get_script_pubkey(impl())}; 436 } 437 }; 438 439 class TransactionOutputView : public View<btck_TransactionOutput>, public TransactionOutputApi<TransactionOutputView> 440 { 441 public: 442 explicit TransactionOutputView(const btck_TransactionOutput* ptr) : View{ptr} {} 443 }; 444 445 class TransactionOutput : public Handle<btck_TransactionOutput, btck_transaction_output_copy, btck_transaction_output_destroy>, public TransactionOutputApi<TransactionOutput> 446 { 447 public: 448 explicit TransactionOutput(const ScriptPubkey& script_pubkey, int64_t amount) 449 : Handle{btck_transaction_output_create(script_pubkey.get(), amount)} {} 450 451 TransactionOutput(const TransactionOutputView& view) 452 : Handle(view) {} 453 }; 454 455 template <typename Derived> 456 class TxidApi 457 { 458 private: 459 auto impl() const 460 { 461 return static_cast<const Derived*>(this)->get(); 462 } 463 464 friend Derived; 465 TxidApi() = default; 466 467 public: 468 bool operator==(const TxidApi& other) const 469 { 470 return btck_txid_equals(impl(), other.impl()) != 0; 471 } 472 473 bool operator!=(const TxidApi& other) const 474 { 475 return btck_txid_equals(impl(), other.impl()) == 0; 476 } 477 478 std::array<std::byte, 32> ToBytes() const 479 { 480 std::array<std::byte, 32> hash; 481 btck_txid_to_bytes(impl(), reinterpret_cast<unsigned char*>(hash.data())); 482 return hash; 483 } 484 }; 485 486 class TxidView : public View<btck_Txid>, public TxidApi<TxidView> 487 { 488 public: 489 explicit TxidView(const btck_Txid* ptr) : View{ptr} {} 490 }; 491 492 class Txid : public Handle<btck_Txid, btck_txid_copy, btck_txid_destroy>, public TxidApi<Txid> 493 { 494 public: 495 Txid(const TxidView& view) 496 : Handle(view) {} 497 }; 498 499 template <typename Derived> 500 class OutPointApi 501 { 502 private: 503 auto impl() const 504 { 505 return static_cast<const Derived*>(this)->get(); 506 } 507 508 friend Derived; 509 OutPointApi() = default; 510 511 public: 512 uint32_t index() const 513 { 514 return btck_transaction_out_point_get_index(impl()); 515 } 516 517 TxidView Txid() const 518 { 519 return TxidView{btck_transaction_out_point_get_txid(impl())}; 520 } 521 }; 522 523 class OutPointView : public View<btck_TransactionOutPoint>, public OutPointApi<OutPointView> 524 { 525 public: 526 explicit OutPointView(const btck_TransactionOutPoint* ptr) : View{ptr} {} 527 }; 528 529 class OutPoint : public Handle<btck_TransactionOutPoint, btck_transaction_out_point_copy, btck_transaction_out_point_destroy>, public OutPointApi<OutPoint> 530 { 531 public: 532 OutPoint(const OutPointView& view) 533 : Handle(view) {} 534 }; 535 536 template <typename Derived> 537 class TransactionInputApi 538 { 539 private: 540 auto impl() const 541 { 542 return static_cast<const Derived*>(this)->get(); 543 } 544 545 friend Derived; 546 TransactionInputApi() = default; 547 548 public: 549 OutPointView OutPoint() const 550 { 551 return OutPointView{btck_transaction_input_get_out_point(impl())}; 552 } 553 }; 554 555 class TransactionInputView : public View<btck_TransactionInput>, public TransactionInputApi<TransactionInputView> 556 { 557 public: 558 explicit TransactionInputView(const btck_TransactionInput* ptr) : View{ptr} {} 559 }; 560 561 class TransactionInput : public Handle<btck_TransactionInput, btck_transaction_input_copy, btck_transaction_input_destroy>, public TransactionInputApi<TransactionInput> 562 { 563 public: 564 TransactionInput(const TransactionInputView& view) 565 : Handle(view) {} 566 }; 567 568 template <typename Derived> 569 class TransactionApi 570 { 571 private: 572 auto impl() const 573 { 574 return static_cast<const Derived*>(this)->get(); 575 } 576 577 public: 578 size_t CountOutputs() const 579 { 580 return btck_transaction_count_outputs(impl()); 581 } 582 583 size_t CountInputs() const 584 { 585 return btck_transaction_count_inputs(impl()); 586 } 587 588 TransactionOutputView GetOutput(size_t index) const 589 { 590 return TransactionOutputView{btck_transaction_get_output_at(impl(), index)}; 591 } 592 593 TransactionInputView GetInput(size_t index) const 594 { 595 return TransactionInputView{btck_transaction_get_input_at(impl(), index)}; 596 } 597 598 TxidView Txid() const 599 { 600 return TxidView{btck_transaction_get_txid(impl())}; 601 } 602 603 MAKE_RANGE_METHOD(Outputs, Derived, &TransactionApi<Derived>::CountOutputs, &TransactionApi<Derived>::GetOutput, *static_cast<const Derived*>(this)) 604 605 MAKE_RANGE_METHOD(Inputs, Derived, &TransactionApi<Derived>::CountInputs, &TransactionApi<Derived>::GetInput, *static_cast<const Derived*>(this)) 606 607 std::vector<std::byte> ToBytes() const 608 { 609 return write_bytes(impl(), btck_transaction_to_bytes); 610 } 611 }; 612 613 class TransactionView : public View<btck_Transaction>, public TransactionApi<TransactionView> 614 { 615 public: 616 explicit TransactionView(const btck_Transaction* ptr) : View{ptr} {} 617 }; 618 619 class Transaction : public Handle<btck_Transaction, btck_transaction_copy, btck_transaction_destroy>, public TransactionApi<Transaction> 620 { 621 public: 622 explicit Transaction(std::span<const std::byte> raw_transaction) 623 : Handle{btck_transaction_create(raw_transaction.data(), raw_transaction.size())} {} 624 625 Transaction(const TransactionView& view) 626 : Handle{view} {} 627 }; 628 629 template <typename Derived> 630 bool ScriptPubkeyApi<Derived>::Verify(int64_t amount, 631 const Transaction& tx_to, 632 const std::span<const TransactionOutput> spent_outputs, 633 unsigned int input_index, 634 ScriptVerificationFlags flags, 635 ScriptVerifyStatus& status) const 636 { 637 const btck_TransactionOutput** spent_outputs_ptr = nullptr; 638 std::vector<const btck_TransactionOutput*> raw_spent_outputs; 639 if (spent_outputs.size() > 0) { 640 raw_spent_outputs.reserve(spent_outputs.size()); 641 642 for (const auto& output : spent_outputs) { 643 raw_spent_outputs.push_back(output.get()); 644 } 645 spent_outputs_ptr = raw_spent_outputs.data(); 646 } 647 auto result = btck_script_pubkey_verify( 648 impl(), 649 amount, 650 tx_to.get(), 651 spent_outputs_ptr, spent_outputs.size(), 652 input_index, 653 static_cast<btck_ScriptVerificationFlags>(flags), 654 reinterpret_cast<btck_ScriptVerifyStatus*>(&status)); 655 return result == 1; 656 } 657 658 template <typename Derived> 659 class BlockHashApi 660 { 661 private: 662 auto impl() const 663 { 664 return static_cast<const Derived*>(this)->get(); 665 } 666 667 public: 668 bool operator==(const Derived& other) const 669 { 670 return btck_block_hash_equals(impl(), other.get()) != 0; 671 } 672 673 bool operator!=(const Derived& other) const 674 { 675 return btck_block_hash_equals(impl(), other.get()) == 0; 676 } 677 678 std::array<std::byte, 32> ToBytes() const 679 { 680 std::array<std::byte, 32> hash; 681 btck_block_hash_to_bytes(impl(), reinterpret_cast<unsigned char*>(hash.data())); 682 return hash; 683 } 684 }; 685 686 class BlockHashView: public View<btck_BlockHash>, public BlockHashApi<BlockHashView> 687 { 688 public: 689 explicit BlockHashView(const btck_BlockHash* ptr) : View{ptr} {} 690 }; 691 692 class BlockHash : public Handle<btck_BlockHash, btck_block_hash_copy, btck_block_hash_destroy>, public BlockHashApi<BlockHash> 693 { 694 public: 695 explicit BlockHash(const std::array<std::byte, 32>& hash) 696 : Handle{btck_block_hash_create(reinterpret_cast<const unsigned char*>(hash.data()))} {} 697 698 explicit BlockHash(btck_BlockHash* hash) 699 : Handle{hash} {} 700 701 BlockHash(const BlockHashView& view) 702 : Handle{view} {} 703 }; 704 705 class Block : public Handle<btck_Block, btck_block_copy, btck_block_destroy> 706 { 707 public: 708 Block(const std::span<const std::byte> raw_block) 709 : Handle{btck_block_create(raw_block.data(), raw_block.size())} 710 { 711 } 712 713 Block(btck_Block* block) : Handle{block} {} 714 715 size_t CountTransactions() const 716 { 717 return btck_block_count_transactions(get()); 718 } 719 720 TransactionView GetTransaction(size_t index) const 721 { 722 return TransactionView{btck_block_get_transaction_at(get(), index)}; 723 } 724 725 MAKE_RANGE_METHOD(Transactions, Block, &Block::CountTransactions, &Block::GetTransaction, *this) 726 727 BlockHash GetHash() const 728 { 729 return BlockHash{btck_block_get_hash(get())}; 730 } 731 732 std::vector<std::byte> ToBytes() const 733 { 734 return write_bytes(get(), btck_block_to_bytes); 735 } 736 }; 737 738 inline void logging_disable() 739 { 740 btck_logging_disable(); 741 } 742 743 inline void logging_set_options(const btck_LoggingOptions& logging_options) 744 { 745 btck_logging_set_options(logging_options); 746 } 747 748 inline void logging_set_level_category(LogCategory category, LogLevel level) 749 { 750 btck_logging_set_level_category(static_cast<btck_LogCategory>(category), static_cast<btck_LogLevel>(level)); 751 } 752 753 inline void logging_enable_category(LogCategory category) 754 { 755 btck_logging_enable_category(static_cast<btck_LogCategory>(category)); 756 } 757 758 inline void logging_disable_category(LogCategory category) 759 { 760 btck_logging_disable_category(static_cast<btck_LogCategory>(category)); 761 } 762 763 template <typename T> 764 concept Log = requires(T a, std::string_view message) { 765 { a.LogMessage(message) } -> std::same_as<void>; 766 }; 767 768 template <Log T> 769 class Logger : UniqueHandle<btck_LoggingConnection, btck_logging_connection_destroy> 770 { 771 public: 772 Logger(std::unique_ptr<T> log) 773 : UniqueHandle{btck_logging_connection_create( 774 +[](void* user_data, const char* message, size_t message_len) { static_cast<T*>(user_data)->LogMessage({message, message_len}); }, 775 log.release(), 776 +[](void* user_data) { delete static_cast<T*>(user_data); })} 777 { 778 } 779 }; 780 781 class BlockTreeEntry : public View<btck_BlockTreeEntry> 782 { 783 public: 784 BlockTreeEntry(const btck_BlockTreeEntry* entry) 785 : View{entry} 786 { 787 } 788 789 bool operator==(const BlockTreeEntry& other) const 790 { 791 return btck_block_tree_entry_equals(get(), other.get()) != 0; 792 } 793 794 std::optional<BlockTreeEntry> GetPrevious() const 795 { 796 auto entry{btck_block_tree_entry_get_previous(get())}; 797 if (!entry) return std::nullopt; 798 return entry; 799 } 800 801 int32_t GetHeight() const 802 { 803 return btck_block_tree_entry_get_height(get()); 804 } 805 806 BlockHashView GetHash() const 807 { 808 return BlockHashView{btck_block_tree_entry_get_block_hash(get())}; 809 } 810 }; 811 812 class KernelNotifications 813 { 814 public: 815 virtual ~KernelNotifications() = default; 816 817 virtual void BlockTipHandler(SynchronizationState state, BlockTreeEntry entry, double verification_progress) {} 818 819 virtual void HeaderTipHandler(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) {} 820 821 virtual void ProgressHandler(std::string_view title, int progress_percent, bool resume_possible) {} 822 823 virtual void WarningSetHandler(Warning warning, std::string_view message) {} 824 825 virtual void WarningUnsetHandler(Warning warning) {} 826 827 virtual void FlushErrorHandler(std::string_view error) {} 828 829 virtual void FatalErrorHandler(std::string_view error) {} 830 }; 831 832 class BlockValidationState 833 { 834 private: 835 const btck_BlockValidationState* m_state; 836 837 public: 838 BlockValidationState(const btck_BlockValidationState* state) : m_state{state} {} 839 840 BlockValidationState(const BlockValidationState&) = delete; 841 BlockValidationState& operator=(const BlockValidationState&) = delete; 842 BlockValidationState(BlockValidationState&&) = delete; 843 BlockValidationState& operator=(BlockValidationState&&) = delete; 844 845 ValidationMode GetValidationMode() const 846 { 847 return static_cast<ValidationMode>(btck_block_validation_state_get_validation_mode(m_state)); 848 } 849 850 BlockValidationResult GetBlockValidationResult() const 851 { 852 return static_cast<BlockValidationResult>(btck_block_validation_state_get_block_validation_result(m_state)); 853 } 854 }; 855 856 class ValidationInterface 857 { 858 public: 859 virtual ~ValidationInterface() = default; 860 861 virtual void BlockChecked(Block block, const BlockValidationState state) {} 862 863 virtual void PowValidBlock(BlockTreeEntry entry, Block block) {} 864 865 virtual void BlockConnected(Block block, BlockTreeEntry entry) {} 866 867 virtual void BlockDisconnected(Block block, BlockTreeEntry entry) {} 868 }; 869 870 class ChainParams : public Handle<btck_ChainParameters, btck_chain_parameters_copy, btck_chain_parameters_destroy> 871 { 872 public: 873 ChainParams(ChainType chain_type) 874 : Handle{btck_chain_parameters_create(static_cast<btck_ChainType>(chain_type))} {} 875 }; 876 877 class ContextOptions : public UniqueHandle<btck_ContextOptions, btck_context_options_destroy> 878 { 879 public: 880 ContextOptions() : UniqueHandle{btck_context_options_create()} {} 881 882 void SetChainParams(ChainParams& chain_params) 883 { 884 btck_context_options_set_chainparams(get(), chain_params.get()); 885 } 886 887 template <typename T> 888 void SetNotifications(std::shared_ptr<T> notifications) 889 { 890 static_assert(std::is_base_of_v<KernelNotifications, T>); 891 auto heap_notifications = std::make_unique<std::shared_ptr<T>>(std::move(notifications)); 892 using user_type = std::shared_ptr<T>*; 893 btck_context_options_set_notifications( 894 get(), 895 btck_NotificationInterfaceCallbacks{ 896 .user_data = heap_notifications.release(), 897 .user_data_destroy = +[](void* user_data) { delete static_cast<user_type>(user_data); }, 898 .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); }, 899 .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); }, 900 .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); }, 901 .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}); }, 902 .warning_unset = +[](void* user_data, btck_Warning warning) { (*static_cast<user_type>(user_data))->WarningUnsetHandler(static_cast<Warning>(warning)); }, 903 .flush_error = +[](void* user_data, const char* error, size_t error_len) { (*static_cast<user_type>(user_data))->FlushErrorHandler({error, error_len}); }, 904 .fatal_error = +[](void* user_data, const char* error, size_t error_len) { (*static_cast<user_type>(user_data))->FatalErrorHandler({error, error_len}); }, 905 }); 906 } 907 908 template <typename T> 909 void SetValidationInterface(std::shared_ptr<T> validation_interface) 910 { 911 static_assert(std::is_base_of_v<ValidationInterface, T>); 912 auto heap_vi = std::make_unique<std::shared_ptr<T>>(std::move(validation_interface)); 913 using user_type = std::shared_ptr<T>*; 914 btck_context_options_set_validation_interface( 915 get(), 916 btck_ValidationInterfaceCallbacks{ 917 .user_data = heap_vi.release(), 918 .user_data_destroy = +[](void* user_data) { delete static_cast<user_type>(user_data); }, 919 .block_checked = +[](void* user_data, btck_Block* block, const btck_BlockValidationState* state) { (*static_cast<user_type>(user_data))->BlockChecked(Block{block}, BlockValidationState{state}); }, 920 .pow_valid_block = +[](void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry) { (*static_cast<user_type>(user_data))->PowValidBlock(BlockTreeEntry{entry}, Block{block}); }, 921 .block_connected = +[](void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry) { (*static_cast<user_type>(user_data))->BlockConnected(Block{block}, BlockTreeEntry{entry}); }, 922 .block_disconnected = +[](void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry) { (*static_cast<user_type>(user_data))->BlockDisconnected(Block{block}, BlockTreeEntry{entry}); }, 923 }); 924 } 925 }; 926 927 class Context : public Handle<btck_Context, btck_context_copy, btck_context_destroy> 928 { 929 public: 930 Context(ContextOptions& opts) 931 : Handle{btck_context_create(opts.get())} {} 932 933 Context() 934 : Handle{btck_context_create(ContextOptions{}.get())} {} 935 936 bool interrupt() 937 { 938 return btck_context_interrupt(get()) == 0; 939 } 940 }; 941 942 class ChainstateManagerOptions : public UniqueHandle<btck_ChainstateManagerOptions, btck_chainstate_manager_options_destroy> 943 { 944 public: 945 ChainstateManagerOptions(const Context& context, std::string_view data_dir, std::string_view blocks_dir) 946 : UniqueHandle{btck_chainstate_manager_options_create( 947 context.get(), data_dir.data(), data_dir.length(), blocks_dir.data(), blocks_dir.length())} 948 { 949 } 950 951 void SetWorkerThreads(int worker_threads) 952 { 953 btck_chainstate_manager_options_set_worker_threads_num(get(), worker_threads); 954 } 955 956 bool SetWipeDbs(bool wipe_block_tree, bool wipe_chainstate) 957 { 958 return btck_chainstate_manager_options_set_wipe_dbs(get(), wipe_block_tree, wipe_chainstate) == 0; 959 } 960 961 void UpdateBlockTreeDbInMemory(bool block_tree_db_in_memory) 962 { 963 btck_chainstate_manager_options_update_block_tree_db_in_memory(get(), block_tree_db_in_memory); 964 } 965 966 void UpdateChainstateDbInMemory(bool chainstate_db_in_memory) 967 { 968 btck_chainstate_manager_options_update_chainstate_db_in_memory(get(), chainstate_db_in_memory); 969 } 970 }; 971 972 class ChainView : public View<btck_Chain> 973 { 974 public: 975 explicit ChainView(const btck_Chain* ptr) : View{ptr} {} 976 977 int32_t Height() const 978 { 979 return btck_chain_get_height(get()); 980 } 981 982 int CountEntries() const 983 { 984 return btck_chain_get_height(get()) + 1; 985 } 986 987 BlockTreeEntry GetByHeight(int height) const 988 { 989 auto index{btck_chain_get_by_height(get(), height)}; 990 if (!index) throw std::runtime_error("No entry in the chain at the provided height"); 991 return index; 992 } 993 994 bool Contains(BlockTreeEntry& entry) const 995 { 996 return btck_chain_contains(get(), entry.get()); 997 } 998 999 MAKE_RANGE_METHOD(Entries, ChainView, &ChainView::CountEntries, &ChainView::GetByHeight, *this) 1000 }; 1001 1002 template <typename Derived> 1003 class CoinApi 1004 { 1005 private: 1006 auto impl() const 1007 { 1008 return static_cast<const Derived*>(this)->get(); 1009 } 1010 1011 friend Derived; 1012 CoinApi() = default; 1013 1014 public: 1015 uint32_t GetConfirmationHeight() const { return btck_coin_confirmation_height(impl()); } 1016 1017 bool IsCoinbase() const { return btck_coin_is_coinbase(impl()) == 1; } 1018 1019 TransactionOutputView GetOutput() const 1020 { 1021 return TransactionOutputView{btck_coin_get_output(impl())}; 1022 } 1023 }; 1024 1025 class CoinView : public View<btck_Coin>, public CoinApi<CoinView> 1026 { 1027 public: 1028 explicit CoinView(const btck_Coin* ptr) : View{ptr} {} 1029 }; 1030 1031 class Coin : public Handle<btck_Coin, btck_coin_copy, btck_coin_destroy>, public CoinApi<Coin> 1032 { 1033 public: 1034 Coin(btck_Coin* coin) : Handle{coin} {} 1035 1036 Coin(const CoinView& view) : Handle{view} {} 1037 }; 1038 1039 template <typename Derived> 1040 class TransactionSpentOutputsApi 1041 { 1042 private: 1043 auto impl() const 1044 { 1045 return static_cast<const Derived*>(this)->get(); 1046 } 1047 1048 friend Derived; 1049 TransactionSpentOutputsApi() = default; 1050 1051 public: 1052 size_t Count() const 1053 { 1054 return btck_transaction_spent_outputs_count(impl()); 1055 } 1056 1057 CoinView GetCoin(size_t index) const 1058 { 1059 return CoinView{btck_transaction_spent_outputs_get_coin_at(impl(), index)}; 1060 } 1061 1062 MAKE_RANGE_METHOD(Coins, Derived, &TransactionSpentOutputsApi<Derived>::Count, &TransactionSpentOutputsApi<Derived>::GetCoin, *static_cast<const Derived*>(this)) 1063 }; 1064 1065 class TransactionSpentOutputsView : public View<btck_TransactionSpentOutputs>, public TransactionSpentOutputsApi<TransactionSpentOutputsView> 1066 { 1067 public: 1068 explicit TransactionSpentOutputsView(const btck_TransactionSpentOutputs* ptr) : View{ptr} {} 1069 }; 1070 1071 class TransactionSpentOutputs : public Handle<btck_TransactionSpentOutputs, btck_transaction_spent_outputs_copy, btck_transaction_spent_outputs_destroy>, 1072 public TransactionSpentOutputsApi<TransactionSpentOutputs> 1073 { 1074 public: 1075 TransactionSpentOutputs(btck_TransactionSpentOutputs* transaction_spent_outputs) : Handle{transaction_spent_outputs} {} 1076 1077 TransactionSpentOutputs(const TransactionSpentOutputsView& view) : Handle{view} {} 1078 }; 1079 1080 class BlockSpentOutputs : public Handle<btck_BlockSpentOutputs, btck_block_spent_outputs_copy, btck_block_spent_outputs_destroy> 1081 { 1082 public: 1083 BlockSpentOutputs(btck_BlockSpentOutputs* block_spent_outputs) 1084 : Handle{block_spent_outputs} 1085 { 1086 } 1087 1088 size_t Count() const 1089 { 1090 return btck_block_spent_outputs_count(get()); 1091 } 1092 1093 TransactionSpentOutputsView GetTxSpentOutputs(size_t tx_undo_index) const 1094 { 1095 return TransactionSpentOutputsView{btck_block_spent_outputs_get_transaction_spent_outputs_at(get(), tx_undo_index)}; 1096 } 1097 1098 MAKE_RANGE_METHOD(TxsSpentOutputs, BlockSpentOutputs, &BlockSpentOutputs::Count, &BlockSpentOutputs::GetTxSpentOutputs, *this) 1099 }; 1100 1101 class ChainMan : UniqueHandle<btck_ChainstateManager, btck_chainstate_manager_destroy> 1102 { 1103 public: 1104 ChainMan(const Context& context, const ChainstateManagerOptions& chainman_opts) 1105 : UniqueHandle{btck_chainstate_manager_create(chainman_opts.get())} 1106 { 1107 } 1108 1109 bool ImportBlocks(const std::span<const std::string> paths) 1110 { 1111 std::vector<const char*> c_paths; 1112 std::vector<size_t> c_paths_lens; 1113 c_paths.reserve(paths.size()); 1114 c_paths_lens.reserve(paths.size()); 1115 for (const auto& path : paths) { 1116 c_paths.push_back(path.c_str()); 1117 c_paths_lens.push_back(path.length()); 1118 } 1119 1120 return btck_chainstate_manager_import_blocks(get(), c_paths.data(), c_paths_lens.data(), c_paths.size()) == 0; 1121 } 1122 1123 bool ProcessBlock(const Block& block, bool* new_block) 1124 { 1125 int _new_block; 1126 int res = btck_chainstate_manager_process_block(get(), block.get(), &_new_block); 1127 if (new_block) *new_block = _new_block == 1; 1128 return res == 0; 1129 } 1130 1131 ChainView GetChain() const 1132 { 1133 return ChainView{btck_chainstate_manager_get_active_chain(get())}; 1134 } 1135 1136 std::optional<BlockTreeEntry> GetBlockTreeEntry(const BlockHash& block_hash) const 1137 { 1138 auto entry{btck_chainstate_manager_get_block_tree_entry_by_hash(get(), block_hash.get())}; 1139 if (!entry) return std::nullopt; 1140 return entry; 1141 } 1142 1143 std::optional<Block> ReadBlock(const BlockTreeEntry& entry) const 1144 { 1145 auto block{btck_block_read(get(), entry.get())}; 1146 if (!block) return std::nullopt; 1147 return block; 1148 } 1149 1150 BlockSpentOutputs ReadBlockSpentOutputs(const BlockTreeEntry& entry) const 1151 { 1152 return btck_block_spent_outputs_read(get(), entry.get()); 1153 } 1154 }; 1155 1156 } // namespace btck 1157 1158 #endif // BITCOIN_KERNEL_BITCOINKERNEL_WRAPPER_H