Skip to content
Open
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
Binary file modified native/osx-x64/libbitcoinkernel.dylib
Binary file not shown.
19 changes: 19 additions & 0 deletions src/BitcoinKernel.Core/Abstractions/Transaction.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Runtime.InteropServices;
using BitcoinKernel.Core.Exceptions;
using BitcoinKernel.Core.TransactionValidation;
using BitcoinKernel.Interop;

namespace BitcoinKernel.Core.Abstractions;
Expand Down Expand Up @@ -158,6 +159,24 @@ public Transaction Copy()
return new Transaction(copyHandle);
}

/// <summary>
/// Validates this transaction against consensus rules.
/// </summary>
/// <returns>A TxValidationState containing the validation result.</returns>
public TxValidationState Validate()
{
return TransactionValidator.CheckTransaction(this);
}

/// <summary>
/// Checks if this transaction is valid according to consensus rules.
/// </summary>
/// <returns>True if the transaction is valid, false otherwise.</returns>
public bool IsValid()
{
return TransactionValidator.IsValid(this);
}

internal IntPtr Handle => _handle;

public void Dispose()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using BitcoinKernel.Core.Abstractions;
using BitcoinKernel.Core.Exceptions;
using BitcoinKernel.Interop;

namespace BitcoinKernel.Core.TransactionValidation;

/// <summary>
/// Provides transaction validation functionality for consensus checks.
/// </summary>
public static class TransactionValidator
{
/// <summary>
/// Checks if a transaction is valid according to consensus rules.
/// This is more efficient than CheckTransaction() when you only need a boolean result.
/// </summary>
/// <param name="transaction">The transaction to validate.</param>
/// <returns>True if the transaction is valid, false otherwise.</returns>
/// <exception cref="ArgumentNullException">Thrown when transaction is null.</exception>
public static bool IsValid(Transaction transaction)
{
ArgumentNullException.ThrowIfNull(transaction);
return NativeMethods.CheckTransaction(transaction.Handle, out nint statePtr) == 1;
}

/// <summary>
/// Performs context-free consensus checks on a transaction and returns detailed validation state.
/// Use this when you need to know why a transaction is invalid.
/// For simple validation, use IsValid() instead.
/// </summary>
/// <param name="transaction">The transaction to validate.</param>
/// <returns>A TxValidationState containing the validation result.</returns>
/// <exception cref="ArgumentNullException">Thrown when transaction is null.</exception>
/// <exception cref="TransactionException">Thrown when validation fails to execute.</exception>
public static TxValidationState CheckTransaction(Transaction transaction)
{
ArgumentNullException.ThrowIfNull(transaction);

IntPtr statePtr;
int result = NativeMethods.CheckTransaction(transaction.Handle, out statePtr);

if (statePtr == IntPtr.Zero)
throw new TransactionException("Failed to create validation state");

return new TxValidationState(statePtr);
}
}
75 changes: 75 additions & 0 deletions src/BitcoinKernel.Core/TransactionValidation/TxValidationState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using BitcoinKernel.Interop;
using BitcoinKernel.Interop.Enums;

namespace BitcoinKernel.Core.TransactionValidation;

/// <summary>
/// Represents the validation state of a transaction after consensus checks.
/// </summary>
public sealed class TxValidationState : IDisposable
{
private IntPtr _handle;
private bool _disposed;

internal TxValidationState(IntPtr handle)
{
_handle = handle != IntPtr.Zero
? handle
: throw new ArgumentException("Invalid validation state handle", nameof(handle));
}

/// <summary>
/// Gets the validation mode (VALID, INVALID, or INTERNAL_ERROR).
/// </summary>
public ValidationMode Mode
{
get
{
ThrowIfDisposed();
return NativeMethods.TxValidationStateGetValidationMode(_handle);
}
}

/// <summary>
/// Gets the detailed validation result indicating why the transaction was invalid.
/// </summary>
public TxValidationResult Result
{
get
{
ThrowIfDisposed();
return NativeMethods.TxValidationStateGetTxValidationResult(_handle);
}
}

/// <summary>
/// Returns true if the transaction is valid.
/// </summary>
public bool IsValid => Mode == ValidationMode.VALID;

/// <summary>
/// Returns true if the transaction is invalid.
/// </summary>
public bool IsInvalid => Mode == ValidationMode.INVALID;

/// <summary>
/// Returns true if an internal error occurred during validation.
/// </summary>
public bool IsError => Mode == ValidationMode.INTERNAL_ERROR;

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

public void Dispose()
{
if (!_disposed && _handle != IntPtr.Zero)
{
NativeMethods.TxValidationStateDestroy(_handle);
_handle = IntPtr.Zero;
_disposed = true;
}
}
}
23 changes: 23 additions & 0 deletions src/BitcoinKernel.Interop/Enums/TxValidationResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace BitcoinKernel.Interop.Enums
{
/// <summary>
/// A granular "reason" why a transaction was invalid.
/// Mirrors consensus/validation.h: enum class TxValidationResult.
/// </summary>
public enum TxValidationResult : uint
{
UNSET = 0,
CONSENSUS = 1,
INPUTS_NOT_STANDARD = 2,
NOT_STANDARD = 3,
MISSING_INPUTS = 4,
PREMATURE_SPEND = 5,
WITNESS_MUTATED = 6,
WITNESS_STRIPPED = 7,
CONFLICT = 8,
MEMPOOL_POLICY = 9,
NO_MEMPOOL = 10,
RECONSIDERABLE = 11,
UNKNOWN = 12
}
}
29 changes: 29 additions & 0 deletions src/BitcoinKernel.Interop/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,13 @@ public static extern int TransactionToBytes(
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_destroy")]
public static extern void TransactionDestroy(IntPtr transaction);

/// <summary>
/// Run consensus/tx_check::CheckTransaction on a transaction and return the filled state.
/// Returns 1 if valid, 0 if invalid.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_check_transaction")]
public static extern int CheckTransaction(IntPtr tx, out IntPtr out_state);

/// <summary>
/// Gets the script pubkey from a transaction output.
/// Returns a pointer to btck_ScriptPubkey.
Expand Down Expand Up @@ -404,6 +411,28 @@ public static extern int TransactionToBytes(

#endregion

#region TxValidationState Operations

/// <summary>
/// Gets the validation mode from a transaction validation state.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_tx_validation_state_get_validation_mode")]
public static extern ValidationMode TxValidationStateGetValidationMode(IntPtr state);

/// <summary>
/// Gets the transaction validation result from a transaction validation state.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_tx_validation_state_get_tx_validation_result")]
public static extern TxValidationResult TxValidationStateGetTxValidationResult(IntPtr state);

/// <summary>
/// Destroys a transaction validation state.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_tx_validation_state_destroy")]
public static extern void TxValidationStateDestroy(IntPtr state);

#endregion

#region ScriptPubkey Operations

/// <summary>
Expand Down
Loading