Skip to content
Merged
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ jobs:
platform: linux_amd64

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

Expand Down
6 changes: 3 additions & 3 deletions examples/BasicUsage/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,16 @@ static void FullChainstateExample()
{
var chain = chainstate.GetActiveChain();
Console.WriteLine($" Chain height: {chain.Height}");
Console.WriteLine($" Genesis hash: {Convert.ToHexString(chain.GetGenesis().GetBlockHash())}");
Console.WriteLine($" Genesis hash: {Convert.ToHexString(chain.GetGenesis().GetHash())}");

if (chain.Height > 0)
{
var tip = chain.GetTip();
Console.WriteLine($" Tip hash: {Convert.ToHexString(tip.GetBlockHash())}");
Console.WriteLine($" Tip hash: {Convert.ToHexString(tip.GetHash())}");

var genesis = chain.GetBlockByHeight(0);
if (genesis != null)
Console.WriteLine($" Block 0 hash: {Convert.ToHexString(genesis.GetBlockHash())}");
Console.WriteLine($" Block 0 hash: {Convert.ToHexString(genesis.GetHash())}");
}

Console.WriteLine(" Chain queries working");
Expand Down
2 changes: 1 addition & 1 deletion examples/BlockProcessing/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ static void Main(string[] args)
var activeChain = chainstate.GetActiveChain();
Console.WriteLine($"Block processed! Chain height: {activeChain.Height}");
var tip = activeChain.GetTip();
Console.WriteLine($" - Tip: {BitConverter.ToString(tip.GetBlockHash()).Replace("-", "")}");
Console.WriteLine($" - Tip: {BitConverter.ToString(tip.GetHash()).Replace("-", "")}");
}
else
{
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.
31 changes: 31 additions & 0 deletions src/BitcoinKernel.Interop/Enums/BlockCheckFlags.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace BitcoinKernel.Interop.Enums;

/// <summary>
/// Flags controlling optional context-free block checks performed by
/// btck_block_check. The base checks (size limits, coinbase structure,
/// transaction checks, sigop limits) always run; these flags toggle the
/// optional proof-of-work and merkle-root checks.
/// </summary>
[Flags]
public enum BlockCheckFlags : uint
{
/// <summary>
/// Run the base context-free block checks only.
/// </summary>
Base = 0,

/// <summary>
/// Run CheckProofOfWork via CheckBlockHeader.
/// </summary>
Pow = 1U << 0,

/// <summary>
/// Verify merkle root (and mutation detection).
/// </summary>
Merkle = 1U << 1,

/// <summary>
/// Enable all optional context-free block checks.
/// </summary>
All = Pow | Merkle
}
3 changes: 2 additions & 1 deletion src/BitcoinKernel.Interop/Enums/ChainType.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace BitcoinKernel.Interop.Enums;

public enum ChainType : uint
// btck_ChainType is a uint8_t in bitcoinkernel.h.
public enum ChainType : byte
{
MAINNET = 0,
TESTNET = 1,
Expand Down
3 changes: 2 additions & 1 deletion src/BitcoinKernel.Interop/Enums/LogLevel.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace BitcoinKernel.Interop.Enums;

public enum LogLevel : uint
// btck_LogLevel is a uint8_t in bitcoinkernel.h.
public enum LogLevel : byte
{
TRACE = 0,
DEBUG = 1,
Expand Down
72 changes: 72 additions & 0 deletions src/BitcoinKernel.Interop/Enums/TxValidationResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
namespace BitcoinKernel.Interop.Enums;

/// <summary>
/// A granular "reason" why a transaction was invalid.
/// </summary>
public enum TxValidationResult : uint
{
/// <summary>
/// Initial value. Tx has not yet been rejected.
/// </summary>
UNSET = 0,

/// <summary>
/// Invalid by consensus rules.
/// </summary>
CONSENSUS = 1,

/// <summary>
/// Inputs (covered by txid) failed policy rules.
/// </summary>
INPUTS_NOT_STANDARD = 2,

/// <summary>
/// Otherwise didn't meet local policy rules.
/// </summary>
NOT_STANDARD = 3,

/// <summary>
/// Transaction was missing some of its inputs.
/// </summary>
MISSING_INPUTS = 4,

/// <summary>
/// Transaction spends a coinbase too early, or violates locktime/sequence locks.
/// </summary>
PREMATURE_SPEND = 5,

/// <summary>
/// Witness may have been malleated or is prior to SegWit activation.
/// </summary>
WITNESS_MUTATED = 6,

/// <summary>
/// Transaction is missing a witness.
/// </summary>
WITNESS_STRIPPED = 7,

/// <summary>
/// Tx already in mempool or conflicts with a tx in the chain.
/// </summary>
CONFLICT = 8,

/// <summary>
/// Violated mempool's fee/size/descendant/RBF/etc limits.
/// </summary>
MEMPOOL_POLICY = 9,

/// <summary>
/// This node does not have a mempool so can't validate the transaction.
/// </summary>
NO_MEMPOOL = 10,

/// <summary>
/// Fails some policy, but might be acceptable if submitted in a (different) package.
/// </summary>
RECONSIDERABLE = 11,

/// <summary>
/// Transaction was not validated because package failed.
/// </summary>
UNKNOWN = 12
}
95 changes: 90 additions & 5 deletions src/BitcoinKernel.Interop/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ static NativeMethods()
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_parameters_destroy")]
public static extern void ChainParametersDestroy(IntPtr chain_params);

/// <summary>
/// Gets the consensus parameters from chain parameters. The returned pointer
/// is unowned and only valid for the lifetime of the chain parameters.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chain_parameters_get_consensus_params")]
public static extern IntPtr ChainParametersGetConsensusParams(IntPtr chain_parameters);

#endregion

#region Chainstate Manager
Expand Down Expand Up @@ -143,14 +150,14 @@ public static extern IntPtr ChainstateManagerGetBlockTreeEntryByHash(
public static extern IntPtr ChainstateManagerGetBestEntry(IntPtr manager);

/// <summary>
/// Processes and validates a block header.
/// Returns 0 on success.
/// Processes and validates a block header. Returns a newly-allocated
/// btck_BlockValidationState (owned by the caller) describing the outcome,
/// or IntPtr.Zero on failure.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_chainstate_manager_process_block_header")]
public static extern int ChainstateManagerProcessBlockHeader(
public static extern IntPtr ChainstateManagerProcessBlockHeader(
IntPtr manager,
IntPtr header,
IntPtr block_validation_state);
IntPtr header);

/// <summary>
/// Imports blocks from an array of file paths.
Expand Down Expand Up @@ -320,6 +327,26 @@ public static extern int BlockToBytes(
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_get_transaction_at")]
public static extern IntPtr BlockGetTransactionAt(IntPtr block, nuint index);

/// <summary>
/// Returns the ancestor of a block tree entry at the given height.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_tree_entry_get_ancestor")]
public static extern IntPtr BlockTreeEntryGetAncestor(IntPtr block_tree_entry, int height);

/// <summary>
/// Performs context-free validation checks on a block.
/// Runs base checks (size, coinbase, tx, sigops) plus optional POW and
/// merkle-root checks controlled by <paramref name="flags"/>. The
/// validation_state is updated in-place.
/// Returns 1 if the block passed the checks, 0 otherwise.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_check")]
public static extern int BlockCheck(
IntPtr block,
IntPtr consensus_params,
BlockCheckFlags flags,
IntPtr validation_state);

#endregion

#region BlockHash Operations
Expand Down Expand Up @@ -409,6 +436,15 @@ public static extern IntPtr BlockHeaderCreate(
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_destroy")]
public static extern void BlockHeaderDestroy(IntPtr header);

/// <summary>
/// Serializes a block header to 80 bytes.
/// Returns 0 on success.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_block_header_to_bytes")]
public static extern int BlockHeaderToBytes(
IntPtr header,
[MarshalAs(UnmanagedType.LPArray, SizeConst = 80)] byte[] output);

#endregion

#region Chain Operations
Expand Down Expand Up @@ -519,6 +555,20 @@ public static extern int TransactionToBytes(
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_output_destroy")]
public static extern void TransactionOutputDestroy(IntPtr output);

/// <summary>
/// Gets the nLockTime value of a transaction.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_get_locktime")]
public static extern uint TransactionGetLocktime(IntPtr transaction);

/// <summary>
/// Runs context-free consensus validation on a transaction.
/// The validation_state is reset on entry and updated in-place.
/// Returns 1 if valid, 0 if invalid.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_check")]
public static extern int TransactionCheck(IntPtr tx, IntPtr validation_state);

#endregion

#region PrecomputedTransactionData Operations
Expand Down Expand Up @@ -867,6 +917,12 @@ public static extern IntPtr TransactionSpentOutputsGetCoinAt(
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_input_destroy")]
public static extern void TransactionInputDestroy(IntPtr transaction_input);

/// <summary>
/// Gets the nSequence value of a transaction input.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_transaction_input_get_sequence")]
public static extern uint TransactionInputGetSequence(IntPtr transaction_input);

#endregion

#region TransactionOutPoint Operations
Expand Down Expand Up @@ -897,4 +953,33 @@ public static extern IntPtr TransactionSpentOutputsGetCoinAt(

#endregion

#region Tx Validation State

/// <summary>
/// Creates a new transaction validation state.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "btck_tx_validation_state_create")]
public static extern IntPtr TxValidationStateCreate();

/// <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 validation_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 validation_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 validation_state);

#endregion


}
15 changes: 8 additions & 7 deletions src/BitcoinKernel/Chain/ChainStateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,15 @@ public bool ProcessBlockHeader(BlockHeader header, out BlockValidationState vali
ThrowIfDisposed();
ArgumentNullException.ThrowIfNull(header);

using var state = new BlockValidationState();
int result = NativeMethods.ChainstateManagerProcessBlockHeader(
_handle,
header.Handle,
state.Handle);
// The native call returns a newly-allocated validation state (owned here).
var statePtr = NativeMethods.ChainstateManagerProcessBlockHeader(_handle, header.Handle);
if (statePtr == IntPtr.Zero)
{
throw new ChainstateManagerException("Failed to process block header");
}

validationState = state.Copy();
return result == 0;
validationState = new BlockValidationState(statePtr);
return validationState.ValidationMode == Interop.Enums.ValidationMode.VALID;
}

/// <summary>
Expand Down
35 changes: 29 additions & 6 deletions src/BitcoinKernel/Primitives/Block.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,18 @@ public int TransactionCount
}

/// <summary>
/// Gets the block hash.
/// Gets the block hash as a 32-byte array.
/// </summary>
public byte[] GetHash()
{
using var blockHash = GetBlockHash();
return blockHash.ToBytes();
}

/// <summary>
/// Gets the block hash as an owned <see cref="BlockHash"/> object.
/// </summary>
public BlockHash GetBlockHash()
{
ThrowIfDisposed();
var hashPtr = NativeMethods.BlockGetHash(_handle);
Expand All @@ -68,8 +77,22 @@ public byte[] GetHash()
throw new BlockException("Failed to get block hash");
}

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

/// <summary>
/// Creates an owned copy of this block.
/// </summary>
public Block Copy()
{
ThrowIfDisposed();
var copy = NativeMethods.BlockCopy(_handle);
if (copy == IntPtr.Zero)
{
throw new BlockException("Failed to copy block");
}

return new Block(copy);
}

/// <summary>
Expand All @@ -93,19 +116,19 @@ public BlockHeader GetHeader()
public byte[] ToBytes()
{
ThrowIfDisposed();
byte[]? result = null;
var result = new List<byte>();

NativeMethods.BlockToBytes(_handle, (data, size, userData) =>
{
unsafe
{
var span = new ReadOnlySpan<byte>((byte*)data, (int)size);
result = span.ToArray();
result.AddRange(span);
}
return 0;
}, IntPtr.Zero);

return result ?? Array.Empty<byte>();
return result.ToArray();
}

/// <summary>
Expand Down
Loading
Loading