Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Changelog

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.1.2] - 2026-01-26

### Added
- Kernel bindings test handler for conformance testing
- Support for block validation and chain management in examples
- Improved project documentation and examples

### Changed
- N/A

### Fixed
- Minor bug fixes and stability improvements

### Security
- N/A

## [0.1.1] - Previous Release

Initial pre-release version with basic functionality.

---

**Note**: This library is still in early development. Version 0.1.x releases are pre-release and not recommended for production use.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ BitcoinKernel.NET brings Bitcoin Core's robust consensus engine to .NET applicat

| Package | Version | Description |
|---------|---------|-------------|
| **BitcoinKernel** | 0.1.1 | High-level API with fluent builder pattern |
| **BitcoinKernel.Core** | 0.1.1 | Managed wrappers and native bindings |
| **BitcoinKernel** | 0.1.2 | High-level API with fluent builder pattern |
| **BitcoinKernel.Core** | 0.1.2 | Managed wrappers and native bindings |


## Quick Start
Expand Down
Binary file modified native/linux-x64/libbitcoinkernel.so
Binary file not shown.
Binary file modified native/osx-x64/libbitcoinkernel.dylib
Binary file not shown.
15 changes: 15 additions & 0 deletions src/BitcoinKernel.Core/Abstractions/Block.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ public byte[] GetHash()
return blockHash.ToBytes();
}

/// <summary>
/// Gets the block header from this block.
/// </summary>
public BlockHeader GetHeader()
{
ThrowIfDisposed();
var headerPtr = NativeMethods.BlockGetHeader(_handle);
if (headerPtr == IntPtr.Zero)
{
throw new BlockException("Failed to get block header");
}

return new BlockHeader(headerPtr);
}

/// <summary>
/// Serializes the block to bytes.
/// </summary>
Expand Down
157 changes: 157 additions & 0 deletions src/BitcoinKernel.Core/Abstractions/BlockHeader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
using BitcoinKernel.Core.Exceptions;
using BitcoinKernel.Interop;

namespace BitcoinKernel.Core.Abstractions;

/// <summary>
/// Represents a block header containing metadata about a block.
/// </summary>
public sealed class BlockHeader : IDisposable
{
private IntPtr _handle;
private bool _disposed;
private readonly bool _ownsHandle;

internal BlockHeader(IntPtr handle, bool ownsHandle = true)
{
_handle = handle != IntPtr.Zero
? handle
: throw new ArgumentException("Invalid block header handle", nameof(handle));
_ownsHandle = ownsHandle;
}

/// <summary>
/// Creates a block header from raw serialized data (80 bytes).
/// </summary>
public static BlockHeader FromBytes(byte[] rawHeaderData)
{
ArgumentNullException.ThrowIfNull(rawHeaderData, nameof(rawHeaderData));
if (rawHeaderData.Length != 80)
throw new ArgumentException("Block header must be exactly 80 bytes", nameof(rawHeaderData));

IntPtr headerPtr = NativeMethods.BlockHeaderCreate(rawHeaderData, (UIntPtr)rawHeaderData.Length);

if (headerPtr == IntPtr.Zero)
{
throw new BlockException("Failed to create block header from raw data");
}

return new BlockHeader(headerPtr);
}

internal IntPtr Handle
{
get
{
ThrowIfDisposed();
return _handle;
}
}

/// <summary>
/// Gets the block hash of this header.
/// </summary>
public byte[] GetHash()
{
ThrowIfDisposed();
var hashPtr = NativeMethods.BlockHeaderGetHash(_handle);
if (hashPtr == IntPtr.Zero)
{
throw new BlockException("Failed to get block hash from header");
}

using var blockHash = new BlockHash(hashPtr);
return blockHash.ToBytes();
}

/// <summary>
/// Gets the previous block hash from this header.
/// </summary>
public byte[] GetPrevHash()
{
ThrowIfDisposed();
var hashPtr = NativeMethods.BlockHeaderGetPrevHash(_handle);
if (hashPtr == IntPtr.Zero)
{
throw new BlockException("Failed to get previous block hash from header");
}

// The hash pointer is unowned and only valid for the lifetime of the header
var bytes = new byte[32];
NativeMethods.BlockHashToBytes(hashPtr, bytes);
return bytes;
}

/// <summary>
/// Gets the timestamp from this header (Unix epoch seconds).
/// </summary>
public uint Timestamp
{
get
{
ThrowIfDisposed();
return NativeMethods.BlockHeaderGetTimestamp(_handle);
}
}

/// <summary>
/// Gets the nBits difficulty target from this header (compact format).
/// </summary>
public uint Bits
{
get
{
ThrowIfDisposed();
return NativeMethods.BlockHeaderGetBits(_handle);
}
}

/// <summary>
/// Gets the version from this header.
/// </summary>
public int Version
{
get
{
ThrowIfDisposed();
return NativeMethods.BlockHeaderGetVersion(_handle);
}
}

/// <summary>
/// Gets the nonce from this header.
/// </summary>
public uint Nonce
{
get
{
ThrowIfDisposed();
return NativeMethods.BlockHeaderGetNonce(_handle);
}
}

private void ThrowIfDisposed()
{
if (_disposed)
throw new ObjectDisposedException(nameof(BlockHeader));
}

public void Dispose()
{
if (!_disposed)
{
if (_handle != IntPtr.Zero && _ownsHandle)
{
NativeMethods.BlockHeaderDestroy(_handle);
_handle = IntPtr.Zero;
}
_disposed = true;
}
}

~BlockHeader()
{
if (_ownsHandle)
Dispose();
}
}
14 changes: 13 additions & 1 deletion src/BitcoinKernel.Core/Abstractions/BlockIndex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,16 @@ public byte[] GetBlockHash()
? new BlockIndex(prevPtr, ownsHandle: false)
: null;
}
}

/// <summary>
/// Gets the block header associated with this block index.
/// </summary>
public BlockHeader GetBlockHeader()
{
var headerPtr = NativeMethods.BlockTreeEntryGetBlockHeader(_handle);
if (headerPtr == IntPtr.Zero)
throw new InvalidOperationException("Failed to get block header from block index");

return new BlockHeader(headerPtr);
}
}
105 changes: 105 additions & 0 deletions src/BitcoinKernel.Core/Abstractions/BlockValidationState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using BitcoinKernel.Core.Exceptions;
using BitcoinKernel.Interop;
using BitcoinKernel.Interop.Enums;

namespace BitcoinKernel.Core.Abstractions;

/// <summary>
/// Represents the validation state of a block.
/// </summary>
public sealed class BlockValidationState : IDisposable
{
private IntPtr _handle;
private bool _disposed;

/// <summary>
/// Creates a new block validation state.
/// </summary>
public BlockValidationState()
{
_handle = NativeMethods.BlockValidationStateCreate();
if (_handle == IntPtr.Zero)
{
throw new BlockException("Failed to create block validation state");
}
}

internal BlockValidationState(IntPtr handle)
{
if (handle == IntPtr.Zero)
{
throw new ArgumentException("Invalid block validation state handle", nameof(handle));
}

_handle = handle;
}

internal IntPtr Handle
{
get
{
ThrowIfDisposed();
return _handle;
}
}

/// <summary>
/// Gets the validation mode (valid, invalid, or internal error).
/// </summary>
public ValidationMode ValidationMode
{
get
{
ThrowIfDisposed();
return NativeMethods.BlockValidationStateGetValidationMode(_handle);
}
}

/// <summary>
/// Gets the block validation result (detailed error reason if invalid).
/// </summary>
public Interop.Enums.BlockValidationResult ValidationResult
{
get
{
ThrowIfDisposed();
return NativeMethods.BlockValidationStateGetBlockValidationResult(_handle);
}
}

/// <summary>
/// Creates a copy of this block validation state.
/// </summary>
public BlockValidationState Copy()
{
ThrowIfDisposed();
var copyPtr = NativeMethods.BlockValidationStateCopy(_handle);
if (copyPtr == IntPtr.Zero)
{
throw new BlockException("Failed to copy block validation state");
}
return new BlockValidationState(copyPtr);
}

private void ThrowIfDisposed()
{
if (_disposed)
throw new ObjectDisposedException(nameof(BlockValidationState));
}


public void Dispose()
{
if (!_disposed)
{
if (_handle != IntPtr.Zero)
{
NativeMethods.BlockValidationStateDestroy(_handle);
_handle = IntPtr.Zero;
}
_disposed = true;
}
}

~BlockValidationState() => Dispose();
}
2 changes: 1 addition & 1 deletion src/BitcoinKernel.Core/BitcoinKernel.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<!-- NuGet Package Metadata -->
<PackageId>BitcoinKernel.Core</PackageId>
<Version>0.1.1</Version>
<Version>0.1.2</Version>
<Authors>JanB84</Authors>
<Description>.NET bindings and managed wrappers for libbitcoinkernel. Provides direct access to Bitcoin Core consensus and validation logic with automatic memory management.</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
Expand Down
34 changes: 34 additions & 0 deletions src/BitcoinKernel.Core/Chain/ChainStateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,40 @@ public Abstractions.Chain GetActiveChain()
return new Abstractions.Chain(chainPtr);
}

/// <summary>
/// Gets the block index with the most cumulative proof of work.
/// </summary>
public BlockIndex GetBestBlockIndex()
{
ThrowIfDisposed();
IntPtr indexPtr = NativeMethods.ChainstateManagerGetBestEntry(_handle);
if (indexPtr == IntPtr.Zero)
throw new KernelException("Failed to get best block index");

return new BlockIndex(indexPtr, ownsHandle: false);
}

/// <summary>
/// Processes and validates a block header.
/// </summary>
/// <param name="header">The block header to process.</param>
/// <param name="validationState">The validation state result.</param>
/// <returns>True if processing was successful, false otherwise.</returns>
public bool ProcessBlockHeader(BlockHeader header, out BlockValidationState validationState)
{
ThrowIfDisposed();
ArgumentNullException.ThrowIfNull(header);

using var state = new BlockValidationState();
int result = NativeMethods.ChainstateManagerProcessBlockHeader(
_handle,
header.Handle,
state.Handle);

validationState = state.Copy();
return result == 0;
}

/// <summary>
/// Processes a block through validation.
/// </summary>
Expand Down
Loading
Loading