// Copyright (c) 2024-present The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_KERNEL_BITCOINKERNEL_WRAPPER_H #define BITCOIN_KERNEL_BITCOINKERNEL_WRAPPER_H #include #include #include #include #include #include #include #include #include #include #include #include #include namespace btck { enum class LogCategory : btck_LogCategory { ALL = btck_LogCategory_ALL, BENCH = btck_LogCategory_BENCH, BLOCKSTORAGE = btck_LogCategory_BLOCKSTORAGE, COINDB = btck_LogCategory_COINDB, LEVELDB = btck_LogCategory_LEVELDB, MEMPOOL = btck_LogCategory_MEMPOOL, PRUNE = btck_LogCategory_PRUNE, RAND = btck_LogCategory_RAND, REINDEX = btck_LogCategory_REINDEX, VALIDATION = btck_LogCategory_VALIDATION, KERNEL = btck_LogCategory_KERNEL }; enum class LogLevel : btck_LogLevel { TRACE_LEVEL = btck_LogLevel_TRACE, DEBUG_LEVEL = btck_LogLevel_DEBUG, INFO_LEVEL = btck_LogLevel_INFO }; enum class ChainType : btck_ChainType { MAINNET = btck_ChainType_MAINNET, TESTNET = btck_ChainType_TESTNET, TESTNET_4 = btck_ChainType_TESTNET_4, SIGNET = btck_ChainType_SIGNET, REGTEST = btck_ChainType_REGTEST }; enum class SynchronizationState : btck_SynchronizationState { INIT_REINDEX = btck_SynchronizationState_INIT_REINDEX, INIT_DOWNLOAD = btck_SynchronizationState_INIT_DOWNLOAD, POST_INIT = btck_SynchronizationState_POST_INIT }; enum class Warning : btck_Warning { UNKNOWN_NEW_RULES_ACTIVATED = btck_Warning_UNKNOWN_NEW_RULES_ACTIVATED, LARGE_WORK_INVALID_CHAIN = btck_Warning_LARGE_WORK_INVALID_CHAIN }; enum class ValidationMode : btck_ValidationMode { VALID = btck_ValidationMode_VALID, INVALID = btck_ValidationMode_INVALID, INTERNAL_ERROR = btck_ValidationMode_INTERNAL_ERROR }; enum class BlockValidationResult : btck_BlockValidationResult { UNSET = btck_BlockValidationResult_UNSET, CONSENSUS = btck_BlockValidationResult_CONSENSUS, CACHED_INVALID = btck_BlockValidationResult_CACHED_INVALID, INVALID_HEADER = btck_BlockValidationResult_INVALID_HEADER, MUTATED = btck_BlockValidationResult_MUTATED, MISSING_PREV = btck_BlockValidationResult_MISSING_PREV, INVALID_PREV = btck_BlockValidationResult_INVALID_PREV, TIME_FUTURE = btck_BlockValidationResult_TIME_FUTURE, HEADER_LOW_WORK = btck_BlockValidationResult_HEADER_LOW_WORK }; enum class ScriptVerifyStatus : btck_ScriptVerifyStatus { OK = btck_ScriptVerifyStatus_OK, ERROR_INVALID_FLAGS_COMBINATION = btck_ScriptVerifyStatus_ERROR_INVALID_FLAGS_COMBINATION, ERROR_SPENT_OUTPUTS_REQUIRED = btck_ScriptVerifyStatus_ERROR_SPENT_OUTPUTS_REQUIRED, }; enum class ScriptVerificationFlags : btck_ScriptVerificationFlags { NONE = btck_ScriptVerificationFlags_NONE, P2SH = btck_ScriptVerificationFlags_P2SH, DERSIG = btck_ScriptVerificationFlags_DERSIG, NULLDUMMY = btck_ScriptVerificationFlags_NULLDUMMY, CHECKLOCKTIMEVERIFY = btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY, CHECKSEQUENCEVERIFY = btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY, WITNESS = btck_ScriptVerificationFlags_WITNESS, TAPROOT = btck_ScriptVerificationFlags_TAPROOT, ALL = btck_ScriptVerificationFlags_ALL }; template struct is_bitmask_enum : std::false_type { }; template <> struct is_bitmask_enum : std::true_type { }; template concept BitmaskEnum = is_bitmask_enum::value; template constexpr T operator|(T lhs, T rhs) { return static_cast( static_cast>(lhs) | static_cast>(rhs)); } template constexpr T operator&(T lhs, T rhs) { return static_cast( static_cast>(lhs) & static_cast>(rhs)); } template constexpr T operator^(T lhs, T rhs) { return static_cast( static_cast>(lhs) ^ static_cast>(rhs)); } template constexpr T operator~(T value) { return static_cast(~static_cast>(value)); } template constexpr T& operator|=(T& lhs, T rhs) { return lhs = lhs | rhs; } template constexpr T& operator&=(T& lhs, T rhs) { return lhs = lhs & rhs; } template constexpr T& operator^=(T& lhs, T rhs) { return lhs = lhs ^ rhs; } template T check(T ptr) { if (ptr == nullptr) { throw std::runtime_error("failed to instantiate btck object"); } return ptr; } template class Iterator { public: using iterator_category = std::random_access_iterator_tag; using iterator_concept = std::random_access_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = ValueType; private: const Collection* m_collection; size_t m_idx; public: Iterator() = default; Iterator(const Collection* ptr) : m_collection{ptr}, m_idx{0} {} Iterator(const Collection* ptr, size_t idx) : m_collection{ptr}, m_idx{idx} {} // This is just a view, so return a copy. auto operator*() const { return (*m_collection)[m_idx]; } auto operator->() const { return (*m_collection)[m_idx]; } auto& operator++() { m_idx++; return *this; } auto operator++(int) { Iterator tmp = *this; ++(*this); return tmp; } auto& operator--() { m_idx--; return *this; } auto operator--(int) { auto temp = *this; --m_idx; return temp; } auto& operator+=(difference_type n) { m_idx += n; return *this; } auto& operator-=(difference_type n) { m_idx -= n; return *this; } auto operator+(difference_type n) const { return Iterator(m_collection, m_idx + n); } auto operator-(difference_type n) const { return Iterator(m_collection, m_idx - n); } auto operator-(const Iterator& other) const { return static_cast(m_idx) - static_cast(other.m_idx); } ValueType operator[](difference_type n) const { return (*m_collection)[m_idx + n]; } auto operator<=>(const Iterator& other) const { return m_idx <=> other.m_idx; } bool operator==(const Iterator& other) const { return m_collection == other.m_collection && m_idx == other.m_idx; } private: friend Iterator operator+(difference_type n, const Iterator& it) { return it + n; } }; template concept IndexedContainer = requires(const Container& c, SizeFunc size_func, GetFunc get_func, std::size_t i) { { std::invoke(size_func, c) } -> std::convertible_to; { std::invoke(get_func, c, i) }; // Return type is deduced }; template requires IndexedContainer class Range { public: using value_type = std::invoke_result_t; using difference_type = std::ptrdiff_t; using iterator = Iterator; using const_iterator = iterator; private: const Container* m_container; public: explicit Range(const Container& container) : m_container(&container) { static_assert(std::ranges::random_access_range); } iterator begin() const { return iterator(this, 0); } iterator end() const { return iterator(this, size()); } const_iterator cbegin() const { return begin(); } const_iterator cend() const { return end(); } size_t size() const { return std::invoke(SizeFunc, *m_container); } bool empty() const { return size() == 0; } value_type operator[](size_t index) const { return std::invoke(GetFunc, *m_container, index); } value_type at(size_t index) const { if (index >= size()) { throw std::out_of_range("Index out of range"); } return (*this)[index]; } value_type front() const { return (*this)[0]; } value_type back() const { return (*this)[size() - 1]; } }; #define MAKE_RANGE_METHOD(method_name, ContainerType, SizeFunc, GetFunc, container_expr) \ auto method_name() const & { \ return Range{container_expr}; \ } \ auto method_name() const && = delete; template std::vector write_bytes(const T* object, int (*to_bytes)(const T*, btck_WriteBytes, void*)) { std::vector bytes; struct UserData { std::vector* bytes; std::exception_ptr exception; }; UserData user_data = UserData{.bytes = &bytes, .exception = nullptr}; constexpr auto const write = +[](const void* buffer, size_t len, void* user_data) -> int { auto& data = *reinterpret_cast(user_data); auto& bytes = *data.bytes; try { auto const* first = static_cast(buffer); auto const* last = first + len; bytes.insert(bytes.end(), first, last); return 0; } catch (...) { data.exception = std::current_exception(); return -1; } }; if (to_bytes(object, write, &user_data) != 0) { std::rethrow_exception(user_data.exception); } return bytes; } template class View { protected: const CType* m_ptr; public: explicit View(const CType* ptr) : m_ptr{check(ptr)} {} const CType* get() const { return m_ptr; } }; template class Handle { protected: CType* m_ptr; public: explicit Handle(CType* ptr) : m_ptr{check(ptr)} {} // Copy constructors Handle(const Handle& other) : m_ptr{check(CopyFunc(other.m_ptr))} {} Handle& operator=(const Handle& other) { if (this != &other) { Handle temp(other); std::swap(m_ptr, temp.m_ptr); } return *this; } // Move constructors Handle(Handle&& other) noexcept : m_ptr(other.m_ptr) { other.m_ptr = nullptr; } Handle& operator=(Handle&& other) noexcept { DestroyFunc(m_ptr); m_ptr = std::exchange(other.m_ptr, nullptr); return *this; } template requires std::derived_from> Handle(const ViewType& view) : Handle{CopyFunc(view.get())} { } ~Handle() { DestroyFunc(m_ptr); } CType* get() { return m_ptr; } const CType* get() const { return m_ptr; } }; template class UniqueHandle { protected: struct Deleter { void operator()(CType* ptr) const noexcept { if (ptr) DestroyFunc(ptr); } }; std::unique_ptr m_ptr; public: explicit UniqueHandle(CType* ptr) : m_ptr{check(ptr)} {} CType* get() { return m_ptr.get(); } const CType* get() const { return m_ptr.get(); } }; class Transaction; class TransactionOutput; template class ScriptPubkeyApi { private: auto impl() const { return static_cast(this)->get(); } friend Derived; ScriptPubkeyApi() = default; public: bool Verify(int64_t amount, const Transaction& tx_to, std::span spent_outputs, unsigned int input_index, ScriptVerificationFlags flags, ScriptVerifyStatus& status) const; std::vector ToBytes() const { return write_bytes(impl(), btck_script_pubkey_to_bytes); } }; class ScriptPubkeyView : public View, public ScriptPubkeyApi { public: explicit ScriptPubkeyView(const btck_ScriptPubkey* ptr) : View{ptr} {} }; class ScriptPubkey : public Handle, public ScriptPubkeyApi { public: explicit ScriptPubkey(std::span raw) : Handle{btck_script_pubkey_create(raw.data(), raw.size())} {} ScriptPubkey(const ScriptPubkeyView& view) : Handle(view) {} }; template class TransactionOutputApi { private: auto impl() const { return static_cast(this)->get(); } friend Derived; TransactionOutputApi() = default; public: int64_t Amount() const { return btck_transaction_output_get_amount(impl()); } ScriptPubkeyView GetScriptPubkey() const { return ScriptPubkeyView{btck_transaction_output_get_script_pubkey(impl())}; } }; class TransactionOutputView : public View, public TransactionOutputApi { public: explicit TransactionOutputView(const btck_TransactionOutput* ptr) : View{ptr} {} }; class TransactionOutput : public Handle, public TransactionOutputApi { public: explicit TransactionOutput(const ScriptPubkey& script_pubkey, int64_t amount) : Handle{btck_transaction_output_create(script_pubkey.get(), amount)} {} TransactionOutput(const TransactionOutputView& view) : Handle(view) {} }; template class TxidApi { private: auto impl() const { return static_cast(this)->get(); } friend Derived; TxidApi() = default; public: bool operator==(const TxidApi& other) const { return btck_txid_equals(impl(), other.impl()) != 0; } bool operator!=(const TxidApi& other) const { return btck_txid_equals(impl(), other.impl()) == 0; } std::array ToBytes() const { std::array hash; btck_txid_to_bytes(impl(), reinterpret_cast(hash.data())); return hash; } }; class TxidView : public View, public TxidApi { public: explicit TxidView(const btck_Txid* ptr) : View{ptr} {} }; class Txid : public Handle, public TxidApi { public: Txid(const TxidView& view) : Handle(view) {} }; template class OutPointApi { private: auto impl() const { return static_cast(this)->get(); } friend Derived; OutPointApi() = default; public: uint32_t index() const { return btck_transaction_out_point_get_index(impl()); } TxidView Txid() const { return TxidView{btck_transaction_out_point_get_txid(impl())}; } }; class OutPointView : public View, public OutPointApi { public: explicit OutPointView(const btck_TransactionOutPoint* ptr) : View{ptr} {} }; class OutPoint : public Handle, public OutPointApi { public: OutPoint(const OutPointView& view) : Handle(view) {} }; template class TransactionInputApi { private: auto impl() const { return static_cast(this)->get(); } friend Derived; TransactionInputApi() = default; public: OutPointView OutPoint() const { return OutPointView{btck_transaction_input_get_out_point(impl())}; } }; class TransactionInputView : public View, public TransactionInputApi { public: explicit TransactionInputView(const btck_TransactionInput* ptr) : View{ptr} {} }; class TransactionInput : public Handle, public TransactionInputApi { public: TransactionInput(const TransactionInputView& view) : Handle(view) {} }; template class TransactionApi { private: auto impl() const { return static_cast(this)->get(); } public: size_t CountOutputs() const { return btck_transaction_count_outputs(impl()); } size_t CountInputs() const { return btck_transaction_count_inputs(impl()); } TransactionOutputView GetOutput(size_t index) const { return TransactionOutputView{btck_transaction_get_output_at(impl(), index)}; } TransactionInputView GetInput(size_t index) const { return TransactionInputView{btck_transaction_get_input_at(impl(), index)}; } TxidView Txid() const { return TxidView{btck_transaction_get_txid(impl())}; } MAKE_RANGE_METHOD(Outputs, Derived, &TransactionApi::CountOutputs, &TransactionApi::GetOutput, *static_cast(this)) MAKE_RANGE_METHOD(Inputs, Derived, &TransactionApi::CountInputs, &TransactionApi::GetInput, *static_cast(this)) std::vector ToBytes() const { return write_bytes(impl(), btck_transaction_to_bytes); } }; class TransactionView : public View, public TransactionApi { public: explicit TransactionView(const btck_Transaction* ptr) : View{ptr} {} }; class Transaction : public Handle, public TransactionApi { public: explicit Transaction(std::span raw_transaction) : Handle{btck_transaction_create(raw_transaction.data(), raw_transaction.size())} {} Transaction(const TransactionView& view) : Handle{view} {} }; template bool ScriptPubkeyApi::Verify(int64_t amount, const Transaction& tx_to, const std::span spent_outputs, unsigned int input_index, ScriptVerificationFlags flags, ScriptVerifyStatus& status) const { const btck_TransactionOutput** spent_outputs_ptr = nullptr; std::vector raw_spent_outputs; if (spent_outputs.size() > 0) { raw_spent_outputs.reserve(spent_outputs.size()); for (const auto& output : spent_outputs) { raw_spent_outputs.push_back(output.get()); } spent_outputs_ptr = raw_spent_outputs.data(); } auto result = btck_script_pubkey_verify( impl(), amount, tx_to.get(), spent_outputs_ptr, spent_outputs.size(), input_index, static_cast(flags), reinterpret_cast(&status)); return result == 1; } template class BlockHashApi { private: auto impl() const { return static_cast(this)->get(); } public: bool operator==(const Derived& other) const { return btck_block_hash_equals(impl(), other.get()) != 0; } bool operator!=(const Derived& other) const { return btck_block_hash_equals(impl(), other.get()) == 0; } std::array ToBytes() const { std::array hash; btck_block_hash_to_bytes(impl(), reinterpret_cast(hash.data())); return hash; } }; class BlockHashView: public View, public BlockHashApi { public: explicit BlockHashView(const btck_BlockHash* ptr) : View{ptr} {} }; class BlockHash : public Handle, public BlockHashApi { public: explicit BlockHash(const std::array& hash) : Handle{btck_block_hash_create(reinterpret_cast(hash.data()))} {} explicit BlockHash(btck_BlockHash* hash) : Handle{hash} {} BlockHash(const BlockHashView& view) : Handle{view} {} }; class Block : public Handle { public: Block(const std::span raw_block) : Handle{btck_block_create(raw_block.data(), raw_block.size())} { } Block(btck_Block* block) : Handle{block} {} size_t CountTransactions() const { return btck_block_count_transactions(get()); } TransactionView GetTransaction(size_t index) const { return TransactionView{btck_block_get_transaction_at(get(), index)}; } MAKE_RANGE_METHOD(Transactions, Block, &Block::CountTransactions, &Block::GetTransaction, *this) BlockHash GetHash() const { return BlockHash{btck_block_get_hash(get())}; } std::vector ToBytes() const { return write_bytes(get(), btck_block_to_bytes); } }; inline void logging_disable() { btck_logging_disable(); } inline void logging_set_options(const btck_LoggingOptions& logging_options) { btck_logging_set_options(logging_options); } inline void logging_set_level_category(LogCategory category, LogLevel level) { btck_logging_set_level_category(static_cast(category), static_cast(level)); } inline void logging_enable_category(LogCategory category) { btck_logging_enable_category(static_cast(category)); } inline void logging_disable_category(LogCategory category) { btck_logging_disable_category(static_cast(category)); } template concept Log = requires(T a, std::string_view message) { { a.LogMessage(message) } -> std::same_as; }; template class Logger : UniqueHandle { public: Logger(std::unique_ptr log) : UniqueHandle{btck_logging_connection_create( +[](void* user_data, const char* message, size_t message_len) { static_cast(user_data)->LogMessage({message, message_len}); }, log.release(), +[](void* user_data) { delete static_cast(user_data); })} { } }; class BlockTreeEntry : public View { public: BlockTreeEntry(const btck_BlockTreeEntry* entry) : View{entry} { } std::optional GetPrevious() const { auto entry{btck_block_tree_entry_get_previous(get())}; if (!entry) return std::nullopt; return entry; } int32_t GetHeight() const { return btck_block_tree_entry_get_height(get()); } BlockHashView GetHash() const { return BlockHashView{btck_block_tree_entry_get_block_hash(get())}; } }; class KernelNotifications { public: virtual ~KernelNotifications() = default; virtual void BlockTipHandler(SynchronizationState state, BlockTreeEntry entry, double verification_progress) {} virtual void HeaderTipHandler(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) {} virtual void ProgressHandler(std::string_view title, int progress_percent, bool resume_possible) {} virtual void WarningSetHandler(Warning warning, std::string_view message) {} virtual void WarningUnsetHandler(Warning warning) {} virtual void FlushErrorHandler(std::string_view error) {} virtual void FatalErrorHandler(std::string_view error) {} }; class BlockValidationState { private: const btck_BlockValidationState* m_state; public: BlockValidationState(const btck_BlockValidationState* state) : m_state{state} {} BlockValidationState(const BlockValidationState&) = delete; BlockValidationState& operator=(const BlockValidationState&) = delete; BlockValidationState(BlockValidationState&&) = delete; BlockValidationState& operator=(BlockValidationState&&) = delete; ValidationMode GetValidationMode() const { return static_cast(btck_block_validation_state_get_validation_mode(m_state)); } BlockValidationResult GetBlockValidationResult() const { return static_cast(btck_block_validation_state_get_block_validation_result(m_state)); } }; class ValidationInterface { public: virtual ~ValidationInterface() = default; virtual void BlockChecked(Block block, const BlockValidationState state) {} virtual void PowValidBlock(BlockTreeEntry entry, Block block) {} virtual void BlockConnected(Block block, BlockTreeEntry entry) {} virtual void BlockDisconnected(Block block, BlockTreeEntry entry) {} }; class ChainParams : public Handle { public: ChainParams(ChainType chain_type) : Handle{btck_chain_parameters_create(static_cast(chain_type))} {} }; class ContextOptions : public UniqueHandle { public: ContextOptions() : UniqueHandle{btck_context_options_create()} {} void SetChainParams(ChainParams& chain_params) { btck_context_options_set_chainparams(get(), chain_params.get()); } template void SetNotifications(std::shared_ptr notifications) { static_assert(std::is_base_of_v); auto heap_notifications = std::make_unique>(std::move(notifications)); using user_type = std::shared_ptr*; btck_context_options_set_notifications( get(), btck_NotificationInterfaceCallbacks{ .user_data = heap_notifications.release(), .user_data_destroy = +[](void* user_data) { delete static_cast(user_data); }, .block_tip = +[](void* user_data, btck_SynchronizationState state, const btck_BlockTreeEntry* entry, double verification_progress) { (*static_cast(user_data))->BlockTipHandler(static_cast(state), BlockTreeEntry{entry}, verification_progress); }, .header_tip = +[](void* user_data, btck_SynchronizationState state, int64_t height, int64_t timestamp, int presync) { (*static_cast(user_data))->HeaderTipHandler(static_cast(state), height, timestamp, presync == 1); }, .progress = +[](void* user_data, const char* title, size_t title_len, int progress_percent, int resume_possible) { (*static_cast(user_data))->ProgressHandler({title, title_len}, progress_percent, resume_possible == 1); }, .warning_set = +[](void* user_data, btck_Warning warning, const char* message, size_t message_len) { (*static_cast(user_data))->WarningSetHandler(static_cast(warning), {message, message_len}); }, .warning_unset = +[](void* user_data, btck_Warning warning) { (*static_cast(user_data))->WarningUnsetHandler(static_cast(warning)); }, .flush_error = +[](void* user_data, const char* error, size_t error_len) { (*static_cast(user_data))->FlushErrorHandler({error, error_len}); }, .fatal_error = +[](void* user_data, const char* error, size_t error_len) { (*static_cast(user_data))->FatalErrorHandler({error, error_len}); }, }); } template void SetValidationInterface(std::shared_ptr validation_interface) { static_assert(std::is_base_of_v); auto heap_vi = std::make_unique>(std::move(validation_interface)); using user_type = std::shared_ptr*; btck_context_options_set_validation_interface( get(), btck_ValidationInterfaceCallbacks{ .user_data = heap_vi.release(), .user_data_destroy = +[](void* user_data) { delete static_cast(user_data); }, .block_checked = +[](void* user_data, btck_Block* block, const btck_BlockValidationState* state) { (*static_cast(user_data))->BlockChecked(Block{block}, BlockValidationState{state}); }, .pow_valid_block = +[](void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry) { (*static_cast(user_data))->PowValidBlock(BlockTreeEntry{entry}, Block{block}); }, .block_connected = +[](void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry) { (*static_cast(user_data))->BlockConnected(Block{block}, BlockTreeEntry{entry}); }, .block_disconnected = +[](void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry) { (*static_cast(user_data))->BlockDisconnected(Block{block}, BlockTreeEntry{entry}); }, }); } }; class Context : public Handle { public: Context(ContextOptions& opts) : Handle{btck_context_create(opts.get())} {} Context() : Handle{btck_context_create(ContextOptions{}.get())} {} bool interrupt() { return btck_context_interrupt(get()) == 0; } }; class ChainstateManagerOptions : public UniqueHandle { public: ChainstateManagerOptions(const Context& context, const std::string& data_dir, const std::string& blocks_dir) : UniqueHandle{btck_chainstate_manager_options_create(context.get(), data_dir.c_str(), data_dir.length(), blocks_dir.c_str(), blocks_dir.length())} { } void SetWorkerThreads(int worker_threads) { btck_chainstate_manager_options_set_worker_threads_num(get(), worker_threads); } bool SetWipeDbs(bool wipe_block_tree, bool wipe_chainstate) { return btck_chainstate_manager_options_set_wipe_dbs(get(), wipe_block_tree, wipe_chainstate) == 0; } void UpdateBlockTreeDbInMemory(bool block_tree_db_in_memory) { btck_chainstate_manager_options_update_block_tree_db_in_memory(get(), block_tree_db_in_memory); } void UpdateChainstateDbInMemory(bool chainstate_db_in_memory) { btck_chainstate_manager_options_update_chainstate_db_in_memory(get(), chainstate_db_in_memory); } }; class ChainView : public View { public: explicit ChainView(const btck_Chain* ptr) : View{ptr} {} BlockTreeEntry Tip() const { return btck_chain_get_tip(get()); } int32_t Height() const { return btck_chain_get_height(get()); } int CountEntries() const { return btck_chain_get_height(get()) + 1; } BlockTreeEntry Genesis() const { return btck_chain_get_genesis(get()); } BlockTreeEntry GetByHeight(int height) const { auto index{btck_chain_get_by_height(get(), height)}; if (!index) throw std::runtime_error("No entry in the chain at the provided height"); return index; } bool Contains(BlockTreeEntry& entry) const { return btck_chain_contains(get(), entry.get()); } MAKE_RANGE_METHOD(Entries, ChainView, &ChainView::CountEntries, &ChainView::GetByHeight, *this) }; template class CoinApi { private: auto impl() const { return static_cast(this)->get(); } friend Derived; CoinApi() = default; public: uint32_t GetConfirmationHeight() const { return btck_coin_confirmation_height(impl()); } bool IsCoinbase() const { return btck_coin_is_coinbase(impl()) == 1; } TransactionOutputView GetOutput() const { return TransactionOutputView{btck_coin_get_output(impl())}; } }; class CoinView : public View, public CoinApi { public: explicit CoinView(const btck_Coin* ptr) : View{ptr} {} }; class Coin : public Handle, public CoinApi { public: Coin(btck_Coin* coin) : Handle{coin} {} Coin(const CoinView& view) : Handle{view} {} }; template class TransactionSpentOutputsApi { private: auto impl() const { return static_cast(this)->get(); } friend Derived; TransactionSpentOutputsApi() = default; public: size_t Count() const { return btck_transaction_spent_outputs_count(impl()); } CoinView GetCoin(size_t index) const { return CoinView{btck_transaction_spent_outputs_get_coin_at(impl(), index)}; } MAKE_RANGE_METHOD(Coins, Derived, &TransactionSpentOutputsApi::Count, &TransactionSpentOutputsApi::GetCoin, *static_cast(this)) }; class TransactionSpentOutputsView : public View, public TransactionSpentOutputsApi { public: explicit TransactionSpentOutputsView(const btck_TransactionSpentOutputs* ptr) : View{ptr} {} }; class TransactionSpentOutputs : public Handle, public TransactionSpentOutputsApi { public: TransactionSpentOutputs(btck_TransactionSpentOutputs* transaction_spent_outputs) : Handle{transaction_spent_outputs} {} TransactionSpentOutputs(const TransactionSpentOutputsView& view) : Handle{view} {} }; class BlockSpentOutputs : public Handle { public: BlockSpentOutputs(btck_BlockSpentOutputs* block_spent_outputs) : Handle{block_spent_outputs} { } size_t Count() const { return btck_block_spent_outputs_count(get()); } TransactionSpentOutputsView GetTxSpentOutputs(size_t tx_undo_index) const { return TransactionSpentOutputsView{btck_block_spent_outputs_get_transaction_spent_outputs_at(get(), tx_undo_index)}; } MAKE_RANGE_METHOD(TxsSpentOutputs, BlockSpentOutputs, &BlockSpentOutputs::Count, &BlockSpentOutputs::GetTxSpentOutputs, *this) }; class ChainMan : UniqueHandle { public: ChainMan(const Context& context, const ChainstateManagerOptions& chainman_opts) : UniqueHandle{btck_chainstate_manager_create(chainman_opts.get())} { } bool ImportBlocks(const std::span paths) { std::vector c_paths; std::vector c_paths_lens; c_paths.reserve(paths.size()); c_paths_lens.reserve(paths.size()); for (const auto& path : paths) { c_paths.push_back(path.c_str()); c_paths_lens.push_back(path.length()); } return btck_chainstate_manager_import_blocks(get(), c_paths.data(), c_paths_lens.data(), c_paths.size()) == 0; } bool ProcessBlock(const Block& block, bool* new_block) { int _new_block; int res = btck_chainstate_manager_process_block(get(), block.get(), &_new_block); if (new_block) *new_block = _new_block == 1; return res == 0; } ChainView GetChain() const { return ChainView{btck_chainstate_manager_get_active_chain(get())}; } std::optional GetBlockTreeEntry(const BlockHash& block_hash) const { auto entry{btck_chainstate_manager_get_block_tree_entry_by_hash(get(), block_hash.get())}; if (!entry) return std::nullopt; return entry; } std::optional ReadBlock(const BlockTreeEntry& entry) const { auto block{btck_block_read(get(), entry.get())}; if (!block) return std::nullopt; return block; } BlockSpentOutputs ReadBlockSpentOutputs(const BlockTreeEntry& entry) const { return btck_block_spent_outputs_read(get(), entry.get()); } }; } // namespace btck #endif // BITCOIN_KERNEL_BITCOINKERNEL_WRAPPER_H