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
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.1'
TEST_VERSION: '0.0.2'
TEST_REPO: 'stringintech/kernel-bindings-tests'
TEST_DIR: '.conformance-tests'

Expand Down
98 changes: 53 additions & 45 deletions tools/kernel-bindings-test-handler/Handlers/ScriptVerifyHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public ScriptVerifyHandler(KernelContext context)
/// <summary>
/// Handles a script verification request.
/// </summary>
public Response Handle(string requestId, ScriptVerifyParams parameters)
public Response Handle(string requestId, BtckScriptPubkeyVerifyParams parameters)
{
try
{
Expand All @@ -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));
}
}

Expand All @@ -58,53 +58,49 @@ public Response Handle(string requestId, ScriptVerifyParams parameters)
return new Response
{
Id = requestId,
Success = new { }
Result = true
};
}
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "inputIndex")
{
return new Response
{
Id = requestId,
Result = null,
Error = new ErrorResponse
{
Type = "ScriptVerify",
Variant = "TxInputIndex"
Code = new ErrorCode
{
Type = "btck_ScriptVerifyStatus",
Member = "TxInputIndex"
}
}
};
}
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,
Error = new ErrorResponse
return new Response
{
Type = "ScriptVerify",
Variant = 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,
Result = null,
Error = new ErrorResponse
{
Type = "ScriptVerify",
Variant = "Invalid"
Code = new ErrorCode
{
Type = "btck_ScriptVerifyStatus",
Member = MapScriptVerifyStatus(ex.Status)
}
}
};
}
Expand Down Expand Up @@ -132,9 +128,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;
}
}

Expand All @@ -152,18 +157,24 @@ private ScriptVerificationFlags ParseFlags(object? flags)
/// </summary>
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,
_ => throw new ArgumentException($"Unknown flag: {flagStr}")
};
}
Expand All @@ -175,12 +186,9 @@ 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_INVALID_FLAGS_COMBINATION => "ERROR_INVALID_FLAGS_COMBINATION",
ScriptVerifyStatus.ERROR_SPENT_OUTPUTS_REQUIRED => "ERROR_SPENT_OUTPUTS_REQUIRED",
_ => "ERROR_INVALID"
};
}
}
38 changes: 28 additions & 10 deletions tools/kernel-bindings-test-handler/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ static async Task<int> Main(string[] args)
Id = "unknown",
Error = new ErrorResponse
{
Type = "InvalidRequest"
Code = new ErrorCode
{
Type = "InvalidRequest"
}
}
};
}
Expand All @@ -62,7 +65,10 @@ static async Task<int> Main(string[] args)
Id = "unknown",
Error = new ErrorResponse
{
Type = "InvalidRequest"
Code = new ErrorCode
{
Type = "InvalidRequest"
}
}
};
}
Expand Down Expand Up @@ -91,42 +97,51 @@ 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
{
Id = request.Id,
Error = new ErrorResponse
{
Type = "InvalidParams"
Code = new ErrorCode
{
Type = "InvalidParams"
}
}
};
}

var scriptVerifyParams = JsonSerializer.Deserialize<ScriptVerifyParams>(request.Params.Value, jsonOptions);
var btckScriptPubkeyVerifyParams = JsonSerializer.Deserialize<BtckScriptPubkeyVerifyParams>(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
{
Id = request.Id,
Error = new ErrorResponse
{
Type = "MethodNotFound"
Code = new ErrorCode
{
Type = "MethodNotFound"
}
}
};
}
Expand All @@ -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"
}
}
};
}
Expand Down
14 changes: 7 additions & 7 deletions tools/kernel-bindings-test-handler/Protocol/Request.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ public class Request
}

/// <summary>
/// Parameters for script_pubkey.verify method.
/// Parameters for btck_script_pubkey_verify method.
/// </summary>
public class ScriptVerifyParams
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")]
Expand All @@ -39,17 +39,17 @@ public class ScriptVerifyParams
public List<SpentOutput>? 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
}

/// <summary>
/// Represents a spent output.
/// </summary>
public class SpentOutput
{
[JsonPropertyName("script_pubkey_hex")]
[JsonPropertyName("script_pubkey")]
public string ScriptPubKeyHex { get; set; } = string.Empty;

[JsonPropertyName("amount")]
public long Amount { get; set; }
public long Value { get; set; }
}
18 changes: 13 additions & 5 deletions tools/kernel-bindings-test-handler/Protocol/Response.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -23,11 +23,19 @@ public class Response
/// Represents an error response.
/// </summary>
public class ErrorResponse
{
[JsonPropertyName("code")]
public ErrorCode Code { get; set; } = new();
}

/// <summary>
/// Represents an error code with type and member information.
/// </summary>
public class ErrorCode
{
[JsonPropertyName("type")]
public string Type { get; set; } = string.Empty;

[JsonPropertyName("variant")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Variant { get; set; }
[JsonPropertyName("member")]
public string Member { get; set; } = string.Empty;
}
Loading