Skip to content

Commit 9e8e8f0

Browse files
authored
Include Pr33822 changes
* Add BlockValidationState class and related P/Invoke methods with tests * Add support for block headers * Added script verification with precomputed transaction data and add Taproot transaction test * Update native binaries for Linux and macOS * Update version to 0.1.2 and add changelog for release notes
1 parent f0f14b4 commit 9e8e8f0

16 files changed

Lines changed: 993 additions & 45 deletions

File tree

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [0.1.2] - 2026-01-26
9+
10+
### Added
11+
- Kernel bindings test handler for conformance testing
12+
- Support for block validation and chain management in examples
13+
- Improved project documentation and examples
14+
15+
### Changed
16+
- N/A
17+
18+
### Fixed
19+
- Minor bug fixes and stability improvements
20+
21+
### Security
22+
- N/A
23+
24+
## [0.1.1] - Previous Release
25+
26+
Initial pre-release version with basic functionality.
27+
28+
---
29+
30+
**Note**: This library is still in early development. Version 0.1.x releases are pre-release and not recommended for production use.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ BitcoinKernel.NET brings Bitcoin Core's robust consensus engine to .NET applicat
1515

1616
| Package | Version | Description |
1717
|---------|---------|-------------|
18-
| **BitcoinKernel** | 0.1.1 | High-level API with fluent builder pattern |
19-
| **BitcoinKernel.Core** | 0.1.1 | Managed wrappers and native bindings |
18+
| **BitcoinKernel** | 0.1.2 | High-level API with fluent builder pattern |
19+
| **BitcoinKernel.Core** | 0.1.2 | Managed wrappers and native bindings |
2020

2121

2222
## Quick Start
-2.89 MB
Binary file not shown.
-174 KB
Binary file not shown.

src/BitcoinKernel.Core/Abstractions/Block.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,21 @@ public byte[] GetHash()
7272
return blockHash.ToBytes();
7373
}
7474

75+
/// <summary>
76+
/// Gets the block header from this block.
77+
/// </summary>
78+
public BlockHeader GetHeader()
79+
{
80+
ThrowIfDisposed();
81+
var headerPtr = NativeMethods.BlockGetHeader(_handle);
82+
if (headerPtr == IntPtr.Zero)
83+
{
84+
throw new BlockException("Failed to get block header");
85+
}
86+
87+
return new BlockHeader(headerPtr);
88+
}
89+
7590
/// <summary>
7691
/// Serializes the block to bytes.
7792
/// </summary>
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
using BitcoinKernel.Core.Exceptions;
2+
using BitcoinKernel.Interop;
3+
4+
namespace BitcoinKernel.Core.Abstractions;
5+
6+
/// <summary>
7+
/// Represents a block header containing metadata about a block.
8+
/// </summary>
9+
public sealed class BlockHeader : IDisposable
10+
{
11+
private IntPtr _handle;
12+
private bool _disposed;
13+
private readonly bool _ownsHandle;
14+
15+
internal BlockHeader(IntPtr handle, bool ownsHandle = true)
16+
{
17+
_handle = handle != IntPtr.Zero
18+
? handle
19+
: throw new ArgumentException("Invalid block header handle", nameof(handle));
20+
_ownsHandle = ownsHandle;
21+
}
22+
23+
/// <summary>
24+
/// Creates a block header from raw serialized data (80 bytes).
25+
/// </summary>
26+
public static BlockHeader FromBytes(byte[] rawHeaderData)
27+
{
28+
ArgumentNullException.ThrowIfNull(rawHeaderData, nameof(rawHeaderData));
29+
if (rawHeaderData.Length != 80)
30+
throw new ArgumentException("Block header must be exactly 80 bytes", nameof(rawHeaderData));
31+
32+
IntPtr headerPtr = NativeMethods.BlockHeaderCreate(rawHeaderData, (UIntPtr)rawHeaderData.Length);
33+
34+
if (headerPtr == IntPtr.Zero)
35+
{
36+
throw new BlockException("Failed to create block header from raw data");
37+
}
38+
39+
return new BlockHeader(headerPtr);
40+
}
41+
42+
internal IntPtr Handle
43+
{
44+
get
45+
{
46+
ThrowIfDisposed();
47+
return _handle;
48+
}
49+
}
50+
51+
/// <summary>
52+
/// Gets the block hash of this header.
53+
/// </summary>
54+
public byte[] GetHash()
55+
{
56+
ThrowIfDisposed();
57+
var hashPtr = NativeMethods.BlockHeaderGetHash(_handle);
58+
if (hashPtr == IntPtr.Zero)
59+
{
60+
throw new BlockException("Failed to get block hash from header");
61+
}
62+
63+
using var blockHash = new BlockHash(hashPtr);
64+
return blockHash.ToBytes();
65+
}
66+
67+
/// <summary>
68+
/// Gets the previous block hash from this header.
69+
/// </summary>
70+
public byte[] GetPrevHash()
71+
{
72+
ThrowIfDisposed();
73+
var hashPtr = NativeMethods.BlockHeaderGetPrevHash(_handle);
74+
if (hashPtr == IntPtr.Zero)
75+
{
76+
throw new BlockException("Failed to get previous block hash from header");
77+
}
78+
79+
// The hash pointer is unowned and only valid for the lifetime of the header
80+
var bytes = new byte[32];
81+
NativeMethods.BlockHashToBytes(hashPtr, bytes);
82+
return bytes;
83+
}
84+
85+
/// <summary>
86+
/// Gets the timestamp from this header (Unix epoch seconds).
87+
/// </summary>
88+
public uint Timestamp
89+
{
90+
get
91+
{
92+
ThrowIfDisposed();
93+
return NativeMethods.BlockHeaderGetTimestamp(_handle);
94+
}
95+
}
96+
97+
/// <summary>
98+
/// Gets the nBits difficulty target from this header (compact format).
99+
/// </summary>
100+
public uint Bits
101+
{
102+
get
103+
{
104+
ThrowIfDisposed();
105+
return NativeMethods.BlockHeaderGetBits(_handle);
106+
}
107+
}
108+
109+
/// <summary>
110+
/// Gets the version from this header.
111+
/// </summary>
112+
public int Version
113+
{
114+
get
115+
{
116+
ThrowIfDisposed();
117+
return NativeMethods.BlockHeaderGetVersion(_handle);
118+
}
119+
}
120+
121+
/// <summary>
122+
/// Gets the nonce from this header.
123+
/// </summary>
124+
public uint Nonce
125+
{
126+
get
127+
{
128+
ThrowIfDisposed();
129+
return NativeMethods.BlockHeaderGetNonce(_handle);
130+
}
131+
}
132+
133+
private void ThrowIfDisposed()
134+
{
135+
if (_disposed)
136+
throw new ObjectDisposedException(nameof(BlockHeader));
137+
}
138+
139+
public void Dispose()
140+
{
141+
if (!_disposed)
142+
{
143+
if (_handle != IntPtr.Zero && _ownsHandle)
144+
{
145+
NativeMethods.BlockHeaderDestroy(_handle);
146+
_handle = IntPtr.Zero;
147+
}
148+
_disposed = true;
149+
}
150+
}
151+
152+
~BlockHeader()
153+
{
154+
if (_ownsHandle)
155+
Dispose();
156+
}
157+
}

src/BitcoinKernel.Core/Abstractions/BlockIndex.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,16 @@ public byte[] GetBlockHash()
5151
? new BlockIndex(prevPtr, ownsHandle: false)
5252
: null;
5353
}
54-
}
54+
55+
/// <summary>
56+
/// Gets the block header associated with this block index.
57+
/// </summary>
58+
public BlockHeader GetBlockHeader()
59+
{
60+
var headerPtr = NativeMethods.BlockTreeEntryGetBlockHeader(_handle);
61+
if (headerPtr == IntPtr.Zero)
62+
throw new InvalidOperationException("Failed to get block header from block index");
63+
64+
return new BlockHeader(headerPtr);
65+
}
66+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
using BitcoinKernel.Core.Exceptions;
2+
using BitcoinKernel.Interop;
3+
using BitcoinKernel.Interop.Enums;
4+
5+
namespace BitcoinKernel.Core.Abstractions;
6+
7+
/// <summary>
8+
/// Represents the validation state of a block.
9+
/// </summary>
10+
public sealed class BlockValidationState : IDisposable
11+
{
12+
private IntPtr _handle;
13+
private bool _disposed;
14+
15+
/// <summary>
16+
/// Creates a new block validation state.
17+
/// </summary>
18+
public BlockValidationState()
19+
{
20+
_handle = NativeMethods.BlockValidationStateCreate();
21+
if (_handle == IntPtr.Zero)
22+
{
23+
throw new BlockException("Failed to create block validation state");
24+
}
25+
}
26+
27+
internal BlockValidationState(IntPtr handle)
28+
{
29+
if (handle == IntPtr.Zero)
30+
{
31+
throw new ArgumentException("Invalid block validation state handle", nameof(handle));
32+
}
33+
34+
_handle = handle;
35+
}
36+
37+
internal IntPtr Handle
38+
{
39+
get
40+
{
41+
ThrowIfDisposed();
42+
return _handle;
43+
}
44+
}
45+
46+
/// <summary>
47+
/// Gets the validation mode (valid, invalid, or internal error).
48+
/// </summary>
49+
public ValidationMode ValidationMode
50+
{
51+
get
52+
{
53+
ThrowIfDisposed();
54+
return NativeMethods.BlockValidationStateGetValidationMode(_handle);
55+
}
56+
}
57+
58+
/// <summary>
59+
/// Gets the block validation result (detailed error reason if invalid).
60+
/// </summary>
61+
public Interop.Enums.BlockValidationResult ValidationResult
62+
{
63+
get
64+
{
65+
ThrowIfDisposed();
66+
return NativeMethods.BlockValidationStateGetBlockValidationResult(_handle);
67+
}
68+
}
69+
70+
/// <summary>
71+
/// Creates a copy of this block validation state.
72+
/// </summary>
73+
public BlockValidationState Copy()
74+
{
75+
ThrowIfDisposed();
76+
var copyPtr = NativeMethods.BlockValidationStateCopy(_handle);
77+
if (copyPtr == IntPtr.Zero)
78+
{
79+
throw new BlockException("Failed to copy block validation state");
80+
}
81+
return new BlockValidationState(copyPtr);
82+
}
83+
84+
private void ThrowIfDisposed()
85+
{
86+
if (_disposed)
87+
throw new ObjectDisposedException(nameof(BlockValidationState));
88+
}
89+
90+
91+
public void Dispose()
92+
{
93+
if (!_disposed)
94+
{
95+
if (_handle != IntPtr.Zero)
96+
{
97+
NativeMethods.BlockValidationStateDestroy(_handle);
98+
_handle = IntPtr.Zero;
99+
}
100+
_disposed = true;
101+
}
102+
}
103+
104+
~BlockValidationState() => Dispose();
105+
}

src/BitcoinKernel.Core/BitcoinKernel.Core.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<!-- NuGet Package Metadata -->
1010
<PackageId>BitcoinKernel.Core</PackageId>
11-
<Version>0.1.1</Version>
11+
<Version>0.1.2</Version>
1212
<Authors>JanB84</Authors>
1313
<Description>.NET bindings and managed wrappers for libbitcoinkernel. Provides direct access to Bitcoin Core consensus and validation logic with automatic memory management.</Description>
1414
<PackageLicenseExpression>MIT</PackageLicenseExpression>

src/BitcoinKernel.Core/Chain/ChainStateManager.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,40 @@ public Abstractions.Chain GetActiveChain()
6464
return new Abstractions.Chain(chainPtr);
6565
}
6666

67+
/// <summary>
68+
/// Gets the block index with the most cumulative proof of work.
69+
/// </summary>
70+
public BlockIndex GetBestBlockIndex()
71+
{
72+
ThrowIfDisposed();
73+
IntPtr indexPtr = NativeMethods.ChainstateManagerGetBestEntry(_handle);
74+
if (indexPtr == IntPtr.Zero)
75+
throw new KernelException("Failed to get best block index");
76+
77+
return new BlockIndex(indexPtr, ownsHandle: false);
78+
}
79+
80+
/// <summary>
81+
/// Processes and validates a block header.
82+
/// </summary>
83+
/// <param name="header">The block header to process.</param>
84+
/// <param name="validationState">The validation state result.</param>
85+
/// <returns>True if processing was successful, false otherwise.</returns>
86+
public bool ProcessBlockHeader(BlockHeader header, out BlockValidationState validationState)
87+
{
88+
ThrowIfDisposed();
89+
ArgumentNullException.ThrowIfNull(header);
90+
91+
using var state = new BlockValidationState();
92+
int result = NativeMethods.ChainstateManagerProcessBlockHeader(
93+
_handle,
94+
header.Handle,
95+
state.Handle);
96+
97+
validationState = state.Copy();
98+
return result == 0;
99+
}
100+
67101
/// <summary>
68102
/// Processes a block through validation.
69103
/// </summary>

0 commit comments

Comments
 (0)