Skip to content

Commit b53a26f

Browse files
authored
Add kernel bindings test handler and update solution files
- Introduced a new kernel bindings test handler for conformance testing. - Updated the solution file to include the new project. - Enhanced README with usage instructions and protocol details. - Implemented request and response handling per specification - Added build and test scripts for easier development and verification.
1 parent 613e645 commit b53a26f

10 files changed

Lines changed: 677 additions & 0 deletions

File tree

BitcoinKernel.NET.sln

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{B3
99
EndProject
1010
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
1111
EndProject
12+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{F8E63A4F-7D3E-4B2A-9C1D-8A5F6E9B3C2D}"
13+
EndProject
1214
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BitcoinKernel.Tests", "tests\BitcoinKernel.Tests\BitcoinKernel.Tests.csproj", "{BC90EFB4-1692-CBCC-EF52-778255F591E2}"
1315
EndProject
1416
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicUsage", "examples\BasicUsage\BasicUsage.csproj", "{0E2DDF4A-A1FE-5424-03EA-7A8E76751354}"
@@ -23,6 +25,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlockProcessing", "examples
2325
EndProject
2426
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BitcoinKernel.Core.Tests", "tests\BitcoinKernel.Core.Tests\BitcoinKernel.Core.Tests.csproj", "{267842B2-D915-4B9E-8448-F9B5816D4A0A}"
2527
EndProject
28+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "kernel-bindings-test-handler", "tools\kernel-bindings-test-handler\kernel-bindings-test-handler.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
29+
EndProject
2630
Global
2731
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2832
Debug|Any CPU = Debug|Any CPU
@@ -117,6 +121,18 @@ Global
117121
{267842B2-D915-4B9E-8448-F9B5816D4A0A}.Release|x64.Build.0 = Release|Any CPU
118122
{267842B2-D915-4B9E-8448-F9B5816D4A0A}.Release|x86.ActiveCfg = Release|Any CPU
119123
{267842B2-D915-4B9E-8448-F9B5816D4A0A}.Release|x86.Build.0 = Release|Any CPU
124+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
125+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
126+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.ActiveCfg = Debug|Any CPU
127+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.Build.0 = Debug|Any CPU
128+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x86.ActiveCfg = Debug|Any CPU
129+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x86.Build.0 = Debug|Any CPU
130+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU
131+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU
132+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.ActiveCfg = Release|Any CPU
133+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.Build.0 = Release|Any CPU
134+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.ActiveCfg = Release|Any CPU
135+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.Build.0 = Release|Any CPU
120136
EndGlobalSection
121137
GlobalSection(SolutionProperties) = preSolution
122138
HideSolutionNode = FALSE
@@ -129,6 +145,7 @@ Global
129145
{D6F509B1-C990-0533-2DD1-CFFBA7506249} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
130146
{23E19BC3-8829-42BE-BCB4-A2050BE04975} = {B36A84DF-456D-A817-6EDD-3EC3E7F6E11F}
131147
{267842B2-D915-4B9E-8448-F9B5816D4A0A} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
148+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890} = {F8E63A4F-7D3E-4B2A-9C1D-8A5F6E9B3C2D}
132149
EndGlobalSection
133150
GlobalSection(ExtensibilityGlobals) = postSolution
134151
SolutionGuid = {B999E480-512A-4C50-9574-4AECEAC73E1C}

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# BitcoinKernel.NET
22

33
.NET bindings and high-level library for [libbitcoinkernel](https://github.com/bitcoin/bitcoin/tree/master/src/kernel), providing access to Bitcoin Core's consensus and validation logic.
4+
5+
46
⚠️🚧 This library is still under contruction. ⚠️🚧
57

68
This library uses [libbitcoinkernel](https://github.com/bitcoin/bitcoin/tree/master/src/kernel) which is in an experimental state, do not use for production purposes.
@@ -68,6 +70,19 @@ Explore the [examples](examples/) directory for complete working samples:
6870
- **[BasicUsage](examples/BasicUsage/)** - Getting started with the high-level API
6971
- **[BlockProcessing](examples/BlockProcessing/)** - Block validation and chain management
7072

73+
## Tools
74+
75+
### Kernel Bindings Test Handler
76+
77+
A conformance test handler for Kernel bindings Test handler framework, see [tools/kernel-bindings-test-handler](tools/kernel-bindings-test-handler/) for details.
78+
79+
**Usage:**
80+
```bash
81+
dotnet run --project tools/kernel-bindings-test-handler
82+
```
83+
84+
The handler communicates via stdin/stdout and is designed for automated conformance testing.
85+
7186
## Building from Source
7287

7388
### Prerequisites
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
using BitcoinKernel.Core;
2+
using BitcoinKernel.Core.Abstractions;
3+
using BitcoinKernel.Core.Exceptions;
4+
using BitcoinKernel.Core.ScriptVerification;
5+
using BitcoinKernel.Interop.Enums;
6+
using BitcoinKernel.TestHandler.Protocol;
7+
8+
namespace BitcoinKernel.TestHandler.Handlers;
9+
10+
/// <summary>
11+
/// Handles script_pubkey.verify method requests.
12+
/// </summary>
13+
public class ScriptVerifyHandler
14+
{
15+
private readonly KernelContext _context;
16+
17+
public ScriptVerifyHandler(KernelContext context)
18+
{
19+
_context = context;
20+
}
21+
22+
/// <summary>
23+
/// Handles a script verification request.
24+
/// </summary>
25+
public Response Handle(string requestId, ScriptVerifyParams parameters)
26+
{
27+
try
28+
{
29+
// Parse input data
30+
var scriptPubKey = ScriptPubKey.FromHex(parameters.ScriptPubKeyHex);
31+
var transaction = Transaction.FromHex(parameters.TxHex);
32+
33+
// Parse spent outputs if provided
34+
var spentOutputs = new List<TxOut>();
35+
if (parameters.SpentOutputs != null && parameters.SpentOutputs.Any())
36+
{
37+
foreach (var output in parameters.SpentOutputs)
38+
{
39+
var outputScriptPubKey = ScriptPubKey.FromHex(output.ScriptPubKeyHex);
40+
spentOutputs.Add(new TxOut(outputScriptPubKey, output.Amount));
41+
}
42+
}
43+
44+
// Parse flags
45+
var flags = ParseFlags(parameters.Flags);
46+
47+
// Verify the script
48+
ScriptVerifier.VerifyScript(
49+
scriptPubKey,
50+
parameters.Amount,
51+
transaction,
52+
parameters.InputIndex,
53+
spentOutputs,
54+
flags
55+
);
56+
57+
// Success
58+
return new Response
59+
{
60+
Id = requestId,
61+
Success = new { }
62+
};
63+
}
64+
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "inputIndex")
65+
{
66+
return new Response
67+
{
68+
Id = requestId,
69+
Error = new ErrorResponse
70+
{
71+
Type = "ScriptVerify",
72+
Variant = "TxInputIndex"
73+
}
74+
};
75+
}
76+
catch (ScriptVerificationException ex)
77+
{
78+
return new Response
79+
{
80+
Id = requestId,
81+
Error = new ErrorResponse
82+
{
83+
Type = "ScriptVerify",
84+
Variant = MapScriptVerifyStatus(ex.Status)
85+
}
86+
};
87+
}
88+
catch (Exception
89+
#if DEBUG
90+
ex
91+
#endif
92+
)
93+
{
94+
// Log to stderr for debugging (can be disabled in production)
95+
#if DEBUG
96+
Console.Error.WriteLine($"Exception: {ex.GetType().Name}: {ex.Message}");
97+
Console.Error.WriteLine($"StackTrace: {ex.StackTrace}");
98+
#endif
99+
100+
// Generic error for unexpected exceptions
101+
return new Response
102+
{
103+
Id = requestId,
104+
Error = new ErrorResponse
105+
{
106+
Type = "ScriptVerify",
107+
Variant = "Invalid"
108+
}
109+
};
110+
}
111+
}
112+
113+
/// <summary>
114+
/// Parses flags from either uint or string format.
115+
/// </summary>
116+
private ScriptVerificationFlags ParseFlags(object? flags)
117+
{
118+
if (flags == null)
119+
return ScriptVerificationFlags.None;
120+
121+
// Handle numeric flags
122+
if (flags is uint or int or long)
123+
{
124+
return (ScriptVerificationFlags)Convert.ToUInt32(flags);
125+
}
126+
127+
// Handle System.Text.Json JsonElement
128+
if (flags.GetType().Name == "JsonElement")
129+
{
130+
var jsonElement = (System.Text.Json.JsonElement)flags;
131+
if (jsonElement.ValueKind == System.Text.Json.JsonValueKind.Number)
132+
{
133+
return (ScriptVerificationFlags)jsonElement.GetUInt32();
134+
}
135+
else if (jsonElement.ValueKind == System.Text.Json.JsonValueKind.String)
136+
{
137+
return ParseFlagString(jsonElement.GetString() ?? string.Empty);
138+
}
139+
}
140+
141+
// Handle string flags
142+
if (flags is string flagStr)
143+
{
144+
return ParseFlagString(flagStr);
145+
}
146+
147+
return ScriptVerificationFlags.None;
148+
}
149+
150+
/// <summary>
151+
/// Parses a string flag name to ScriptVerificationFlags.
152+
/// </summary>
153+
private ScriptVerificationFlags ParseFlagString(string flagStr)
154+
{
155+
return flagStr.ToUpperInvariant() switch
156+
{
157+
"VERIFY_NONE" or "NONE" => ScriptVerificationFlags.None,
158+
"VERIFY_P2SH" or "P2SH" => ScriptVerificationFlags.P2SH,
159+
"VERIFY_DERSIG" or "DERSIG" => ScriptVerificationFlags.DerSig,
160+
"VERIFY_NULLDUMMY" or "NULLDUMMY" => ScriptVerificationFlags.NullDummy,
161+
"VERIFY_CHECKLOCKTIMEVERIFY" or "CHECKLOCKTIMEVERIFY" => ScriptVerificationFlags.CheckLockTimeVerify,
162+
"VERIFY_CHECKSEQUENCEVERIFY" or "CHECKSEQUENCEVERIFY" => ScriptVerificationFlags.CheckSequenceVerify,
163+
"VERIFY_WITNESS" or "WITNESS" => ScriptVerificationFlags.Witness,
164+
"VERIFY_TAPROOT" or "TAPROOT" => ScriptVerificationFlags.Taproot,
165+
"VERIFY_ALL" or "ALL" => ScriptVerificationFlags.All,
166+
"VERIFY_ALL_PRE_TAPROOT" or "ALL_PRE_TAPROOT" => ScriptVerificationFlags.AllPreTaproot,
167+
_ => throw new ArgumentException($"Unknown flag: {flagStr}")
168+
};
169+
}
170+
171+
/// <summary>
172+
/// Maps ScriptVerifyStatus to error variant strings.
173+
/// </summary>
174+
private string MapScriptVerifyStatus(ScriptVerifyStatus status)
175+
{
176+
return status switch
177+
{
178+
ScriptVerifyStatus.ERROR_TX_INPUT_INDEX => "TxInputIndex",
179+
ScriptVerifyStatus.ERROR_INVALID_FLAGS => "InvalidFlags",
180+
ScriptVerifyStatus.ERROR_INVALID_FLAGS_COMBINATION => "InvalidFlagsCombination",
181+
ScriptVerifyStatus.ERROR_SPENT_OUTPUTS_MISMATCH => "SpentOutputsMismatch",
182+
ScriptVerifyStatus.ERROR_SPENT_OUTPUTS_REQUIRED => "SpentOutputsRequired",
183+
_ => "Invalid"
184+
};
185+
}
186+
}

0 commit comments

Comments
 (0)