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
16 changes: 8 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ jobs:
path: native/${{ matrix.rid }}

- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: ${{ matrix.dotnet-version }}

Expand All @@ -138,13 +138,13 @@ jobs:
run: dotnet build --configuration Release --no-restore

- name: Test with dotnet
run: dotnet test --no-restore --logger trx --results-directory "TestResults-${{ matrix.dotnet-version }}"
run: dotnet test --no-restore --report-xunit-trx --results-directory "TestResults-${{ matrix.os }}"

- name: Upload dotnet test results
uses: actions/upload-artifact@v7
with:
name: dotnet-results--${{ matrix.os }}
path: TestResults--${{ matrix.os }}
name: dotnet-results-${{ matrix.os }}
path: TestResults-${{ matrix.os }}
if: ${{ always() }}

lint:
Expand All @@ -156,7 +156,7 @@ jobs:
uses: actions/checkout@v7

- name: Setup .NET
uses: actions/setup-dotnet@v5.4.0
uses: actions/setup-dotnet@v6
with:
dotnet-version: '10.0.x'

Expand All @@ -175,7 +175,7 @@ jobs:
uses: actions/checkout@v7

- name: Setup .NET
uses: actions/setup-dotnet@v5.4.0
uses: actions/setup-dotnet@v6
with:
dotnet-version: '10.0.x'

Expand Down Expand Up @@ -204,7 +204,7 @@ jobs:
rid: linux-x64

env:
TEST_VERSION: '0.0.4-alpha.pr14.5'
TEST_VERSION: '0.0.4-alpha.pr17.1'
TEST_REPO: 'stringintech/kernel-bindings-tests'
TEST_DIR: '.conformance-tests'

Expand All @@ -219,7 +219,7 @@ jobs:
path: native/${{ matrix.rid }}

- name: Setup .NET
uses: actions/setup-dotnet@v5.4.0
uses: actions/setup-dotnet@v6
with:
dotnet-version: '10.0.x'

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
fetch-depth: 0

- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: '10.0.x'

Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ 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.32.0]

### Added
- Bindings for the Bitcoin Core v32 kernel API; CI builds `libbitcoinkernel` from `v32.0rc2` (`bc795e60`)
- `TransactionInput.GetWitnessStack()` and the new `WitnessStack` type for reading witness items
- `TransactionInput.GetScriptSig()` returning the raw script sig bytes
- `ChainParameters.CreateSignet(byte[] challenge)` for signets with a custom challenge
- `ChainstateManagerOptions.SetDatabaseCacheBytes(ulong)` to size the total database cache (default 450 MiB)
- `MockTime.Set(...)` and `MockTime.Reset()` to override the kernel clock in tests

### Changed
- Package version jumps from 0.3.0 to 0.32.0 so the minor version matches the targeted Bitcoin Core release
- Test project moved from xunit 2.9.3 to xunit.v3 4.0.1; `global.json` opts `dotnet test` into Microsoft.Testing.Platform.
- Replaced the VSTest-only `coverlet.collector` with `Microsoft.Testing.Extensions.CodeCoverage`

## [0.3.0] - 2026-05-26

### Breaking Changes
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ This library uses [libbitcoinkernel](https://github.com/bitcoin/bitcoin/tree/mas

| Package | Version | Description |
|---------|---------|-------------|
| **BitcoinKernel** | 0.3.0 | Managed wrappers and native bindings |
| **BitcoinKernel** | 0.32.0 | Managed wrappers and native bindings |

The minor version tracks the Bitcoin Core release whose kernel API the package binds, so 0.32.x targets Bitcoin Core v32.

```bash
dotnet add package BitcoinKernel
Expand Down
5 changes: 5 additions & 0 deletions global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"test": {
"runner": "Microsoft.Testing.Platform"
}
}
2 changes: 1 addition & 1 deletion native/BITCOIN_COMMIT
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ba48852f9e758df8e67ce5f51c0e3e2b68713ab4
bc795e60dbb2c6e9c9556949731912429290626a
Binary file modified native/linux-x64/libbitcoinkernel.so
Binary file not shown.
Binary file modified native/osx-x64/libbitcoinkernel.dylib
Binary file not shown.
78 changes: 78 additions & 0 deletions src/BitcoinKernel.Interop/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ static NativeMethods()
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_parameters_create")]
public static extern IntPtr ChainParametersCreate(ChainType chain_type);

/// <summary>
/// Creates signet chain parameters with a user-provided challenge.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_parameters_create_signet")]
public static extern IntPtr ChainParametersCreateSignet(
byte[] challenge,
nuint challenge_len);

/// <summary>
/// Destroys chain parameters.
/// </summary>
Expand Down Expand Up @@ -224,6 +232,16 @@ public static extern void ChainstateManagerOptionsUpdateChainstateDbInMemory(
IntPtr options,
int chainstate_db_in_memory);

/// <summary>
/// Sets the total database cache in bytes. The kernel splits it between the
/// block tree db, the chainstate db and the in-memory coins cache.
/// Returns 0 on success, non-zero if the value is rejected.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_options_set_database_cache_bytes")]
public static extern int ChainstateManagerOptionsSetDatabaseCacheBytes(
IntPtr options,
ulong database_cache_bytes);

#endregion

#region Block Operations
Expand Down Expand Up @@ -923,6 +941,54 @@ public static extern IntPtr TransactionSpentOutputsGetCoinAt(
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_input_get_sequence")]
public static extern uint TransactionInputGetSequence(IntPtr transaction_input);

/// <summary>
/// Gets the witness stack of a transaction input.
/// The returned stack is unowned and only valid for the lifetime of the input.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_input_get_witness_stack")]
public static extern IntPtr TransactionInputGetWitnessStack(IntPtr transaction_input);

/// <summary>
/// Serializes the script sig of a transaction input.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_input_get_script_sig")]
public static extern int TransactionInputGetScriptSig(
IntPtr transaction_input,
WriteBytes writer,
IntPtr user_data);

#endregion

#region WitnessStack Operations

/// <summary>
/// Gets the number of items in a witness stack.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_witness_stack_count_items")]
public static extern nuint WitnessStackCountItems(IntPtr witness_stack);

/// <summary>
/// Serializes the witness stack item at the specified index.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_witness_stack_get_item_at")]
public static extern int WitnessStackGetItemAt(
IntPtr witness_stack,
nuint index,
WriteBytes writer,
IntPtr user_data);

/// <summary>
/// Copies a witness stack.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_witness_stack_copy")]
public static extern IntPtr WitnessStackCopy(IntPtr witness_stack);

/// <summary>
/// Destroys a witness stack.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_witness_stack_destroy")]
public static extern void WitnessStackDestroy(IntPtr witness_stack);

#endregion

#region TransactionOutPoint Operations
Expand Down Expand Up @@ -981,5 +1047,17 @@ public static extern IntPtr TransactionSpentOutputsGetCoinAt(

#endregion

#region Testing

/// <summary>
/// Overrides the kernel's clock with a fixed Unix timestamp, or restores the
/// system clock when timestamp is 0. Affects all kernel time reads globally.
/// Returns 0 on success, non-zero if timestamp is outside [0, 4294967295].
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_set_mock_time")]
public static extern int SetMockTime(long timestamp);

#endregion


}
2 changes: 1 addition & 1 deletion src/BitcoinKernel/BitcoinKernel.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<!-- NuGet Package Metadata -->
<PackageId>BitcoinKernel</PackageId>
<Version>0.3.0</Version>
<Version>0.32.0</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
22 changes: 22 additions & 0 deletions src/BitcoinKernel/Chain/ChainParameters.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using BitcoinKernel.Exceptions;
using BitcoinKernel.Interop;
using BitcoinKernel.Interop.Enums;

Expand All @@ -16,6 +17,27 @@ public ChainParameters(ChainType chainType)
throw new InvalidOperationException($"Failed to create chain parameters for chain type: {chainType}. The native library may not be loaded correctly.");
}

private ChainParameters(IntPtr handle)
{
_handle = handle;
}

/// <summary>
/// Creates signet chain parameters with a custom challenge script.
/// Blocks must satisfy the challenge to be valid.
/// </summary>
/// <param name="challenge">The raw signet challenge script bytes.</param>
public static ChainParameters CreateSignet(byte[] challenge)
{
ArgumentNullException.ThrowIfNull(challenge);

var handle = NativeMethods.ChainParametersCreateSignet(challenge, (nuint)challenge.Length);
if (handle == IntPtr.Zero)
throw new ChainParametersException(ChainType.SIGNET, "Failed to create signet chain parameters from the given challenge.");

return new ChainParameters(handle);
}

internal IntPtr Handle
{
get
Expand Down
17 changes: 17 additions & 0 deletions src/BitcoinKernel/Chain/ChainStateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,23 @@ public ChainstateManagerOptions SetChainstateDbInMemory(bool inMemory)
return this;
}

/// <summary>
/// Sets the total database cache in bytes. The kernel splits it between the
/// block tree db, the chainstate db and the in-memory coins cache.
/// Defaults to 450 MiB when not set. Values below 4 MiB are rejected, and so
/// are values above 1 GiB on 32-bit systems.
/// </summary>
public ChainstateManagerOptions SetDatabaseCacheBytes(ulong databaseCacheBytes)
{
ThrowIfDisposed();
int result = NativeMethods.ChainstateManagerOptionsSetDatabaseCacheBytes(_handle, databaseCacheBytes);

if (result != 0)
throw new KernelException($"Failed to set database cache to {databaseCacheBytes} bytes (error code: {result})");

return this;
}

private void ThrowIfDisposed()
{
if (_disposed)
Expand Down
40 changes: 40 additions & 0 deletions src/BitcoinKernel/MockTime.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using BitcoinKernel.Exceptions;
using BitcoinKernel.Interop;

namespace BitcoinKernel;

/// <summary>
/// Overrides the clock the kernel reads, for tests. The override is process-wide
/// and affects every context, so gate its use (for example to regtest) yourself.
/// </summary>
public static class MockTime
{
/// <summary>
/// Fixes the kernel clock at the given Unix timestamp in seconds.
/// </summary>
/// <param name="unixSeconds">Between 1 and 4294967295, the largest block header timestamp.</param>
public static void Set(long unixSeconds)
{
if (unixSeconds < 1 || unixSeconds > uint.MaxValue)
throw new ArgumentOutOfRangeException(nameof(unixSeconds), $"Mock time must be between 1 and {uint.MaxValue}");

Apply(unixSeconds);
}

/// <summary>
/// Fixes the kernel clock at the given time, truncated to whole seconds.
/// </summary>
public static void Set(DateTimeOffset time) => Set(time.ToUnixTimeSeconds());

/// <summary>
/// Restores the system clock.
/// </summary>
public static void Reset() => Apply(0);

private static void Apply(long unixSeconds)
{
int result = NativeMethods.SetMockTime(unixSeconds);
if (result != 0)
throw new KernelException($"Failed to set mock time to {unixSeconds} (error code: {result})");
}
}
41 changes: 41 additions & 0 deletions src/BitcoinKernel/Primitives/TransactionInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,47 @@ public OutPoint GetOutPoint()
return new OutPoint(outPointPtr, ownsHandle: false);
}

/// <summary>
/// Gets the witness stack of this input. The returned stack is a
/// non-owning view whose lifetime is tied to this input.
/// </summary>
public WitnessStack GetWitnessStack()
{
ThrowIfDisposed();
var witnessPtr = NativeMethods.TransactionInputGetWitnessStack(_handle);
if (witnessPtr == IntPtr.Zero)
throw new TransactionException("Failed to get witness stack from transaction input");

return new WitnessStack(witnessPtr, ownsHandle: false);
}

/// <summary>
/// Gets the raw script sig bytes of this input. Empty for inputs that
/// carry no script sig, such as native segwit spends.
/// </summary>
public byte[] GetScriptSig()
{
ThrowIfDisposed();
var bytes = new List<byte>();
NativeMethods.WriteBytes writer = (data, len, _) =>
{
// An empty script sig may arrive as a null pointer with len 0.
if (len == 0)
return 0;

var buffer = new byte[len];
System.Runtime.InteropServices.Marshal.Copy(data, buffer, 0, (int)len);
bytes.AddRange(buffer);
return 0;
};

int result = NativeMethods.TransactionInputGetScriptSig(_handle, writer, IntPtr.Zero);
if (result != 0)
throw new TransactionException("Failed to serialize script sig");

return bytes.ToArray();
}

/// <summary>
/// Creates an owned copy of this transaction input.
/// </summary>
Expand Down
Loading
Loading