From 92f5e3525d9c8150d5bb2f0461ee4670c73dda68 Mon Sep 17 00:00:00 2001 From: jan Date: Thu, 27 Nov 2025 12:21:52 +0100 Subject: [PATCH 1/4] Refactor script verification handling / json parsing to use new specifications of Kernel-bindings-handler --- .../Handlers/ScriptVerifyHandler.cs | 92 +++++++++++++------ tools/kernel-bindings-test-handler/Program.cs | 38 ++++++-- .../Protocol/Request.cs | 10 +- .../Protocol/Response.cs | 13 ++- 4 files changed, 109 insertions(+), 44 deletions(-) diff --git a/tools/kernel-bindings-test-handler/Handlers/ScriptVerifyHandler.cs b/tools/kernel-bindings-test-handler/Handlers/ScriptVerifyHandler.cs index e55a479..ec79cf9 100644 --- a/tools/kernel-bindings-test-handler/Handlers/ScriptVerifyHandler.cs +++ b/tools/kernel-bindings-test-handler/Handlers/ScriptVerifyHandler.cs @@ -22,7 +22,7 @@ public ScriptVerifyHandler(KernelContext context) /// /// Handles a script verification request. /// - public Response Handle(string requestId, ScriptVerifyParams parameters) + public Response Handle(string requestId, BtckScriptPubkeyVerifyParams parameters) { try { @@ -37,7 +37,7 @@ public Response Handle(string requestId, ScriptVerifyParams parameters) foreach (var output in parameters.SpentOutputs) { var outputScriptPubKey = ScriptPubKey.FromHex(output.ScriptPubKeyHex); - spentOutputs.Add(new TxOut(outputScriptPubKey, output.Amount)); + spentOutputs.Add(new TxOut(outputScriptPubKey, output.Value)); } } @@ -58,7 +58,7 @@ public Response Handle(string requestId, ScriptVerifyParams parameters) return new Response { Id = requestId, - Success = new { } + Success = true }; } catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "inputIndex") @@ -66,10 +66,14 @@ public Response Handle(string requestId, ScriptVerifyParams parameters) return new Response { Id = requestId, + Success = false, Error = new ErrorResponse { - Type = "ScriptVerify", - Variant = "TxInputIndex" + Code = new ErrorCode + { + Type = "btck_ScriptVerifyStatus", + Member = "TxInputIndex" + } } }; } @@ -78,10 +82,14 @@ public Response Handle(string requestId, ScriptVerifyParams parameters) return new Response { Id = requestId, + Success = false, Error = new ErrorResponse { - Type = "ScriptVerify", - Variant = MapScriptVerifyStatus(ex.Status) + Code = new ErrorCode + { + Type = "btck_ScriptVerifyStatus", + Member = MapScriptVerifyStatus(ex.Status) + } } }; } @@ -101,10 +109,14 @@ public Response Handle(string requestId, ScriptVerifyParams parameters) return new Response { Id = requestId, + Success = false, Error = new ErrorResponse { - Type = "ScriptVerify", - Variant = "Invalid" + Code = new ErrorCode + { + Type = "btck_ScriptVerifyStatus", + Member = "Invalid" + } } }; } @@ -132,9 +144,18 @@ private ScriptVerificationFlags ParseFlags(object? flags) { return (ScriptVerificationFlags)jsonElement.GetUInt32(); } - else if (jsonElement.ValueKind == System.Text.Json.JsonValueKind.String) + else if (jsonElement.ValueKind == System.Text.Json.JsonValueKind.Array) { - return ParseFlagString(jsonElement.GetString() ?? string.Empty); + // Handle array of string flags - combine them with OR + ScriptVerificationFlags combinedFlags = ScriptVerificationFlags.None; + foreach (var element in jsonElement.EnumerateArray()) + { + if (element.ValueKind == System.Text.Json.JsonValueKind.String) + { + combinedFlags |= ParseFlagString(element.GetString() ?? string.Empty); + } + } + return combinedFlags; } } @@ -152,18 +173,35 @@ private ScriptVerificationFlags ParseFlags(object? flags) /// private ScriptVerificationFlags ParseFlagString(string flagStr) { + // Handle btck_ prefixed format (e.g., "btck_ScriptVerificationFlags_WITNESS") + if (flagStr.StartsWith("btck_ScriptVerificationFlags_", StringComparison.OrdinalIgnoreCase)) + { + flagStr = flagStr.Substring("btck_ScriptVerificationFlags_".Length); + } + return flagStr.ToUpperInvariant() switch { - "VERIFY_NONE" or "NONE" => ScriptVerificationFlags.None, - "VERIFY_P2SH" or "P2SH" => ScriptVerificationFlags.P2SH, - "VERIFY_DERSIG" or "DERSIG" => ScriptVerificationFlags.DerSig, - "VERIFY_NULLDUMMY" or "NULLDUMMY" => ScriptVerificationFlags.NullDummy, - "VERIFY_CHECKLOCKTIMEVERIFY" or "CHECKLOCKTIMEVERIFY" => ScriptVerificationFlags.CheckLockTimeVerify, - "VERIFY_CHECKSEQUENCEVERIFY" or "CHECKSEQUENCEVERIFY" => ScriptVerificationFlags.CheckSequenceVerify, - "VERIFY_WITNESS" or "WITNESS" => ScriptVerificationFlags.Witness, - "VERIFY_TAPROOT" or "TAPROOT" => ScriptVerificationFlags.Taproot, - "VERIFY_ALL" or "ALL" => ScriptVerificationFlags.All, - "VERIFY_ALL_PRE_TAPROOT" or "ALL_PRE_TAPROOT" => ScriptVerificationFlags.AllPreTaproot, + "NONE" => ScriptVerificationFlags.None, + "P2SH" => ScriptVerificationFlags.P2SH, + "DERSIG" => ScriptVerificationFlags.DerSig, + "NULLDUMMY" => ScriptVerificationFlags.NullDummy, + "CHECKLOCKTIMEVERIFY" => ScriptVerificationFlags.CheckLockTimeVerify, + "CHECKSEQUENCEVERIFY" => ScriptVerificationFlags.CheckSequenceVerify, + "WITNESS" => ScriptVerificationFlags.Witness, + "TAPROOT" => ScriptVerificationFlags.Taproot, + "ALL" => ScriptVerificationFlags.All, + "ALL_PRE_TAPROOT" => ScriptVerificationFlags.AllPreTaproot, + // Legacy format support + "VERIFY_NONE" => ScriptVerificationFlags.None, + "VERIFY_P2SH" => ScriptVerificationFlags.P2SH, + "VERIFY_DERSIG" => ScriptVerificationFlags.DerSig, + "VERIFY_NULLDUMMY" => ScriptVerificationFlags.NullDummy, + "VERIFY_CHECKLOCKTIMEVERIFY" => ScriptVerificationFlags.CheckLockTimeVerify, + "VERIFY_CHECKSEQUENCEVERIFY" => ScriptVerificationFlags.CheckSequenceVerify, + "VERIFY_WITNESS" => ScriptVerificationFlags.Witness, + "VERIFY_TAPROOT" => ScriptVerificationFlags.Taproot, + "VERIFY_ALL" => ScriptVerificationFlags.All, + "VERIFY_ALL_PRE_TAPROOT" => ScriptVerificationFlags.AllPreTaproot, _ => throw new ArgumentException($"Unknown flag: {flagStr}") }; } @@ -175,12 +213,12 @@ private string MapScriptVerifyStatus(ScriptVerifyStatus status) { return status switch { - ScriptVerifyStatus.ERROR_TX_INPUT_INDEX => "TxInputIndex", - ScriptVerifyStatus.ERROR_INVALID_FLAGS => "InvalidFlags", - ScriptVerifyStatus.ERROR_INVALID_FLAGS_COMBINATION => "InvalidFlagsCombination", - ScriptVerifyStatus.ERROR_SPENT_OUTPUTS_MISMATCH => "SpentOutputsMismatch", - ScriptVerifyStatus.ERROR_SPENT_OUTPUTS_REQUIRED => "SpentOutputsRequired", - _ => "Invalid" + ScriptVerifyStatus.ERROR_TX_INPUT_INDEX => "ERROR_TX_INPUT_INDEX", + ScriptVerifyStatus.ERROR_INVALID_FLAGS => "ERROR_INVALID_FLAGS", + ScriptVerifyStatus.ERROR_INVALID_FLAGS_COMBINATION => "ERROR_INVALID_FLAGS_COMBINATION", + ScriptVerifyStatus.ERROR_SPENT_OUTPUTS_MISMATCH => "ERROR_SPENT_OUTPUTS_MISMATCH", + ScriptVerifyStatus.ERROR_SPENT_OUTPUTS_REQUIRED => "ERROR_SPENT_OUTPUTS_REQUIRED", + _ => "ERROR_INVALID" }; } } diff --git a/tools/kernel-bindings-test-handler/Program.cs b/tools/kernel-bindings-test-handler/Program.cs index ccd89e1..8c4d7ae 100644 --- a/tools/kernel-bindings-test-handler/Program.cs +++ b/tools/kernel-bindings-test-handler/Program.cs @@ -46,7 +46,10 @@ static async Task Main(string[] args) Id = "unknown", Error = new ErrorResponse { - Type = "InvalidRequest" + Code = new ErrorCode + { + Type = "InvalidRequest" + } } }; } @@ -62,7 +65,10 @@ static async Task Main(string[] args) Id = "unknown", Error = new ErrorResponse { - Type = "InvalidRequest" + Code = new ErrorCode + { + Type = "InvalidRequest" + } } }; } @@ -91,7 +97,7 @@ private static Response HandleRequest(Request request, ScriptVerifyHandler scrip { switch (request.Method) { - case "script_pubkey.verify": + case "btck_script_pubkey_verify": if (request.Params == null) { return new Response @@ -99,26 +105,32 @@ private static Response HandleRequest(Request request, ScriptVerifyHandler scrip Id = request.Id, Error = new ErrorResponse { - Type = "InvalidParams" + Code = new ErrorCode + { + Type = "InvalidParams" + } } }; } - var scriptVerifyParams = JsonSerializer.Deserialize(request.Params.Value, jsonOptions); + var btckScriptPubkeyVerifyParams = JsonSerializer.Deserialize(request.Params.Value, jsonOptions); - if (scriptVerifyParams == null) + if (btckScriptPubkeyVerifyParams == null) { return new Response { Id = request.Id, Error = new ErrorResponse { - Type = "InvalidParams" + Code = new ErrorCode + { + Type = "InvalidParams" + } } }; } - return scriptVerifyHandler.Handle(request.Id, scriptVerifyParams); + return scriptVerifyHandler.Handle(request.Id, btckScriptPubkeyVerifyParams); default: return new Response @@ -126,7 +138,10 @@ private static Response HandleRequest(Request request, ScriptVerifyHandler scrip Id = request.Id, Error = new ErrorResponse { - Type = "MethodNotFound" + Code = new ErrorCode + { + Type = "MethodNotFound" + } } }; } @@ -138,7 +153,10 @@ private static Response HandleRequest(Request request, ScriptVerifyHandler scrip Id = request.Id, Error = new ErrorResponse { - Type = "InternalError" + Code = new ErrorCode + { + Type = "InternalError" + } } }; } diff --git a/tools/kernel-bindings-test-handler/Protocol/Request.cs b/tools/kernel-bindings-test-handler/Protocol/Request.cs index 0d75185..8f344ec 100644 --- a/tools/kernel-bindings-test-handler/Protocol/Request.cs +++ b/tools/kernel-bindings-test-handler/Protocol/Request.cs @@ -19,9 +19,9 @@ public class Request } /// -/// Parameters for script_pubkey.verify method. +/// Parameters for btck_script_pubkey_verify method. /// -public class ScriptVerifyParams +public class BtckScriptPubkeyVerifyParams { [JsonPropertyName("script_pubkey_hex")] public string ScriptPubKeyHex { get; set; } = string.Empty; @@ -39,7 +39,7 @@ public class ScriptVerifyParams public List? SpentOutputs { get; set; } [JsonPropertyName("flags")] - public object? Flags { get; set; } // Can be uint or string + public JsonElement? Flags { get; set; } // Can be uint, or array } /// @@ -50,6 +50,6 @@ public class SpentOutput [JsonPropertyName("script_pubkey_hex")] public string ScriptPubKeyHex { get; set; } = string.Empty; - [JsonPropertyName("amount")] - public long Amount { get; set; } + [JsonPropertyName("value")] + public long Value { get; set; } } diff --git a/tools/kernel-bindings-test-handler/Protocol/Response.cs b/tools/kernel-bindings-test-handler/Protocol/Response.cs index 1c49f2a..48838e5 100644 --- a/tools/kernel-bindings-test-handler/Protocol/Response.cs +++ b/tools/kernel-bindings-test-handler/Protocol/Response.cs @@ -23,11 +23,20 @@ public class Response /// Represents an error response. /// public class ErrorResponse +{ + [JsonPropertyName("code")] + public ErrorCode Code { get; set; } = new(); +} + +/// +/// Represents an error code with type and member information. +/// +public class ErrorCode { [JsonPropertyName("type")] public string Type { get; set; } = string.Empty; - [JsonPropertyName("variant")] + [JsonPropertyName("member")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? Variant { get; set; } + public string? Member { get; set; } } From ffc3c5871ccdb3a146f20c626c4ab0ed7191af6a Mon Sep 17 00:00:00 2001 From: jan Date: Tue, 2 Dec 2025 13:59:46 +0100 Subject: [PATCH 2/4] Refactor Response class to replace 'Success' property with 'Result' for consistency in response handling --- .../Handlers/ScriptVerifyHandler.cs | 43 ++++++------------- .../Protocol/Request.cs | 8 ++-- .../Protocol/Response.cs | 4 +- 3 files changed, 18 insertions(+), 37 deletions(-) diff --git a/tools/kernel-bindings-test-handler/Handlers/ScriptVerifyHandler.cs b/tools/kernel-bindings-test-handler/Handlers/ScriptVerifyHandler.cs index ec79cf9..89c3adc 100644 --- a/tools/kernel-bindings-test-handler/Handlers/ScriptVerifyHandler.cs +++ b/tools/kernel-bindings-test-handler/Handlers/ScriptVerifyHandler.cs @@ -58,7 +58,7 @@ public Response Handle(string requestId, BtckScriptPubkeyVerifyParams parameters return new Response { Id = requestId, - Success = true + Result = true }; } catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "inputIndex") @@ -66,7 +66,7 @@ public Response Handle(string requestId, BtckScriptPubkeyVerifyParams parameters return new Response { Id = requestId, - Success = false, + Result = null, Error = new ErrorResponse { Code = new ErrorCode @@ -79,43 +79,27 @@ public Response Handle(string requestId, BtckScriptPubkeyVerifyParams parameters } catch (ScriptVerificationException ex) { - return new Response + // If status is OK, the script just failed verification (result: false) + // If status is not OK, it's an actual error condition + if (ex.Status == ScriptVerifyStatus.OK) { - Id = requestId, - Success = false, - Error = new ErrorResponse + return new Response { - Code = new ErrorCode - { - Type = "btck_ScriptVerifyStatus", - Member = MapScriptVerifyStatus(ex.Status) - } - } - }; - } - catch (Exception -#if DEBUG - ex -#endif - ) - { - // Log to stderr for debugging (can be disabled in production) -#if DEBUG - Console.Error.WriteLine($"Exception: {ex.GetType().Name}: {ex.Message}"); - Console.Error.WriteLine($"StackTrace: {ex.StackTrace}"); -#endif + Id = requestId, + Result = false + }; + } - // Generic error for unexpected exceptions return new Response { Id = requestId, - Success = false, + Result = null, Error = new ErrorResponse { Code = new ErrorCode { Type = "btck_ScriptVerifyStatus", - Member = "Invalid" + Member = MapScriptVerifyStatus(ex.Status) } } }; @@ -213,10 +197,7 @@ private string MapScriptVerifyStatus(ScriptVerifyStatus status) { return status switch { - ScriptVerifyStatus.ERROR_TX_INPUT_INDEX => "ERROR_TX_INPUT_INDEX", - ScriptVerifyStatus.ERROR_INVALID_FLAGS => "ERROR_INVALID_FLAGS", ScriptVerifyStatus.ERROR_INVALID_FLAGS_COMBINATION => "ERROR_INVALID_FLAGS_COMBINATION", - ScriptVerifyStatus.ERROR_SPENT_OUTPUTS_MISMATCH => "ERROR_SPENT_OUTPUTS_MISMATCH", ScriptVerifyStatus.ERROR_SPENT_OUTPUTS_REQUIRED => "ERROR_SPENT_OUTPUTS_REQUIRED", _ => "ERROR_INVALID" }; diff --git a/tools/kernel-bindings-test-handler/Protocol/Request.cs b/tools/kernel-bindings-test-handler/Protocol/Request.cs index 8f344ec..79f9cd8 100644 --- a/tools/kernel-bindings-test-handler/Protocol/Request.cs +++ b/tools/kernel-bindings-test-handler/Protocol/Request.cs @@ -23,13 +23,13 @@ public class Request /// public class BtckScriptPubkeyVerifyParams { - [JsonPropertyName("script_pubkey_hex")] + [JsonPropertyName("script_pubkey")] public string ScriptPubKeyHex { get; set; } = string.Empty; [JsonPropertyName("amount")] public long Amount { get; set; } - [JsonPropertyName("tx_hex")] + [JsonPropertyName("tx_to")] public string TxHex { get; set; } = string.Empty; [JsonPropertyName("input_index")] @@ -47,9 +47,9 @@ public class BtckScriptPubkeyVerifyParams /// public class SpentOutput { - [JsonPropertyName("script_pubkey_hex")] + [JsonPropertyName("script_pubkey")] public string ScriptPubKeyHex { get; set; } = string.Empty; - [JsonPropertyName("value")] + [JsonPropertyName("amount")] public long Value { get; set; } } diff --git a/tools/kernel-bindings-test-handler/Protocol/Response.cs b/tools/kernel-bindings-test-handler/Protocol/Response.cs index 48838e5..39d7e5a 100644 --- a/tools/kernel-bindings-test-handler/Protocol/Response.cs +++ b/tools/kernel-bindings-test-handler/Protocol/Response.cs @@ -10,9 +10,9 @@ public class Response [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; - [JsonPropertyName("success")] + [JsonPropertyName("result")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public object? Success { get; set; } + public object? Result { get; set; } [JsonPropertyName("error")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] From 3a6a6fad8cf3ce0d90148595b3ba6978c21affbe Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 3 Dec 2025 09:33:56 +0100 Subject: [PATCH 3/4] Update CI configuration kernel_bindings_tests to version 0.0.2 and modify ErrorCode class to ensure Member is always initialized --- .github/workflows/ci.yml | 2 +- tools/kernel-bindings-test-handler/Protocol/Response.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c7d2b1..1264b21 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,7 +96,7 @@ jobs: platform: linux_amd64 env: - TEST_VERSION: '0.0.1' + TEST_VERSION: '0.0.2' TEST_REPO: 'stringintech/kernel-bindings-tests' TEST_DIR: '.conformance-tests' diff --git a/tools/kernel-bindings-test-handler/Protocol/Response.cs b/tools/kernel-bindings-test-handler/Protocol/Response.cs index 39d7e5a..e75f655 100644 --- a/tools/kernel-bindings-test-handler/Protocol/Response.cs +++ b/tools/kernel-bindings-test-handler/Protocol/Response.cs @@ -37,6 +37,5 @@ public class ErrorCode public string Type { get; set; } = string.Empty; [JsonPropertyName("member")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? Member { get; set; } + public string Member { get; set; } = string.Empty; } From 22a6dc76df065ec3382026c615e063a16133a370 Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 3 Dec 2025 09:36:55 +0100 Subject: [PATCH 4/4] Remove legacy script verification flag support from ScriptVerifyHandler --- .../Handlers/ScriptVerifyHandler.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tools/kernel-bindings-test-handler/Handlers/ScriptVerifyHandler.cs b/tools/kernel-bindings-test-handler/Handlers/ScriptVerifyHandler.cs index 89c3adc..7e0c29f 100644 --- a/tools/kernel-bindings-test-handler/Handlers/ScriptVerifyHandler.cs +++ b/tools/kernel-bindings-test-handler/Handlers/ScriptVerifyHandler.cs @@ -175,17 +175,6 @@ private ScriptVerificationFlags ParseFlagString(string flagStr) "TAPROOT" => ScriptVerificationFlags.Taproot, "ALL" => ScriptVerificationFlags.All, "ALL_PRE_TAPROOT" => ScriptVerificationFlags.AllPreTaproot, - // Legacy format support - "VERIFY_NONE" => ScriptVerificationFlags.None, - "VERIFY_P2SH" => ScriptVerificationFlags.P2SH, - "VERIFY_DERSIG" => ScriptVerificationFlags.DerSig, - "VERIFY_NULLDUMMY" => ScriptVerificationFlags.NullDummy, - "VERIFY_CHECKLOCKTIMEVERIFY" => ScriptVerificationFlags.CheckLockTimeVerify, - "VERIFY_CHECKSEQUENCEVERIFY" => ScriptVerificationFlags.CheckSequenceVerify, - "VERIFY_WITNESS" => ScriptVerificationFlags.Witness, - "VERIFY_TAPROOT" => ScriptVerificationFlags.Taproot, - "VERIFY_ALL" => ScriptVerificationFlags.All, - "VERIFY_ALL_PRE_TAPROOT" => ScriptVerificationFlags.AllPreTaproot, _ => throw new ArgumentException($"Unknown flag: {flagStr}") }; }