diff --git a/BitcoinKernel.NET.sln b/BitcoinKernel.NET.sln index b23123f..dc51df9 100644 --- a/BitcoinKernel.NET.sln +++ b/BitcoinKernel.NET.sln @@ -11,19 +11,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{F8E63A4F-7D3E-4B2A-9C1D-8A5F6E9B3C2D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BitcoinKernel.Tests", "tests\BitcoinKernel.Tests\BitcoinKernel.Tests.csproj", "{BC90EFB4-1692-CBCC-EF52-778255F591E2}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicUsage", "examples\BasicUsage\BasicUsage.csproj", "{0E2DDF4A-A1FE-5424-03EA-7A8E76751354}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BitcoinKernel.Interop", "src\BitcoinKernel.Interop\BitcoinKernel.Interop.csproj", "{9C4AFB5E-ED75-909E-FC61-1BD17BB6BC23}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BitcoinKernel.Core", "src\BitcoinKernel.Core\BitcoinKernel.Core.csproj", "{2BDCF8E8-DD35-3BA0-0148-5A2588335678}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BitcoinKernel", "src\BitcoinKernel\BitcoinKernel.csproj", "{D6F509B1-C990-0533-2DD1-CFFBA7506249}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BitcoinKernel", "src\BitcoinKernel\BitcoinKernel.csproj", "{2BDCF8E8-DD35-3BA0-0148-5A2588335678}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlockProcessing", "examples\BlockProcessing\BlockProcessing.csproj", "{23E19BC3-8829-42BE-BCB4-A2050BE04975}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BitcoinKernel.Core.Tests", "tests\BitcoinKernel.Core.Tests\BitcoinKernel.Core.Tests.csproj", "{267842B2-D915-4B9E-8448-F9B5816D4A0A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BitcoinKernel.Tests", "tests\BitcoinKernel.Tests\BitcoinKernel.Tests.csproj", "{267842B2-D915-4B9E-8448-F9B5816D4A0A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "kernel-bindings-test-handler", "tools\kernel-bindings-test-handler\kernel-bindings-test-handler.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}" EndProject @@ -37,18 +33,6 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {BC90EFB4-1692-CBCC-EF52-778255F591E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BC90EFB4-1692-CBCC-EF52-778255F591E2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BC90EFB4-1692-CBCC-EF52-778255F591E2}.Debug|x64.ActiveCfg = Debug|Any CPU - {BC90EFB4-1692-CBCC-EF52-778255F591E2}.Debug|x64.Build.0 = Debug|Any CPU - {BC90EFB4-1692-CBCC-EF52-778255F591E2}.Debug|x86.ActiveCfg = Debug|Any CPU - {BC90EFB4-1692-CBCC-EF52-778255F591E2}.Debug|x86.Build.0 = Debug|Any CPU - {BC90EFB4-1692-CBCC-EF52-778255F591E2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BC90EFB4-1692-CBCC-EF52-778255F591E2}.Release|Any CPU.Build.0 = Release|Any CPU - {BC90EFB4-1692-CBCC-EF52-778255F591E2}.Release|x64.ActiveCfg = Release|Any CPU - {BC90EFB4-1692-CBCC-EF52-778255F591E2}.Release|x64.Build.0 = Release|Any CPU - {BC90EFB4-1692-CBCC-EF52-778255F591E2}.Release|x86.ActiveCfg = Release|Any CPU - {BC90EFB4-1692-CBCC-EF52-778255F591E2}.Release|x86.Build.0 = Release|Any CPU {0E2DDF4A-A1FE-5424-03EA-7A8E76751354}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0E2DDF4A-A1FE-5424-03EA-7A8E76751354}.Debug|Any CPU.Build.0 = Debug|Any CPU {0E2DDF4A-A1FE-5424-03EA-7A8E76751354}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -85,18 +69,6 @@ Global {2BDCF8E8-DD35-3BA0-0148-5A2588335678}.Release|x64.Build.0 = Release|Any CPU {2BDCF8E8-DD35-3BA0-0148-5A2588335678}.Release|x86.ActiveCfg = Release|Any CPU {2BDCF8E8-DD35-3BA0-0148-5A2588335678}.Release|x86.Build.0 = Release|Any CPU - {D6F509B1-C990-0533-2DD1-CFFBA7506249}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D6F509B1-C990-0533-2DD1-CFFBA7506249}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D6F509B1-C990-0533-2DD1-CFFBA7506249}.Debug|x64.ActiveCfg = Debug|Any CPU - {D6F509B1-C990-0533-2DD1-CFFBA7506249}.Debug|x64.Build.0 = Debug|Any CPU - {D6F509B1-C990-0533-2DD1-CFFBA7506249}.Debug|x86.ActiveCfg = Debug|Any CPU - {D6F509B1-C990-0533-2DD1-CFFBA7506249}.Debug|x86.Build.0 = Debug|Any CPU - {D6F509B1-C990-0533-2DD1-CFFBA7506249}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D6F509B1-C990-0533-2DD1-CFFBA7506249}.Release|Any CPU.Build.0 = Release|Any CPU - {D6F509B1-C990-0533-2DD1-CFFBA7506249}.Release|x64.ActiveCfg = Release|Any CPU - {D6F509B1-C990-0533-2DD1-CFFBA7506249}.Release|x64.Build.0 = Release|Any CPU - {D6F509B1-C990-0533-2DD1-CFFBA7506249}.Release|x86.ActiveCfg = Release|Any CPU - {D6F509B1-C990-0533-2DD1-CFFBA7506249}.Release|x86.Build.0 = Release|Any CPU {23E19BC3-8829-42BE-BCB4-A2050BE04975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {23E19BC3-8829-42BE-BCB4-A2050BE04975}.Debug|Any CPU.Build.0 = Debug|Any CPU {23E19BC3-8829-42BE-BCB4-A2050BE04975}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -138,11 +110,9 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {BC90EFB4-1692-CBCC-EF52-778255F591E2} = {0AB3BF05-4346-4AA6-1389-037BE0695223} {0E2DDF4A-A1FE-5424-03EA-7A8E76751354} = {B36A84DF-456D-A817-6EDD-3EC3E7F6E11F} {9C4AFB5E-ED75-909E-FC61-1BD17BB6BC23} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} {2BDCF8E8-DD35-3BA0-0148-5A2588335678} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - {D6F509B1-C990-0533-2DD1-CFFBA7506249} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} {23E19BC3-8829-42BE-BCB4-A2050BE04975} = {B36A84DF-456D-A817-6EDD-3EC3E7F6E11F} {267842B2-D915-4B9E-8448-F9B5816D4A0A} = {0AB3BF05-4346-4AA6-1389-037BE0695223} {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} = {F8E63A4F-7D3E-4B2A-9C1D-8A5F6E9B3C2D} diff --git a/CHANGELOG.md b/CHANGELOG.md index c578f89..3e8e471 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.0] - 2026-03-03 + +### Breaking Changes +- Removed `BitcoinKernel` facade package and `KernelLibrary` fluent builder; consumers now use the managed API directly +- Renamed package/namespaces from `BitcoinKernel.Core` to `BitcoinKernel` +- `Chain` class moved from `BitcoinKernel.Abstractions` to `BitcoinKernel.Chain`; import `using BitcoinKernel.Chain;` to access it +- `BitcoinKernel.Abstractions` namespace renamed to `BitcoinKernel.Primatives`; update any `using BitcoinKernel.Abstractions;` imports accordingly +- `LoggingConnection` moved from the global namespace into `BitcoinKernel`; add `using BitcoinKernel;` if not already present + +### Changed +- Examples rewritten to use the managed API directly without the fluent builder + ## [0.1.2] - 2026-01-26 ### Added diff --git a/README.md b/README.md index fe83811..637326e 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,36 @@ # BitcoinKernel.NET -.NET bindings and high-level library for [libbitcoinkernel](https://github.com/bitcoin/bitcoin/tree/master/src/kernel), providing access to Bitcoin Core's consensus and validation logic. +.NET bindings for [libbitcoinkernel](https://github.com/bitcoin/bitcoin/tree/master/src/kernel), providing access to Bitcoin Core's consensus and validation logic. +⚠️🚧 This library is still under construction. ⚠️🚧 -⚠️🚧 This library is still under contruction. ⚠️🚧 - -This library uses [libbitcoinkernel](https://github.com/bitcoin/bitcoin/tree/master/src/kernel) which is in an experimental state, do not use for production purposes. - -## Overview - -BitcoinKernel.NET brings Bitcoin Core's robust consensus engine to .NET applications through a clean, idiomatic C# API. Built on top of libbitcoinkernel, it provides everything from low-level P/Invoke bindings to high-level abstractions for common Bitcoin operations. +This library uses [libbitcoinkernel](https://github.com/bitcoin/bitcoin/tree/master/src/kernel) which is in an experimental state, do not use for production purposes. ## Packages | Package | Version | Description | |---------|---------|-------------| -| **BitcoinKernel** | 0.1.2 | High-level API with fluent builder pattern | -| **BitcoinKernel.Core** | 0.1.2 | Managed wrappers and native bindings | - - -## Quick Start - -### Installation +| **BitcoinKernel** | 0.2.0 | Managed wrappers and native bindings | ```bash dotnet add package BitcoinKernel ``` -or - -```bash -dotnet add package BitcoinKernel.Core -``` - ## Architecture -The library is organized in three layers: +The library is organized in two layers: 1. **BitcoinKernel.Interop** - P/Invoke bindings to libbitcoinkernel (bundled, not published separately) -2. **BitcoinKernel.Core** - Managed C# wrappers with automatic memory management -3. **BitcoinKernel** - High-level facade with fluent API +2. **BitcoinKernel** - Managed C# wrappers with automatic memory management ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ BitcoinKernel β”‚ ← Fluent API, simple usage -β”‚ (Facade Layer) β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ BitcoinKernel.Core β”‚ ← Managed wrappers, IDisposable +β”‚ BitcoinKernel β”‚ ← Managed wrappers, IDisposable β”‚ (Wrapper Layer) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ BitcoinKernel.Interop β”‚ ← P/Invoke bindings +β”‚ BitcoinKernel.Interop β”‚ ← P/Invoke bindings (bundled) β”‚ (Binding Layer) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ @@ -63,26 +40,44 @@ The library is organized in three layers: β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` +## Quick Start + +```csharp +using BitcoinKernel; +using BitcoinKernel.Chain; +using BitcoinKernel.Interop.Enums; + +using var logging = new LoggingConnection((category, message, level) => + Console.WriteLine($"[{category}] {message}")); + +using var chainParams = new ChainParameters(ChainType.MAINNET); +using var contextOptions = new KernelContextOptions().SetChainParams(chainParams); +using var context = new KernelContext(contextOptions); +using var options = new ChainstateManagerOptions(context, dataDir, blocksDir); +using var chainstate = new ChainstateManager(context, chainParams, options); + +var chain = chainstate.GetActiveChain(); +Console.WriteLine($"Height: {chain.Height}"); +Console.WriteLine($"Genesis: {Convert.ToHexString(chain.GetGenesis().GetBlockHash())}"); +``` + ## Examples Explore the [examples](examples/) directory for complete working samples: -- **[BasicUsage](examples/BasicUsage/)** - Getting started with the high-level API -- **[BlockProcessing](examples/BlockProcessing/)** - Block validation and chain management +- **[BasicUsage](examples/BasicUsage/)** - Getting started with chain queries +- **[BlockProcessing](examples/BlockProcessing/)** - Block validation and processing ## Tools ### Kernel Bindings Test Handler -A conformance test handler for Kernel bindings Test handler framework, see [tools/kernel-bindings-test-handler](tools/kernel-bindings-test-handler/) for details. +A conformance test handler for the Kernel bindings test framework, see [tools/kernel-bindings-test-handler](tools/kernel-bindings-test-handler/) for details. -**Usage:** ```bash dotnet run --project tools/kernel-bindings-test-handler ``` -The handler communicates via stdin/stdout and is designed for automated conformance testing. - ## Building from Source ### Prerequisites @@ -109,7 +104,7 @@ This package includes pre-built `libbitcoinkernel` binaries for: - macOS (x64, ARM64) - others will follow -For other platforms, for now, you'll need to build libbitcoinkernel from the [Bitcoin Core repository](https://github.com/bitcoin/bitcoin). +For other platforms, you'll need to build libbitcoinkernel from the [Bitcoin Core repository](https://github.com/bitcoin/bitcoin). ## Documentation @@ -135,4 +130,4 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file - Built on [libbitcoinkernel](https://github.com/bitcoin/bitcoin/tree/master/src/kernel) from Bitcoin Core -**Note**: This library provides access to Bitcoin Core's consensus engine. The libbitcoinkernel and this package is stil experimental and not ready for production use. +**Note**: This library provides access to Bitcoin Core's consensus engine. libbitcoinkernel and this package are still experimental and not ready for production use. diff --git a/examples/BasicUsage/Program.cs b/examples/BasicUsage/Program.cs index b2ff21c..d7c21db 100644 --- a/examples/BasicUsage/Program.cs +++ b/examples/BasicUsage/Program.cs @@ -1,76 +1,63 @@ -ο»Ώusing System; -using BitcoinKernel; - -namespace FacadeExample -{ - class Program - { - static void Main(string[] args) - { - Console.WriteLine("=== Bitcoin Kernel Basic Builder Example ===\n"); - - FullChainstateExample(); - - } - - static void FullChainstateExample() - { - Console.WriteLine("2. Full Chainstate Example:"); - - Console.WriteLine(" Creating builder..."); - var builder = KernelLibrary.Create() - .ForMainnet() - .WithWorkerThreads(2) - .WithDirectories("/tmp/regtest-data2", "/tmp/regtest-data/blocks2"); - - Console.WriteLine(" Configuring logging..."); - builder = builder.WithLogging((category, message, level) => - { - if (level <= (int)BitcoinKernel.Interop.Enums.LogLevel.INFO) // Only INFO and above - Console.WriteLine($" [{category}] {message}"); - }); - - Console.WriteLine(" Building kernel..."); - using var kernel = builder.Build(); - - Console.WriteLine(" Kernel built successfully!"); - Console.WriteLine(" βœ“ Chainstate initialized automatically"); - - // Process blocks - try - { - - Console.WriteLine(" βœ“ Ready to process blocks"); - - // Show new query methods - Console.WriteLine($" Chain height: {kernel.GetChainHeight()}"); - Console.WriteLine($" Genesis hash: {Convert.ToHexString(kernel.GetGenesisBlockHash())}"); - - if (kernel.GetChainHeight() > 0) - { - var tipHash = kernel.GetChainTipHash(); - Console.WriteLine($" Tip hash: {Convert.ToHexString(tipHash)}"); - - var blockInfo = kernel.GetBlockInfo(0); - if (blockInfo != null) - { - Console.WriteLine($" Block 0 hash: {Convert.ToHexString(blockInfo.Hash)}"); - } - } - - Console.WriteLine(" βœ“ Chain queries working"); - } - catch (Exception ex) - { - Console.WriteLine($" βœ— Error: {ex.Message}"); +using BitcoinKernel; +using BitcoinKernel.Chain; +using BitcoinKernel.Interop.Enums; + +namespace BasicUsage; + +class Program +{ + static void Main(string[] args) + { + Console.WriteLine("=== Bitcoin Kernel Basic Usage Example ===\n"); + FullChainstateExample(); + } + + static void FullChainstateExample() + { + Console.WriteLine("Creating kernel..."); + + var dataDir = "/tmp/regtest-data2"; + var blocksDir = "/tmp/regtest-data/blocks2"; + + using var logging = new LoggingConnection((category, message, level) => + { + if (level <= (int)LogLevel.INFO) + Console.WriteLine($" [{category}] {message}"); + }); + + using var chainParams = new ChainParameters(ChainType.MAINNET); + using var contextOptions = new KernelContextOptions().SetChainParams(chainParams); + using var context = new KernelContext(contextOptions); + using var options = new ChainstateManagerOptions(context, dataDir, blocksDir) + .SetWorkerThreads(2); + using var chainstate = new ChainstateManager(context, chainParams, options); + + Console.WriteLine(" Kernel created successfully!"); + + try + { + var chain = chainstate.GetActiveChain(); + Console.WriteLine($" Chain height: {chain.Height}"); + Console.WriteLine($" Genesis hash: {Convert.ToHexString(chain.GetGenesis().GetBlockHash())}"); + + if (chain.Height > 0) + { + var tip = chain.GetTip(); + Console.WriteLine($" Tip hash: {Convert.ToHexString(tip.GetBlockHash())}"); + + var genesis = chain.GetBlockByHeight(0); + if (genesis != null) + Console.WriteLine($" Block 0 hash: {Convert.ToHexString(genesis.GetBlockHash())}"); } - - Console.WriteLine("\nPress any key to exit..."); - Console.ReadKey(); - - kernel.Dispose(); - Console.WriteLine(" Kernel disposed."); - } - } -} \ No newline at end of file + Console.WriteLine(" Chain queries working"); + } + catch (Exception ex) + { + Console.WriteLine($" Error: {ex.Message}"); + } + + Console.WriteLine("\nPress any key to exit..."); + Console.ReadKey(); + } +} diff --git a/examples/BlockProcessing/Program.cs b/examples/BlockProcessing/Program.cs index 3cadbc9..8b48deb 100644 --- a/examples/BlockProcessing/Program.cs +++ b/examples/BlockProcessing/Program.cs @@ -1,167 +1,135 @@ -ο»Ώusing System; using System.IO; using BitcoinKernel; +using BitcoinKernel.Primatives; +using BitcoinKernel.Chain; +using BitcoinKernel.Interop.Enums; -namespace BlockProcessing +namespace BlockProcessing; + +class Program { - class Program + static void Main(string[] args) { - static void Main(string[] args) + Console.WriteLine("Bitcoin Kernel Block Processing Example"); + Console.WriteLine("====================================="); + + var dataDir = Path.Combine(Path.GetTempPath(), $"bitcoinkernel_{Guid.NewGuid()}"); + var blocksDir = Path.Combine(dataDir, "blocks"); + + try { - Console.WriteLine("Bitcoin Kernel Block Processing Example"); - Console.WriteLine("====================================="); + using var logging = new LoggingConnection((category, message, level) => + { + if (level <= 2) + Console.WriteLine($"[{category}] {message}"); + }); + + using var chainParams = new ChainParameters(ChainType.MAINNET); + using var contextOptions = new KernelContextOptions().SetChainParams(chainParams); + using var context = new KernelContext(contextOptions); + using var options = new ChainstateManagerOptions(context, dataDir, blocksDir); + using var chainstate = new ChainstateManager(context, chainParams, options); + Console.WriteLine("Created kernel for mainnet"); + Console.WriteLine("Chainstate initialized"); + + byte[] sampleBlockData; try { - // Step 1: Create kernel library with fluent builder - using var kernel = KernelLibrary.Create() - .ForMainnet() - .WithLogging((category, message, level) => - { - // Simple logging callback - only show important messages - if (level <= 2) // Info and below - { - Console.WriteLine($"[{category}] {message}"); - } - }) - .Build(); - - Console.WriteLine("βœ“ Created kernel library for mainnet"); - Console.WriteLine("βœ“ Chainstate automatically initialized by builder"); - - // Step 3: Create a sample block for processing - // For demonstration, we'll try to create a block - // Note: This is a simplified example - real blocks are complex - byte[] sampleBlockData; - try - { - sampleBlockData = CreateSampleBlock(); - Console.WriteLine("βœ“ Created sample block data"); - } - catch (Exception ex) - { - Console.WriteLine($"⚠ Block creation failed: {ex.Message}"); - Console.WriteLine(" This is expected for simplified block data."); - Console.WriteLine(" The kernel library setup was successful!"); - return; - } + sampleBlockData = CreateSampleBlock(); + Console.WriteLine("Created sample block data"); + } + catch (Exception ex) + { + Console.WriteLine($"Block creation failed: {ex.Message}"); + Console.WriteLine("This is expected for simplified block data."); + return; + } + + DisplayBlockInfo(sampleBlockData); - // Step 4: Display block information before processing - DisplayBlockInfo(sampleBlockData); + Console.WriteLine("\nProcessing block..."); + try + { + using var block = Block.FromBytes(sampleBlockData); + bool isNew = chainstate.ProcessBlock(block); - // Step 5: Process the block through validation - Console.WriteLine("\nProcessing block..."); - try + if (isNew) { - bool success = kernel.ProcessBlock(sampleBlockData); - - if (success) - { - Console.WriteLine("βœ“ Block processed successfully!"); - - // Step 6: Get active chain information - var activeChain = kernel.Chainstate.GetActiveChain(); - Console.WriteLine($" - Active chain height: {activeChain.Height}"); - - var tip = activeChain.GetTip(); - Console.WriteLine($" - Active chain tip: {BitConverter.ToString(tip.GetBlockHash()).Replace("-", "")}"); - } - else - { - Console.WriteLine("βœ— Block processing failed - this may be expected for invalid block data"); - } + var activeChain = chainstate.GetActiveChain(); + Console.WriteLine($"Block processed! Chain height: {activeChain.Height}"); + var tip = activeChain.GetTip(); + Console.WriteLine($" - Tip: {BitConverter.ToString(tip.GetBlockHash()).Replace("-", "")}"); } - catch (Exception ex) + else { - Console.WriteLine($"βœ— Block processing error: {ex.Message}"); - Console.WriteLine(" This is expected for simplified/invalid block data."); + Console.WriteLine("Block processing failed - expected for invalid block data"); } - - Console.WriteLine("\nβœ“ Block processing example completed successfully!"); - Console.WriteLine(" (Note: Block processing may fail with simplified data, but the kernel setup works!)"); } catch (Exception ex) { - Console.WriteLine($"βœ— Error: {ex.Message}"); - if (ex.InnerException != null) - { - Console.WriteLine($"Inner exception: {ex.InnerException.Message}"); - } - Console.WriteLine($"Stack trace: {ex.StackTrace}"); + Console.WriteLine($"Block processing error: {ex.Message}"); + Console.WriteLine("This is expected for simplified/invalid block data."); } - } - private static byte[] CreateSampleBlock() + Console.WriteLine("\nBlock processing example completed!"); + } + catch (Exception ex) { - // Create a minimal block for demonstration - // This is a simplified example - real blocks would be much more complex - // In practice, you'd typically read block data from files or create from templates - - // For this example, we'll create a block with minimal valid structure - // Note: This won't be a real Bitcoin block, just demonstrates the API - - // A very basic block header structure (simplified) - // Version (4 bytes) + Previous Block Hash (32 bytes) + Merkle Root (32 bytes) + - // Timestamp (4 bytes) + Bits (4 bytes) + Nonce (4 bytes) = 80 bytes minimum + Console.WriteLine($"Error: {ex.Message}"); + if (ex.InnerException != null) + Console.WriteLine($"Inner exception: {ex.InnerException.Message}"); + } + } - byte[] blockData = new byte[80]; + private static byte[] CreateSampleBlock() + { + byte[] blockData = new byte[80]; - // Version: 1 (little endian) - BitConverter.GetBytes(1).CopyTo(blockData, 0); + // Version: 1 (little endian) + BitConverter.GetBytes(1).CopyTo(blockData, 0); - // Previous block hash: all zeros for genesis-like block - // (indices 4-35 remain 0) + // Previous block hash: all zeros (indices 4-35) + // Merkle root: all zeros (indices 36-67) - // Merkle root: all zeros for simplicity - // (indices 36-67 remain 0) + // Timestamp: current Unix timestamp + uint timestamp = (uint)(DateTimeOffset.UtcNow.ToUnixTimeSeconds()); + BitConverter.GetBytes(timestamp).CopyTo(blockData, 68); - // Timestamp: current Unix timestamp - uint timestamp = (uint)(DateTimeOffset.UtcNow.ToUnixTimeSeconds()); - BitConverter.GetBytes(timestamp).CopyTo(blockData, 68); + // Bits: 0x1d00ffff (Bitcoin mainnet difficulty) + BitConverter.GetBytes(0x1d00ffffu).CopyTo(blockData, 72); - // Bits: 0x1d00ffff (Bitcoin mainnet difficulty) - BitConverter.GetBytes(0x1d00ffffu).CopyTo(blockData, 72); + // Nonce: 0 (indices 76-79) - // Nonce: 0 for this example - // (indices 76-79 remain 0) + return blockData; + } - return blockData; - } + private static void DisplayBlockInfo(byte[] blockData) + { + Console.WriteLine("\nBlock Information:"); + Console.WriteLine("-----------------"); - private static void DisplayBlockInfo(byte[] blockData) + try { - Console.WriteLine("\nBlock Information:"); - Console.WriteLine("-----------------"); + Console.WriteLine($"Block Size: {blockData.Length} bytes"); + Console.WriteLine($"Block Data (first 32 bytes): {BitConverter.ToString(blockData.Take(32).ToArray()).Replace("-", " ")}"); - try - { - // Display basic block info - Console.WriteLine($"Block Size: {blockData.Length} bytes"); - - // Display first 32 bytes of block data for inspection - Console.WriteLine($"Block Data (first 32 bytes): {BitConverter.ToString(blockData.Take(32).ToArray()).Replace("-", " ")}"); + uint version = BitConverter.ToUInt32(blockData, 0); + Console.WriteLine($"Version: {version}"); - // Parse version (first 4 bytes, little endian) - uint version = BitConverter.ToUInt32(blockData, 0); - Console.WriteLine($"Version: {version}"); + uint timestamp = BitConverter.ToUInt32(blockData, 68); + DateTime blockTime = DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime; + Console.WriteLine($"Timestamp: {timestamp} ({blockTime:yyyy-MM-dd HH:mm:ss UTC})"); - // Parse timestamp (bytes 68-71, little endian) - uint timestamp = BitConverter.ToUInt32(blockData, 68); - DateTime blockTime = DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime; - Console.WriteLine($"Timestamp: {timestamp} ({blockTime:yyyy-MM-dd HH:mm:ss UTC})"); + uint bits = BitConverter.ToUInt32(blockData, 72); + Console.WriteLine($"Bits: 0x{bits:X8}"); - // Parse bits (bytes 72-75, little endian) - uint bits = BitConverter.ToUInt32(blockData, 72); - Console.WriteLine($"Bits: 0x{bits:X8}"); - - // Parse nonce (bytes 76-79, little endian) - uint nonce = BitConverter.ToUInt32(blockData, 76); - Console.WriteLine($"Nonce: {nonce}"); - } - catch (Exception ex) - { - Console.WriteLine($"Error parsing block info: {ex.Message}"); - } + uint nonce = BitConverter.ToUInt32(blockData, 76); + Console.WriteLine($"Nonce: {nonce}"); + } + catch (Exception ex) + { + Console.WriteLine($"Error parsing block info: {ex.Message}"); } } } diff --git a/src/BitcoinKernel.Core/Abstractions/BlockSpentOutputs.cs b/src/BitcoinKernel.Core/Abstractions/BlockSpentOutputs.cs deleted file mode 100644 index 532cd74..0000000 --- a/src/BitcoinKernel.Core/Abstractions/BlockSpentOutputs.cs +++ /dev/null @@ -1,121 +0,0 @@ -namespace BitcoinKernel.Core.Abstractions -{ - using BitcoinKernel.Interop; - using System; - - /// - /// Represents the spent outputs for all transactions in a block. - /// - public class BlockSpentOutputs : IDisposable - { - private IntPtr _handle; - private bool _disposed; - - internal BlockSpentOutputs(IntPtr handle) - { - if (handle == IntPtr.Zero) - { - throw new ArgumentException("Invalid block spent outputs handle", nameof(handle)); - } - - _handle = handle; - } - - /// - /// Gets the number of transactions with spent outputs in this block. - /// This excludes the coinbase transaction. - /// - public int Count - { - get - { - ThrowIfDisposed(); - return (int)NativeMethods.BlockSpentOutputsCount(_handle); - } - } - - /// - /// Gets the spent outputs for a transaction at the specified index. - /// The returned object is only valid for the lifetime of this BlockSpentOutputs. - /// - /// The zero-based index of the transaction (excluding coinbase). - /// A TransactionSpentOutputs object for the specified transaction. - public TransactionSpentOutputs GetTransactionSpentOutputs(int transactionIndex) - { - ThrowIfDisposed(); - - if (transactionIndex < 0 || transactionIndex >= Count) - { - throw new ArgumentOutOfRangeException(nameof(transactionIndex)); - } - - var txSpentOutputsPtr = NativeMethods.BlockSpentOutputsGetTransactionSpentOutputsAt( - _handle, (nuint)transactionIndex); - - if (txSpentOutputsPtr == IntPtr.Zero) - { - throw new InvalidOperationException($"Failed to get transaction spent outputs at index {transactionIndex}"); - } - - // Don't own the handle - it's only valid for the lifetime of BlockSpentOutputs - return new TransactionSpentOutputs(txSpentOutputsPtr, ownsHandle: false); - } - - /// - /// Enumerates all transaction spent outputs in the block. - /// - /// An enumerable of TransactionSpentOutputs for each transaction (excluding coinbase). - public IEnumerable EnumerateTransactionSpentOutputs() - { - ThrowIfDisposed(); - - int count = Count; - for (int i = 0; i < count; i++) - { - yield return GetTransactionSpentOutputs(i); - } - } - - internal IntPtr Handle - { - get - { - ThrowIfDisposed(); - return _handle; - } - } - - private void ThrowIfDisposed() - { - if (_disposed) - { - throw new ObjectDisposedException(nameof(BlockSpentOutputs)); - } - } - - protected virtual void Dispose(bool disposing) - { - if (!_disposed) - { - if (_handle != IntPtr.Zero) - { - NativeMethods.BlockSpentOutputsDestroy(_handle); - _handle = IntPtr.Zero; - } - - _disposed = true; - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~BlockSpentOutputs() - { - Dispose(false); - } - } -} diff --git a/src/BitcoinKernel.Core/Abstractions/Coin.cs b/src/BitcoinKernel.Core/Abstractions/Coin.cs deleted file mode 100644 index 17266b6..0000000 --- a/src/BitcoinKernel.Core/Abstractions/Coin.cs +++ /dev/null @@ -1,124 +0,0 @@ -namespace BitcoinKernel.Core.Abstractions -{ - using BitcoinKernel.Interop; - using System; - - /// - /// Represents a coin (unspent transaction output) with confirmation height information. - /// - public class Coin : IDisposable - { - private IntPtr _handle; - private bool _disposed; - private readonly bool _ownsHandle; - - internal Coin(IntPtr handle, bool ownsHandle = true) - { - if (handle == IntPtr.Zero) - { - throw new ArgumentException("Invalid coin handle", nameof(handle)); - } - - _handle = handle; - _ownsHandle = ownsHandle; - } - - /// - /// Gets the block height where the transaction that created this coin was included. - /// - public uint ConfirmationHeight - { - get - { - ThrowIfDisposed(); - return NativeMethods.CoinConfirmationHeight(_handle); - } - } - - /// - /// Gets a value indicating whether this coin is from a coinbase transaction. - /// - public bool IsCoinbase - { - get - { - ThrowIfDisposed(); - return NativeMethods.CoinIsCoinbase(_handle) != 0; - } - } - - /// - /// Gets the transaction output of this coin. - /// The returned object is only valid for the lifetime of this Coin. - /// - public TxOut GetOutput() - { - ThrowIfDisposed(); - var outputPtr = NativeMethods.CoinGetOutput(_handle); - if (outputPtr == IntPtr.Zero) - { - throw new InvalidOperationException("Failed to get coin output"); - } - - // Don't own the handle - it's only valid for the lifetime of the Coin - return new TxOut(outputPtr, ownsHandle: false); - } - - /// - /// Creates a copy of this coin. - /// - public Coin Copy() - { - ThrowIfDisposed(); - var copiedHandle = NativeMethods.CoinCopy(_handle); - if (copiedHandle == IntPtr.Zero) - { - throw new InvalidOperationException("Failed to copy coin"); - } - - return new Coin(copiedHandle, ownsHandle: true); - } - - internal IntPtr Handle - { - get - { - ThrowIfDisposed(); - return _handle; - } - } - - private void ThrowIfDisposed() - { - if (_disposed) - { - throw new ObjectDisposedException(nameof(Coin)); - } - } - - protected virtual void Dispose(bool disposing) - { - if (!_disposed) - { - if (_ownsHandle && _handle != IntPtr.Zero) - { - NativeMethods.CoinDestroy(_handle); - _handle = IntPtr.Zero; - } - - _disposed = true; - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~Coin() - { - Dispose(false); - } - } -} diff --git a/src/BitcoinKernel.Core/BitcoinKernel.Core.csproj b/src/BitcoinKernel.Core/BitcoinKernel.Core.csproj deleted file mode 100644 index e414cfd..0000000 --- a/src/BitcoinKernel.Core/BitcoinKernel.Core.csproj +++ /dev/null @@ -1,48 +0,0 @@ -ο»Ώ - - - net9.0 - enable - enable - true - - - BitcoinKernel.Core - 0.1.2 - JanB84 - .NET bindings and managed wrappers for libbitcoinkernel. Provides direct access to Bitcoin Core consensus and validation logic with automatic memory management. - MIT - https://github.com/JanB84/BitcoinKernel.NET - https://github.com/JanB84/BitcoinKernel.NET - git - bitcoin;kernel;libbitcoinkernel;wrapper;core;blockchain - false - true - snupkg - - $(TargetsForTfmSpecificContentInPackage);IncludeReferencedProjectsInPackage - - - - - - - - - - - - <_PackageFiles Include="$(OutputPath)BitcoinKernel.Interop.dll"> - Content - lib/$(TargetFramework)/BitcoinKernel.Interop.dll - - - - <_PackageFiles Include="$(OutputPath)runtimes\**\*.*" Condition="Exists('$(OutputPath)runtimes')"> - Content - runtimes/%(RecursiveDir)%(Filename)%(Extension) - - - - - diff --git a/src/BitcoinKernel.Interop/BitcoinKernel.Interop.csproj b/src/BitcoinKernel.Interop/BitcoinKernel.Interop.csproj index 0b39cd6..ca9447e 100644 --- a/src/BitcoinKernel.Interop/BitcoinKernel.Interop.csproj +++ b/src/BitcoinKernel.Interop/BitcoinKernel.Interop.csproj @@ -7,6 +7,12 @@ true + + + <_Parameter1>BitcoinKernel + + + diff --git a/src/BitcoinKernel.Interop/Delegates/DestroyCallback.cs b/src/BitcoinKernel.Interop/Delegates/DestroyCallback.cs index db7bd40..c4dfe17 100644 --- a/src/BitcoinKernel.Interop/Delegates/DestroyCallback.cs +++ b/src/BitcoinKernel.Interop/Delegates/DestroyCallback.cs @@ -6,4 +6,4 @@ namespace BitcoinKernel.Interop.Delegates; /// Function signature for freeing user data. /// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public delegate void DestroyCallback(IntPtr user_data); +internal delegate void DestroyCallback(IntPtr user_data); diff --git a/src/BitcoinKernel.Interop/Delegates/LoggingCallback.cs b/src/BitcoinKernel.Interop/Delegates/LoggingCallback.cs index 963599f..00b2265 100644 --- a/src/BitcoinKernel.Interop/Delegates/LoggingCallback.cs +++ b/src/BitcoinKernel.Interop/Delegates/LoggingCallback.cs @@ -7,7 +7,7 @@ namespace BitcoinKernel.Interop.Delegates; /// internal logs will pass through this callback. /// [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public delegate void LoggingCallback( +internal delegate void LoggingCallback( IntPtr user_data, IntPtr message, nuint message_len); \ No newline at end of file diff --git a/src/BitcoinKernel.Interop/Delegates/Notification/NotifyBlockTip.cs b/src/BitcoinKernel.Interop/Delegates/Notification/NotifyBlockTip.cs index 0a3bb53..e6ca914 100644 --- a/src/BitcoinKernel.Interop/Delegates/Notification/NotifyBlockTip.cs +++ b/src/BitcoinKernel.Interop/Delegates/Notification/NotifyBlockTip.cs @@ -3,7 +3,7 @@ namespace BitcoinKernel.Interop.Delegates.Notification; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public delegate void NotifyBlockTip( +internal delegate void NotifyBlockTip( IntPtr user_data, IntPtr block_index); diff --git a/src/BitcoinKernel.Interop/Delegates/Notification/NotifyFatalError.cs b/src/BitcoinKernel.Interop/Delegates/Notification/NotifyFatalError.cs index a919608..4ba4cf1 100644 --- a/src/BitcoinKernel.Interop/Delegates/Notification/NotifyFatalError.cs +++ b/src/BitcoinKernel.Interop/Delegates/Notification/NotifyFatalError.cs @@ -3,6 +3,6 @@ namespace BitcoinKernel.Interop.Delegates.Notification; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public delegate void NotifyFatalError( +internal delegate void NotifyFatalError( IntPtr user_data, [MarshalAs(UnmanagedType.LPUTF8Str)] string error_message); \ No newline at end of file diff --git a/src/BitcoinKernel.Interop/Delegates/Notification/NotifyFlushError.cs b/src/BitcoinKernel.Interop/Delegates/Notification/NotifyFlushError.cs index f524baa..3e583b0 100644 --- a/src/BitcoinKernel.Interop/Delegates/Notification/NotifyFlushError.cs +++ b/src/BitcoinKernel.Interop/Delegates/Notification/NotifyFlushError.cs @@ -4,6 +4,6 @@ namespace BitcoinKernel.Interop.Delegates.Notification; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public delegate void NotifyFlushError( +internal delegate void NotifyFlushError( IntPtr user_data, [MarshalAs(UnmanagedType.LPUTF8Str)] string error_message); \ No newline at end of file diff --git a/src/BitcoinKernel.Interop/Delegates/Notification/NotifyHeaderTip.cs b/src/BitcoinKernel.Interop/Delegates/Notification/NotifyHeaderTip.cs index ed12508..5e01408 100644 --- a/src/BitcoinKernel.Interop/Delegates/Notification/NotifyHeaderTip.cs +++ b/src/BitcoinKernel.Interop/Delegates/Notification/NotifyHeaderTip.cs @@ -3,7 +3,7 @@ namespace BitcoinKernel.Interop.Delegates.Notification; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public delegate void NotifyHeaderTip( +internal delegate void NotifyHeaderTip( IntPtr user_data, IntPtr block_index, long timestamp); \ No newline at end of file diff --git a/src/BitcoinKernel.Interop/Delegates/Notification/NotifyProgress.cs b/src/BitcoinKernel.Interop/Delegates/Notification/NotifyProgress.cs index 3919a0f..9e8ce0f 100644 --- a/src/BitcoinKernel.Interop/Delegates/Notification/NotifyProgress.cs +++ b/src/BitcoinKernel.Interop/Delegates/Notification/NotifyProgress.cs @@ -4,7 +4,7 @@ namespace BitcoinKernel.Interop.Delegates.Notification; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public delegate void NotifyProgress( +internal delegate void NotifyProgress( IntPtr user_data, [MarshalAs(UnmanagedType.LPUTF8Str)] string title, int progress_percent, diff --git a/src/BitcoinKernel.Interop/Delegates/Notification/NotifyWarningSet.cs b/src/BitcoinKernel.Interop/Delegates/Notification/NotifyWarningSet.cs index e7f2170..0dc8758 100644 --- a/src/BitcoinKernel.Interop/Delegates/Notification/NotifyWarningSet.cs +++ b/src/BitcoinKernel.Interop/Delegates/Notification/NotifyWarningSet.cs @@ -4,6 +4,6 @@ namespace BitcoinKernel.Interop.Delegates.Notification; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public delegate void NotifyWarningSet( +internal delegate void NotifyWarningSet( IntPtr user_data, Warning warning); \ No newline at end of file diff --git a/src/BitcoinKernel.Interop/Delegates/Notification/NotifyWarningUnset.cs b/src/BitcoinKernel.Interop/Delegates/Notification/NotifyWarningUnset.cs index 64f3779..13b6bea 100644 --- a/src/BitcoinKernel.Interop/Delegates/Notification/NotifyWarningUnset.cs +++ b/src/BitcoinKernel.Interop/Delegates/Notification/NotifyWarningUnset.cs @@ -4,6 +4,6 @@ namespace BitcoinKernel.Interop.Delegates.Notification; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public delegate void NotifyWarningUnset( +internal delegate void NotifyWarningUnset( IntPtr user_data, Warning warning); diff --git a/src/BitcoinKernel.Interop/Delegates/Validation/ValidationBlockChecked.cs b/src/BitcoinKernel.Interop/Delegates/Validation/ValidationBlockChecked.cs index 9ed384a..9e7ee9b 100644 --- a/src/BitcoinKernel.Interop/Delegates/Validation/ValidationBlockChecked.cs +++ b/src/BitcoinKernel.Interop/Delegates/Validation/ValidationBlockChecked.cs @@ -3,7 +3,7 @@ namespace BitcoinKernel.Interop.Delegates.Validation; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public delegate void ValidationBlockChecked( +internal delegate void ValidationBlockChecked( IntPtr user_data, IntPtr block, IntPtr validation_state); \ No newline at end of file diff --git a/src/BitcoinKernel.Interop/Delegates/Validation/ValidationBlockConnected.cs b/src/BitcoinKernel.Interop/Delegates/Validation/ValidationBlockConnected.cs index ed9525c..57e0a9e 100644 --- a/src/BitcoinKernel.Interop/Delegates/Validation/ValidationBlockConnected.cs +++ b/src/BitcoinKernel.Interop/Delegates/Validation/ValidationBlockConnected.cs @@ -3,7 +3,7 @@ namespace BitcoinKernel.Interop.Delegates.Validation; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public delegate void ValidationBlockConnected( +internal delegate void ValidationBlockConnected( IntPtr user_data, IntPtr block_index, IntPtr block); \ No newline at end of file diff --git a/src/BitcoinKernel.Interop/Delegates/Validation/ValidationBlockDisconnected.cs b/src/BitcoinKernel.Interop/Delegates/Validation/ValidationBlockDisconnected.cs index fbd73e2..c7f501a 100644 --- a/src/BitcoinKernel.Interop/Delegates/Validation/ValidationBlockDisconnected.cs +++ b/src/BitcoinKernel.Interop/Delegates/Validation/ValidationBlockDisconnected.cs @@ -3,6 +3,6 @@ namespace BitcoinKernel.Interop.Delegates.Validation; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public delegate void ValidationBlockDisconnected( +internal delegate void ValidationBlockDisconnected( IntPtr user_data, IntPtr block); \ No newline at end of file diff --git a/src/BitcoinKernel.Interop/Delegates/Validation/ValidationNewPoWValidBlock.cs b/src/BitcoinKernel.Interop/Delegates/Validation/ValidationNewPoWValidBlock.cs index 238894d..da2ece4 100644 --- a/src/BitcoinKernel.Interop/Delegates/Validation/ValidationNewPoWValidBlock.cs +++ b/src/BitcoinKernel.Interop/Delegates/Validation/ValidationNewPoWValidBlock.cs @@ -3,7 +3,7 @@ namespace BitcoinKernel.Interop.Delegates.Validation; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public delegate void ValidationNewPoWValidBlock( +internal delegate void ValidationNewPoWValidBlock( IntPtr user_data, IntPtr block_index, IntPtr block); \ No newline at end of file diff --git a/src/BitcoinKernel.Interop/Enums/ChainType.cs b/src/BitcoinKernel.Interop/Enums/ChainType.cs index 80e0993..f7e9ea1 100644 --- a/src/BitcoinKernel.Interop/Enums/ChainType.cs +++ b/src/BitcoinKernel.Interop/Enums/ChainType.cs @@ -1,11 +1,10 @@ -namespace BitcoinKernel.Interop.Enums +namespace BitcoinKernel.Interop.Enums; + +public enum ChainType : uint { - public enum ChainType : uint - { - MAINNET = 0, - TESTNET = 1, - TESTNET_4 = 2, - SIGNET = 3, - REGTEST = 4 - } -} \ No newline at end of file + MAINNET = 0, + TESTNET = 1, + TESTNET_4 = 2, + SIGNET = 3, + REGTEST = 4 +} diff --git a/src/BitcoinKernel.Interop/Enums/LogCategory.cs b/src/BitcoinKernel.Interop/Enums/LogCategory.cs index 16e5c56..d503355 100644 --- a/src/BitcoinKernel.Interop/Enums/LogCategory.cs +++ b/src/BitcoinKernel.Interop/Enums/LogCategory.cs @@ -1,63 +1,61 @@ -namespace BitcoinKernel.Interop.Enums +namespace BitcoinKernel.Interop.Enums; +/// +/// A collection of logging categories that may be encountered by kernel code. +/// +public enum LogCategory : byte { /// - /// A collection of logging categories that may be encountered by kernel code. - /// - public enum LogCategory : byte - { - /// - /// All categories. - /// - All = 0, - - /// - /// Benchmark logging. - /// - Bench = 1, - - /// - /// Block storage operations. - /// - BlockStorage = 2, - - /// - /// Coin database operations. - /// - CoinDb = 3, - - /// - /// LevelDB operations. - /// - LevelDb = 4, - - /// - /// Memory pool operations. - /// - Mempool = 5, - - /// - /// Pruning operations. - /// - Prune = 6, - - /// - /// Random number generation. - /// - Rand = 7, - - /// - /// Reindexing operations. - /// - Reindex = 8, - - /// - /// Validation operations. - /// - Validation = 9, - - /// - /// Kernel operations. - /// - Kernel = 10 - } + /// All categories. + /// + All = 0, + + /// + /// Benchmark logging. + /// + Bench = 1, + + /// + /// Block storage operations. + /// + BlockStorage = 2, + + /// + /// Coin database operations. + /// + CoinDb = 3, + + /// + /// LevelDB operations. + /// + LevelDb = 4, + + /// + /// Memory pool operations. + /// + Mempool = 5, + + /// + /// Pruning operations. + /// + Prune = 6, + + /// + /// Random number generation. + /// + Rand = 7, + + /// + /// Reindexing operations. + /// + Reindex = 8, + + /// + /// Validation operations. + /// + Validation = 9, + + /// + /// Kernel operations. + /// + Kernel = 10 } diff --git a/src/BitcoinKernel.Interop/Enums/ScriptVerificationFlags.cs b/src/BitcoinKernel.Interop/Enums/ScriptVerificationFlags.cs index fd9ddd1..b978bbe 100644 --- a/src/BitcoinKernel.Interop/Enums/ScriptVerificationFlags.cs +++ b/src/BitcoinKernel.Interop/Enums/ScriptVerificationFlags.cs @@ -1,59 +1,57 @@ -namespace BitcoinKernel.Interop.Enums +namespace BitcoinKernel.Interop.Enums; +/// +/// Script verification flags that may be composed with each other. +/// +[Flags] +public enum ScriptVerificationFlags : uint { /// - /// Script verification flags that may be composed with each other. - /// - [Flags] - public enum ScriptVerificationFlags : uint - { - /// - /// No script verification flags. - /// - None = 0, - - /// - /// Evaluate P2SH (BIP16) subscripts. - /// - P2SH = 1U << 0, - - /// - /// Enforce strict DER (BIP66) compliance. - /// - DerSig = 1U << 2, - - /// - /// Enforce NULLDUMMY (BIP147). - /// - NullDummy = 1U << 4, - - /// - /// Enable CHECKLOCKTIMEVERIFY (BIP65). - /// - CheckLockTimeVerify = 1U << 9, - - /// - /// Enable CHECKSEQUENCEVERIFY (BIP112). - /// - CheckSequenceVerify = 1U << 10, - - /// - /// Enable WITNESS (BIP141). - /// - Witness = 1U << 11, - - /// - /// Enable TAPROOT (BIPs 341 & 342). - /// - Taproot = 1U << 17, - - /// - /// All standard script verification flags. - /// - All = P2SH | DerSig | NullDummy | CheckLockTimeVerify | CheckSequenceVerify | Witness | Taproot, - - /// - /// All script verification flags pre taproot(P2SH + Witness). - /// - AllPreTaproot = P2SH | DerSig | NullDummy | CheckLockTimeVerify | CheckSequenceVerify | Witness - } -} \ No newline at end of file + /// No script verification flags. + /// + None = 0, + + /// + /// Evaluate P2SH (BIP16) subscripts. + /// + P2SH = 1U << 0, + + /// + /// Enforce strict DER (BIP66) compliance. + /// + DerSig = 1U << 2, + + /// + /// Enforce NULLDUMMY (BIP147). + /// + NullDummy = 1U << 4, + + /// + /// Enable CHECKLOCKTIMEVERIFY (BIP65). + /// + CheckLockTimeVerify = 1U << 9, + + /// + /// Enable CHECKSEQUENCEVERIFY (BIP112). + /// + CheckSequenceVerify = 1U << 10, + + /// + /// Enable WITNESS (BIP141). + /// + Witness = 1U << 11, + + /// + /// Enable TAPROOT (BIPs 341 & 342). + /// + Taproot = 1U << 17, + + /// + /// All standard script verification flags. + /// + All = P2SH | DerSig | NullDummy | CheckLockTimeVerify | CheckSequenceVerify | Witness | Taproot, + + /// + /// All script verification flags pre taproot(P2SH + Witness). + /// + AllPreTaproot = P2SH | DerSig | NullDummy | CheckLockTimeVerify | CheckSequenceVerify | Witness +} diff --git a/src/BitcoinKernel.Interop/Enums/ScriptVerifyStatus.cs b/src/BitcoinKernel.Interop/Enums/ScriptVerifyStatus.cs index 6364e27..9521e4f 100644 --- a/src/BitcoinKernel.Interop/Enums/ScriptVerifyStatus.cs +++ b/src/BitcoinKernel.Interop/Enums/ScriptVerifyStatus.cs @@ -1,38 +1,36 @@ -namespace BitcoinKernel.Interop.Enums +namespace BitcoinKernel.Interop.Enums; +/// +/// Status codes that may be returned by script verification operations. +/// +public enum ScriptVerifyStatus : byte { /// - /// Status codes that may be returned by script verification operations. + /// Script verified successfully. /// - public enum ScriptVerifyStatus : byte - { - /// - /// Script verified successfully. - /// - OK = 0, + OK = 0, - /// - /// The flags were combined in an invalid way. - /// - ERROR_INVALID_FLAGS_COMBINATION = 1, + /// + /// The flags were combined in an invalid way. + /// + ERROR_INVALID_FLAGS_COMBINATION = 1, - /// - /// The taproot flag was set, so valid spent_outputs have to be provided. - /// - ERROR_SPENT_OUTPUTS_REQUIRED = 2, + /// + /// The taproot flag was set, so valid spent_outputs have to be provided. + /// + ERROR_SPENT_OUTPUTS_REQUIRED = 2, - /// - /// The input index is out of bounds for the transaction. - /// - ERROR_TX_INPUT_INDEX = 3, + /// + /// The input index is out of bounds for the transaction. + /// + ERROR_TX_INPUT_INDEX = 3, - /// - /// The number of spent outputs doesn't match the number of transaction inputs. - /// - ERROR_SPENT_OUTPUTS_MISMATCH = 4, + /// + /// The number of spent outputs doesn't match the number of transaction inputs. + /// + ERROR_SPENT_OUTPUTS_MISMATCH = 4, - /// - /// The verification flags value is invalid. - /// - ERROR_INVALID_FLAGS = 5 - } -} \ No newline at end of file + /// + /// The verification flags value is invalid. + /// + ERROR_INVALID_FLAGS = 5 +} diff --git a/src/BitcoinKernel.Interop/Enums/Warning.cs b/src/BitcoinKernel.Interop/Enums/Warning.cs index 5a98118..d59136c 100644 --- a/src/BitcoinKernel.Interop/Enums/Warning.cs +++ b/src/BitcoinKernel.Interop/Enums/Warning.cs @@ -1,8 +1,7 @@ -namespace BitcoinKernel.Interop.Enums +namespace BitcoinKernel.Interop.Enums; + +public enum Warning { - public enum Warning - { - UnknownNewRulesActivated = 0, - LargeWorkInvalidChain = 1 - } -} \ No newline at end of file + UnknownNewRulesActivated = 0, + LargeWorkInvalidChain = 1 +} diff --git a/src/BitcoinKernel.Interop/NativeMethods.cs b/src/BitcoinKernel.Interop/NativeMethods.cs index 17e7669..1b5c770 100644 --- a/src/BitcoinKernel.Interop/NativeMethods.cs +++ b/src/BitcoinKernel.Interop/NativeMethods.cs @@ -5,898 +5,896 @@ using BitcoinKernel.Interop.Enums; using BitcoinKernel.Interop.Delegates; -namespace BitcoinKernel.Interop +namespace BitcoinKernel.Interop; +/// +/// Low-level P/Invoke declarations for the Bitcoin Kernel C API. +/// Maps directly to the bitcoinkernel.h C header. +/// +internal static class NativeMethods { + #region Library Configuration + + private const string LibName = "bitcoinkernel"; + + // For loading platform-specific libraries + static NativeMethods() + { + NativeLibraryLoader.EnsureLoaded(); + } + + #endregion + + #region Context Management + /// - /// Low-level P/Invoke declarations for the Bitcoin Kernel C API. - /// Maps directly to the bitcoinkernel.h C header. + /// Creates a new kernel context. /// - public static class NativeMethods - { - #region Library Configuration - - private const string LibName = "bitcoinkernel"; - - // For loading platform-specific libraries - static NativeMethods() - { - NativeLibraryLoader.EnsureLoaded(); - } - - #endregion - - #region Context Management - - /// - /// Creates a new kernel context. - /// - /// Pointer to kernel context, or IntPtr.Zero on failure - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_create")] - public static extern IntPtr ContextCreate(IntPtr options); - - /// - /// Destroys a kernel context and frees associated resources. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_destroy")] - public static extern void ContextDestroy(IntPtr context); - - /// - /// Interrupts long-running operations associated with this context. - /// Returns 0 on success. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_interrupt")] - public static extern int ContextInterrupt(IntPtr context); - - #endregion - - #region Context Options - - /// - /// Creates context options with default values. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_options_create")] - public static extern IntPtr ContextOptionsCreate(); - - /// - /// Destroys context options. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_options_destroy")] - public static extern void ContextOptionsDestroy(IntPtr options); - - /// - /// Sets the chain parameters for the context. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_options_set_chainparams")] - public static extern void ContextOptionsSetChainParams(IntPtr options, IntPtr chain_params); - - /// - /// Sets the notification callbacks for the context. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_options_set_notifications")] - public static extern void ContextOptionsSetNotifications(IntPtr options, NotificationInterfaceCallbacks callbacks); - - /// - /// Sets the validation interface for the context. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_options_set_validation_interface")] - public static extern void ContextOptionsSetValidationInterface(IntPtr options, ValidationInterfaceCallbacks callbacks); - - #endregion - - #region Chain Parameters - - /// - /// Creates chain parameters for the specified chain type. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_parameters_create")] - public static extern IntPtr ChainParametersCreate(ChainType chain_type); - - /// - /// Destroys chain parameters. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_parameters_destroy")] - public static extern void ChainParametersDestroy(IntPtr chain_params); - - #endregion - - #region Chainstate Manager - - /// - /// Creates a chainstate manager. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_create")] - public static extern IntPtr ChainstateManagerCreate(IntPtr options); - - /// - /// Destroys a chainstate manager. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_destroy")] - public static extern void ChainstateManagerDestroy(IntPtr manager); - - /// - /// Processes a block through validation. - /// Returns 0 on success. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_process_block")] - public static extern int ChainstateManagerProcessBlock( - IntPtr manager, - IntPtr block, - ref int new_block); - - /// - /// Gets a block tree entry by its block hash. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_get_block_tree_entry_by_hash")] - public static extern IntPtr ChainstateManagerGetBlockTreeEntryByHash( - IntPtr manager, - IntPtr block_hash); - - /// - /// Gets the active chain from the chainstate manager. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_get_active_chain")] - public static extern IntPtr ChainstateManagerGetActiveChain(IntPtr manager); - - /// - /// Gets the block tree entry with the most cumulative proof of work. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_get_best_entry")] - public static extern IntPtr ChainstateManagerGetBestEntry(IntPtr manager); - - /// - /// Processes and validates a block header. - /// Returns 0 on success. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_process_block_header")] - public static extern int ChainstateManagerProcessBlockHeader( - IntPtr manager, - IntPtr header, - IntPtr block_validation_state); - - /// - /// Imports blocks from an array of file paths. - /// Returns 0 on success. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_import_blocks")] - public static extern int ChainstateManagerImportBlocks( - IntPtr manager, - [MarshalAs(UnmanagedType.LPArray)] string[] block_file_paths_data, - nuint[] block_file_paths_lens, - nuint block_file_paths_data_len); - - #endregion - - #region Chainstate Manager Options - - /// - /// Creates chainstate manager options. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_options_create")] - public static extern IntPtr ChainstateManagerOptionsCreate( - IntPtr context, - [MarshalAs(UnmanagedType.LPUTF8Str)] string data_directory, - nuint data_directory_len, - [MarshalAs(UnmanagedType.LPUTF8Str)] string blocks_directory, - nuint blocks_directory_len); - - /// - /// Destroys chainstate manager options. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_options_destroy")] - public static extern void ChainstateManagerOptionsDestroy(IntPtr options); - - /// - /// Sets the number of worker threads for script verification. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_options_set_worker_threads_num")] - public static extern void ChainstateManagerOptionsSetWorkerThreads( - IntPtr options, - int worker_threads); - - /// - /// Sets whether to wipe the databases on load. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_options_set_wipe_dbs")] - public static extern int ChainstateManagerOptionsSetWipeDbs( - IntPtr options, - int wipe_block_tree_db, - int wipe_chainstate_db); - - /// - /// Sets block tree db in memory. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_options_update_block_tree_db_in_memory")] - public static extern void ChainstateManagerOptionsUpdateBlockTreeDbInMemory( - IntPtr options, - int block_tree_db_in_memory); - - /// - /// Sets chainstate db in memory. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_options_update_chainstate_db_in_memory")] - public static extern void ChainstateManagerOptionsUpdateChainstateDbInMemory( - IntPtr options, - int chainstate_db_in_memory); - - #endregion - - #region Block Operations - - /// - /// Creates a block from raw data. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_create")] - public static extern IntPtr BlockCreate( - byte[] raw_block, - UIntPtr raw_block_len); - - /// - /// Destroys a block. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_destroy")] - public static extern void BlockDestroy(IntPtr block); - - /// - /// Reads a block from disk by its block tree entry. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_read")] - public static extern IntPtr BlockRead( - IntPtr chainstate_manager, - IntPtr block_tree_entry); - - /// - /// Gets the number of transactions in a block. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_count_transactions")] - public static extern nuint BlockCountTransactions(IntPtr block); - - /// - /// Gets the block hash. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_get_hash")] - public static extern IntPtr BlockGetHash(IntPtr block); - - /// - /// Gets the block header from a block. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_get_header")] - public static extern IntPtr BlockGetHeader(IntPtr block); - - /// - /// Serializes the block to bytes. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_to_bytes")] - public static extern int BlockToBytes( - IntPtr block, - WriteBytes writer, - IntPtr user_data); - - /// - /// Delegate for writing bytes during serialization. - /// - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate int WriteBytes(IntPtr bytes, nuint size, IntPtr userdata); - - /// - /// Gets the block hash from a block tree entry. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_tree_entry_get_block_hash")] - public static extern IntPtr BlockTreeEntryGetBlockHash(IntPtr block_tree_entry); - - /// - /// Gets the block height from a block tree entry. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_tree_entry_get_height")] - public static extern int BlockTreeEntryGetHeight(IntPtr block_tree_entry); - - /// - /// Gets the previous block tree entry from a block tree entry. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_tree_entry_get_previous")] - public static extern IntPtr BlockTreeEntryGetPrevious(IntPtr block_tree_entry); - - /// - /// Gets the block header from a block tree entry. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_tree_entry_get_block_header")] - public static extern IntPtr BlockTreeEntryGetBlockHeader(IntPtr block_tree_entry); - - /// - /// Checks if two block tree entries are equal. Two block tree entries are equal when they - /// point to the same block. - /// Returns 1 if equal, 0 otherwise. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_tree_entry_equals")] - public static extern int BlockTreeEntryEquals(IntPtr entry1, IntPtr entry2); - - /// - /// Copies a block (reference counted). - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_copy")] - public static extern IntPtr BlockCopy(IntPtr block); - - /// - /// Gets a transaction at the specified index in a block. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_get_transaction_at")] - public static extern IntPtr BlockGetTransactionAt(IntPtr block, nuint index); - - #endregion - - #region BlockHash Operations - - /// - /// Creates a block hash from 32 bytes. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_hash_create")] - public static extern IntPtr BlockHashCreate(IntPtr hash); - - /// - /// Creates a block hash from 32 bytes (unsafe version). - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_hash_create")] - public static extern unsafe IntPtr BlockHashCreate(byte* hash); - - /// - /// Serializes the block hash to bytes (32 bytes). - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_hash_to_bytes")] - public static extern void BlockHashToBytes(IntPtr block_hash, [MarshalAs(UnmanagedType.LPArray, SizeConst = 32)] byte[] output); - - /// - /// Destroys a block hash. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_hash_destroy")] - public static extern void BlockHashDestroy(IntPtr block_hash); - - #endregion - - #region Block Header Operations - - /// - /// Creates a block header from raw serialized data (80 bytes). - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_create")] - public static extern IntPtr BlockHeaderCreate( - byte[] raw_block_header, - UIntPtr raw_block_header_len); - - /// - /// Copies a block header. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_copy")] - public static extern IntPtr BlockHeaderCopy(IntPtr header); - - /// - /// Gets the block hash from a block header. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_get_hash")] - public static extern IntPtr BlockHeaderGetHash(IntPtr header); - - /// - /// Gets the previous block hash from a block header. - /// The returned hash is unowned and only valid for the lifetime of the block header. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_get_prev_hash")] - public static extern IntPtr BlockHeaderGetPrevHash(IntPtr header); - - /// - /// Gets the timestamp from a block header (Unix epoch seconds). - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_get_timestamp")] - public static extern uint BlockHeaderGetTimestamp(IntPtr header); - - /// - /// Gets the nBits difficulty target from a block header (compact format). - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_get_bits")] - public static extern uint BlockHeaderGetBits(IntPtr header); - - /// - /// Gets the version from a block header. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_get_version")] - public static extern int BlockHeaderGetVersion(IntPtr header); - - /// - /// Gets the nonce from a block header. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_get_nonce")] - public static extern uint BlockHeaderGetNonce(IntPtr header); - - /// - /// Destroys a block header. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_destroy")] - public static extern void BlockHeaderDestroy(IntPtr header); - - #endregion - - #region Chain Operations - - /// - /// Gets the height of the chain. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_get_height")] - public static extern int ChainGetHeight(IntPtr chain); - - /// - /// Gets a block tree entry by height. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_get_by_height")] - public static extern IntPtr ChainGetByHeight(IntPtr chain, int height); - - /// - /// Checks if a block tree entry is in the chain. - /// Returns 1 if in chain, 0 otherwise. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_contains")] - public static extern int ChainContains(IntPtr chain, IntPtr block_tree_entry); - - #endregion - - #region Transaction Operations - - /// - /// Creates a transaction from serialized data. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_create")] - public static extern IntPtr TransactionCreate(IntPtr raw_transaction, nuint raw_transaction_len); - - /// - /// Copies a transaction (reference counted). - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_copy")] - public static extern IntPtr TransactionCopy(IntPtr transaction); - - /// - /// Serializes a transaction to bytes. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_to_bytes")] - public static extern int TransactionToBytes( - IntPtr transaction, - WriteBytes writer, - IntPtr user_data); - - /// - /// Gets the transaction ID (txid). - /// Returns a pointer to btck_Txid (not owned, lifetime depends on transaction). - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_get_txid")] - public static extern IntPtr TransactionGetTxid(IntPtr transaction); - - /// - /// Gets the number of outputs in a transaction. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_count_outputs")] - public static extern nuint TransactionCountOutputs(IntPtr transaction); - - /// - /// Gets the number of inputs in a transaction. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_count_inputs")] - public static extern nuint TransactionCountInputs(IntPtr transaction); - - /// - /// Gets a transaction output at the specified index. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_get_output_at")] - public static extern IntPtr TransactionGetOutputAt(IntPtr transaction, nuint index); - - /// - /// Gets a transaction input at the specified index. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_get_input_at")] - public static extern IntPtr TransactionGetInputAt(IntPtr transaction, nuint index); - - /// - /// Destroys a transaction. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_destroy")] - public static extern void TransactionDestroy(IntPtr transaction); - - /// - /// Gets the script pubkey from a transaction output. - /// Returns a pointer to btck_ScriptPubkey. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_output_get_script_pubkey")] - public static extern IntPtr TransactionOutputGetScriptPubkey(IntPtr output); - - /// - /// Gets the value (amount) from a transaction output. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_output_get_amount")] - public static extern long TransactionOutputGetAmount(IntPtr output); - - /// - /// Copy a transaction output. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_output_copy")] - public static extern IntPtr TransactionOutputCopy(IntPtr output); - - /// - /// Destroys a transaction output. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_output_destroy")] - public static extern void TransactionOutputDestroy(IntPtr output); - - #endregion - - #region PrecomputedTransactionData Operations - - /// - /// Creates precomputed transaction data for script verification. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_precomputed_transaction_data_create")] - public static extern IntPtr PrecomputedTransactionDataCreate( - IntPtr tx_to, - IntPtr[] spent_outputs, - nuint spent_outputs_len); - - /// - /// Copies precomputed transaction data. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_precomputed_transaction_data_copy")] - public static extern IntPtr PrecomputedTransactionDataCopy(IntPtr precomputed_txdata); - - /// - /// Destroys precomputed transaction data. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_precomputed_transaction_data_destroy")] - public static extern void PrecomputedTransactionDataDestroy(IntPtr precomputed_txdata); - - #endregion - - #region ScriptPubkey Operations - - /// - /// Creates a script pubkey from serialized data. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_script_pubkey_create")] - public static extern IntPtr ScriptPubkeyCreate(IntPtr script_pubkey_data, nuint script_pubkey_len); - - /// - /// Copies a script pubkey. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_script_pubkey_copy")] - public static extern IntPtr ScriptPubkeyCopy(IntPtr script_pubkey); - - /// - /// Verifies a script pubkey. - /// Returns 1 if valid, 0 otherwise. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_script_pubkey_verify")] - public static extern int ScriptPubkeyVerify( - IntPtr script_pubkey, - long amount, - IntPtr tx_to, - IntPtr precomputed_txdata, - uint input_index, - uint flags, - IntPtr status); - - /// - /// Serializes a script pubkey to bytes. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_script_pubkey_to_bytes")] - public static extern int ScriptPubkeyToBytes( - IntPtr script_pubkey, - WriteBytes writer, - IntPtr user_data); - - /// - /// Destroys a script pubkey. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_script_pubkey_destroy")] - public static extern void ScriptPubkeyDestroy(IntPtr script_pubkey); - - #endregion - - #region TransactionOutput Operations (Additional) - - /// - /// Creates a transaction output from a script pubkey and amount. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_output_create")] - public static extern IntPtr TransactionOutputCreate(IntPtr script_pubkey, long amount); - - #endregion - - #region Logging - - /// - /// Creates a logging connection with a callback. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_logging_connection_create")] - public static extern IntPtr LoggingConnectionCreate( - LoggingCallback callback, - IntPtr user_data, - DestroyCallback? user_data_destroy_callback); - - /// - /// Destroys a logging connection. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_logging_connection_destroy")] - public static extern void LoggingConnectionDestroy(IntPtr connection); - - /// - /// Disables logging permanently. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_logging_disable")] - public static extern void LoggingDisable(); - - /// - /// Sets the log level for a category. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_logging_set_level_category")] - public static extern void LoggingSetLevelCategory(LogCategory category, LogLevel level); - - /// - /// Sets logging options. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_logging_set_options")] - public static extern void LoggingSetOptions(LoggingOptions options); - - /// - /// Enables a log category. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_logging_enable_category")] - public static extern void LoggingEnableCategory(LogCategory category); - - /// - /// Disables a log category. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_logging_disable_category")] - public static extern void LoggingDisableCategory(LogCategory category); - - #endregion - - #region Block Validation State - - /// - /// Creates a new block validation state. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_validation_state_create")] - public static extern IntPtr BlockValidationStateCreate(); - - /// - /// Gets the validation mode from a block validation state. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_validation_state_get_validation_mode")] - public static extern ValidationMode BlockValidationStateGetValidationMode(IntPtr validation_state); - - /// - /// Gets the block validation result from a block validation state. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_validation_state_get_block_validation_result")] - public static extern BlockValidationResult BlockValidationStateGetBlockValidationResult(IntPtr validation_state); - - /// - /// Copies a block validation state. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_validation_state_copy")] - public static extern IntPtr BlockValidationStateCopy(IntPtr validation_state); - - /// - /// Destroys a block validation state. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_validation_state_destroy")] - public static extern void BlockValidationStateDestroy(IntPtr validation_state); - - #endregion - - #region Txid Operations - - /// - /// Copies a txid. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_txid_copy")] - public static extern IntPtr TxidCopy(IntPtr txid); - - /// - /// Checks if two txids are equal. - /// Returns 1 if equal, 0 otherwise. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_txid_equals")] - public static extern int TxidEquals(IntPtr txid1, IntPtr txid2); - - /// - /// Serializes a txid to bytes (32 bytes). - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_txid_to_bytes")] - public static extern void TxidToBytes(IntPtr txid, [MarshalAs(UnmanagedType.LPArray, SizeConst = 32)] byte[] output); - - /// - /// Destroys a txid. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_txid_destroy")] - public static extern void TxidDestroy(IntPtr txid); - - #endregion - - #region Coin Operations - - /// - /// Copies a coin. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_coin_copy")] - public static extern IntPtr CoinCopy(IntPtr coin); - - /// - /// Returns the block height where the transaction that created this coin was included. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_coin_confirmation_height")] - public static extern uint CoinConfirmationHeight(IntPtr coin); - - /// - /// Returns whether the containing transaction was a coinbase. - /// Returns 1 if coinbase, 0 otherwise. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_coin_is_coinbase")] - public static extern int CoinIsCoinbase(IntPtr coin); - - /// - /// Gets the transaction output of a coin. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_coin_get_output")] - public static extern IntPtr CoinGetOutput(IntPtr coin); - - /// - /// Destroys a coin. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_coin_destroy")] - public static extern void CoinDestroy(IntPtr coin); - - #endregion - - - - #region BlockSpentOutputs Operations - - /// - /// Reads block spent outputs (undo data) from disk. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_spent_outputs_read")] - public static extern IntPtr BlockSpentOutputsRead( - IntPtr chainstate_manager, - IntPtr block_tree_entry); - - /// - /// Copies block spent outputs. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_spent_outputs_copy")] - public static extern IntPtr BlockSpentOutputsCopy(IntPtr block_spent_outputs); - - /// - /// Gets the count of transaction spent outputs in block spent outputs. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_spent_outputs_count")] - public static extern nuint BlockSpentOutputsCount(IntPtr block_spent_outputs); - - /// - /// Gets transaction spent outputs at the specified index. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_spent_outputs_get_transaction_spent_outputs_at")] - public static extern IntPtr BlockSpentOutputsGetTransactionSpentOutputsAt( - IntPtr block_spent_outputs, - nuint index); - - /// - /// Destroys block spent outputs. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_spent_outputs_destroy")] - public static extern void BlockSpentOutputsDestroy(IntPtr block_spent_outputs); - - #endregion - - #region TransactionSpentOutputs Operations - - /// - /// Copies transaction spent outputs. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_spent_outputs_copy")] - public static extern IntPtr TransactionSpentOutputsCopy(IntPtr transaction_spent_outputs); - - /// - /// Gets the count of coins in transaction spent outputs. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_spent_outputs_count")] - public static extern nuint TransactionSpentOutputsCount(IntPtr transaction_spent_outputs); - - /// - /// Gets a coin at the specified index. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_spent_outputs_get_coin_at")] - public static extern IntPtr TransactionSpentOutputsGetCoinAt( - IntPtr transaction_spent_outputs, - nuint index); - - /// - /// Destroys transaction spent outputs. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_spent_outputs_destroy")] - public static extern void TransactionSpentOutputsDestroy(IntPtr transaction_spent_outputs); - - #endregion - - #region Missing Functions - - /// - /// Copy a context. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_copy")] - public static extern IntPtr ContextCopy(IntPtr context); - - /// - /// Copy chain parameters. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_parameters_copy")] - public static extern IntPtr ChainParametersCopy(IntPtr chain_params); - - /// - /// Checks if two block hashes are equal. - /// Returns 1 if equal, 0 otherwise. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_hash_equals")] - public static extern int BlockHashEquals(IntPtr hash1, IntPtr hash2); - - /// - /// Copy a block hash. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_hash_copy")] - public static extern IntPtr BlockHashCopy(IntPtr block_hash); - - #endregion - - #region TransactionInput Operations - - /// - /// Copies a transaction input. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_input_copy")] - public static extern IntPtr TransactionInputCopy(IntPtr transaction_input); - - /// - /// Gets the transaction out point from a transaction input. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_input_get_out_point")] - public static extern IntPtr TransactionInputGetOutPoint(IntPtr transaction_input); - - /// - /// Destroys a transaction input. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_input_destroy")] - public static extern void TransactionInputDestroy(IntPtr transaction_input); - - #endregion - - #region TransactionOutPoint Operations - - /// - /// Copies a transaction out point. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_out_point_copy")] - public static extern IntPtr TransactionOutPointCopy(IntPtr transaction_out_point); - - /// - /// Gets the output index from a transaction out point. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_out_point_get_index")] - public static extern uint TransactionOutPointGetIndex(IntPtr transaction_out_point); - - /// - /// Gets the txid from a transaction out point. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_out_point_get_txid")] - public static extern IntPtr TransactionOutPointGetTxid(IntPtr transaction_out_point); - - /// - /// Destroys a transaction out point. - /// - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_out_point_destroy")] - public static extern void TransactionOutPointDestroy(IntPtr transaction_out_point); - - #endregion + /// Pointer to kernel context, or IntPtr.Zero on failure + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_create")] + public static extern IntPtr ContextCreate(IntPtr options); - } -} \ No newline at end of file + /// + /// Destroys a kernel context and frees associated resources. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_destroy")] + public static extern void ContextDestroy(IntPtr context); + + /// + /// Interrupts long-running operations associated with this context. + /// Returns 0 on success. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_interrupt")] + public static extern int ContextInterrupt(IntPtr context); + + #endregion + + #region Context Options + + /// + /// Creates context options with default values. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_options_create")] + public static extern IntPtr ContextOptionsCreate(); + + /// + /// Destroys context options. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_options_destroy")] + public static extern void ContextOptionsDestroy(IntPtr options); + + /// + /// Sets the chain parameters for the context. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_options_set_chainparams")] + public static extern void ContextOptionsSetChainParams(IntPtr options, IntPtr chain_params); + + /// + /// Sets the notification callbacks for the context. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_options_set_notifications")] + public static extern void ContextOptionsSetNotifications(IntPtr options, NotificationInterfaceCallbacks callbacks); + + /// + /// Sets the validation interface for the context. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_options_set_validation_interface")] + public static extern void ContextOptionsSetValidationInterface(IntPtr options, ValidationInterfaceCallbacks callbacks); + + #endregion + + #region Chain Parameters + + /// + /// Creates chain parameters for the specified chain type. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_parameters_create")] + public static extern IntPtr ChainParametersCreate(ChainType chain_type); + + /// + /// Destroys chain parameters. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_parameters_destroy")] + public static extern void ChainParametersDestroy(IntPtr chain_params); + + #endregion + + #region Chainstate Manager + + /// + /// Creates a chainstate manager. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_create")] + public static extern IntPtr ChainstateManagerCreate(IntPtr options); + + /// + /// Destroys a chainstate manager. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_destroy")] + public static extern void ChainstateManagerDestroy(IntPtr manager); + + /// + /// Processes a block through validation. + /// Returns 0 on success. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_process_block")] + public static extern int ChainstateManagerProcessBlock( + IntPtr manager, + IntPtr block, + ref int new_block); + + /// + /// Gets a block tree entry by its block hash. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_get_block_tree_entry_by_hash")] + public static extern IntPtr ChainstateManagerGetBlockTreeEntryByHash( + IntPtr manager, + IntPtr block_hash); + + /// + /// Gets the active chain from the chainstate manager. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_get_active_chain")] + public static extern IntPtr ChainstateManagerGetActiveChain(IntPtr manager); + + /// + /// Gets the block tree entry with the most cumulative proof of work. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_get_best_entry")] + public static extern IntPtr ChainstateManagerGetBestEntry(IntPtr manager); + + /// + /// Processes and validates a block header. + /// Returns 0 on success. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_process_block_header")] + public static extern int ChainstateManagerProcessBlockHeader( + IntPtr manager, + IntPtr header, + IntPtr block_validation_state); + + /// + /// Imports blocks from an array of file paths. + /// Returns 0 on success. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_import_blocks")] + public static extern int ChainstateManagerImportBlocks( + IntPtr manager, + [MarshalAs(UnmanagedType.LPArray)] string[] block_file_paths_data, + nuint[] block_file_paths_lens, + nuint block_file_paths_data_len); + + #endregion + + #region Chainstate Manager Options + + /// + /// Creates chainstate manager options. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_options_create")] + public static extern IntPtr ChainstateManagerOptionsCreate( + IntPtr context, + [MarshalAs(UnmanagedType.LPUTF8Str)] string data_directory, + nuint data_directory_len, + [MarshalAs(UnmanagedType.LPUTF8Str)] string blocks_directory, + nuint blocks_directory_len); + + /// + /// Destroys chainstate manager options. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_options_destroy")] + public static extern void ChainstateManagerOptionsDestroy(IntPtr options); + + /// + /// Sets the number of worker threads for script verification. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_options_set_worker_threads_num")] + public static extern void ChainstateManagerOptionsSetWorkerThreads( + IntPtr options, + int worker_threads); + + /// + /// Sets whether to wipe the databases on load. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_options_set_wipe_dbs")] + public static extern int ChainstateManagerOptionsSetWipeDbs( + IntPtr options, + int wipe_block_tree_db, + int wipe_chainstate_db); + + /// + /// Sets block tree db in memory. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_options_update_block_tree_db_in_memory")] + public static extern void ChainstateManagerOptionsUpdateBlockTreeDbInMemory( + IntPtr options, + int block_tree_db_in_memory); + + /// + /// Sets chainstate db in memory. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_options_update_chainstate_db_in_memory")] + public static extern void ChainstateManagerOptionsUpdateChainstateDbInMemory( + IntPtr options, + int chainstate_db_in_memory); + + #endregion + + #region Block Operations + + /// + /// Creates a block from raw data. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_create")] + public static extern IntPtr BlockCreate( + byte[] raw_block, + UIntPtr raw_block_len); + + /// + /// Destroys a block. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_destroy")] + public static extern void BlockDestroy(IntPtr block); + + /// + /// Reads a block from disk by its block tree entry. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_read")] + public static extern IntPtr BlockRead( + IntPtr chainstate_manager, + IntPtr block_tree_entry); + + /// + /// Gets the number of transactions in a block. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_count_transactions")] + public static extern nuint BlockCountTransactions(IntPtr block); + + /// + /// Gets the block hash. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_get_hash")] + public static extern IntPtr BlockGetHash(IntPtr block); + + /// + /// Gets the block header from a block. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_get_header")] + public static extern IntPtr BlockGetHeader(IntPtr block); + + /// + /// Serializes the block to bytes. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_to_bytes")] + public static extern int BlockToBytes( + IntPtr block, + WriteBytes writer, + IntPtr user_data); + + /// + /// Delegate for writing bytes during serialization. + /// + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate int WriteBytes(IntPtr bytes, nuint size, IntPtr userdata); + + /// + /// Gets the block hash from a block tree entry. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_tree_entry_get_block_hash")] + public static extern IntPtr BlockTreeEntryGetBlockHash(IntPtr block_tree_entry); + + /// + /// Gets the block height from a block tree entry. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_tree_entry_get_height")] + public static extern int BlockTreeEntryGetHeight(IntPtr block_tree_entry); + + /// + /// Gets the previous block tree entry from a block tree entry. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_tree_entry_get_previous")] + public static extern IntPtr BlockTreeEntryGetPrevious(IntPtr block_tree_entry); + + /// + /// Gets the block header from a block tree entry. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_tree_entry_get_block_header")] + public static extern IntPtr BlockTreeEntryGetBlockHeader(IntPtr block_tree_entry); + + /// + /// Checks if two block tree entries are equal. Two block tree entries are equal when they + /// point to the same block. + /// Returns 1 if equal, 0 otherwise. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_tree_entry_equals")] + public static extern int BlockTreeEntryEquals(IntPtr entry1, IntPtr entry2); + + /// + /// Copies a block (reference counted). + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_copy")] + public static extern IntPtr BlockCopy(IntPtr block); + + /// + /// Gets a transaction at the specified index in a block. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_get_transaction_at")] + public static extern IntPtr BlockGetTransactionAt(IntPtr block, nuint index); + + #endregion + + #region BlockHash Operations + + /// + /// Creates a block hash from 32 bytes. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_hash_create")] + public static extern IntPtr BlockHashCreate(IntPtr hash); + + /// + /// Creates a block hash from 32 bytes (unsafe version). + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_hash_create")] + public static extern unsafe IntPtr BlockHashCreate(byte* hash); + + /// + /// Serializes the block hash to bytes (32 bytes). + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_hash_to_bytes")] + public static extern void BlockHashToBytes(IntPtr block_hash, [MarshalAs(UnmanagedType.LPArray, SizeConst = 32)] byte[] output); + + /// + /// Destroys a block hash. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_hash_destroy")] + public static extern void BlockHashDestroy(IntPtr block_hash); + + #endregion + + #region Block Header Operations + + /// + /// Creates a block header from raw serialized data (80 bytes). + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_create")] + public static extern IntPtr BlockHeaderCreate( + byte[] raw_block_header, + UIntPtr raw_block_header_len); + + /// + /// Copies a block header. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_copy")] + public static extern IntPtr BlockHeaderCopy(IntPtr header); + + /// + /// Gets the block hash from a block header. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_get_hash")] + public static extern IntPtr BlockHeaderGetHash(IntPtr header); + + /// + /// Gets the previous block hash from a block header. + /// The returned hash is unowned and only valid for the lifetime of the block header. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_get_prev_hash")] + public static extern IntPtr BlockHeaderGetPrevHash(IntPtr header); + + /// + /// Gets the timestamp from a block header (Unix epoch seconds). + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_get_timestamp")] + public static extern uint BlockHeaderGetTimestamp(IntPtr header); + + /// + /// Gets the nBits difficulty target from a block header (compact format). + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_get_bits")] + public static extern uint BlockHeaderGetBits(IntPtr header); + + /// + /// Gets the version from a block header. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_get_version")] + public static extern int BlockHeaderGetVersion(IntPtr header); + + /// + /// Gets the nonce from a block header. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_get_nonce")] + public static extern uint BlockHeaderGetNonce(IntPtr header); + + /// + /// Destroys a block header. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_destroy")] + public static extern void BlockHeaderDestroy(IntPtr header); + + #endregion + + #region Chain Operations + + /// + /// Gets the height of the chain. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_get_height")] + public static extern int ChainGetHeight(IntPtr chain); + + /// + /// Gets a block tree entry by height. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_get_by_height")] + public static extern IntPtr ChainGetByHeight(IntPtr chain, int height); + + /// + /// Checks if a block tree entry is in the chain. + /// Returns 1 if in chain, 0 otherwise. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_contains")] + public static extern int ChainContains(IntPtr chain, IntPtr block_tree_entry); + + #endregion + + #region Transaction Operations + + /// + /// Creates a transaction from serialized data. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_create")] + public static extern IntPtr TransactionCreate(IntPtr raw_transaction, nuint raw_transaction_len); + + /// + /// Copies a transaction (reference counted). + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_copy")] + public static extern IntPtr TransactionCopy(IntPtr transaction); + + /// + /// Serializes a transaction to bytes. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_to_bytes")] + public static extern int TransactionToBytes( + IntPtr transaction, + WriteBytes writer, + IntPtr user_data); + + /// + /// Gets the transaction ID (txid). + /// Returns a pointer to btck_Txid (not owned, lifetime depends on transaction). + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_get_txid")] + public static extern IntPtr TransactionGetTxid(IntPtr transaction); + + /// + /// Gets the number of outputs in a transaction. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_count_outputs")] + public static extern nuint TransactionCountOutputs(IntPtr transaction); + + /// + /// Gets the number of inputs in a transaction. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_count_inputs")] + public static extern nuint TransactionCountInputs(IntPtr transaction); + + /// + /// Gets a transaction output at the specified index. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_get_output_at")] + public static extern IntPtr TransactionGetOutputAt(IntPtr transaction, nuint index); + + /// + /// Gets a transaction input at the specified index. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_get_input_at")] + public static extern IntPtr TransactionGetInputAt(IntPtr transaction, nuint index); + + /// + /// Destroys a transaction. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_destroy")] + public static extern void TransactionDestroy(IntPtr transaction); + + /// + /// Gets the script pubkey from a transaction output. + /// Returns a pointer to btck_ScriptPubkey. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_output_get_script_pubkey")] + public static extern IntPtr TransactionOutputGetScriptPubkey(IntPtr output); + + /// + /// Gets the value (amount) from a transaction output. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_output_get_amount")] + public static extern long TransactionOutputGetAmount(IntPtr output); + + /// + /// Copy a transaction output. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_output_copy")] + public static extern IntPtr TransactionOutputCopy(IntPtr output); + + /// + /// Destroys a transaction output. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_output_destroy")] + public static extern void TransactionOutputDestroy(IntPtr output); + + #endregion + + #region PrecomputedTransactionData Operations + + /// + /// Creates precomputed transaction data for script verification. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_precomputed_transaction_data_create")] + public static extern IntPtr PrecomputedTransactionDataCreate( + IntPtr tx_to, + IntPtr[] spent_outputs, + nuint spent_outputs_len); + + /// + /// Copies precomputed transaction data. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_precomputed_transaction_data_copy")] + public static extern IntPtr PrecomputedTransactionDataCopy(IntPtr precomputed_txdata); + + /// + /// Destroys precomputed transaction data. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_precomputed_transaction_data_destroy")] + public static extern void PrecomputedTransactionDataDestroy(IntPtr precomputed_txdata); + + #endregion + + #region ScriptPubkey Operations + + /// + /// Creates a script pubkey from serialized data. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_script_pubkey_create")] + public static extern IntPtr ScriptPubkeyCreate(IntPtr script_pubkey_data, nuint script_pubkey_len); + + /// + /// Copies a script pubkey. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_script_pubkey_copy")] + public static extern IntPtr ScriptPubkeyCopy(IntPtr script_pubkey); + + /// + /// Verifies a script pubkey. + /// Returns 1 if valid, 0 otherwise. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_script_pubkey_verify")] + public static extern int ScriptPubkeyVerify( + IntPtr script_pubkey, + long amount, + IntPtr tx_to, + IntPtr precomputed_txdata, + uint input_index, + uint flags, + IntPtr status); + + /// + /// Serializes a script pubkey to bytes. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_script_pubkey_to_bytes")] + public static extern int ScriptPubkeyToBytes( + IntPtr script_pubkey, + WriteBytes writer, + IntPtr user_data); + + /// + /// Destroys a script pubkey. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_script_pubkey_destroy")] + public static extern void ScriptPubkeyDestroy(IntPtr script_pubkey); + + #endregion + + #region TransactionOutput Operations (Additional) + + /// + /// Creates a transaction output from a script pubkey and amount. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_output_create")] + public static extern IntPtr TransactionOutputCreate(IntPtr script_pubkey, long amount); + + #endregion + + #region Logging + + /// + /// Creates a logging connection with a callback. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_logging_connection_create")] + public static extern IntPtr LoggingConnectionCreate( + LoggingCallback callback, + IntPtr user_data, + DestroyCallback? user_data_destroy_callback); + + /// + /// Destroys a logging connection. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_logging_connection_destroy")] + public static extern void LoggingConnectionDestroy(IntPtr connection); + + /// + /// Disables logging permanently. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_logging_disable")] + public static extern void LoggingDisable(); + + /// + /// Sets the log level for a category. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_logging_set_level_category")] + public static extern void LoggingSetLevelCategory(LogCategory category, LogLevel level); + + /// + /// Sets logging options. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_logging_set_options")] + public static extern void LoggingSetOptions(LoggingOptions options); + + /// + /// Enables a log category. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_logging_enable_category")] + public static extern void LoggingEnableCategory(LogCategory category); + + /// + /// Disables a log category. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_logging_disable_category")] + public static extern void LoggingDisableCategory(LogCategory category); + + #endregion + + #region Block Validation State + + /// + /// Creates a new block validation state. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_validation_state_create")] + public static extern IntPtr BlockValidationStateCreate(); + + /// + /// Gets the validation mode from a block validation state. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_validation_state_get_validation_mode")] + public static extern ValidationMode BlockValidationStateGetValidationMode(IntPtr validation_state); + + /// + /// Gets the block validation result from a block validation state. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_validation_state_get_block_validation_result")] + public static extern BlockValidationResult BlockValidationStateGetBlockValidationResult(IntPtr validation_state); + + /// + /// Copies a block validation state. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_validation_state_copy")] + public static extern IntPtr BlockValidationStateCopy(IntPtr validation_state); + + /// + /// Destroys a block validation state. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_validation_state_destroy")] + public static extern void BlockValidationStateDestroy(IntPtr validation_state); + + #endregion + + #region Txid Operations + + /// + /// Copies a txid. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_txid_copy")] + public static extern IntPtr TxidCopy(IntPtr txid); + + /// + /// Checks if two txids are equal. + /// Returns 1 if equal, 0 otherwise. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_txid_equals")] + public static extern int TxidEquals(IntPtr txid1, IntPtr txid2); + + /// + /// Serializes a txid to bytes (32 bytes). + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_txid_to_bytes")] + public static extern void TxidToBytes(IntPtr txid, [MarshalAs(UnmanagedType.LPArray, SizeConst = 32)] byte[] output); + + /// + /// Destroys a txid. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_txid_destroy")] + public static extern void TxidDestroy(IntPtr txid); + + #endregion + + #region Coin Operations + + /// + /// Copies a coin. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_coin_copy")] + public static extern IntPtr CoinCopy(IntPtr coin); + + /// + /// Returns the block height where the transaction that created this coin was included. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_coin_confirmation_height")] + public static extern uint CoinConfirmationHeight(IntPtr coin); + + /// + /// Returns whether the containing transaction was a coinbase. + /// Returns 1 if coinbase, 0 otherwise. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_coin_is_coinbase")] + public static extern int CoinIsCoinbase(IntPtr coin); + + /// + /// Gets the transaction output of a coin. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_coin_get_output")] + public static extern IntPtr CoinGetOutput(IntPtr coin); + + /// + /// Destroys a coin. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_coin_destroy")] + public static extern void CoinDestroy(IntPtr coin); + + #endregion + + + + #region BlockSpentOutputs Operations + + /// + /// Reads block spent outputs (undo data) from disk. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_spent_outputs_read")] + public static extern IntPtr BlockSpentOutputsRead( + IntPtr chainstate_manager, + IntPtr block_tree_entry); + + /// + /// Copies block spent outputs. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_spent_outputs_copy")] + public static extern IntPtr BlockSpentOutputsCopy(IntPtr block_spent_outputs); + + /// + /// Gets the count of transaction spent outputs in block spent outputs. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_spent_outputs_count")] + public static extern nuint BlockSpentOutputsCount(IntPtr block_spent_outputs); + + /// + /// Gets transaction spent outputs at the specified index. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_spent_outputs_get_transaction_spent_outputs_at")] + public static extern IntPtr BlockSpentOutputsGetTransactionSpentOutputsAt( + IntPtr block_spent_outputs, + nuint index); + + /// + /// Destroys block spent outputs. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_spent_outputs_destroy")] + public static extern void BlockSpentOutputsDestroy(IntPtr block_spent_outputs); + + #endregion + + #region TransactionSpentOutputs Operations + + /// + /// Copies transaction spent outputs. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_spent_outputs_copy")] + public static extern IntPtr TransactionSpentOutputsCopy(IntPtr transaction_spent_outputs); + + /// + /// Gets the count of coins in transaction spent outputs. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_spent_outputs_count")] + public static extern nuint TransactionSpentOutputsCount(IntPtr transaction_spent_outputs); + + /// + /// Gets a coin at the specified index. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_spent_outputs_get_coin_at")] + public static extern IntPtr TransactionSpentOutputsGetCoinAt( + IntPtr transaction_spent_outputs, + nuint index); + + /// + /// Destroys transaction spent outputs. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_spent_outputs_destroy")] + public static extern void TransactionSpentOutputsDestroy(IntPtr transaction_spent_outputs); + + #endregion + + #region Missing Functions + + /// + /// Copy a context. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_context_copy")] + public static extern IntPtr ContextCopy(IntPtr context); + + /// + /// Copy chain parameters. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_parameters_copy")] + public static extern IntPtr ChainParametersCopy(IntPtr chain_params); + + /// + /// Checks if two block hashes are equal. + /// Returns 1 if equal, 0 otherwise. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_hash_equals")] + public static extern int BlockHashEquals(IntPtr hash1, IntPtr hash2); + + /// + /// Copy a block hash. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_hash_copy")] + public static extern IntPtr BlockHashCopy(IntPtr block_hash); + + #endregion + + #region TransactionInput Operations + + /// + /// Copies a transaction input. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_input_copy")] + public static extern IntPtr TransactionInputCopy(IntPtr transaction_input); + + /// + /// Gets the transaction out point from a transaction input. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_input_get_out_point")] + public static extern IntPtr TransactionInputGetOutPoint(IntPtr transaction_input); + + /// + /// Destroys a transaction input. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_input_destroy")] + public static extern void TransactionInputDestroy(IntPtr transaction_input); + + #endregion + + #region TransactionOutPoint Operations + + /// + /// Copies a transaction out point. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_out_point_copy")] + public static extern IntPtr TransactionOutPointCopy(IntPtr transaction_out_point); + + /// + /// Gets the output index from a transaction out point. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_out_point_get_index")] + public static extern uint TransactionOutPointGetIndex(IntPtr transaction_out_point); + + /// + /// Gets the txid from a transaction out point. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_out_point_get_txid")] + public static extern IntPtr TransactionOutPointGetTxid(IntPtr transaction_out_point); + + /// + /// Destroys a transaction out point. + /// + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_out_point_destroy")] + public static extern void TransactionOutPointDestroy(IntPtr transaction_out_point); + + #endregion + +} diff --git a/src/BitcoinKernel.Interop/Structs/LoggingOptions.cs b/src/BitcoinKernel.Interop/Structs/LoggingOptions.cs index b623e51..42cc099 100644 --- a/src/BitcoinKernel.Interop/Structs/LoggingOptions.cs +++ b/src/BitcoinKernel.Interop/Structs/LoggingOptions.cs @@ -1,14 +1,13 @@ using System.Runtime.InteropServices; -namespace BitcoinKernel.Interop.Structs +namespace BitcoinKernel.Interop.Structs; + +[StructLayout(LayoutKind.Sequential)] +internal struct LoggingOptions { - [StructLayout(LayoutKind.Sequential)] - public struct LoggingOptions - { - public int LogTimestamps; - public int LogTimeMicros; - public int LogThreadNames; - public int LogSourceLocations; - public int AlwaysPrintCategoryLevels; - } -} \ No newline at end of file + public int LogTimestamps; + public int LogTimeMicros; + public int LogThreadNames; + public int LogSourceLocations; + public int AlwaysPrintCategoryLevels; +} diff --git a/src/BitcoinKernel.Interop/Structs/NotificationInterfaceCallbacks.cs b/src/BitcoinKernel.Interop/Structs/NotificationInterfaceCallbacks.cs index 599ba14..583a013 100644 --- a/src/BitcoinKernel.Interop/Structs/NotificationInterfaceCallbacks.cs +++ b/src/BitcoinKernel.Interop/Structs/NotificationInterfaceCallbacks.cs @@ -1,18 +1,17 @@ using System.Runtime.InteropServices; using BitcoinKernel.Interop.Delegates.Notification; -namespace BitcoinKernel.Interop.Structs +namespace BitcoinKernel.Interop.Structs; + +[StructLayout(LayoutKind.Sequential)] +internal struct NotificationInterfaceCallbacks { - [StructLayout(LayoutKind.Sequential)] - public struct NotificationInterfaceCallbacks - { - public IntPtr UserData; - public NotifyBlockTip BlockTip; - public NotifyHeaderTip HeaderTip; - public NotifyProgress Progress; - public NotifyWarningSet WarningSet; - public NotifyWarningUnset WarningUnset; - public NotifyFlushError FlushError; - public NotifyFatalError FatalError; - } -} \ No newline at end of file + public IntPtr UserData; + public NotifyBlockTip BlockTip; + public NotifyHeaderTip HeaderTip; + public NotifyProgress Progress; + public NotifyWarningSet WarningSet; + public NotifyWarningUnset WarningUnset; + public NotifyFlushError FlushError; + public NotifyFatalError FatalError; +} diff --git a/src/BitcoinKernel.Interop/Structs/ValidationInterfaceCallbacks.cs b/src/BitcoinKernel.Interop/Structs/ValidationInterfaceCallbacks.cs index 8bd4a36..6215fae 100644 --- a/src/BitcoinKernel.Interop/Structs/ValidationInterfaceCallbacks.cs +++ b/src/BitcoinKernel.Interop/Structs/ValidationInterfaceCallbacks.cs @@ -1,15 +1,14 @@ using System.Runtime.InteropServices; using BitcoinKernel.Interop.Delegates.Validation; -namespace BitcoinKernel.Interop.Structs +namespace BitcoinKernel.Interop.Structs; + +[StructLayout(LayoutKind.Sequential)] +internal struct ValidationInterfaceCallbacks { - [StructLayout(LayoutKind.Sequential)] - public struct ValidationInterfaceCallbacks - { - public IntPtr UserData; - public ValidationBlockChecked BlockChecked; - public ValidationNewPoWValidBlock NewPoWValidBlock; - public ValidationBlockConnected BlockConnected; - public ValidationBlockDisconnected BlockDisconnected; - } -} \ No newline at end of file + public IntPtr UserData; + public ValidationBlockChecked BlockChecked; + public ValidationNewPoWValidBlock NewPoWValidBlock; + public ValidationBlockConnected BlockConnected; + public ValidationBlockDisconnected BlockDisconnected; +} diff --git a/src/BitcoinKernel/BitcoinKernel.csproj b/src/BitcoinKernel/BitcoinKernel.csproj index 985896a..ee489a7 100644 --- a/src/BitcoinKernel/BitcoinKernel.csproj +++ b/src/BitcoinKernel/BitcoinKernel.csproj @@ -4,31 +4,45 @@ net9.0 enable enable - false + true BitcoinKernel - 0.1.2 + 0.2.0 JanB84 - .NET library for Bitcoin Core functionality. Provides a simple, fluent API for Bitcoin consensus validation, script verification, and blockchain operations powered by libbitcoinkernel. + .NET bindings and managed wrappers for libbitcoinkernel. Provides direct access to Bitcoin Core consensus and validation logic with automatic memory management. MIT https://github.com/JanB84/BitcoinKernel.NET https://github.com/JanB84/BitcoinKernel.NET git - bitcoin;kernel;libbitcoinkernel;blockchain;facade;easy + bitcoin;kernel;libbitcoinkernel;wrapper;core;blockchain false true snupkg - README.md + + $(TargetsForTfmSpecificContentInPackage);IncludeReferencedProjectsInPackage - - - - - + + + + + + + <_PackageFiles Include="$(OutputPath)BitcoinKernel.Interop.dll"> + Content + lib/$(TargetFramework)/BitcoinKernel.Interop.dll + + + + <_PackageFiles Include="$(OutputPath)runtimes\**\*.*" Condition="Exists('$(OutputPath)runtimes')"> + Content + runtimes/%(RecursiveDir)%(Filename)%(Extension) + + + diff --git a/src/BitcoinKernel.Core/BlockProcessing/BlockProcessor.cs b/src/BitcoinKernel/BlockProcessing/BlockProcessor.cs similarity index 97% rename from src/BitcoinKernel.Core/BlockProcessing/BlockProcessor.cs rename to src/BitcoinKernel/BlockProcessing/BlockProcessor.cs index c0834ba..98fbf25 100644 --- a/src/BitcoinKernel.Core/BlockProcessing/BlockProcessor.cs +++ b/src/BitcoinKernel/BlockProcessing/BlockProcessor.cs @@ -1,12 +1,12 @@ using System; -using BitcoinKernel.Core.Abstractions; -using BitcoinKernel.Core.Chain; +using BitcoinKernel.Primatives; +using BitcoinKernel.Chain; -using BitcoinKernel.Core.Exceptions; +using BitcoinKernel.Exceptions; using BitcoinKernel.Interop; using BitcoinKernel.Interop.Enums; -namespace BitcoinKernel.Core.BlockProcessing; +namespace BitcoinKernel.BlockProcessing; /// /// Handles block processing operations including validation and chain integration. diff --git a/src/BitcoinKernel.Core/BlockProcessing/BlockTreeEntry.cs b/src/BitcoinKernel/BlockProcessing/BlockTreeEntry.cs similarity index 95% rename from src/BitcoinKernel.Core/BlockProcessing/BlockTreeEntry.cs rename to src/BitcoinKernel/BlockProcessing/BlockTreeEntry.cs index 4f481f2..30a8c58 100644 --- a/src/BitcoinKernel.Core/BlockProcessing/BlockTreeEntry.cs +++ b/src/BitcoinKernel/BlockProcessing/BlockTreeEntry.cs @@ -1,8 +1,8 @@ -using BitcoinKernel.Core.Abstractions; -using BitcoinKernel.Core.Exceptions; +using BitcoinKernel.Primatives; +using BitcoinKernel.Exceptions; using BitcoinKernel.Interop; -namespace BitcoinKernel.Core.BlockProcessing; +namespace BitcoinKernel.BlockProcessing; /// /// Represents an entry in the block tree (block index). diff --git a/src/BitcoinKernel.Core/BlockProcessing/BlockValidationResult.cs b/src/BitcoinKernel/BlockProcessing/BlockValidationResult.cs similarity index 95% rename from src/BitcoinKernel.Core/BlockProcessing/BlockValidationResult.cs rename to src/BitcoinKernel/BlockProcessing/BlockValidationResult.cs index db1149d..79e228d 100644 --- a/src/BitcoinKernel.Core/BlockProcessing/BlockValidationResult.cs +++ b/src/BitcoinKernel/BlockProcessing/BlockValidationResult.cs @@ -1,6 +1,6 @@ using BitcoinKernel.Interop.Enums; -namespace BitcoinKernel.Core.BlockProcessing; +namespace BitcoinKernel.BlockProcessing; /// /// Result of a block validation operation. diff --git a/src/BitcoinKernel.Core/Abstractions/Chain.cs b/src/BitcoinKernel/Chain/Chain.cs similarity index 92% rename from src/BitcoinKernel.Core/Abstractions/Chain.cs rename to src/BitcoinKernel/Chain/Chain.cs index ce10e3a..271c168 100644 --- a/src/BitcoinKernel.Core/Abstractions/Chain.cs +++ b/src/BitcoinKernel/Chain/Chain.cs @@ -1,93 +1,93 @@ - - -using System.Dynamic; -using BitcoinKernel.Core.Exceptions; -using BitcoinKernel.Interop; - -namespace BitcoinKernel.Core.Abstractions; - - -/// -/// Represents the active blockchain. -/// -public sealed class Chain -{ - private readonly IntPtr _handle; - - internal Chain(IntPtr handle) - { - _handle = handle != IntPtr.Zero - ? handle - : throw new ArgumentException("Invalid chain handle", nameof(handle)); - } - - /// - /// Gets the height of the chain. - /// - public int Height => NativeMethods.ChainGetHeight(_handle); - - /// - /// Gets the tip of the chain. - /// - public BlockIndex GetTip() - { - - //get chain by height - IntPtr tipPtr = NativeMethods.ChainGetByHeight(_handle, Height); - if (tipPtr == IntPtr.Zero) - throw new KernelException("Failed to get chain tip"); - - - return new BlockIndex(tipPtr, ownsHandle: false); - } - - /// - /// Gets a block index by height. - /// - public BlockIndex? GetBlockByHeight(int height) - { - ArgumentOutOfRangeException.ThrowIfNegative(height, nameof(height)); - - IntPtr blockPtr = NativeMethods.ChainGetByHeight(_handle, height); - return blockPtr != IntPtr.Zero - ? new BlockIndex(blockPtr, ownsHandle: false) - : null; - } - - /// - /// Gets the genesis block. - /// - public BlockIndex GetGenesis() - { - IntPtr genesisPtr = NativeMethods.ChainGetByHeight(_handle, 0); - if (genesisPtr == IntPtr.Zero) - throw new KernelException("Failed to get genesis block"); - - return new BlockIndex(genesisPtr, ownsHandle: false); - } - - /// - /// Checks if a block index is part of this chain. - /// - public bool Contains(BlockIndex blockIndex) - { - ArgumentNullException.ThrowIfNull(blockIndex); - - return NativeMethods.ChainContains(_handle, blockIndex.Handle) != 0; - } - - /// - /// Enumerates all blocks in the chain from genesis to tip. - /// - public IEnumerable EnumerateBlocks() - { - for (int height = 0; height <= Height; height++) - { - var block = GetBlockByHeight(height); - if (block != null) - { - yield return block; - } - } - } -} \ No newline at end of file + + +using BitcoinKernel.Primatives; +using BitcoinKernel.Exceptions; +using BitcoinKernel.Interop; + +namespace BitcoinKernel.Chain; + + +/// +/// Represents the active blockchain. +/// +public sealed class Chain +{ + private readonly IntPtr _handle; + + internal Chain(IntPtr handle) + { + _handle = handle != IntPtr.Zero + ? handle + : throw new ArgumentException("Invalid chain handle", nameof(handle)); + } + + /// + /// Gets the height of the chain. + /// + public int Height => NativeMethods.ChainGetHeight(_handle); + + /// + /// Gets the tip of the chain. + /// + public BlockIndex GetTip() + { + + //get chain by height + IntPtr tipPtr = NativeMethods.ChainGetByHeight(_handle, Height); + if (tipPtr == IntPtr.Zero) + throw new KernelException("Failed to get chain tip"); + + + return new BlockIndex(tipPtr, ownsHandle: false); + } + + /// + /// Gets a block index by height. + /// + public BlockIndex? GetBlockByHeight(int height) + { + ArgumentOutOfRangeException.ThrowIfNegative(height, nameof(height)); + + IntPtr blockPtr = NativeMethods.ChainGetByHeight(_handle, height); + return blockPtr != IntPtr.Zero + ? new BlockIndex(blockPtr, ownsHandle: false) + : null; + } + + /// + /// Gets the genesis block. + /// + public BlockIndex GetGenesis() + { + IntPtr genesisPtr = NativeMethods.ChainGetByHeight(_handle, 0); + if (genesisPtr == IntPtr.Zero) + throw new KernelException("Failed to get genesis block"); + + return new BlockIndex(genesisPtr, ownsHandle: false); + } + + /// + /// Checks if a block index is part of this chain. + /// + public bool Contains(BlockIndex blockIndex) + { + ArgumentNullException.ThrowIfNull(blockIndex); + + return NativeMethods.ChainContains(_handle, blockIndex.Handle) != 0; + } + + /// + /// Enumerates all blocks in the chain from genesis to tip. + /// + public IEnumerable EnumerateBlocks() + { + for (int height = 0; height <= Height; height++) + { + var block = GetBlockByHeight(height); + if (block != null) + { + yield return block; + } + } + } +} diff --git a/src/BitcoinKernel.Core/Chain/ChainParameters.cs b/src/BitcoinKernel/Chain/ChainParameters.cs similarity index 96% rename from src/BitcoinKernel.Core/Chain/ChainParameters.cs rename to src/BitcoinKernel/Chain/ChainParameters.cs index 3d51d38..52f66ff 100644 --- a/src/BitcoinKernel.Core/Chain/ChainParameters.cs +++ b/src/BitcoinKernel/Chain/ChainParameters.cs @@ -1,7 +1,7 @@ using BitcoinKernel.Interop; using BitcoinKernel.Interop.Enums; -namespace BitcoinKernel.Core.Chain; +namespace BitcoinKernel.Chain; public sealed class ChainParameters : IDisposable { diff --git a/src/BitcoinKernel.Core/Chain/ChainStateManager.cs b/src/BitcoinKernel/Chain/ChainStateManager.cs similarity index 98% rename from src/BitcoinKernel.Core/Chain/ChainStateManager.cs rename to src/BitcoinKernel/Chain/ChainStateManager.cs index 000c639..f44e048 100644 --- a/src/BitcoinKernel.Core/Chain/ChainStateManager.cs +++ b/src/BitcoinKernel/Chain/ChainStateManager.cs @@ -1,10 +1,10 @@ using System; using System.Runtime.InteropServices; -using BitcoinKernel.Core.Exceptions; +using BitcoinKernel.Exceptions; using BitcoinKernel.Interop; -using BitcoinKernel.Core.Abstractions; +using BitcoinKernel.Primatives; -namespace BitcoinKernel.Core.Chain; +namespace BitcoinKernel.Chain; /// /// Manages the blockchain state and validation operations. @@ -54,14 +54,14 @@ internal IntPtr Handle /// /// Gets the active chain. /// - public Abstractions.Chain GetActiveChain() + public Chain GetActiveChain() { ThrowIfDisposed(); IntPtr chainPtr = NativeMethods.ChainstateManagerGetActiveChain(_handle); if (chainPtr == IntPtr.Zero) throw new KernelException("Failed to get active chain"); - return new Abstractions.Chain(chainPtr); + return new Chain(chainPtr); } /// diff --git a/src/BitcoinKernel.Core/Exceptions/Exceptions.cs b/src/BitcoinKernel/Exceptions/Exceptions.cs similarity index 99% rename from src/BitcoinKernel.Core/Exceptions/Exceptions.cs rename to src/BitcoinKernel/Exceptions/Exceptions.cs index 3b05ae1..d53d73a 100644 --- a/src/BitcoinKernel.Core/Exceptions/Exceptions.cs +++ b/src/BitcoinKernel/Exceptions/Exceptions.cs @@ -1,6 +1,6 @@ using BitcoinKernel.Interop.Enums; -namespace BitcoinKernel.Core.Exceptions; +namespace BitcoinKernel.Exceptions; /// /// Base exception for all Bitcoin Kernel-related errors. diff --git a/src/BitcoinKernel.Core/KernelContext.cs b/src/BitcoinKernel/KernelContext.cs similarity index 91% rename from src/BitcoinKernel.Core/KernelContext.cs rename to src/BitcoinKernel/KernelContext.cs index dd2240c..728bf94 100644 --- a/src/BitcoinKernel.Core/KernelContext.cs +++ b/src/BitcoinKernel/KernelContext.cs @@ -1,7 +1,7 @@ -ο»Ώusing BitcoinKernel.Core.Exceptions; +ο»Ώusing BitcoinKernel.Exceptions; using BitcoinKernel.Interop; -namespace BitcoinKernel.Core; +namespace BitcoinKernel; /// /// Represents a Bitcoin Kernel context - the main entry point for the library. diff --git a/src/BitcoinKernel.Core/KernelContextOptions.cs b/src/BitcoinKernel/KernelContextOptions.cs similarity index 94% rename from src/BitcoinKernel.Core/KernelContextOptions.cs rename to src/BitcoinKernel/KernelContextOptions.cs index 15cd981..1ccca0b 100644 --- a/src/BitcoinKernel.Core/KernelContextOptions.cs +++ b/src/BitcoinKernel/KernelContextOptions.cs @@ -1,8 +1,8 @@ -using BitcoinKernel.Core.Chain; -using BitcoinKernel.Core.Exceptions; +using BitcoinKernel.Chain; +using BitcoinKernel.Exceptions; using BitcoinKernel.Interop; -namespace BitcoinKernel.Core; +namespace BitcoinKernel; /// /// Options for creating a kernel context. /// diff --git a/src/BitcoinKernel/KernelLibrary.cs b/src/BitcoinKernel/KernelLibrary.cs deleted file mode 100644 index 38ba7d6..0000000 --- a/src/BitcoinKernel/KernelLibrary.cs +++ /dev/null @@ -1,602 +0,0 @@ -ο»Ώusing BitcoinKernel.Core; -using BitcoinKernel.Core.Abstractions; -using BitcoinKernel.Core.BlockProcessing; -using BitcoinKernel.Core.Chain; -using BitcoinKernel.Core.ScriptVerification; -using BitcoinKernel.Interop.Enums; -using System.IO; - - -namespace BitcoinKernel -{ - /// - /// Simplified facade for common Bitcoin Kernel operations. - /// Provides a fluent, easy-to-use API for typical scenarios. - /// - public sealed class KernelLibrary : IDisposable - { - private readonly KernelContext _context; - private readonly ChainParameters _chainParams; - private ChainstateManager? _chainstateManager; - private LoggingConnection? _loggingConnection; - private BlockProcessor? _blockProcessor; - private bool _disposed; - - /// - /// Gets the block processor for validation and processing operations. - /// - private BlockProcessor Processor - { - get - { - ThrowIfDisposed(); - return _blockProcessor ??= new BlockProcessor(Chainstate); - } - } - - private KernelLibrary( - KernelContext context, - ChainParameters chainParams) - { - _context = context; - _chainParams = chainParams; - } - - /// - /// Creates a new kernel library instance with fluent builder. - /// - public static KernelBuilder Create() => new KernelBuilder(); - - /// - /// Gets the active chainstate manager (if initialized). - /// - public ChainstateManager Chainstate - { - get - { - ThrowIfDisposed(); - if (_chainstateManager == null) - throw new InvalidOperationException("Chainstate not initialized. Call InitializeChainstate() first."); - return _chainstateManager; - } - } - - /// - /// Initializes the chainstate manager with the specified data directory. - /// Internal use only - chainstate is automatically initialized by the builder. - /// - internal KernelLibrary InitializeChainstate( - string dataDirectory, - string? blocksDirectory = null, - int workerThreads = 4) - { - ThrowIfDisposed(); - - // Use dataDirectory/blocks if blocks directory not specified - string blocksDir = blocksDirectory ?? System.IO.Path.Combine(dataDirectory, "blocks"); - - var options = new ChainstateManagerOptions(_context, dataDirectory, blocksDir) - .SetWorkerThreads(workerThreads); - - _chainstateManager = new ChainstateManager(_context, _chainParams, options); - - // Reset block processor when chainstate changes - _blockProcessor = null; - - return this; - } - - - public KernelLibrary EnableLogging(Action callback) - { - ThrowIfDisposed(); - _loggingConnection?.Dispose(); - _loggingConnection = new LoggingConnection(callback); - return this; - } - - /// - /// Processes a block through validation and potentially adds it to the chain. - /// - /// The serialized block data. - /// True if the block was successfully processed and was new. - public bool ProcessBlock(byte[] blockData) - { - ThrowIfDisposed(); - if (blockData == null) throw new ArgumentNullException(nameof(blockData)); - if (blockData.Length == 0) throw new ArgumentException("Block data cannot be empty", nameof(blockData)); - - var result = Processor.ProcessBlock(blockData); - return result.Success && result.IsNewBlock; - } - - /// - /// Processes a block from hex string through validation and potentially adds it to the chain. - /// - /// The block data as a hex string. - /// True if the block was successfully processed and was new. - public bool ProcessBlock(string blockHex) - { - ThrowIfDisposed(); - if (string.IsNullOrWhiteSpace(blockHex)) - throw new ArgumentException("Block hex cannot be null or empty", nameof(blockHex)); - - byte[] blockData = Convert.FromHexString(blockHex); - return ProcessBlock(blockData); - } - - /// - /// Verifies a script pubkey against a transaction input. - /// This is a high-level convenience method that handles object creation from hex strings. - /// - /// The hex representation of the output script to verify against. - /// The amount of the output being spent. - /// The hex representation of the transaction containing the input to verify. - /// The index of the transaction input to verify. - /// A list of hex strings, where each string is a spent output. - /// Script verification flags. - /// True if the script is valid, otherwise false. - public bool VerifyScript( - string scriptPubKeyHex, - long amount, - string transactionHex, - uint inputIndex, - List spentOutputsHex, - ScriptVerificationFlags flags = ScriptVerificationFlags.All) - { - using var scriptPubKey = ScriptPubKey.FromHex(scriptPubKeyHex); - using var transaction = Transaction.FromHex(transactionHex); - var spentOutputs = spentOutputsHex.Select(hex => - { - // This is a simplification. We assume the spent output is just a script pubkey. - // A proper implementation would need to decode the full TxOut structure. - // For now, we create a TxOut with the script and a default amount. - return new TxOut(ScriptPubKey.FromHex(hex), 0); - }).ToList(); - - try - { - return ScriptVerifier.VerifyScript(scriptPubKey, amount, transaction, inputIndex, spentOutputs, flags); - } - finally - { - foreach (var txOut in spentOutputs) - { - txOut.Dispose(); - } - } - } - - public bool ImportBlocks(string blockFilePath) - { - ThrowIfDisposed(); - if (_chainstateManager == null) - throw new InvalidOperationException("Chainstate not initialized"); - - return _chainstateManager.ImportBlocks(new[] { blockFilePath }); - } - - /// - /// Gets the current chain height (number of blocks). - /// - public int GetChainHeight() - { - ThrowIfDisposed(); - return Chainstate.GetActiveChain().Height; - } - - /// - /// Gets the hash of the chain tip (best block). - /// - public byte[] GetChainTipHash() - { - ThrowIfDisposed(); - var tip = Chainstate.GetActiveChain().GetTip(); - return tip.GetBlockHash(); - } - - /// - /// Gets the hash of the genesis block. - /// - public byte[] GetGenesisBlockHash() - { - ThrowIfDisposed(); - var genesis = Chainstate.GetActiveChain().GetGenesis(); - return genesis.GetBlockHash(); - } - - /// - /// Gets a block hash by height. - /// - /// The block height. - /// The block hash, or null if height is invalid. - public byte[]? GetBlockHash(int height) - { - ThrowIfDisposed(); - try - { - var chain = Chainstate.GetActiveChain(); - var blockIndex = chain.GetBlockByHeight(height); - return blockIndex?.GetBlockHash(); - } - catch (ArgumentOutOfRangeException) - { - return null; - } - } - - /// - /// Gets the spent outputs (UTXOs) for a block at the specified height. - /// - /// The block height. - /// The spent outputs for the block, or null if height is invalid. - public BlockSpentOutputs? GetSpentOutputs(int height) - { - ThrowIfDisposed(); - try - { - var chain = Chainstate.GetActiveChain(); - var blockIndex = chain.GetBlockByHeight(height); - if (blockIndex == null) return null; - return Chainstate.ReadSpentOutputs(blockIndex); - } - catch (ArgumentOutOfRangeException) - { - return null; - } - } - - /// - /// Enumerates all block hashes from genesis to tip. - /// - public IEnumerable EnumerateBlockHashes() - { - ThrowIfDisposed(); - var chain = Chainstate.GetActiveChain(); - foreach (var blockIndex in chain.EnumerateBlocks()) - { - yield return blockIndex.GetBlockHash(); - } - } - - /// - /// Gets basic information about a block at the specified height. - /// - /// The block height. - /// Block information, or null if height is invalid. - public BlockInfo? GetBlockInfo(int height) - { - ThrowIfDisposed(); - try - { - var chain = Chainstate.GetActiveChain(); - var blockIndex = chain.GetBlockByHeight(height); - if (blockIndex == null) return null; - - return new BlockInfo - { - Height = blockIndex.Height, - Hash = blockIndex.GetBlockHash(), - PreviousHash = blockIndex.GetPrevious()?.GetBlockHash() - }; - } - catch (ArgumentOutOfRangeException) - { - return null; - } - } - - /// - /// Gets block tree entry (metadata) by block hash. - /// - /// The hash of the block (32 bytes). - /// The block tree entry, or null if not found. - public Core.BlockProcessing.BlockTreeEntry? GetBlockTreeEntry(byte[] blockHash) - { - ThrowIfDisposed(); - if (blockHash == null) throw new ArgumentNullException(nameof(blockHash)); - if (blockHash.Length != 32) throw new ArgumentException("Block hash must be 32 bytes", nameof(blockHash)); - - return Processor.GetBlockTreeEntry(blockHash); - } - - /// - /// Gets block tree entry (metadata) by block hash from hex string. - /// - /// The hash of the block as a hex string. - /// The block tree entry, or null if not found. - public Core.BlockProcessing.BlockTreeEntry? GetBlockTreeEntry(string blockHashHex) - { - ThrowIfDisposed(); - if (string.IsNullOrWhiteSpace(blockHashHex)) - throw new ArgumentException("Block hash hex cannot be null or empty", nameof(blockHashHex)); - - try - { - byte[] blockHash = Convert.FromHexString(blockHashHex); - return GetBlockTreeEntry(blockHash); - } - catch (FormatException ex) - { - throw new ArgumentException($"Invalid hex string: {ex.Message}", nameof(blockHashHex), ex); - } - } - - /// - /// Represents basic block information. - /// - public class BlockInfo - { - public int Height { get; set; } - public byte[] Hash { get; set; } = Array.Empty(); - public byte[]? PreviousHash { get; set; } - } - - private void ThrowIfDisposed() - { - if (_disposed) - throw new ObjectDisposedException(nameof(KernelLibrary)); - } - - public void Dispose() - { - if (!_disposed) - { - _chainstateManager?.Dispose(); - _loggingConnection?.Dispose(); - _chainParams?.Dispose(); - _context?.Dispose(); - _disposed = true; - } - GC.SuppressFinalize(this); - } - - ~KernelLibrary() => Dispose(); - - /// - /// Fluent builder for KernelLibrary. - /// - public sealed class KernelBuilder - { - private ChainType _chainType = ChainType.MAINNET; - private Action? _loggingCallback; - private int _workerThreads = 4; - private string? _dataDirectory; - private string? _blocksDirectory; - private bool _wipeBlockTree; - private bool _wipeChainstate; - - internal KernelBuilder() { } - - /// - /// Sets the chain type (Mainnet, Testnet, Testnet_4, Regtest, Signet). - /// - private KernelBuilder ForChain(ChainType chainType) - { - _chainType = chainType; - return this; - } - - /// - /// Configures mainnet. - /// - public KernelBuilder ForMainnet() => ForChain(ChainType.MAINNET); - - /// - /// Configures testnet. - /// - public KernelBuilder ForTestnet() => ForChain(ChainType.TESTNET); - - /// - /// Configures testnet_4. - /// - public KernelBuilder ForTestnet4() => ForChain(ChainType.TESTNET_4); - - /// - /// Configures regtest. - /// - public KernelBuilder ForRegtest() => ForChain(ChainType.REGTEST); - - /// - /// Configures signet. - /// - public KernelBuilder ForSignet() => ForChain(ChainType.SIGNET); - - /// - /// Sets up logging with a callback. - /// - public KernelBuilder WithLogging(Action callback) - { - _loggingCallback = callback; - return this; - } - - /// - /// Sets the number of worker threads for validation. - /// - /// Number of worker threads (must be at least 1). - public KernelBuilder WithWorkerThreads(int threads) - { - if (threads < 1) - throw new ArgumentOutOfRangeException(nameof(threads), "Worker threads must be at least 1"); - - _workerThreads = threads; - return this; - } - - /// - /// Sets custom data and blocks directories. - /// - /// The data directory path. - /// The blocks directory path. - public KernelBuilder WithDirectories(string dataDirectory, string blocksDirectory) - { - if (string.IsNullOrWhiteSpace(dataDirectory)) - throw new ArgumentException("Data directory cannot be null or empty", nameof(dataDirectory)); - if (string.IsNullOrWhiteSpace(blocksDirectory)) - throw new ArgumentException("Blocks directory cannot be null or empty", nameof(blocksDirectory)); - - _dataDirectory = dataDirectory; - _blocksDirectory = blocksDirectory; - return this; - } - - /// - /// Enables database wiping on startup (useful for testing). - /// - public KernelBuilder WithWipeDatabases(bool wipeBlockTree = false, bool wipeChainstate = false) - { - _wipeBlockTree = wipeBlockTree; - _wipeChainstate = wipeChainstate; - return this; - } - - /// - /// Builds the KernelLibrary instance. - /// - public KernelLibrary Build() - { - var chainParams = new ChainParameters(_chainType); - - var contextOptions = new KernelContextOptions() - .SetChainParams(chainParams); - - var context = new KernelContext(contextOptions); - - var library = new KernelLibrary(context, chainParams); - - if (_loggingCallback != null) - { - library.EnableLogging(_loggingCallback); - } - - // Initialize chainstate with builder options - string dataDir = _dataDirectory ?? Path.Combine(Path.GetTempPath(), $"bitcoinkernel_{Guid.NewGuid()}"); - string blocksDir = _blocksDirectory ?? Path.Combine(dataDir, "blocks"); - - var options = new ChainstateManagerOptions(context, dataDir, blocksDir) - .SetWorkerThreads(_workerThreads); - - if (_wipeBlockTree || _wipeChainstate) - { - options.SetWipeDbs(_wipeBlockTree, _wipeChainstate); - } - - library.InitializeChainstate(dataDir, blocksDir, _workerThreads); - - return library; - } - } - - /// - /// Validates a block without adding it to the chain. - /// - /// The block data to validate. - /// True if the block is valid, false otherwise. - public bool ValidateBlock(byte[] blockData) - { - ThrowIfDisposed(); - - if (blockData == null || blockData.Length < 80) - return false; - - try - { - using var block = Block.FromBytes(blockData); - var result = Processor.ValidateBlock(block); - return result.IsValid; - } - catch - { - // If parsing or validation fails, the block is invalid - return false; - } - } - - /// - /// Validates a block from hex string without adding it to the chain. - /// - /// The block data as a hex string. - /// True if the block is valid, false otherwise. - public bool ValidateBlock(string blockHex) - { - ThrowIfDisposed(); - - if (string.IsNullOrWhiteSpace(blockHex)) - return false; - - try - { - byte[] blockData = Convert.FromHexString(blockHex); - return ValidateBlock(blockData); - } - catch - { - return false; - } - } - - /// - /// Validates a block and returns detailed validation results. - /// - /// The block data to validate. - /// Detailed validation result. - public Core.BlockProcessing.BlockValidationResult ValidateBlockDetailed(byte[] blockData) - { - ThrowIfDisposed(); - - if (blockData == null || blockData.Length < 80) - { - return new Core.BlockProcessing.BlockValidationResult( - isValid: false, - mode: ValidationMode.INVALID, - errorMessage: "Block data is null or too small"); - } - - try - { - using var block = Block.FromBytes(blockData); - return Processor.ValidateBlock(block); - } - catch (Exception ex) - { - return new Core.BlockProcessing.BlockValidationResult( - isValid: false, - mode: ValidationMode.INTERNAL_ERROR, - errorMessage: ex.Message); - } - } - - /// - /// Validates a block from hex string and returns detailed validation results. - /// - /// The block data as a hex string. - /// Detailed validation result. - public Core.BlockProcessing.BlockValidationResult ValidateBlockDetailed(string blockHex) - { - ThrowIfDisposed(); - - if (string.IsNullOrWhiteSpace(blockHex)) - { - return new Core.BlockProcessing.BlockValidationResult( - isValid: false, - mode: ValidationMode.INVALID, - errorMessage: "Block hex cannot be null or empty"); - } - - try - { - byte[] blockData = Convert.FromHexString(blockHex); - return ValidateBlockDetailed(blockData); - } - catch (Exception ex) - { - return new Core.BlockProcessing.BlockValidationResult( - isValid: false, - mode: ValidationMode.INTERNAL_ERROR, - errorMessage: $"Failed to parse hex string: {ex.Message}"); - } - } - } - -} \ No newline at end of file diff --git a/src/BitcoinKernel.Core/LoggingConnection.cs b/src/BitcoinKernel/LoggingConnection.cs similarity index 98% rename from src/BitcoinKernel.Core/LoggingConnection.cs rename to src/BitcoinKernel/LoggingConnection.cs index 280c263..309e92c 100644 --- a/src/BitcoinKernel.Core/LoggingConnection.cs +++ b/src/BitcoinKernel/LoggingConnection.cs @@ -2,6 +2,8 @@ using BitcoinKernel.Interop; using BitcoinKernel.Interop.Delegates; +namespace BitcoinKernel; + public sealed class LoggingConnection : IDisposable { private IntPtr _handle; diff --git a/src/BitcoinKernel.Core/Abstractions/Block.cs b/src/BitcoinKernel/Primatives/Block.cs similarity index 98% rename from src/BitcoinKernel.Core/Abstractions/Block.cs rename to src/BitcoinKernel/Primatives/Block.cs index 993f7ed..69d3a8d 100644 --- a/src/BitcoinKernel.Core/Abstractions/Block.cs +++ b/src/BitcoinKernel/Primatives/Block.cs @@ -1,8 +1,8 @@ -using BitcoinKernel.Core.Exceptions; +using BitcoinKernel.Exceptions; using BitcoinKernel.Interop; -namespace BitcoinKernel.Core.Abstractions; +namespace BitcoinKernel.Primatives; /// /// Represents a block in the blockchain. diff --git a/src/BitcoinKernel.Core/Abstractions/BlockHash.cs b/src/BitcoinKernel/Primatives/BlockHash.cs similarity index 95% rename from src/BitcoinKernel.Core/Abstractions/BlockHash.cs rename to src/BitcoinKernel/Primatives/BlockHash.cs index 9fce4cf..b322e54 100644 --- a/src/BitcoinKernel.Core/Abstractions/BlockHash.cs +++ b/src/BitcoinKernel/Primatives/BlockHash.cs @@ -1,7 +1,7 @@ -using BitcoinKernel.Core.Exceptions; +using BitcoinKernel.Exceptions; using BitcoinKernel.Interop; -namespace BitcoinKernel.Core.Abstractions; +namespace BitcoinKernel.Primatives; /// /// Represents a block hash. /// diff --git a/src/BitcoinKernel.Core/Abstractions/BlockHeader.cs b/src/BitcoinKernel/Primatives/BlockHeader.cs similarity index 97% rename from src/BitcoinKernel.Core/Abstractions/BlockHeader.cs rename to src/BitcoinKernel/Primatives/BlockHeader.cs index 5abdc7c..d9acf18 100644 --- a/src/BitcoinKernel.Core/Abstractions/BlockHeader.cs +++ b/src/BitcoinKernel/Primatives/BlockHeader.cs @@ -1,7 +1,7 @@ -using BitcoinKernel.Core.Exceptions; +using BitcoinKernel.Exceptions; using BitcoinKernel.Interop; -namespace BitcoinKernel.Core.Abstractions; +namespace BitcoinKernel.Primatives; /// /// Represents a block header containing metadata about a block. diff --git a/src/BitcoinKernel.Core/Abstractions/BlockIndex.cs b/src/BitcoinKernel/Primatives/BlockIndex.cs similarity index 97% rename from src/BitcoinKernel.Core/Abstractions/BlockIndex.cs rename to src/BitcoinKernel/Primatives/BlockIndex.cs index a0cdd1c..d12fcd8 100644 --- a/src/BitcoinKernel.Core/Abstractions/BlockIndex.cs +++ b/src/BitcoinKernel/Primatives/BlockIndex.cs @@ -1,6 +1,6 @@ using BitcoinKernel.Interop; -namespace BitcoinKernel.Core.Abstractions; +namespace BitcoinKernel.Primatives; /// /// Represents a block index entry in the block tree. diff --git a/src/BitcoinKernel/Primatives/BlockSpentOutputs.cs b/src/BitcoinKernel/Primatives/BlockSpentOutputs.cs new file mode 100644 index 0000000..96d4535 --- /dev/null +++ b/src/BitcoinKernel/Primatives/BlockSpentOutputs.cs @@ -0,0 +1,120 @@ +using System; +using BitcoinKernel.Interop; + +namespace BitcoinKernel.Primatives; + +/// +/// Represents the spent outputs for all transactions in a block. +/// +public class BlockSpentOutputs : IDisposable +{ + private IntPtr _handle; + private bool _disposed; + + internal BlockSpentOutputs(IntPtr handle) + { + if (handle == IntPtr.Zero) + { + throw new ArgumentException("Invalid block spent outputs handle", nameof(handle)); + } + + _handle = handle; + } + + /// + /// Gets the number of transactions with spent outputs in this block. + /// This excludes the coinbase transaction. + /// + public int Count + { + get + { + ThrowIfDisposed(); + return (int)NativeMethods.BlockSpentOutputsCount(_handle); + } + } + + /// + /// Gets the spent outputs for a transaction at the specified index. + /// The returned object is only valid for the lifetime of this BlockSpentOutputs. + /// + /// The zero-based index of the transaction (excluding coinbase). + /// A TransactionSpentOutputs object for the specified transaction. + public TransactionSpentOutputs GetTransactionSpentOutputs(int transactionIndex) + { + ThrowIfDisposed(); + + if (transactionIndex < 0 || transactionIndex >= Count) + { + throw new ArgumentOutOfRangeException(nameof(transactionIndex)); + } + + var txSpentOutputsPtr = NativeMethods.BlockSpentOutputsGetTransactionSpentOutputsAt( + _handle, (nuint)transactionIndex); + + if (txSpentOutputsPtr == IntPtr.Zero) + { + throw new InvalidOperationException($"Failed to get transaction spent outputs at index {transactionIndex}"); + } + + // Don't own the handle - it's only valid for the lifetime of BlockSpentOutputs + return new TransactionSpentOutputs(txSpentOutputsPtr, ownsHandle: false); + } + + /// + /// Enumerates all transaction spent outputs in the block. + /// + /// An enumerable of TransactionSpentOutputs for each transaction (excluding coinbase). + public IEnumerable EnumerateTransactionSpentOutputs() + { + ThrowIfDisposed(); + + int count = Count; + for (int i = 0; i < count; i++) + { + yield return GetTransactionSpentOutputs(i); + } + } + + internal IntPtr Handle + { + get + { + ThrowIfDisposed(); + return _handle; + } + } + + private void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(BlockSpentOutputs)); + } + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (_handle != IntPtr.Zero) + { + NativeMethods.BlockSpentOutputsDestroy(_handle); + _handle = IntPtr.Zero; + } + + _disposed = true; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~BlockSpentOutputs() + { + Dispose(false); + } +} diff --git a/src/BitcoinKernel.Core/Abstractions/BlockValidationState.cs b/src/BitcoinKernel/Primatives/BlockValidationState.cs similarity index 96% rename from src/BitcoinKernel.Core/Abstractions/BlockValidationState.cs rename to src/BitcoinKernel/Primatives/BlockValidationState.cs index 240a4a4..8c04870 100644 --- a/src/BitcoinKernel.Core/Abstractions/BlockValidationState.cs +++ b/src/BitcoinKernel/Primatives/BlockValidationState.cs @@ -1,8 +1,8 @@ -using BitcoinKernel.Core.Exceptions; +using BitcoinKernel.Exceptions; using BitcoinKernel.Interop; using BitcoinKernel.Interop.Enums; -namespace BitcoinKernel.Core.Abstractions; +namespace BitcoinKernel.Primatives; /// /// Represents the validation state of a block. diff --git a/src/BitcoinKernel/Primatives/Coin.cs b/src/BitcoinKernel/Primatives/Coin.cs new file mode 100644 index 0000000..d556c55 --- /dev/null +++ b/src/BitcoinKernel/Primatives/Coin.cs @@ -0,0 +1,123 @@ +using System; +using BitcoinKernel.Interop; + +namespace BitcoinKernel.Primatives; + +/// +/// Represents a coin (unspent transaction output) with confirmation height information. +/// +public class Coin : IDisposable +{ + private IntPtr _handle; + private bool _disposed; + private readonly bool _ownsHandle; + + internal Coin(IntPtr handle, bool ownsHandle = true) + { + if (handle == IntPtr.Zero) + { + throw new ArgumentException("Invalid coin handle", nameof(handle)); + } + + _handle = handle; + _ownsHandle = ownsHandle; + } + + /// + /// Gets the block height where the transaction that created this coin was included. + /// + public uint ConfirmationHeight + { + get + { + ThrowIfDisposed(); + return NativeMethods.CoinConfirmationHeight(_handle); + } + } + + /// + /// Gets a value indicating whether this coin is from a coinbase transaction. + /// + public bool IsCoinbase + { + get + { + ThrowIfDisposed(); + return NativeMethods.CoinIsCoinbase(_handle) != 0; + } + } + + /// + /// Gets the transaction output of this coin. + /// The returned object is only valid for the lifetime of this Coin. + /// + public TxOut GetOutput() + { + ThrowIfDisposed(); + var outputPtr = NativeMethods.CoinGetOutput(_handle); + if (outputPtr == IntPtr.Zero) + { + throw new InvalidOperationException("Failed to get coin output"); + } + + // Don't own the handle - it's only valid for the lifetime of the Coin + return new TxOut(outputPtr, ownsHandle: false); + } + + /// + /// Creates a copy of this coin. + /// + public Coin Copy() + { + ThrowIfDisposed(); + var copiedHandle = NativeMethods.CoinCopy(_handle); + if (copiedHandle == IntPtr.Zero) + { + throw new InvalidOperationException("Failed to copy coin"); + } + + return new Coin(copiedHandle, ownsHandle: true); + } + + internal IntPtr Handle + { + get + { + ThrowIfDisposed(); + return _handle; + } + } + + private void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(Coin)); + } + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (_ownsHandle && _handle != IntPtr.Zero) + { + NativeMethods.CoinDestroy(_handle); + _handle = IntPtr.Zero; + } + + _disposed = true; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~Coin() + { + Dispose(false); + } +} diff --git a/src/BitcoinKernel.Core/Abstractions/ScriptPubKey.cs b/src/BitcoinKernel/Primatives/ScriptPubKey.cs similarity index 96% rename from src/BitcoinKernel.Core/Abstractions/ScriptPubKey.cs rename to src/BitcoinKernel/Primatives/ScriptPubKey.cs index 4c4093f..f1bd5ad 100644 --- a/src/BitcoinKernel.Core/Abstractions/ScriptPubKey.cs +++ b/src/BitcoinKernel/Primatives/ScriptPubKey.cs @@ -1,8 +1,8 @@ using System.Net; -using BitcoinKernel.Core.Exceptions; +using BitcoinKernel.Exceptions; using BitcoinKernel.Interop; -namespace BitcoinKernel.Core.Abstractions; +namespace BitcoinKernel.Primatives; /// A single script pubkey containing spending conditions for a transaction output. /// diff --git a/src/BitcoinKernel.Core/Abstractions/Transaction.cs b/src/BitcoinKernel/Primatives/Transaction.cs similarity index 98% rename from src/BitcoinKernel.Core/Abstractions/Transaction.cs rename to src/BitcoinKernel/Primatives/Transaction.cs index ffe3702..3bc4247 100644 --- a/src/BitcoinKernel.Core/Abstractions/Transaction.cs +++ b/src/BitcoinKernel/Primatives/Transaction.cs @@ -1,9 +1,9 @@ using System; using System.Runtime.InteropServices; -using BitcoinKernel.Core.Exceptions; +using BitcoinKernel.Exceptions; using BitcoinKernel.Interop; -namespace BitcoinKernel.Core.Abstractions; +namespace BitcoinKernel.Primatives; /// /// Managed wrapper for Bitcoin transactions with automatic memory management. diff --git a/src/BitcoinKernel.Core/Abstractions/TransactionSpentOutputs.cs b/src/BitcoinKernel/Primatives/TransactionSpentOutputs.cs similarity index 98% rename from src/BitcoinKernel.Core/Abstractions/TransactionSpentOutputs.cs rename to src/BitcoinKernel/Primatives/TransactionSpentOutputs.cs index 3e1109d..c8836c0 100644 --- a/src/BitcoinKernel.Core/Abstractions/TransactionSpentOutputs.cs +++ b/src/BitcoinKernel/Primatives/TransactionSpentOutputs.cs @@ -1,4 +1,4 @@ -namespace BitcoinKernel.Core.Abstractions; +namespace BitcoinKernel.Primatives; using BitcoinKernel.Interop; using System; diff --git a/src/BitcoinKernel.Core/Abstractions/TxOut.cs b/src/BitcoinKernel/Primatives/TxOut.cs similarity index 97% rename from src/BitcoinKernel.Core/Abstractions/TxOut.cs rename to src/BitcoinKernel/Primatives/TxOut.cs index 1bb6923..5bfac9d 100644 --- a/src/BitcoinKernel.Core/Abstractions/TxOut.cs +++ b/src/BitcoinKernel/Primatives/TxOut.cs @@ -1,9 +1,9 @@ using System.Runtime.InteropServices; -using BitcoinKernel.Core.Exceptions; +using BitcoinKernel.Exceptions; using BitcoinKernel.Interop; -namespace BitcoinKernel.Core.Abstractions; +namespace BitcoinKernel.Primatives; /// /// Represents a transaction output (TxOut) in a Bitcoin transaction. diff --git a/src/BitcoinKernel/README.md b/src/BitcoinKernel/README.md deleted file mode 100644 index 69e2f73..0000000 --- a/src/BitcoinKernel/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# BitcoinKernel - -A high-level C# library for Bitcoin Core functionality, providing a simple and fluent API for working with Bitcoin blockchain operations via libbitcoinkernel. -⚠️🚧 This library is still under contruction. ⚠️🚧 - -## Installation - -```bash -dotnet add package BitcoinKernel -``` - -## Quick Start - -```csharp -using BitcoinKernel; -using BitcoinKernel.Interop.Enums; - -// Create a kernel instance for regtest -using var kernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - -// Get chain information -int height = kernel.GetChainHeight(); -byte[] tipHash = kernel.GetChainTipHash(); - -// Verify a script -bool isValid = kernel.VerifyScript( - scriptPubKeyHex: "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", - amount: 100000, - transactionHex: "02000000...", - inputIndex: 0, - spentOutputsHex: new List(), - flags: ScriptVerificationFlags.All -); -``` - - -## Documentation - -For more information, visit the [GitHub repository](https://github.com/janB84/BitcoinKernel.NET). - -## License - -MIT diff --git a/src/BitcoinKernel.Core/ScriptVerification/PrecomputedTransactionData.cs b/src/BitcoinKernel/ScriptVerification/PrecomputedTransactionData.cs similarity index 94% rename from src/BitcoinKernel.Core/ScriptVerification/PrecomputedTransactionData.cs rename to src/BitcoinKernel/ScriptVerification/PrecomputedTransactionData.cs index 19d6ebd..75b0839 100644 --- a/src/BitcoinKernel.Core/ScriptVerification/PrecomputedTransactionData.cs +++ b/src/BitcoinKernel/ScriptVerification/PrecomputedTransactionData.cs @@ -1,8 +1,8 @@ -using BitcoinKernel.Core.Abstractions; -using BitcoinKernel.Core.Exceptions; +using BitcoinKernel.Primatives; +using BitcoinKernel.Exceptions; using BitcoinKernel.Interop; -namespace BitcoinKernel.Core.ScriptVerification; +namespace BitcoinKernel.ScriptVerification; /// /// Holds precomputed transaction data used to accelerate repeated script verification diff --git a/src/BitcoinKernel.Core/ScriptVerification/ScriptVerifier.cs b/src/BitcoinKernel/ScriptVerification/ScriptVerifier.cs similarity index 97% rename from src/BitcoinKernel.Core/ScriptVerification/ScriptVerifier.cs rename to src/BitcoinKernel/ScriptVerification/ScriptVerifier.cs index ca63fbc..fbc6d64 100644 --- a/src/BitcoinKernel.Core/ScriptVerification/ScriptVerifier.cs +++ b/src/BitcoinKernel/ScriptVerification/ScriptVerifier.cs @@ -1,13 +1,13 @@ using System; using System.Runtime.InteropServices; -using BitcoinKernel.Core.Exceptions; +using BitcoinKernel.Exceptions; using BitcoinKernel.Interop; using BitcoinKernel.Interop.Enums; -using BitcoinKernel.Core.Abstractions; +using BitcoinKernel.Primatives; -namespace BitcoinKernel.Core.ScriptVerification; +namespace BitcoinKernel.ScriptVerification; /// /// Handles script verification operations. diff --git a/tests/BitcoinKernel.Core.Tests/BitcoinKernel.Core.Tests.csproj b/tests/BitcoinKernel.Core.Tests/BitcoinKernel.Core.Tests.csproj deleted file mode 100644 index 11925a3..0000000 --- a/tests/BitcoinKernel.Core.Tests/BitcoinKernel.Core.Tests.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - net9.0 - enable - enable - false - - false - true - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/tests/BitcoinKernel.Core.Tests/BlockProcessingTests.cs b/tests/BitcoinKernel.Core.Tests/BlockProcessingTests.cs deleted file mode 100644 index 843157d..0000000 --- a/tests/BitcoinKernel.Core.Tests/BlockProcessingTests.cs +++ /dev/null @@ -1,497 +0,0 @@ -using BitcoinKernel.Core.Chain; -using BitcoinKernel.Core.Exceptions; -using BitcoinKernel.Interop.Enums; -using Xunit; - -namespace BitcoinKernel.Core.Tests -{ - public class BlockProcessingTests : IDisposable - { - private KernelContext? _context; - private ChainParameters? _chainParams; - private string? _tempDir; - - public void Dispose() - { - _chainParams?.Dispose(); - _context?.Dispose(); - - if (!string.IsNullOrEmpty(_tempDir) && Directory.Exists(_tempDir)) - { - Directory.Delete(_tempDir, true); - } - } - - private (KernelContext, ChainParameters, string) TestingSetup() - { - // Create kernel context for regtest - _chainParams = new ChainParameters(ChainType.REGTEST); - var contextOptions = new KernelContextOptions() - .SetChainParams(_chainParams); - _context = new KernelContext(contextOptions); - - // Create temporary directory - _tempDir = Path.Combine(Path.GetTempPath(), $"test_chainman_regtest_{Guid.NewGuid()}"); - Directory.CreateDirectory(_tempDir); - - return (_context, _chainParams, _tempDir); - } - - private List ReadBlockData() - { - var blockData = new List(); - // The file is in the test project directory - var testAssemblyDir = Path.GetDirectoryName(typeof(BlockProcessingTests).Assembly.Location); - var projectDir = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(testAssemblyDir))); - var blockDataFile = Path.Combine(projectDir!, "TestData", "block_data.txt"); - - if (!File.Exists(blockDataFile)) - { - throw new FileNotFoundException($"Block data file not found: {blockDataFile}"); - } - - foreach (var line in File.ReadLines(blockDataFile)) - { - if (!string.IsNullOrWhiteSpace(line)) - { - blockData.Add(Convert.FromHexString(line.Trim())); - } - } - - return blockData; - } - - private void ProcessBlockData(ChainstateManager chainstateManager, List blockData) - { - foreach (var rawBlock in blockData) - { - using var block = Abstractions.Block.FromBytes(rawBlock); - chainstateManager.ProcessBlock(block); - } - } - - private (ChainstateManager, List) SetupChainstateManager() - { - var (context, chainParams, dataDir) = TestingSetup(); - var blockData = ReadBlockData(); - - var options = new ChainstateManagerOptions(context, dataDir, Path.Combine(dataDir, "blocks")); - var chainstateManager = new ChainstateManager(context, chainParams, options); - - return (chainstateManager, blockData); - } - - [Fact] - public void TestProcessData() - { - // Arrange - var (context, chainParams, dataDir) = TestingSetup(); - var blocksDir = Path.Combine(dataDir, "blocks"); - var blockData = ReadBlockData(); - - - var options = new ChainstateManagerOptions(context, dataDir, blocksDir); - using var chainstateManager = new ChainstateManager(context, chainParams, options); - - // Act & Assert - foreach (var rawBlock in blockData) - { - using var block = Abstractions.Block.FromBytes(rawBlock); - var result = chainstateManager.ProcessBlock(block); - - // Assert the block was processed successfully (is new) - Assert.True(result, "Block should be new and processed successfully"); - } - } - - [Fact] - public void TestValidateAny() - { - // Arrange - var (context, chainParams, dataDir) = TestingSetup(); - var blocksDir = Path.Combine(dataDir, "blocks"); - var blockData = ReadBlockData(); - - var options = new ChainstateManagerOptions(context, dataDir, blocksDir); - using var chainstateManager = new ChainstateManager(context, chainParams, options); - - // Act & Assert - chainstateManager.ImportBlocks(); - using var block2 = Abstractions.Block.FromBytes(blockData[1]); - - // The block should be invalid and processing should fail - var exception = Assert.Throws(() => - chainstateManager.ProcessBlock(block2)); - - // Verify it's a validation error (non-zero error code) - Assert.Contains("Failed to process block", exception.Message); - } - - [Fact] - public void TestReindex() - { - // Arrange - var (context, chainParams, dataDir) = TestingSetup(); - var blocksDir = Path.Combine(dataDir, "blocks"); - var blockData = ReadBlockData(); - - // Act - Process blocks - { - var options = new ChainstateManagerOptions(context, dataDir, blocksDir); - using var chainstateManager = new ChainstateManager(context, chainParams, options); - - foreach (var rawBlock in blockData) - { - using var block = Abstractions.Block.FromBytes(rawBlock); - var result = chainstateManager.ProcessBlock(block); - Assert.True(result, "Block should be new and processed successfully"); - } - } - - - // Act - Reindex - var reindexOptions = new ChainstateManagerOptions(context, dataDir, blocksDir) - .SetWipeDbs(false, true); - using var chainstateManagerReindex = new ChainstateManager(context, chainParams, reindexOptions); - var result_reindex = chainstateManagerReindex.ImportBlocks(); - - Assert.True(result_reindex, "Reindexing should be successful"); - - // Assert - Verify chainstate is intact after reindex - var activeChain = chainstateManagerReindex.GetActiveChain(); - Assert.Equal(blockData.Count, activeChain.Height); - } - - [Fact] - public void TestInvalidBlock() - { - // Arrange - var (context, chainParams, dataDir) = TestingSetup(); - var blocksDir = Path.Combine(dataDir, "blocks"); - - for (int i = 0; i < 10; i++) - { - var options = new ChainstateManagerOptions(context, dataDir, blocksDir); - using var chainstateManager = new ChainstateManager(context, chainParams, options); - - // Not a block - var invalidBlockData = Convert.FromHexString("deadbeef"); - Assert.Throws(() => Abstractions.Block.FromBytes(invalidBlockData)); - - // Invalid block - var invalidBlockHex = "010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd" + - "1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299" + - "0101000000010000000000000000000000000000000000000000000000000000000000000000ffff" + - "ffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec1" + - "1600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf62" + - "1e73a82cbf2342c858eeac00000000"; - - using var block = Abstractions.Block.FromBytes(Convert.FromHexString(invalidBlockHex)); - - // The block should be invalid and processing should fail - var exception = Assert.Throws(() => - chainstateManager.ProcessBlock(block)); - - // Verify it's a validation error (non-zero error code) - Assert.Contains("Failed to process block", exception.Message); - } - } - - [Fact] - public void TestScanTransactions() - { - // Arrange - Setup test environment with blocks - var setup = SetupChainstateManager(); - using var chainstateManager = setup.Item1; - var blockData = setup.Item2; - - // Act - Process all test blocks - ProcessBlockData(chainstateManager, blockData); - - var activeChain = chainstateManager.GetActiveChain(); - - // Verify we can iterate through the chain by height - for (int height = 0; height <= activeChain.Height; height++) - { - var blockIndex = activeChain.GetBlockByHeight(height); - Assert.NotNull(blockIndex); - Assert.Equal(height, blockIndex.Height); - } - - // Get the tip and read its spent outputs - var tipBlockIndex = activeChain.GetTip(); - Assert.NotNull(tipBlockIndex); - - using var spentOutputsTip = chainstateManager.ReadSpentOutputs(tipBlockIndex); - Assert.NotNull(spentOutputsTip); - - // The number of transaction spent outputs should match transactions minus coinbase - // This is validated by checking that Count returns a valid value - var tipSpentOutputsCount = spentOutputsTip.Count; - Assert.True(tipSpentOutputsCount >= 0, "Spent outputs count should be non-negative"); - - // If we have a previous block, scan its transactions in detail - var previousBlock = tipBlockIndex.GetPrevious(); - if (previousBlock != null) - { - using var spentOutputs = chainstateManager.ReadSpentOutputs(previousBlock); - var spentOutputsCount = spentOutputs.Count; - - // Scan each transaction's spent outputs - for (int txIndex = 0; txIndex < spentOutputsCount; txIndex++) - { - using var txSpentOutputs = spentOutputs.GetTransactionSpentOutputs(txIndex); - var coinsCount = txSpentOutputs.Count; - - // Verify we can access each coin - for (int coinIndex = 0; coinIndex < coinsCount; coinIndex++) - { - using var coin = txSpentOutputs.GetCoin(coinIndex); - - // Verify coin properties - Assert.True(coin.ConfirmationHeight >= 0, "Confirmation height should be non-negative"); - - // We should be able to get the output - using var output = coin.GetOutput(); - Assert.NotNull(output); - - // Verify we can get the script pubkey from the output - var scriptPubkeyBytes = output.GetScriptPubkey(); - Assert.NotNull(scriptPubkeyBytes); - Assert.True(scriptPubkeyBytes.Length >= 0, "Script pubkey should have valid length"); - - // Verify we can get the amount - var amount = output.Amount; - Assert.True(amount >= 0, "Amount should be non-negative"); - } - } - } - } - - [Fact] - public void TestChainOperations() - { - // Arrange - Setup test environment with blocks - var setup = SetupChainstateManager(); - using var chainstateManager = setup.Item1; - var blockData = setup.Item2; - - // Process all test blocks - ProcessBlockData(chainstateManager, blockData); - - var chain = chainstateManager.GetActiveChain(); - - // Test genesis via GetGenesis() method - var genesis = chain.GetGenesis(); - Assert.NotNull(genesis); - Assert.Equal(0, genesis.Height); - var genesisHash = genesis.GetBlockHash(); - Assert.NotNull(genesisHash); - - // Test tip block - var tip = chain.GetTip(); - Assert.NotNull(tip); - var tipHeight = tip.Height; - var tipHash = tip.GetBlockHash(); - - Assert.True(tipHeight > 0); - Assert.False(genesisHash.SequenceEqual(tipHash)); - - // Test accessing block by height - genesis - var genesisViaHeight = chain.GetBlockByHeight(0); - Assert.NotNull(genesisViaHeight); - Assert.Equal(0, genesisViaHeight.Height); - Assert.True(genesisHash.SequenceEqual(genesisViaHeight.GetBlockHash())); - - // Test accessing block by height - tip - var tipViaHeight = chain.GetBlockByHeight(tipHeight); - Assert.NotNull(tipViaHeight); - Assert.Equal(tipHeight, tipViaHeight.Height); - Assert.True(tipHash.SequenceEqual(tipViaHeight.GetBlockHash())); - - // Test invalid height returns null - var invalidEntry = chain.GetBlockByHeight(9999); - Assert.Null(invalidEntry); - - // Test Contains method - Assert.True(chain.Contains(genesis)); - Assert.True(chain.Contains(tip)); - - // Test iteration through chain - int count = 0; - foreach (var currentBlockIndex in chain.EnumerateBlocks()) - { - Assert.True(chain.Contains(currentBlockIndex)); - Assert.Equal(count, currentBlockIndex.Height); - count++; - } - - // Verify we iterated through the entire chain - Assert.Equal(tipHeight + 1, count); - } - - [Fact] - public void TestBlockSpentOutputsIterator() - { - // Arrange - Setup test environment with blocks - var setup = SetupChainstateManager(); - using var chainstateManager = setup.Item1; - var blockData = setup.Item2; - - // Process all test blocks - ProcessBlockData(chainstateManager, blockData); - - var activeChain = chainstateManager.GetActiveChain(); - var blockIndexTip = activeChain.GetTip(); - using var spentOutputs = chainstateManager.ReadSpentOutputs(blockIndexTip); - - // Test count via iterator matches Count property - var countViaIterator = spentOutputs.EnumerateTransactionSpentOutputs().Count(); - Assert.Equal(spentOutputs.Count, countViaIterator); - - // Test collecting all transaction spent outputs - var txSpentVec = spentOutputs.EnumerateTransactionSpentOutputs().ToList(); - Assert.Equal(spentOutputs.Count, txSpentVec.Count); - - // Test that each transaction spent output from iterator matches by index - int i = 0; - foreach (var txSpent in spentOutputs.EnumerateTransactionSpentOutputs()) - { - using var txSpentViaIndex = spentOutputs.GetTransactionSpentOutputs(i); - Assert.Equal(txSpent.Count, txSpentViaIndex.Count); - i++; - } - - // Test iterator length tracking - var initialLen = spentOutputs.Count; - - if (initialLen > 0) - { - // After skipping one element, we have initialLen - 1 remaining - var remaining = spentOutputs.EnumerateTransactionSpentOutputs().Skip(1).Count(); - Assert.Equal(initialLen - 1, remaining); - } - } - - [Fact] - public void TestTransactionSpentOutputsIterator() - { - // Arrange - Setup test environment with blocks - var setup = SetupChainstateManager(); - using var chainstateManager = setup.Item1; - var blockData = setup.Item2; - - // Process all test blocks - ProcessBlockData(chainstateManager, blockData); - - var activeChain = chainstateManager.GetActiveChain(); - var blockIndexTip = activeChain.GetTip(); - using var spentOutputs = chainstateManager.ReadSpentOutputs(blockIndexTip); - - using var txSpent = spentOutputs.GetTransactionSpentOutputs(0); - - // Test count via iterator matches Count property - var countViaIterator = txSpent.EnumerateCoins().Count(); - Assert.Equal(txSpent.Count, countViaIterator); - - // Test collecting all coins - var coins = txSpent.EnumerateCoins().ToList(); - Assert.Equal(txSpent.Count, coins.Count); - - // Test that each coin from iterator matches coin by index - int i = 0; - foreach (var coin in txSpent.EnumerateCoins()) - { - using var coinViaIndex = txSpent.GetCoin(i); - Assert.Equal(coin.ConfirmationHeight, coinViaIndex.ConfirmationHeight); - Assert.Equal(coin.IsCoinbase, coinViaIndex.IsCoinbase); - i++; - } - - // Test iterator length tracking - var initialLen = txSpent.Count; - - if (initialLen > 0) - { - // After skipping one element, we have initialLen - 1 remaining - var remaining = txSpent.EnumerateCoins().Skip(1).Count(); - Assert.Equal(initialLen - 1, remaining); - } - - // Test filtering coinbase coins - var coinbaseCoins = txSpent.EnumerateCoins().Where(coin => coin.IsCoinbase).ToList(); - - foreach (var coin in coinbaseCoins) - { - Assert.True(coin.IsCoinbase); - } - } - - [Fact] - public void TestNestedIteration() - { - // Arrange - Setup test environment with blocks - var setup = SetupChainstateManager(); - using var chainstateManager = setup.Item1; - var blockData = setup.Item2; - - // Process all test blocks - ProcessBlockData(chainstateManager, blockData); - - var activeChain = chainstateManager.GetActiveChain(); - var blockIndex = activeChain.GetBlockByHeight(1); - Assert.NotNull(blockIndex); - - using var spentOutputs = chainstateManager.ReadSpentOutputs(blockIndex); - - // Count total coins by nested iteration - int totalCoins = 0; - foreach (var txSpent in spentOutputs.EnumerateTransactionSpentOutputs()) - { - foreach (var coin in txSpent.EnumerateCoins()) - { - totalCoins++; - } - } - - // Calculate expected total using LINQ - int expectedTotal = spentOutputs.EnumerateTransactionSpentOutputs() - .Sum(txSpent => txSpent.Count); - - Assert.Equal(expectedTotal, totalCoins); - } - - [Fact] - public void TestIteratorWithBlockTransactions() - { - // Arrange - Setup test environment with blocks - var setup = SetupChainstateManager(); - using var chainstateManager = setup.Item1; - var blockData = setup.Item2; - - // Process all test blocks - ProcessBlockData(chainstateManager, blockData); - - var activeChain = chainstateManager.GetActiveChain(); - var blockIndex = activeChain.GetBlockByHeight(1); - Assert.NotNull(blockIndex); - - // Use the block data we already have (index 1 corresponds to blockData[1]) - using var block = Abstractions.Block.FromBytes(blockData[1]); - using var spentOutputs = chainstateManager.ReadSpentOutputs(blockIndex); - - // Zip block transactions (skipping coinbase) with spent outputs - // Each transaction's input count should match its spent outputs count - int txIndex = 0; - foreach (var tx in block.GetTransactions().Skip(1)) - { - using var txSpent = spentOutputs.GetTransactionSpentOutputs(txIndex); - Assert.Equal(tx.InputCount, txSpent.Count); - txIndex++; - } - - // Verify we processed all spent outputs - Assert.Equal(spentOutputs.Count, txIndex); - } - } -} \ No newline at end of file diff --git a/tests/BitcoinKernel.Core.Tests/BlockTests.cs b/tests/BitcoinKernel.Core.Tests/BlockTests.cs deleted file mode 100644 index 0188108..0000000 --- a/tests/BitcoinKernel.Core.Tests/BlockTests.cs +++ /dev/null @@ -1,72 +0,0 @@ -using BitcoinKernel.Core.Abstractions; -using Xunit; - -namespace BitcoinKernel.Core.Tests -{ - public class BlockTests - { - private List ReadBlockData() - { - var blockData = new List(); - var testAssemblyDir = Path.GetDirectoryName(typeof(BlockTests).Assembly.Location); - var projectDir = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(testAssemblyDir))); - var blockDataFile = Path.Combine(projectDir!, "TestData", "block_data.txt"); - - if (!File.Exists(blockDataFile)) - { - throw new FileNotFoundException($"Block data file not found: {blockDataFile}"); - } - - foreach (var line in File.ReadLines(blockDataFile)) - { - if (!string.IsNullOrWhiteSpace(line)) - { - blockData.Add(Convert.FromHexString(line.Trim())); - } - } - - return blockData; - } - - [Fact] - public void TestBlockTransactionsIterator() - { - var blockData = ReadBlockData(); - - using var block = Block.FromBytes(blockData[5]); - - // Test that iterator count matches transaction count - var txCountViaIterator = block.GetTransactions().Count(); - Assert.Equal(block.TransactionCount, txCountViaIterator); - - // Test collecting all transactions - var txs = block.GetTransactions().ToList(); - Assert.Equal(block.TransactionCount, txs.Count); - - // Test that each transaction from iterator matches transaction by index - int i = 0; - foreach (var tx in block.GetTransactions()) - { - using var txViaIndex = block.GetTransaction(i); - Assert.NotNull(txViaIndex); - Assert.Equal(tx.InputCount, txViaIndex.InputCount); - Assert.Equal(tx.OutputCount, txViaIndex.OutputCount); - i++; - } - - // Test iterator length decreases as we consume it - var iter = block.GetTransactions().GetEnumerator(); - var initialLen = block.TransactionCount; - - // Move to first element - Assert.True(iter.MoveNext()); - // After consuming one element, we have initialLen - 1 remaining - var remaining = block.GetTransactions().Skip(1).Count(); - Assert.Equal(initialLen - 1, remaining); - - // Test skipping coinbase transaction - var nonCoinbaseTxs = block.GetTransactions().Skip(1).ToList(); - Assert.Equal(block.TransactionCount - 1, nonCoinbaseTxs.Count); - } - } -} diff --git a/tests/BitcoinKernel.Tests/BitcoinKernel.Tests.csproj b/tests/BitcoinKernel.Tests/BitcoinKernel.Tests.csproj index f48e5a5..d60dcac 100644 --- a/tests/BitcoinKernel.Tests/BitcoinKernel.Tests.csproj +++ b/tests/BitcoinKernel.Tests/BitcoinKernel.Tests.csproj @@ -1,24 +1,26 @@ - - - - net9.0 - enable - enable - false - - false - true - - - - - - - - - - - - - - + + + + net9.0 + enable + enable + false + + false + true + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/BitcoinKernel.Core.Tests/BlockHeaderTests.cs b/tests/BitcoinKernel.Tests/BlockHeaderTests.cs similarity index 98% rename from tests/BitcoinKernel.Core.Tests/BlockHeaderTests.cs rename to tests/BitcoinKernel.Tests/BlockHeaderTests.cs index 59b9e0c..9b0563a 100644 --- a/tests/BitcoinKernel.Core.Tests/BlockHeaderTests.cs +++ b/tests/BitcoinKernel.Tests/BlockHeaderTests.cs @@ -1,12 +1,12 @@ -using BitcoinKernel.Core; -using BitcoinKernel.Core.Abstractions; -using BitcoinKernel.Core.BlockProcessing; -using BitcoinKernel.Core.Chain; -using BitcoinKernel.Core.Exceptions; +using BitcoinKernel; +using BitcoinKernel.Primatives; +using BitcoinKernel.BlockProcessing; +using BitcoinKernel.Chain; +using BitcoinKernel.Exceptions; using BitcoinKernel.Interop.Enums; using Xunit; -namespace BitcoinKernel.Core.Tests; +namespace BitcoinKernel.Tests; public class BlockHeaderTests : IDisposable { diff --git a/tests/BitcoinKernel.Tests/BlockProcessingTests.cs b/tests/BitcoinKernel.Tests/BlockProcessingTests.cs new file mode 100644 index 0000000..741267f --- /dev/null +++ b/tests/BitcoinKernel.Tests/BlockProcessingTests.cs @@ -0,0 +1,497 @@ +using BitcoinKernel.Chain; +using BitcoinKernel.Exceptions; +using BitcoinKernel.Interop.Enums; +using BitcoinKernel.Primatives; +using Xunit; + +namespace BitcoinKernel.Tests; + +public class BlockProcessingTests : IDisposable +{ + private KernelContext? _context; + private ChainParameters? _chainParams; + private string? _tempDir; + + public void Dispose() + { + _chainParams?.Dispose(); + _context?.Dispose(); + + if (!string.IsNullOrEmpty(_tempDir) && Directory.Exists(_tempDir)) + { + Directory.Delete(_tempDir, true); + } + } + + private (KernelContext, ChainParameters, string) TestingSetup() + { + // Create kernel context for regtest + _chainParams = new ChainParameters(ChainType.REGTEST); + var contextOptions = new KernelContextOptions() + .SetChainParams(_chainParams); + _context = new KernelContext(contextOptions); + + // Create temporary directory + _tempDir = Path.Combine(Path.GetTempPath(), $"test_chainman_regtest_{Guid.NewGuid()}"); + Directory.CreateDirectory(_tempDir); + + return (_context, _chainParams, _tempDir); + } + + private List ReadBlockData() + { + var blockData = new List(); + // The file is in the test project directory + var testAssemblyDir = Path.GetDirectoryName(typeof(BlockProcessingTests).Assembly.Location); + var projectDir = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(testAssemblyDir))); + var blockDataFile = Path.Combine(projectDir!, "TestData", "block_data.txt"); + + if (!File.Exists(blockDataFile)) + { + throw new FileNotFoundException($"Block data file not found: {blockDataFile}"); + } + + foreach (var line in File.ReadLines(blockDataFile)) + { + if (!string.IsNullOrWhiteSpace(line)) + { + blockData.Add(Convert.FromHexString(line.Trim())); + } + } + + return blockData; + } + + private void ProcessBlockData(ChainstateManager chainstateManager, List blockData) + { + foreach (var rawBlock in blockData) + { + using var block = Block.FromBytes(rawBlock); + chainstateManager.ProcessBlock(block); + } + } + + private (ChainstateManager, List) SetupChainstateManager() + { + var (context, chainParams, dataDir) = TestingSetup(); + var blockData = ReadBlockData(); + + var options = new ChainstateManagerOptions(context, dataDir, Path.Combine(dataDir, "blocks")); + var chainstateManager = new ChainstateManager(context, chainParams, options); + + return (chainstateManager, blockData); + } + + [Fact] + public void TestProcessData() + { + // Arrange + var (context, chainParams, dataDir) = TestingSetup(); + var blocksDir = Path.Combine(dataDir, "blocks"); + var blockData = ReadBlockData(); + + + var options = new ChainstateManagerOptions(context, dataDir, blocksDir); + using var chainstateManager = new ChainstateManager(context, chainParams, options); + + // Act & Assert + foreach (var rawBlock in blockData) + { + using var block = Block.FromBytes(rawBlock); + var result = chainstateManager.ProcessBlock(block); + + // Assert the block was processed successfully (is new) + Assert.True(result, "Block should be new and processed successfully"); + } + } + + [Fact] + public void TestValidateAny() + { + // Arrange + var (context, chainParams, dataDir) = TestingSetup(); + var blocksDir = Path.Combine(dataDir, "blocks"); + var blockData = ReadBlockData(); + + var options = new ChainstateManagerOptions(context, dataDir, blocksDir); + using var chainstateManager = new ChainstateManager(context, chainParams, options); + + // Act & Assert + chainstateManager.ImportBlocks(); + using var block2 = Block.FromBytes(blockData[1]); + + // The block should be invalid and processing should fail + var exception = Assert.Throws(() => + chainstateManager.ProcessBlock(block2)); + + // Verify it's a validation error (non-zero error code) + Assert.Contains("Failed to process block", exception.Message); + } + + [Fact] + public void TestReindex() + { + // Arrange + var (context, chainParams, dataDir) = TestingSetup(); + var blocksDir = Path.Combine(dataDir, "blocks"); + var blockData = ReadBlockData(); + + // Act - Process blocks + { + var options = new ChainstateManagerOptions(context, dataDir, blocksDir); + using var chainstateManager = new ChainstateManager(context, chainParams, options); + + foreach (var rawBlock in blockData) + { + using var block = Block.FromBytes(rawBlock); + var result = chainstateManager.ProcessBlock(block); + Assert.True(result, "Block should be new and processed successfully"); + } + } + + + // Act - Reindex + var reindexOptions = new ChainstateManagerOptions(context, dataDir, blocksDir) + .SetWipeDbs(false, true); + using var chainstateManagerReindex = new ChainstateManager(context, chainParams, reindexOptions); + var result_reindex = chainstateManagerReindex.ImportBlocks(); + + Assert.True(result_reindex, "Reindexing should be successful"); + + // Assert - Verify chainstate is intact after reindex + var activeChain = chainstateManagerReindex.GetActiveChain(); + Assert.Equal(blockData.Count, activeChain.Height); + } + + [Fact] + public void TestInvalidBlock() + { + // Arrange + var (context, chainParams, dataDir) = TestingSetup(); + var blocksDir = Path.Combine(dataDir, "blocks"); + + for (int i = 0; i < 10; i++) + { + var options = new ChainstateManagerOptions(context, dataDir, blocksDir); + using var chainstateManager = new ChainstateManager(context, chainParams, options); + + // Not a block + var invalidBlockData = Convert.FromHexString("deadbeef"); + Assert.Throws(() => Block.FromBytes(invalidBlockData)); + + // Invalid block + var invalidBlockHex = "010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd" + + "1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299" + + "0101000000010000000000000000000000000000000000000000000000000000000000000000ffff" + + "ffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec1" + + "1600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf62" + + "1e73a82cbf2342c858eeac00000000"; + + using var block = Block.FromBytes(Convert.FromHexString(invalidBlockHex)); + + // The block should be invalid and processing should fail + var exception = Assert.Throws(() => + chainstateManager.ProcessBlock(block)); + + // Verify it's a validation error (non-zero error code) + Assert.Contains("Failed to process block", exception.Message); + } + } + + [Fact] + public void TestScanTransactions() + { + // Arrange - Setup test environment with blocks + var setup = SetupChainstateManager(); + using var chainstateManager = setup.Item1; + var blockData = setup.Item2; + + // Act - Process all test blocks + ProcessBlockData(chainstateManager, blockData); + + var activeChain = chainstateManager.GetActiveChain(); + + // Verify we can iterate through the chain by height + for (int height = 0; height <= activeChain.Height; height++) + { + var blockIndex = activeChain.GetBlockByHeight(height); + Assert.NotNull(blockIndex); + Assert.Equal(height, blockIndex.Height); + } + + // Get the tip and read its spent outputs + var tipBlockIndex = activeChain.GetTip(); + Assert.NotNull(tipBlockIndex); + + using var spentOutputsTip = chainstateManager.ReadSpentOutputs(tipBlockIndex); + Assert.NotNull(spentOutputsTip); + + // The number of transaction spent outputs should match transactions minus coinbase + // This is validated by checking that Count returns a valid value + var tipSpentOutputsCount = spentOutputsTip.Count; + Assert.True(tipSpentOutputsCount >= 0, "Spent outputs count should be non-negative"); + + // If we have a previous block, scan its transactions in detail + var previousBlock = tipBlockIndex.GetPrevious(); + if (previousBlock != null) + { + using var spentOutputs = chainstateManager.ReadSpentOutputs(previousBlock); + var spentOutputsCount = spentOutputs.Count; + + // Scan each transaction's spent outputs + for (int txIndex = 0; txIndex < spentOutputsCount; txIndex++) + { + using var txSpentOutputs = spentOutputs.GetTransactionSpentOutputs(txIndex); + var coinsCount = txSpentOutputs.Count; + + // Verify we can access each coin + for (int coinIndex = 0; coinIndex < coinsCount; coinIndex++) + { + using var coin = txSpentOutputs.GetCoin(coinIndex); + + // Verify coin properties + Assert.True(coin.ConfirmationHeight >= 0, "Confirmation height should be non-negative"); + + // We should be able to get the output + using var output = coin.GetOutput(); + Assert.NotNull(output); + + // Verify we can get the script pubkey from the output + var scriptPubkeyBytes = output.GetScriptPubkey(); + Assert.NotNull(scriptPubkeyBytes); + Assert.True(scriptPubkeyBytes.Length >= 0, "Script pubkey should have valid length"); + + // Verify we can get the amount + var amount = output.Amount; + Assert.True(amount >= 0, "Amount should be non-negative"); + } + } + } + } + + [Fact] + public void TestChainOperations() + { + // Arrange - Setup test environment with blocks + var setup = SetupChainstateManager(); + using var chainstateManager = setup.Item1; + var blockData = setup.Item2; + + // Process all test blocks + ProcessBlockData(chainstateManager, blockData); + + var chain = chainstateManager.GetActiveChain(); + + // Test genesis via GetGenesis() method + var genesis = chain.GetGenesis(); + Assert.NotNull(genesis); + Assert.Equal(0, genesis.Height); + var genesisHash = genesis.GetBlockHash(); + Assert.NotNull(genesisHash); + + // Test tip block + var tip = chain.GetTip(); + Assert.NotNull(tip); + var tipHeight = tip.Height; + var tipHash = tip.GetBlockHash(); + + Assert.True(tipHeight > 0); + Assert.False(genesisHash.SequenceEqual(tipHash)); + + // Test accessing block by height - genesis + var genesisViaHeight = chain.GetBlockByHeight(0); + Assert.NotNull(genesisViaHeight); + Assert.Equal(0, genesisViaHeight.Height); + Assert.True(genesisHash.SequenceEqual(genesisViaHeight.GetBlockHash())); + + // Test accessing block by height - tip + var tipViaHeight = chain.GetBlockByHeight(tipHeight); + Assert.NotNull(tipViaHeight); + Assert.Equal(tipHeight, tipViaHeight.Height); + Assert.True(tipHash.SequenceEqual(tipViaHeight.GetBlockHash())); + + // Test invalid height returns null + var invalidEntry = chain.GetBlockByHeight(9999); + Assert.Null(invalidEntry); + + // Test Contains method + Assert.True(chain.Contains(genesis)); + Assert.True(chain.Contains(tip)); + + // Test iteration through chain + int count = 0; + foreach (var currentBlockIndex in chain.EnumerateBlocks()) + { + Assert.True(chain.Contains(currentBlockIndex)); + Assert.Equal(count, currentBlockIndex.Height); + count++; + } + + // Verify we iterated through the entire chain + Assert.Equal(tipHeight + 1, count); + } + + [Fact] + public void TestBlockSpentOutputsIterator() + { + // Arrange - Setup test environment with blocks + var setup = SetupChainstateManager(); + using var chainstateManager = setup.Item1; + var blockData = setup.Item2; + + // Process all test blocks + ProcessBlockData(chainstateManager, blockData); + + var activeChain = chainstateManager.GetActiveChain(); + var blockIndexTip = activeChain.GetTip(); + using var spentOutputs = chainstateManager.ReadSpentOutputs(blockIndexTip); + + // Test count via iterator matches Count property + var countViaIterator = spentOutputs.EnumerateTransactionSpentOutputs().Count(); + Assert.Equal(spentOutputs.Count, countViaIterator); + + // Test collecting all transaction spent outputs + var txSpentVec = spentOutputs.EnumerateTransactionSpentOutputs().ToList(); + Assert.Equal(spentOutputs.Count, txSpentVec.Count); + + // Test that each transaction spent output from iterator matches by index + int i = 0; + foreach (var txSpent in spentOutputs.EnumerateTransactionSpentOutputs()) + { + using var txSpentViaIndex = spentOutputs.GetTransactionSpentOutputs(i); + Assert.Equal(txSpent.Count, txSpentViaIndex.Count); + i++; + } + + // Test iterator length tracking + var initialLen = spentOutputs.Count; + + if (initialLen > 0) + { + // After skipping one element, we have initialLen - 1 remaining + var remaining = spentOutputs.EnumerateTransactionSpentOutputs().Skip(1).Count(); + Assert.Equal(initialLen - 1, remaining); + } + } + + [Fact] + public void TestTransactionSpentOutputsIterator() + { + // Arrange - Setup test environment with blocks + var setup = SetupChainstateManager(); + using var chainstateManager = setup.Item1; + var blockData = setup.Item2; + + // Process all test blocks + ProcessBlockData(chainstateManager, blockData); + + var activeChain = chainstateManager.GetActiveChain(); + var blockIndexTip = activeChain.GetTip(); + using var spentOutputs = chainstateManager.ReadSpentOutputs(blockIndexTip); + + using var txSpent = spentOutputs.GetTransactionSpentOutputs(0); + + // Test count via iterator matches Count property + var countViaIterator = txSpent.EnumerateCoins().Count(); + Assert.Equal(txSpent.Count, countViaIterator); + + // Test collecting all coins + var coins = txSpent.EnumerateCoins().ToList(); + Assert.Equal(txSpent.Count, coins.Count); + + // Test that each coin from iterator matches coin by index + int i = 0; + foreach (var coin in txSpent.EnumerateCoins()) + { + using var coinViaIndex = txSpent.GetCoin(i); + Assert.Equal(coin.ConfirmationHeight, coinViaIndex.ConfirmationHeight); + Assert.Equal(coin.IsCoinbase, coinViaIndex.IsCoinbase); + i++; + } + + // Test iterator length tracking + var initialLen = txSpent.Count; + + if (initialLen > 0) + { + // After skipping one element, we have initialLen - 1 remaining + var remaining = txSpent.EnumerateCoins().Skip(1).Count(); + Assert.Equal(initialLen - 1, remaining); + } + + // Test filtering coinbase coins + var coinbaseCoins = txSpent.EnumerateCoins().Where(coin => coin.IsCoinbase).ToList(); + + foreach (var coin in coinbaseCoins) + { + Assert.True(coin.IsCoinbase); + } + } + + [Fact] + public void TestNestedIteration() + { + // Arrange - Setup test environment with blocks + var setup = SetupChainstateManager(); + using var chainstateManager = setup.Item1; + var blockData = setup.Item2; + + // Process all test blocks + ProcessBlockData(chainstateManager, blockData); + + var activeChain = chainstateManager.GetActiveChain(); + var blockIndex = activeChain.GetBlockByHeight(1); + Assert.NotNull(blockIndex); + + using var spentOutputs = chainstateManager.ReadSpentOutputs(blockIndex); + + // Count total coins by nested iteration + int totalCoins = 0; + foreach (var txSpent in spentOutputs.EnumerateTransactionSpentOutputs()) + { + foreach (var coin in txSpent.EnumerateCoins()) + { + totalCoins++; + } + } + + // Calculate expected total using LINQ + int expectedTotal = spentOutputs.EnumerateTransactionSpentOutputs() + .Sum(txSpent => txSpent.Count); + + Assert.Equal(expectedTotal, totalCoins); + } + + [Fact] + public void TestIteratorWithBlockTransactions() + { + // Arrange - Setup test environment with blocks + var setup = SetupChainstateManager(); + using var chainstateManager = setup.Item1; + var blockData = setup.Item2; + + // Process all test blocks + ProcessBlockData(chainstateManager, blockData); + + var activeChain = chainstateManager.GetActiveChain(); + var blockIndex = activeChain.GetBlockByHeight(1); + Assert.NotNull(blockIndex); + + // Use the block data we already have (index 1 corresponds to blockData[1]) + using var block = Block.FromBytes(blockData[1]); + using var spentOutputs = chainstateManager.ReadSpentOutputs(blockIndex); + + // Zip block transactions (skipping coinbase) with spent outputs + // Each transaction's input count should match its spent outputs count + int txIndex = 0; + foreach (var tx in block.GetTransactions().Skip(1)) + { + using var txSpent = spentOutputs.GetTransactionSpentOutputs(txIndex); + Assert.Equal(tx.InputCount, txSpent.Count); + txIndex++; + } + + // Verify we processed all spent outputs + Assert.Equal(spentOutputs.Count, txIndex); + } +} diff --git a/tests/BitcoinKernel.Tests/BlockTests.cs b/tests/BitcoinKernel.Tests/BlockTests.cs new file mode 100644 index 0000000..ce87f8e --- /dev/null +++ b/tests/BitcoinKernel.Tests/BlockTests.cs @@ -0,0 +1,71 @@ +using BitcoinKernel.Primatives; +using Xunit; + +namespace BitcoinKernel.Tests; + +public class BlockTests +{ + private List ReadBlockData() + { + var blockData = new List(); + var testAssemblyDir = Path.GetDirectoryName(typeof(BlockTests).Assembly.Location); + var projectDir = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(testAssemblyDir))); + var blockDataFile = Path.Combine(projectDir!, "TestData", "block_data.txt"); + + if (!File.Exists(blockDataFile)) + { + throw new FileNotFoundException($"Block data file not found: {blockDataFile}"); + } + + foreach (var line in File.ReadLines(blockDataFile)) + { + if (!string.IsNullOrWhiteSpace(line)) + { + blockData.Add(Convert.FromHexString(line.Trim())); + } + } + + return blockData; + } + + [Fact] + public void TestBlockTransactionsIterator() + { + var blockData = ReadBlockData(); + + using var block = Block.FromBytes(blockData[5]); + + // Test that iterator count matches transaction count + var txCountViaIterator = block.GetTransactions().Count(); + Assert.Equal(block.TransactionCount, txCountViaIterator); + + // Test collecting all transactions + var txs = block.GetTransactions().ToList(); + Assert.Equal(block.TransactionCount, txs.Count); + + // Test that each transaction from iterator matches transaction by index + int i = 0; + foreach (var tx in block.GetTransactions()) + { + using var txViaIndex = block.GetTransaction(i); + Assert.NotNull(txViaIndex); + Assert.Equal(tx.InputCount, txViaIndex.InputCount); + Assert.Equal(tx.OutputCount, txViaIndex.OutputCount); + i++; + } + + // Test iterator length decreases as we consume it + var iter = block.GetTransactions().GetEnumerator(); + var initialLen = block.TransactionCount; + + // Move to first element + Assert.True(iter.MoveNext()); + // After consuming one element, we have initialLen - 1 remaining + var remaining = block.GetTransactions().Skip(1).Count(); + Assert.Equal(initialLen - 1, remaining); + + // Test skipping coinbase transaction + var nonCoinbaseTxs = block.GetTransactions().Skip(1).ToList(); + Assert.Equal(block.TransactionCount - 1, nonCoinbaseTxs.Count); + } +} diff --git a/tests/BitcoinKernel.Core.Tests/BlockTreeEntryTests.cs b/tests/BitcoinKernel.Tests/BlockTreeEntryTests.cs similarity index 96% rename from tests/BitcoinKernel.Core.Tests/BlockTreeEntryTests.cs rename to tests/BitcoinKernel.Tests/BlockTreeEntryTests.cs index 729f7c2..b9a3e62 100644 --- a/tests/BitcoinKernel.Core.Tests/BlockTreeEntryTests.cs +++ b/tests/BitcoinKernel.Tests/BlockTreeEntryTests.cs @@ -1,9 +1,10 @@ -using BitcoinKernel.Core.BlockProcessing; -using BitcoinKernel.Core.Chain; +using BitcoinKernel.BlockProcessing; +using BitcoinKernel.Chain; using BitcoinKernel.Interop.Enums; +using BitcoinKernel.Primatives; -namespace BitcoinKernel.Core.Tests; +namespace BitcoinKernel.Tests; public class BlockTreeEntryTests : IDisposable { @@ -41,7 +42,7 @@ private void SetupWithBlocks() // Process test blocks foreach (var rawBlock in ReadBlockData()) { - using var block = Abstractions.Block.FromBytes(rawBlock); + using var block = Block.FromBytes(rawBlock); _chainstateManager.ProcessBlock(block); } } diff --git a/tests/BitcoinKernel.Core.Tests/BlockValidationStateTests.cs b/tests/BitcoinKernel.Tests/BlockValidationStateTests.cs similarity index 96% rename from tests/BitcoinKernel.Core.Tests/BlockValidationStateTests.cs rename to tests/BitcoinKernel.Tests/BlockValidationStateTests.cs index 3c46c89..66c15aa 100644 --- a/tests/BitcoinKernel.Core.Tests/BlockValidationStateTests.cs +++ b/tests/BitcoinKernel.Tests/BlockValidationStateTests.cs @@ -1,8 +1,8 @@ -using BitcoinKernel.Core.Abstractions; +using BitcoinKernel.Primatives; using BitcoinKernel.Interop.Enums; using Xunit; -namespace BitcoinKernel.Core.Tests; +namespace BitcoinKernel.Tests; public class BlockValidationStateTests { diff --git a/tests/BitcoinKernel.Core.Tests/KernelContextTest.cs b/tests/BitcoinKernel.Tests/KernelContextTest.cs similarity index 90% rename from tests/BitcoinKernel.Core.Tests/KernelContextTest.cs rename to tests/BitcoinKernel.Tests/KernelContextTest.cs index 86daf19..ab4153e 100644 --- a/tests/BitcoinKernel.Core.Tests/KernelContextTest.cs +++ b/tests/BitcoinKernel.Tests/KernelContextTest.cs @@ -1,7 +1,7 @@ -using BitcoinKernel.Core.Exceptions; +using BitcoinKernel.Exceptions; using Xunit; -namespace BitcoinKernel.Core.Tests; +namespace BitcoinKernel.Tests; public class KernelContextTest { diff --git a/tests/BitcoinKernel.Tests/KernelLibraryIntegrationTests.cs b/tests/BitcoinKernel.Tests/KernelLibraryIntegrationTests.cs deleted file mode 100644 index d5cf44c..0000000 --- a/tests/BitcoinKernel.Tests/KernelLibraryIntegrationTests.cs +++ /dev/null @@ -1,493 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using BitcoinKernel; -using Xunit; - -namespace BitcoinKernel.Tests -{ - /// - /// Integration tests for the BitcoinKernel facade. - /// These tests verify end-to-end functionality of the library. - /// - public class KernelLibraryIntegrationTests : IDisposable - { - private KernelLibrary? _kernel; - - /// - /// Helper property to ensure previous kernel is disposed before assigning a new one. - /// - private KernelLibrary Kernel - { - get => _kernel ?? throw new InvalidOperationException("Kernel not initialized"); - set - { - _kernel?.Dispose(); - _kernel = value; - } - } - - public void Dispose() - { - _kernel?.Dispose(); - _kernel = null; - } - - [Fact] - public void KernelLibrary_CanCreateAndDispose() - { - // Arrange & Act - Kernel = KernelLibrary.Create() - .ForMainnet() - .Build(); - - // Assert - Assert.NotNull(_kernel); - } - - [Fact] - public void KernelLibrary_CanCreateForDifferentChains() - { - // Test mainnet - using var mainnetKernel = KernelLibrary.Create() - .ForMainnet() - .Build(); - Assert.NotNull(mainnetKernel); - - // Test testnet - using var testnetKernel = KernelLibrary.Create() - .ForTestnet() - .Build(); - Assert.NotNull(testnetKernel); - - // test testnet_4 - using var testnet4Kernel = KernelLibrary.Create() - .ForTestnet4() - .Build(); - Assert.NotNull(testnet4Kernel); - - // Test signet - using var signetKernel = KernelLibrary.Create() - .ForSignet() - .Build(); - Assert.NotNull(signetKernel); - - // Test regtest - using var regtestKernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - Assert.NotNull(regtestKernel); - } - - [Fact] - public void KernelLibrary_CanEnableLogging() - { - // Arrange - string? lastLogMessage = null; - void LogCallback(string category, string message, int level) - { - lastLogMessage = $"[{category}] {message} (level: {level})"; - } - - // Act - Kernel = KernelLibrary.Create() - .ForRegtest() - .WithLogging(LogCallback) - .Build(); - - // Assert - Assert.NotNull(_kernel); - // Note: We can't easily test actual logging without triggering log events, - // but we can verify the kernel was created successfully with logging enabled - } - - [Fact] - public void KernelLibrary_CanVerifyScript() - { - // Arrange - Kernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - - // Test data: Simple P2PKH script verification - // This is a basic test - in real scenarios you'd use actual transaction data - string scriptPubkeyHex = "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac"; - string transactionHex = "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700"; - - // Act & Assert - // This should succeed with valid test data - bool result = Kernel.VerifyScript(scriptPubkeyHex, 0, transactionHex, 0, new List { scriptPubkeyHex }); - Assert.True(result); - } - - // [Fact] - // public void KernelLibrary_ScriptVerificationFailsWithInvalidData() - // { - // Arrange - // Kernel = KernelLibrary.Create() - // .ForRegtest() - // .Build(); - - // Use invalid script pubkey (modified last byte) - // string invalidScriptPubkeyHex = "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ff"; - // ScriptPubKey invalidScriptPubKey = ScriptPubKey.FromHex(invalidScriptPubkeyHex); - // string transactionHex = "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700"; - // Transaction transaction = Transaction.FromHex(transactionHex); - // List spentOutputs = new List(); - - // Act & Assert - // This should fail with invalid script data - // bool result = Kernel.VerifyScript(invalidScriptPubKey, 0, transaction, 0, spentOutputs, ScriptVerificationFlags.All); - // Assert.False(result); - // } - - [Fact] - public void KernelLibrary_AutoInitializesChainstate() - { - // Arrange & Act - Kernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - - // Assert - // Chainstate should be automatically initialized by the builder - Assert.NotNull(Kernel.Chainstate); - - // Should be able to query chain information immediately - int height = Kernel.GetChainHeight(); - Assert.True(height >= 0); - } - - [Fact] - public void KernelLibrary_CanGetChainHeight() - { - // Arrange - Kernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - - // Act - int height = Kernel.GetChainHeight(); - - // Assert - Assert.True(height >= 0); // Chain height should be non-negative - } - - [Fact] - public void KernelLibrary_CanGetChainTipHash() - { - // Arrange - Kernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - - // Act - byte[] tipHash = Kernel.GetChainTipHash(); - - // Assert - Assert.NotNull(tipHash); - Assert.Equal(32, tipHash.Length); // Bitcoin hashes are 32 bytes - } - - [Fact] - public void KernelLibrary_CanGetGenesisBlockHash() - { - // Arrange - Kernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - - // Act - byte[] genesisHash = Kernel.GetGenesisBlockHash(); - - // Assert - Assert.NotNull(genesisHash); - Assert.Equal(32, genesisHash.Length); // Bitcoin hashes are 32 bytes - } - - [Fact] - public void KernelLibrary_CanGetBlockHash() - { - // Arrange - Kernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - - // Act - Get genesis block hash (height 0) - byte[]? genesisHash = Kernel.GetBlockHash(0); - - // Assert - Assert.NotNull(genesisHash); - Assert.Equal(32, genesisHash.Length); - - // Test invalid height - byte[]? invalidHash = Kernel.GetBlockHash(-1); - Assert.Null(invalidHash); - } - - [Fact] - public void KernelLibrary_CanEnumerateBlockHashes() - { - // Arrange - Kernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - - // Act - var blockHashes = Kernel.EnumerateBlockHashes().ToList(); - - // Assert - Assert.NotEmpty(blockHashes); - Assert.True(blockHashes.Count >= 1); // At least genesis block - - // All hashes should be 32 bytes - foreach (var hash in blockHashes) - { - Assert.Equal(32, hash.Length); - } - } - - [Fact] - public void KernelLibrary_CanGetBlockInfo() - { - // Arrange - Kernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - - // Act - Get genesis block info - var genesisInfo = Kernel.GetBlockInfo(0); - - // Assert - Assert.NotNull(genesisInfo); - Assert.Equal(0, genesisInfo.Height); - Assert.NotNull(genesisInfo.Hash); - Assert.Equal(32, genesisInfo.Hash.Length); - Assert.Null(genesisInfo.PreviousHash); // Genesis has no previous - - // Test invalid height - var invalidInfo = Kernel.GetBlockInfo(-1); - Assert.Null(invalidInfo); - } - - [Fact] - public void KernelLibrary_CanValidateBlock() - { - // Arrange - Kernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - - // Test that ValidateBlock properly calls the core validation - // With invalid block data, it should return false - byte[] invalidBlockData = new byte[80 + 1 + 60]; // Header + tx count + minimal data - invalidBlockData[80] = 1; // 1 transaction (but not a valid block) - - // Act - bool isValid = Kernel.ValidateBlock(invalidBlockData); - - // Assert - // Now using real validation from BlockProcessor, this should correctly fail - Assert.False(isValid); - - // Test invalid block (too small for header) - byte[] tooSmallBlockData = new byte[40]; // Too small - bool isTooSmallValid = Kernel.ValidateBlock(tooSmallBlockData); - Assert.False(isTooSmallValid); - - // Test null block data - bool isNullValid = Kernel.ValidateBlock((byte[])null!); - Assert.False(isNullValid); - } - - [Fact] - public void KernelLibrary_CanValidateBlockDetailed() - { - // Arrange - Kernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - - // Test that ValidateBlockDetailed returns detailed validation results - byte[] invalidBlockData = new byte[80 + 1 + 60]; // Invalid block data - invalidBlockData[80] = 1; - - // Act - var result = Kernel.ValidateBlockDetailed(invalidBlockData); - - // Assert - Assert.NotNull(result); - Assert.False(result.IsValid); - - // Test with too small block data - byte[] tooSmallData = new byte[40]; - var smallResult = Kernel.ValidateBlockDetailed(tooSmallData); - Assert.False(smallResult.IsValid); - Assert.Contains("too small", smallResult.ErrorMessage ?? "", StringComparison.OrdinalIgnoreCase); - - // Test with null data - var nullResult = Kernel.ValidateBlockDetailed((byte[])null!); - Assert.False(nullResult.IsValid); - } - - [Fact] - public void KernelLibrary_BuilderCanConfigureWorkerThreads() - { - // Arrange & Act - Kernel = KernelLibrary.Create() - .ForRegtest() - .WithWorkerThreads(8) - .Build(); - - // Assert - Assert.NotNull(_kernel); - } - - [Fact] - public void KernelLibrary_BuilderCanConfigureDirectories() - { - // Arrange - string customDataDir = Path.Combine(Path.GetTempPath(), $"BitcoinKernel_Data_{Guid.NewGuid()}"); - string customBlocksDir = Path.Combine(Path.GetTempPath(), $"BitcoinKernel_Blocks_{Guid.NewGuid()}"); - - try - { - // Act - Kernel = KernelLibrary.Create() - .ForRegtest() - .WithDirectories(customDataDir, customBlocksDir) - .Build(); - - // Assert - Assert.NotNull(_kernel); - Assert.NotNull(Kernel.Chainstate); // Should be auto-initialized - } - finally - { - // Cleanup - if (Directory.Exists(customDataDir)) - Directory.Delete(customDataDir, true); - if (Directory.Exists(customBlocksDir)) - Directory.Delete(customBlocksDir, true); - } - } - - [Fact] - public void KernelLibrary_BuilderCanConfigureWipeDatabases() - { - // Arrange & Act - Kernel = KernelLibrary.Create() - .ForRegtest() - .WithWipeDatabases(wipeBlockTree: true, wipeChainstate: true) - .Build(); - - // Assert - Assert.NotNull(_kernel); - Assert.NotNull(Kernel.Chainstate); // Should be auto-initialized - // Note: Database wiping behavior would be tested in integration scenarios - // with actual data persistence - } - - [Fact] - public void KernelLibrary_CanProcessBlockWithHexString() - { - // Arrange - Kernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - - // Create some hex block data (this will fail to parse) - string blockHex = "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c01010000000100000000000000000000"; - - // Act & Assert - // Invalid block data should throw BlockException during parsing - Assert.Throws(() => Kernel.ProcessBlock(blockHex)); - } - - [Fact] - public void KernelLibrary_CanValidateBlockWithHexString() - { - // Arrange - Kernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - - // Create some hex block data - string blockHex = "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c01010000000100000000000000000000"; - - // Act - bool isValid = Kernel.ValidateBlock(blockHex); - - // Assert - Invalid block should return false - Assert.False(isValid); - } - - [Fact] - public void KernelLibrary_CanValidateBlockDetailedWithHexString() - { - // Arrange - Kernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - - string blockHex = "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c01010000000100000000000000000000"; - - // Act - var result = Kernel.ValidateBlockDetailed(blockHex); - - // Assert - Assert.NotNull(result); - Assert.False(result.IsValid); - } - - [Fact] - public void KernelLibrary_CanGetBlockTreeEntry() - { - // Arrange - Kernel = KernelLibrary.Create() - .ForRegtest() - .Build(); - - // Get genesis block hash - byte[] genesisHash = Kernel.GetGenesisBlockHash(); - - // Act - var entry = Kernel.GetBlockTreeEntry(genesisHash); - - // Assert - May be null if block tree entry not available - // This is acceptable as it depends on kernel implementation - if (entry != null) - { - Assert.NotNull(entry); - } - } - - [Fact] - public void KernelLibrary_BuilderValidatesWorkerThreads() - { - // Act & Assert - Assert.Throws(() => - KernelLibrary.Create() - .ForRegtest() - .WithWorkerThreads(0) // Invalid - must be at least 1 - .Build()); - } - - [Fact] - public void KernelLibrary_BuilderValidatesDirectories() - { - // Act & Assert - Assert.Throws(() => - KernelLibrary.Create() - .ForRegtest() - .WithDirectories("", "/some/path") // Empty data directory - .Build()); - - Assert.Throws(() => - KernelLibrary.Create() - .ForRegtest() - .WithDirectories("/some/path", "") // Empty blocks directory - .Build()); - } - } -} \ No newline at end of file diff --git a/tests/BitcoinKernel.Core.Tests/ScriptVerificationTests.cs b/tests/BitcoinKernel.Tests/ScriptVerificationTests.cs similarity index 98% rename from tests/BitcoinKernel.Core.Tests/ScriptVerificationTests.cs rename to tests/BitcoinKernel.Tests/ScriptVerificationTests.cs index 35b8534..b9c9828 100644 --- a/tests/BitcoinKernel.Core.Tests/ScriptVerificationTests.cs +++ b/tests/BitcoinKernel.Tests/ScriptVerificationTests.cs @@ -1,12 +1,12 @@ using System; -using BitcoinKernel.Core.ScriptVerification; -using BitcoinKernel.Core.Abstractions; -using BitcoinKernel.Core.Exceptions; -using BitcoinKernel.Core.Chain; +using BitcoinKernel.ScriptVerification; +using BitcoinKernel.Primatives; +using BitcoinKernel.Exceptions; +using BitcoinKernel.Chain; using BitcoinKernel.Interop.Enums; using Xunit; -namespace BitcoinKernel.Core.Tests; +namespace BitcoinKernel.Tests; public class ScriptVerificationFixture : IDisposable { diff --git a/tests/BitcoinKernel.Core.Tests/TestData/block_data.txt b/tests/BitcoinKernel.Tests/TestData/block_data.txt similarity index 100% rename from tests/BitcoinKernel.Core.Tests/TestData/block_data.txt rename to tests/BitcoinKernel.Tests/TestData/block_data.txt diff --git a/tools/kernel-bindings-test-handler/Handlers/MethodDispatcher.cs b/tools/kernel-bindings-test-handler/Handlers/MethodDispatcher.cs index 08749a0..41cce44 100644 --- a/tools/kernel-bindings-test-handler/Handlers/MethodDispatcher.cs +++ b/tools/kernel-bindings-test-handler/Handlers/MethodDispatcher.cs @@ -1,8 +1,8 @@ -using BitcoinKernel.Core; -using BitcoinKernel.Core.Abstractions; -using BitcoinKernel.Core.Chain; -using BitcoinKernel.Core.Exceptions; -using BitcoinKernel.Core.ScriptVerification; +using BitcoinKernel; +using BitcoinKernel.Primatives; +using BitcoinKernel.Chain; +using BitcoinKernel.Exceptions; +using BitcoinKernel.ScriptVerification; using BitcoinKernel.Interop.Enums; using BitcoinKernel.TestHandler.Protocol; using BitcoinKernel.TestHandler.Registry; @@ -11,7 +11,7 @@ namespace BitcoinKernel.TestHandler.Handlers; /// /// Routes all incoming method calls to the appropriate handler and manages the object registry. -/// Uses BitcoinKernel.Core managed types throughout. +/// Uses BitcoinKernel managed types throughout. /// public sealed class MethodDispatcher : IDisposable { @@ -100,7 +100,7 @@ public Response ChainstateManagerGetActiveChain(string id, string? refName, Btck var manager = Get(csmRef).Manager; var chain = manager.GetActiveChain(); - _registry.Register(refName, new NonOwningRef(chain)); + _registry.Register(refName, new NonOwningRef(chain)); return Responses.Ref(id, refName); } @@ -133,7 +133,7 @@ public Response ChainstateManagerDestroy(string id, BtckChainstateManagerDestroy public Response ChainGetHeight(string id, BtckChainGetHeightParams p) { if (p.Chain?.Ref is not { } chainRef) return RefError(id); - return Responses.Ok(id, GetVal(chainRef).Height); + return Responses.Ok(id, GetVal(chainRef).Height); } public Response ChainGetByHeight(string id, string? refName, BtckChainGetByHeightParams p) @@ -141,7 +141,7 @@ public Response ChainGetByHeight(string id, string? refName, BtckChainGetByHeigh if (refName == null) return RefError(id); if (p.Chain?.Ref is not { } chainRef) return RefError(id); - var blockIndex = GetVal(chainRef).GetBlockByHeight(p.BlockHeight); + var blockIndex = GetVal(chainRef).GetBlockByHeight(p.BlockHeight); if (blockIndex == null) return Responses.EmptyError(id); _registry.Register(refName, new NonOwningRef(blockIndex)); @@ -153,7 +153,7 @@ public Response ChainContains(string id, BtckChainContainsParams p) if (p.Chain?.Ref is not { } chainRef) return RefError(id); if (p.BlockTreeEntry?.Ref is not { } bteRef) return RefError(id); - bool contains = GetVal(chainRef).Contains(GetVal(bteRef)); + bool contains = GetVal(chainRef).Contains(GetVal(bteRef)); return Responses.Ok(id, contains); } diff --git a/tools/kernel-bindings-test-handler/README.md b/tools/kernel-bindings-test-handler/README.md index 9825654..a94db28 100644 --- a/tools/kernel-bindings-test-handler/README.md +++ b/tools/kernel-bindings-test-handler/README.md @@ -7,7 +7,7 @@ It implements the protocol specification [kernel-bindings-test handler-spec](htt The handler is a console application that: - Reads JSON requests line-by-line from stdin -- Processes each request using the BitcoinKernel.Core library +- Processes each request using the BitcoinKernel library - Writes JSON responses to stdout - Exits cleanly when stdin closes @@ -137,12 +137,12 @@ tools/kernel-bindings-test-handler/ ## Dependencies -- BitcoinKernel.Core: The core library being tested +- BitcoinKernel: The core library being tested - System.Text.Json: JSON serialization ## Error Handling -The handler maps BitcoinKernel.Core exceptions to protocol error responses: +The handler maps BitcoinKernel exceptions to protocol error responses: | Exception | Error Type | Error Variant | |-----------|-----------|---------------| diff --git a/tools/kernel-bindings-test-handler/kernel-bindings-test-handler.csproj b/tools/kernel-bindings-test-handler/kernel-bindings-test-handler.csproj index 45fef66..58097f3 100644 --- a/tools/kernel-bindings-test-handler/kernel-bindings-test-handler.csproj +++ b/tools/kernel-bindings-test-handler/kernel-bindings-test-handler.csproj @@ -9,7 +9,7 @@ - +