From 1448027609f37505ce4e241e7b69b48406dc40e4 Mon Sep 17 00:00:00 2001 From: stringintech Date: Wed, 26 Nov 2025 16:13:41 +0330 Subject: [PATCH 1/6] Use explicit flag arrays instead of "VERIFY_ALL_PRE_TAPROOT" This eliminates usage of the "VERIFY_ALL_PRE_TAPROOT" flag which is not exported from `libbitcoinkernel` --- testdata/script_verify_errors.json | 49 ++++++++++++++++++++++++----- testdata/script_verify_success.json | 27 ++++++++++++++-- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/testdata/script_verify_errors.json b/testdata/script_verify_errors.json index 638a3cb..275d7ec 100644 --- a/testdata/script_verify_errors.json +++ b/testdata/script_verify_errors.json @@ -11,7 +11,14 @@ "amount": 0, "tx_hex": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", "input_index": 0, - "flags": "VERIFY_ALL_PRE_TAPROOT", + "flags": [ + "btck_ScriptVerificationFlags_P2SH", + "btck_ScriptVerificationFlags_DERSIG", + "btck_ScriptVerificationFlags_NULLDUMMY", + "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", + "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", + "btck_ScriptVerificationFlags_WITNESS" + ], "spent_outputs": [] }, "expected": { @@ -30,7 +37,14 @@ "amount": 900000, "tx_hex": "01000000000101d9fd94d0ff0026d307c994d0003180a5f248146efb6371d040c5973f5f66d9df0400000017160014b31b31a6cb654cfab3c50567bcf124f48a0beaecffffffff012cbd1c000000000017a914233b74bf0823fa58bbbd26dfc3bb4ae715547167870247304402206f60569cac136c114a58aedd80f6fa1c51b49093e7af883e605c212bdafcd8d202200e91a55f408a021ad2631bc29a67bd6915b2d7e9ef0265627eabd7f7234455f6012103e7e802f50344303c76d12c089c8724c1b230e3b745693bbe16aad536293d15e300000000", "input_index": 0, - "flags": "VERIFY_ALL_PRE_TAPROOT", + "flags": [ + "btck_ScriptVerificationFlags_P2SH", + "btck_ScriptVerificationFlags_DERSIG", + "btck_ScriptVerificationFlags_NULLDUMMY", + "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", + "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", + "btck_ScriptVerificationFlags_WITNESS" + ], "spent_outputs": [] }, "expected": { @@ -49,7 +63,14 @@ "amount": 18393430, "tx_hex": "010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000", "input_index": 0, - "flags": "VERIFY_ALL_PRE_TAPROOT", + "flags": [ + "btck_ScriptVerificationFlags_P2SH", + "btck_ScriptVerificationFlags_DERSIG", + "btck_ScriptVerificationFlags_NULLDUMMY", + "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", + "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", + "btck_ScriptVerificationFlags_WITNESS" + ], "spent_outputs": [] }, "expected": { @@ -68,7 +89,14 @@ "amount": 0, "tx_hex": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", "input_index": 999, - "flags": "VERIFY_ALL_PRE_TAPROOT", + "flags": [ + "btck_ScriptVerificationFlags_P2SH", + "btck_ScriptVerificationFlags_DERSIG", + "btck_ScriptVerificationFlags_NULLDUMMY", + "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", + "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", + "btck_ScriptVerificationFlags_WITNESS" + ], "spent_outputs": [ { "script_pubkey_hex": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", @@ -92,7 +120,14 @@ "amount": 0, "tx_hex": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", "input_index": 0, - "flags": "VERIFY_ALL_PRE_TAPROOT", + "flags": [ + "btck_ScriptVerificationFlags_P2SH", + "btck_ScriptVerificationFlags_DERSIG", + "btck_ScriptVerificationFlags_NULLDUMMY", + "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", + "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", + "btck_ScriptVerificationFlags_WITNESS" + ], "spent_outputs": [ { "script_pubkey_hex": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", @@ -144,7 +179,7 @@ "amount": 0, "tx_hex": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", "input_index": 0, - "flags": "VERIFY_WITNESS", + "flags": ["btck_ScriptVerificationFlags_WITNESS"], "spent_outputs": [ { "script_pubkey_hex": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", @@ -168,7 +203,7 @@ "amount": 0, "tx_hex": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", "input_index": 0, - "flags": "VERIFY_TAPROOT", + "flags": ["btck_ScriptVerificationFlags_TAPROOT"], "spent_outputs": [] }, "expected": { diff --git a/testdata/script_verify_success.json b/testdata/script_verify_success.json index f6a3e53..150dec8 100644 --- a/testdata/script_verify_success.json +++ b/testdata/script_verify_success.json @@ -11,7 +11,14 @@ "amount": 0, "tx_hex": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", "input_index": 0, - "flags": "VERIFY_ALL_PRE_TAPROOT", + "flags": [ + "btck_ScriptVerificationFlags_P2SH", + "btck_ScriptVerificationFlags_DERSIG", + "btck_ScriptVerificationFlags_NULLDUMMY", + "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", + "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", + "btck_ScriptVerificationFlags_WITNESS" + ], "spent_outputs": [] }, "expected": { @@ -27,7 +34,14 @@ "amount": 1900000, "tx_hex": "01000000000101d9fd94d0ff0026d307c994d0003180a5f248146efb6371d040c5973f5f66d9df0400000017160014b31b31a6cb654cfab3c50567bcf124f48a0beaecffffffff012cbd1c000000000017a914233b74bf0823fa58bbbd26dfc3bb4ae715547167870247304402206f60569cac136c114a58aedd80f6fa1c51b49093e7af883e605c212bdafcd8d202200e91a55f408a021ad2631bc29a67bd6915b2d7e9ef0265627eabd7f7234455f6012103e7e802f50344303c76d12c089c8724c1b230e3b745693bbe16aad536293d15e300000000", "input_index": 0, - "flags": "VERIFY_ALL_PRE_TAPROOT", + "flags": [ + "btck_ScriptVerificationFlags_P2SH", + "btck_ScriptVerificationFlags_DERSIG", + "btck_ScriptVerificationFlags_NULLDUMMY", + "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", + "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", + "btck_ScriptVerificationFlags_WITNESS" + ], "spent_outputs": [] }, "expected": { @@ -43,7 +57,14 @@ "amount": 18393430, "tx_hex": "010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000", "input_index": 0, - "flags": "VERIFY_ALL_PRE_TAPROOT", + "flags": [ + "btck_ScriptVerificationFlags_P2SH", + "btck_ScriptVerificationFlags_DERSIG", + "btck_ScriptVerificationFlags_NULLDUMMY", + "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", + "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", + "btck_ScriptVerificationFlags_WITNESS" + ], "spent_outputs": [] }, "expected": { From 13a9039321130f3a341d117eba9f006c02eaa8ff Mon Sep 17 00:00:00 2001 From: stringintech Date: Wed, 26 Nov 2025 16:18:00 +0330 Subject: [PATCH 2/6] Align test parameters with C API naming conventions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update btck_script_pubkey_verify test cases to match the C function signature and parameter names from bitcoinkernel.h: - Method name: Use `btck_script_pubkey_verify` instead of `script_pubkey.verify` - Parameters: Rename fields to match C API: - `script_pubkey_hex` → `script_pubkey` - `tx_hex` → `tx_to` - `value` → `amount` (in spent_outputs array) --- testdata/script_verify_errors.json | 68 ++++++++++++++--------------- testdata/script_verify_success.json | 18 ++++---- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/testdata/script_verify_errors.json b/testdata/script_verify_errors.json index 275d7ec..a7ff8b8 100644 --- a/testdata/script_verify_errors.json +++ b/testdata/script_verify_errors.json @@ -5,11 +5,11 @@ { "id": "error_invalid_p2pkh_wrong_signature", "description": "P2PKH script verification fails due to wrong signature (malformed final opcode)", - "method": "script_pubkey.verify", + "method": "btck_script_pubkey_verify", "params": { - "script_pubkey_hex": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ff", + "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ff", "amount": 0, - "tx_hex": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", + "tx_to": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", "input_index": 0, "flags": [ "btck_ScriptVerificationFlags_P2SH", @@ -31,11 +31,11 @@ { "id": "error_invalid_p2sh_segwit_wrong_amount", "description": "P2SH-wrapped SegWit verification fails due to incorrect amount", - "method": "script_pubkey.verify", + "method": "btck_script_pubkey_verify", "params": { - "script_pubkey_hex": "a91434c06f8c87e355e123bdc6dda4ffabc64b6989ef87", + "script_pubkey": "a91434c06f8c87e355e123bdc6dda4ffabc64b6989ef87", "amount": 900000, - "tx_hex": "01000000000101d9fd94d0ff0026d307c994d0003180a5f248146efb6371d040c5973f5f66d9df0400000017160014b31b31a6cb654cfab3c50567bcf124f48a0beaecffffffff012cbd1c000000000017a914233b74bf0823fa58bbbd26dfc3bb4ae715547167870247304402206f60569cac136c114a58aedd80f6fa1c51b49093e7af883e605c212bdafcd8d202200e91a55f408a021ad2631bc29a67bd6915b2d7e9ef0265627eabd7f7234455f6012103e7e802f50344303c76d12c089c8724c1b230e3b745693bbe16aad536293d15e300000000", + "tx_to": "01000000000101d9fd94d0ff0026d307c994d0003180a5f248146efb6371d040c5973f5f66d9df0400000017160014b31b31a6cb654cfab3c50567bcf124f48a0beaecffffffff012cbd1c000000000017a914233b74bf0823fa58bbbd26dfc3bb4ae715547167870247304402206f60569cac136c114a58aedd80f6fa1c51b49093e7af883e605c212bdafcd8d202200e91a55f408a021ad2631bc29a67bd6915b2d7e9ef0265627eabd7f7234455f6012103e7e802f50344303c76d12c089c8724c1b230e3b745693bbe16aad536293d15e300000000", "input_index": 0, "flags": [ "btck_ScriptVerificationFlags_P2SH", @@ -57,11 +57,11 @@ { "id": "error_invalid_native_segwit_wrong_script", "description": "Native SegWit verification fails due to malformed witness script hash", - "method": "script_pubkey.verify", + "method": "btck_script_pubkey_verify", "params": { - "script_pubkey_hex": "0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58f", + "script_pubkey": "0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58f", "amount": 18393430, - "tx_hex": "010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000", + "tx_to": "010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000", "input_index": 0, "flags": [ "btck_ScriptVerificationFlags_P2SH", @@ -83,11 +83,11 @@ { "id": "error_tx_input_index_out_of_bounds", "description": "Input index exceeds the number of inputs in the transaction", - "method": "script_pubkey.verify", + "method": "btck_script_pubkey_verify", "params": { - "script_pubkey_hex": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", + "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", "amount": 0, - "tx_hex": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", + "tx_to": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", "input_index": 999, "flags": [ "btck_ScriptVerificationFlags_P2SH", @@ -99,8 +99,8 @@ ], "spent_outputs": [ { - "script_pubkey_hex": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", - "value": 100000 + "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", + "amount": 100000 } ] }, @@ -114,11 +114,11 @@ { "id": "error_spent_outputs_mismatch", "description": "Spent outputs array length doesn't match transaction input count", - "method": "script_pubkey.verify", + "method": "btck_script_pubkey_verify", "params": { - "script_pubkey_hex": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", + "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", "amount": 0, - "tx_hex": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", + "tx_to": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", "input_index": 0, "flags": [ "btck_ScriptVerificationFlags_P2SH", @@ -130,12 +130,12 @@ ], "spent_outputs": [ { - "script_pubkey_hex": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", - "value": 100000 + "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", + "amount": 100000 }, { - "script_pubkey_hex": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", - "value": 100000 + "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", + "amount": 100000 } ] }, @@ -149,17 +149,17 @@ { "id": "error_invalid_flags", "description": "Verification flags contain invalid bits", - "method": "script_pubkey.verify", + "method": "btck_script_pubkey_verify", "params": { - "script_pubkey_hex": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", + "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", "amount": 0, - "tx_hex": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", + "tx_to": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", "input_index": 0, "flags": 4294967295, "spent_outputs": [ { - "script_pubkey_hex": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", - "value": 100000 + "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", + "amount": 100000 } ] }, @@ -173,17 +173,17 @@ { "id": "error_invalid_flags_combination", "description": "VERIFY_WITNESS flag requires P2SH flag to be set as well", - "method": "script_pubkey.verify", + "method": "btck_script_pubkey_verify", "params": { - "script_pubkey_hex": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", + "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", "amount": 0, - "tx_hex": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", + "tx_to": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", "input_index": 0, "flags": ["btck_ScriptVerificationFlags_WITNESS"], "spent_outputs": [ { - "script_pubkey_hex": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", - "value": 100000 + "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", + "amount": 100000 } ] }, @@ -197,11 +197,11 @@ { "id": "error_spent_outputs_required", "description": "Taproot verification requires spent outputs to be provided", - "method": "script_pubkey.verify", + "method": "btck_script_pubkey_verify", "params": { - "script_pubkey_hex": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", + "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", "amount": 0, - "tx_hex": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", + "tx_to": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", "input_index": 0, "flags": ["btck_ScriptVerificationFlags_TAPROOT"], "spent_outputs": [] diff --git a/testdata/script_verify_success.json b/testdata/script_verify_success.json index 150dec8..7e0e2a7 100644 --- a/testdata/script_verify_success.json +++ b/testdata/script_verify_success.json @@ -5,11 +5,11 @@ { "id": "valid_p2pkh_legacy", "description": "Valid legacy P2PKH (Pay-to-PubKey-Hash) transaction verification", - "method": "script_pubkey.verify", + "method": "btck_script_pubkey_verify", "params": { - "script_pubkey_hex": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", + "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", "amount": 0, - "tx_hex": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", + "tx_to": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", "input_index": 0, "flags": [ "btck_ScriptVerificationFlags_P2SH", @@ -28,11 +28,11 @@ { "id": "valid_p2sh_wrapped_segwit", "description": "Valid P2SH-wrapped SegWit transaction verification", - "method": "script_pubkey.verify", + "method": "btck_script_pubkey_verify", "params": { - "script_pubkey_hex": "a91434c06f8c87e355e123bdc6dda4ffabc64b6989ef87", + "script_pubkey": "a91434c06f8c87e355e123bdc6dda4ffabc64b6989ef87", "amount": 1900000, - "tx_hex": "01000000000101d9fd94d0ff0026d307c994d0003180a5f248146efb6371d040c5973f5f66d9df0400000017160014b31b31a6cb654cfab3c50567bcf124f48a0beaecffffffff012cbd1c000000000017a914233b74bf0823fa58bbbd26dfc3bb4ae715547167870247304402206f60569cac136c114a58aedd80f6fa1c51b49093e7af883e605c212bdafcd8d202200e91a55f408a021ad2631bc29a67bd6915b2d7e9ef0265627eabd7f7234455f6012103e7e802f50344303c76d12c089c8724c1b230e3b745693bbe16aad536293d15e300000000", + "tx_to": "01000000000101d9fd94d0ff0026d307c994d0003180a5f248146efb6371d040c5973f5f66d9df0400000017160014b31b31a6cb654cfab3c50567bcf124f48a0beaecffffffff012cbd1c000000000017a914233b74bf0823fa58bbbd26dfc3bb4ae715547167870247304402206f60569cac136c114a58aedd80f6fa1c51b49093e7af883e605c212bdafcd8d202200e91a55f408a021ad2631bc29a67bd6915b2d7e9ef0265627eabd7f7234455f6012103e7e802f50344303c76d12c089c8724c1b230e3b745693bbe16aad536293d15e300000000", "input_index": 0, "flags": [ "btck_ScriptVerificationFlags_P2SH", @@ -51,11 +51,11 @@ { "id": "valid_native_segwit_p2wpkh", "description": "Valid native SegWit (P2WPKH) transaction verification", - "method": "script_pubkey.verify", + "method": "btck_script_pubkey_verify", "params": { - "script_pubkey_hex": "0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d", + "script_pubkey": "0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d", "amount": 18393430, - "tx_hex": "010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000", + "tx_to": "010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000", "input_index": 0, "flags": [ "btck_ScriptVerificationFlags_P2SH", From 9828c216ee7143c7c0323c244ff7053658ad9e50 Mon Sep 17 00:00:00 2001 From: stringintech Date: Sat, 29 Nov 2025 15:05:12 +0330 Subject: [PATCH 3/6] Remove unexported error cases Drop test cases for errors not exported by `bitcoinkernel.h` (`TxInputIndex`, `SpentOutputsMismatch`, `InvalidFlags`) to align with C API capabilities. --- testdata/script_verify_errors.json | 90 ------------------------------ 1 file changed, 90 deletions(-) diff --git a/testdata/script_verify_errors.json b/testdata/script_verify_errors.json index a7ff8b8..0be6acd 100644 --- a/testdata/script_verify_errors.json +++ b/testdata/script_verify_errors.json @@ -80,96 +80,6 @@ } } }, - { - "id": "error_tx_input_index_out_of_bounds", - "description": "Input index exceeds the number of inputs in the transaction", - "method": "btck_script_pubkey_verify", - "params": { - "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", - "amount": 0, - "tx_to": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", - "input_index": 999, - "flags": [ - "btck_ScriptVerificationFlags_P2SH", - "btck_ScriptVerificationFlags_DERSIG", - "btck_ScriptVerificationFlags_NULLDUMMY", - "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", - "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", - "btck_ScriptVerificationFlags_WITNESS" - ], - "spent_outputs": [ - { - "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", - "amount": 100000 - } - ] - }, - "expected": { - "error": { - "type": "ScriptVerify", - "variant": "TxInputIndex" - } - } - }, - { - "id": "error_spent_outputs_mismatch", - "description": "Spent outputs array length doesn't match transaction input count", - "method": "btck_script_pubkey_verify", - "params": { - "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", - "amount": 0, - "tx_to": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", - "input_index": 0, - "flags": [ - "btck_ScriptVerificationFlags_P2SH", - "btck_ScriptVerificationFlags_DERSIG", - "btck_ScriptVerificationFlags_NULLDUMMY", - "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", - "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", - "btck_ScriptVerificationFlags_WITNESS" - ], - "spent_outputs": [ - { - "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", - "amount": 100000 - }, - { - "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", - "amount": 100000 - } - ] - }, - "expected": { - "error": { - "type": "ScriptVerify", - "variant": "SpentOutputsMismatch" - } - } - }, - { - "id": "error_invalid_flags", - "description": "Verification flags contain invalid bits", - "method": "btck_script_pubkey_verify", - "params": { - "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", - "amount": 0, - "tx_to": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", - "input_index": 0, - "flags": 4294967295, - "spent_outputs": [ - { - "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac", - "amount": 100000 - } - ] - }, - "expected": { - "error": { - "type": "ScriptVerify", - "variant": "InvalidFlags" - } - } - }, { "id": "error_invalid_flags_combination", "description": "VERIFY_WITNESS flag requires P2SH flag to be set as well", From c6708fe5c8186f48ffce96511bab6f6958d73c02 Mon Sep 17 00:00:00 2001 From: stringintech Date: Wed, 26 Nov 2025 16:52:29 +0330 Subject: [PATCH 4/6] Restructure expected response format and improve semantics Also refactor response validation to be aligned with the improved semantics: - Success case: result contains the return value (or null for void/nullptr), error must be null/omitted - Error case: result must be null/omitted, error contains error details Additional changes: - Replace Error.type/variant with Error.code.type/member - Split validation into separate validateResponseForSuccess/Error functions - Update test cases to reflect new format --- cmd/mock-handler/main.go | 30 +++-- runner/runner.go | 183 +++++++++++++--------------- runner/types.go | 61 ++++++++-- testdata/script_verify_errors.json | 94 ++------------ testdata/script_verify_success.json | 79 +++++++++++- 5 files changed, 242 insertions(+), 205 deletions(-) diff --git a/cmd/mock-handler/main.go b/cmd/mock-handler/main.go index a946b93..e64bc7e 100644 --- a/cmd/mock-handler/main.go +++ b/cmd/mock-handler/main.go @@ -81,8 +81,10 @@ func handleRequest(line string, testIndex map[string]string) error { resp := runner.Response{ ID: req.ID, Error: &runner.Error{ - Type: "Handler", - Variant: "UnknownTest", + Code: &runner.ErrorCode{ + Type: "Handler", + Member: "UNKNOWN_TEST", + }, }, } return writeResponse(resp) @@ -94,8 +96,10 @@ func handleRequest(line string, testIndex map[string]string) error { resp := runner.Response{ ID: req.ID, Error: &runner.Error{ - Type: "Handler", - Variant: "LoadError", + Code: &runner.ErrorCode{ + Type: "Handler", + Member: "LOAD_ERROR", + }, }, } return writeResponse(resp) @@ -113,8 +117,10 @@ func handleRequest(line string, testIndex map[string]string) error { resp := runner.Response{ ID: req.ID, Error: &runner.Error{ - Type: "Handler", - Variant: "TestNotFound", + Code: &runner.ErrorCode{ + Type: "Handler", + Member: "TEST_NOT_FOUND", + }, }, } return writeResponse(resp) @@ -125,8 +131,10 @@ func handleRequest(line string, testIndex map[string]string) error { resp := runner.Response{ ID: req.ID, Error: &runner.Error{ - Type: "Handler", - Variant: "MethodMismatch", + Code: &runner.ErrorCode{ + Type: "Handler", + Member: "METHOD_MISMATCH", + }, }, } return writeResponse(resp) @@ -134,9 +142,9 @@ func handleRequest(line string, testIndex map[string]string) error { // Build response based on expected result return writeResponse(runner.Response{ - ID: req.ID, - Success: testCase.Expected.Success, - Error: testCase.Expected.Error, + ID: req.ID, + Result: testCase.Expected.Result, + Error: testCase.Expected.Error, }) } diff --git a/runner/runner.go b/runner/runner.go index 4b49852..11fed46 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -112,7 +112,15 @@ func (tr *TestRunner) RunTestSuite(suite TestSuite) TestResult { } for _, test := range suite.Tests { - testResult := tr.runTest(test) + err := tr.runTest(test) + testResult := SingleTestResult{ + TestID: test.ID, + Passed: err == nil, + } + if err != nil { + testResult.Message = err.Error() + } + result.TestResults = append(result.TestResults, testResult) if testResult.Passed { result.PassedTests++ @@ -124,8 +132,9 @@ func (tr *TestRunner) RunTestSuite(suite TestSuite) TestResult { return result } -// runTest executes a single test case -func (tr *TestRunner) runTest(test TestCase) SingleTestResult { +// runTest executes a single test case and validates the response. +// Returns an error if communication with the handler fails or validation fails. +func (tr *TestRunner) runTest(test TestCase) error { req := Request{ ID: test.ID, Method: test.Method, @@ -134,118 +143,102 @@ func (tr *TestRunner) runTest(test TestCase) SingleTestResult { resp, err := tr.SendRequest(req) if err != nil { - return SingleTestResult{ - TestID: test.ID, - Passed: false, - Message: fmt.Sprintf("Failed to send request: %v", err), - } - } - if resp.ID != test.ID { - return SingleTestResult{ - TestID: test.ID, - Passed: false, - Message: fmt.Sprintf("Response ID mismatch: expected %s, got %s", test.ID, resp.ID), - } + return err } + return validateResponse(test, resp) } -// validateResponse checks if response matches expected result -func validateResponse(test TestCase, resp *Response) SingleTestResult { - // Check if we expected an error +// validateResponse validates that a response matches the expected test outcome. +// Returns an error if: +// +// Response ID does not match the request ID +// Response does not match the expected outcome (error or success) +func validateResponse(test TestCase, resp *Response) error { + if resp.ID != test.ID { + return fmt.Errorf("response ID mismatch: expected %s, got %s", test.ID, resp.ID) + } + if test.Expected.Error != nil { - if resp.Error == nil { - return SingleTestResult{ - TestID: test.ID, - Passed: false, - Message: fmt.Sprintf("Expected error type %s, but got no error", test.Expected.Error.Type), - } - } + return validateResponseForError(test, resp) + } - // Check error type - if resp.Error.Type != test.Expected.Error.Type { - return SingleTestResult{ - TestID: test.ID, - Passed: false, - Message: fmt.Sprintf("Expected error type %s, got %s", test.Expected.Error.Type, resp.Error.Type), - } - } + return validateResponseForSuccess(test, resp) +} - // Check error variant if specified - if test.Expected.Error.Variant != "" && resp.Error.Variant != test.Expected.Error.Variant { - return SingleTestResult{ - TestID: test.ID, - Passed: false, - Message: fmt.Sprintf("Expected error variant %s, got %s", test.Expected.Error.Variant, resp.Error.Variant), - } - } +// validateResponseForError validates that a response correctly represents an error case. +// It ensures the response contains an error, the result is null or omitted, and if an +// error code is expected, it matches the expected type and member. +func validateResponseForError(test TestCase, resp *Response) error { + if test.Expected.Error == nil { + panic("validateResponseForError expects non-nil error") + } - return SingleTestResult{ - TestID: test.ID, - Passed: true, - Message: "Test passed (expected error matched)", + if resp.Error == nil { + if test.Expected.Error.Code != nil { + return fmt.Errorf("expected error %s.%s, but got no error", + test.Expected.Error.Code.Type, test.Expected.Error.Code.Member) } + return fmt.Errorf("expected error, but got no error") } - // Check if we expected success - if test.Expected.Success != nil { - if resp.Error != nil { - errMsg := fmt.Sprintf("Expected success, but got error: %s", resp.Error.Type) - if resp.Error.Variant != "" { - errMsg += fmt.Sprintf(" (variant: %s)", resp.Error.Variant) - } - return SingleTestResult{ - TestID: test.ID, - Passed: false, - Message: errMsg, - } - } + if !resp.Result.IsNullOrOmitted() { + return fmt.Errorf("expected result to be null or omitted when error is present, got: %s", string(resp.Result)) + } - if resp.Success == nil { - return SingleTestResult{ - TestID: test.ID, - Passed: false, - Message: "Expected success, but response contained no success field", - } + if test.Expected.Error.Code != nil { + if resp.Error.Code == nil { + return fmt.Errorf("expected error code %s.%s, but got error with no code", + test.Expected.Error.Code.Type, test.Expected.Error.Code.Member) } - // Normalize JSON for comparison - var expectedData, actualData interface{} - if err := json.Unmarshal(bytes.TrimSpace(*test.Expected.Success), &expectedData); err != nil { - return SingleTestResult{ - TestID: test.ID, - Passed: false, - Message: fmt.Sprintf("Invalid expected JSON: %v", err), - } + if resp.Error.Code.Type != test.Expected.Error.Code.Type { + return fmt.Errorf("expected error type %s, got %s", test.Expected.Error.Code.Type, resp.Error.Code.Type) } - if err := json.Unmarshal(bytes.TrimSpace(*resp.Success), &actualData); err != nil { - return SingleTestResult{ - TestID: test.ID, - Passed: false, - Message: fmt.Sprintf("Invalid response JSON: %v", err), - } + + if resp.Error.Code.Member != test.Expected.Error.Code.Member { + return fmt.Errorf("expected error member %s, got %s", test.Expected.Error.Code.Member, resp.Error.Code.Member) } - expectedNormalized, _ := json.Marshal(expectedData) - actualNormalized, _ := json.Marshal(actualData) - - if !bytes.Equal(expectedNormalized, actualNormalized) { - return SingleTestResult{ - TestID: test.ID, - Passed: false, - Message: fmt.Sprintf("Success mismatch:\nExpected: %s\nActual: %s", string(expectedNormalized), string(actualNormalized)), - } + } + return nil +} + +// validateResponseForSuccess validates that a response correctly represents a success case. +// It ensures the response contains no error, and if a result is expected, it matches the +// expected value. +func validateResponseForSuccess(test TestCase, resp *Response) error { + if test.Expected.Error != nil { + panic("validateResponseForSuccess expects nil error") + } + + if resp.Error != nil { + if resp.Error.Code != nil { + return fmt.Errorf("expected success with no error, but got error: %s.%s", resp.Error.Code.Type, resp.Error.Code.Member) } - return SingleTestResult{ - TestID: test.ID, - Passed: true, - Message: "Test passed", + return fmt.Errorf("expected success with no error, but got error") + } + + if test.Expected.Result.IsNullOrOmitted() { + if !resp.Result.IsNullOrOmitted() { + return fmt.Errorf("expected null or omitted result, got: %s", string(resp.Result)) } + return nil } - return SingleTestResult{ - TestID: test.ID, - Passed: false, - Message: "Test has no expected result defined", + + expectedNorm, err := test.Expected.Result.Normalize() + if err != nil { + return fmt.Errorf("failed to normalize expected result: %w", err) } + + actualNorm, err := resp.Result.Normalize() + if err != nil { + return fmt.Errorf("failed to normalize actual result: %w", err) + } + + if expectedNorm != actualNorm { + return fmt.Errorf("result mismatch: expected %s, got %s", expectedNorm, actualNorm) + } + return nil } // TestResult contains results from running a test suite diff --git a/runner/types.go b/runner/types.go index f032c67..413c62b 100644 --- a/runner/types.go +++ b/runner/types.go @@ -13,10 +13,12 @@ type TestCase struct { Expected TestExpectation `json:"expected"` } -// TestExpectation defines what response is expected +// TestExpectation defines what response is expected. +// If expecting success, result contains the expected value (or null for void/nullptr) and error must be null. +// If expecting failure, result must be null and error contains the expected error details. type TestExpectation struct { - Success *json.RawMessage `json:"success,omitempty"` // Expected successful result - Error *Error `json:"error,omitempty"` // Expected error + Result Result `json:"result"` // Expected return value (null for void/nullptr/error cases) + Error *Error `json:"error,omitempty"` // Expected error (null for success cases) } // TestSuite represents a collection of test cases @@ -33,15 +35,54 @@ type Request struct { Params json.RawMessage `json:"params"` } -// Response represents a response from the handler +// Response represents a response from the handler. +// If the operation succeeds, result contains the return value (or null for void/nullptr) and error must be null. +// If the operation fails, result must be null and error contains error details. type Response struct { - ID string `json:"id"` - Success *json.RawMessage `json:"success,omitempty"` - Error *Error `json:"error,omitempty"` + ID string `json:"id"` + Result Result `json:"result"` // Return value (null for void/nullptr/error cases) + Error *Error `json:"error,omitempty"` // Error details (null for success cases) } -// Error represents an error response +// Error represents an error response. +// Code can be null for generic errors without specific error codes. type Error struct { - Type string `json:"type"` - Variant string `json:"variant,omitempty"` + Code *ErrorCode `json:"code,omitempty"` +} + +type ErrorCode struct { + Type string `json:"type"` // e.g., "btck_ScriptVerifyStatus" + Member string `json:"member"` // e.g., "ERROR_INVALID_FLAGS_COMBINATION" +} + +// Result is a type alias for json.RawMessage with helper methods. +type Result json.RawMessage + +// MarshalJSON implements json.Marshaler by delegating to json.RawMessage. +func (r Result) MarshalJSON() ([]byte, error) { + return (json.RawMessage)(r).MarshalJSON() +} + +// UnmarshalJSON implements json.Unmarshaler by delegating to json.RawMessage. +func (r *Result) UnmarshalJSON(data []byte) error { + return (*json.RawMessage)(r).UnmarshalJSON(data) +} + +// IsNullOrOmitted checks if the result is nil or represents a JSON null value. +func (r Result) IsNullOrOmitted() bool { + return r == nil || string(r) == "null" +} + +// Normalize normalizes JSON data by parsing and re-marshaling it. +// This ensures consistent formatting and key ordering for comparison. +func (r Result) Normalize() (string, error) { + var v interface{} + if err := json.Unmarshal(r, &v); err != nil { + return "", err + } + normalized, err := json.Marshal(v) + if err != nil { + return "", err + } + return string(normalized), nil } diff --git a/testdata/script_verify_errors.json b/testdata/script_verify_errors.json index 0be6acd..0be810f 100644 --- a/testdata/script_verify_errors.json +++ b/testdata/script_verify_errors.json @@ -1,85 +1,7 @@ { - "name": "Script Verification - Error Cases", - "description": "Test cases for script verification that should fail with specific error type and variant", + "name": "Failed Script Verification Cases", + "description": "Test cases where the verification operation fails to determine validity of the script due to bad user input", "tests": [ - { - "id": "error_invalid_p2pkh_wrong_signature", - "description": "P2PKH script verification fails due to wrong signature (malformed final opcode)", - "method": "btck_script_pubkey_verify", - "params": { - "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ff", - "amount": 0, - "tx_to": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", - "input_index": 0, - "flags": [ - "btck_ScriptVerificationFlags_P2SH", - "btck_ScriptVerificationFlags_DERSIG", - "btck_ScriptVerificationFlags_NULLDUMMY", - "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", - "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", - "btck_ScriptVerificationFlags_WITNESS" - ], - "spent_outputs": [] - }, - "expected": { - "error": { - "type": "ScriptVerify", - "variant": "Invalid" - } - } - }, - { - "id": "error_invalid_p2sh_segwit_wrong_amount", - "description": "P2SH-wrapped SegWit verification fails due to incorrect amount", - "method": "btck_script_pubkey_verify", - "params": { - "script_pubkey": "a91434c06f8c87e355e123bdc6dda4ffabc64b6989ef87", - "amount": 900000, - "tx_to": "01000000000101d9fd94d0ff0026d307c994d0003180a5f248146efb6371d040c5973f5f66d9df0400000017160014b31b31a6cb654cfab3c50567bcf124f48a0beaecffffffff012cbd1c000000000017a914233b74bf0823fa58bbbd26dfc3bb4ae715547167870247304402206f60569cac136c114a58aedd80f6fa1c51b49093e7af883e605c212bdafcd8d202200e91a55f408a021ad2631bc29a67bd6915b2d7e9ef0265627eabd7f7234455f6012103e7e802f50344303c76d12c089c8724c1b230e3b745693bbe16aad536293d15e300000000", - "input_index": 0, - "flags": [ - "btck_ScriptVerificationFlags_P2SH", - "btck_ScriptVerificationFlags_DERSIG", - "btck_ScriptVerificationFlags_NULLDUMMY", - "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", - "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", - "btck_ScriptVerificationFlags_WITNESS" - ], - "spent_outputs": [] - }, - "expected": { - "error": { - "type": "ScriptVerify", - "variant": "Invalid" - } - } - }, - { - "id": "error_invalid_native_segwit_wrong_script", - "description": "Native SegWit verification fails due to malformed witness script hash", - "method": "btck_script_pubkey_verify", - "params": { - "script_pubkey": "0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58f", - "amount": 18393430, - "tx_to": "010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000", - "input_index": 0, - "flags": [ - "btck_ScriptVerificationFlags_P2SH", - "btck_ScriptVerificationFlags_DERSIG", - "btck_ScriptVerificationFlags_NULLDUMMY", - "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", - "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", - "btck_ScriptVerificationFlags_WITNESS" - ], - "spent_outputs": [] - }, - "expected": { - "error": { - "type": "ScriptVerify", - "variant": "Invalid" - } - } - }, { "id": "error_invalid_flags_combination", "description": "VERIFY_WITNESS flag requires P2SH flag to be set as well", @@ -99,8 +21,10 @@ }, "expected": { "error": { - "type": "ScriptVerify", - "variant": "InvalidFlagsCombination" + "code": { + "type": "btck_ScriptVerifyStatus", + "member": "ERROR_INVALID_FLAGS_COMBINATION" + } } } }, @@ -118,8 +42,10 @@ }, "expected": { "error": { - "type": "ScriptVerify", - "variant": "SpentOutputsRequired" + "code": { + "type": "btck_ScriptVerifyStatus", + "member": "ERROR_SPENT_OUTPUTS_REQUIRED" + } } } } diff --git a/testdata/script_verify_success.json b/testdata/script_verify_success.json index 7e0e2a7..bbbb08a 100644 --- a/testdata/script_verify_success.json +++ b/testdata/script_verify_success.json @@ -1,6 +1,6 @@ { - "name": "Script Verification - Success Cases", - "description": "Test cases for script verification that should succeed", + "name": "Successful Script Verification Cases", + "description": "Test cases where the script verification operation executes successfully and returns a boolean result (true for valid scripts, false for invalid scripts)", "tests": [ { "id": "valid_p2pkh_legacy", @@ -22,7 +22,7 @@ "spent_outputs": [] }, "expected": { - "success": {} + "result": true } }, { @@ -45,7 +45,7 @@ "spent_outputs": [] }, "expected": { - "success": {} + "result": true } }, { @@ -68,7 +68,76 @@ "spent_outputs": [] }, "expected": { - "success": {} + "result": true + } + }, + { + "id": "error_invalid_p2pkh_wrong_signature", + "description": "P2PKH script verification fails due to wrong signature (malformed final opcode)", + "method": "btck_script_pubkey_verify", + "params": { + "script_pubkey": "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ff", + "amount": 0, + "tx_to": "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700", + "input_index": 0, + "flags": [ + "btck_ScriptVerificationFlags_P2SH", + "btck_ScriptVerificationFlags_DERSIG", + "btck_ScriptVerificationFlags_NULLDUMMY", + "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", + "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", + "btck_ScriptVerificationFlags_WITNESS" + ], + "spent_outputs": [] + }, + "expected": { + "result": false + } + }, + { + "id": "error_invalid_p2sh_segwit_wrong_amount", + "description": "P2SH-wrapped SegWit verification fails due to incorrect amount", + "method": "btck_script_pubkey_verify", + "params": { + "script_pubkey": "a91434c06f8c87e355e123bdc6dda4ffabc64b6989ef87", + "amount": 900000, + "tx_to": "01000000000101d9fd94d0ff0026d307c994d0003180a5f248146efb6371d040c5973f5f66d9df0400000017160014b31b31a6cb654cfab3c50567bcf124f48a0beaecffffffff012cbd1c000000000017a914233b74bf0823fa58bbbd26dfc3bb4ae715547167870247304402206f60569cac136c114a58aedd80f6fa1c51b49093e7af883e605c212bdafcd8d202200e91a55f408a021ad2631bc29a67bd6915b2d7e9ef0265627eabd7f7234455f6012103e7e802f50344303c76d12c089c8724c1b230e3b745693bbe16aad536293d15e300000000", + "input_index": 0, + "flags": [ + "btck_ScriptVerificationFlags_P2SH", + "btck_ScriptVerificationFlags_DERSIG", + "btck_ScriptVerificationFlags_NULLDUMMY", + "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", + "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", + "btck_ScriptVerificationFlags_WITNESS" + ], + "spent_outputs": [] + }, + "expected": { + "result": false + } + }, + { + "id": "error_invalid_native_segwit_wrong_script", + "description": "Native SegWit verification fails due to malformed witness script hash", + "method": "btck_script_pubkey_verify", + "params": { + "script_pubkey": "0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58f", + "amount": 18393430, + "tx_to": "010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000", + "input_index": 0, + "flags": [ + "btck_ScriptVerificationFlags_P2SH", + "btck_ScriptVerificationFlags_DERSIG", + "btck_ScriptVerificationFlags_NULLDUMMY", + "btck_ScriptVerificationFlags_CHECKLOCKTIMEVERIFY", + "btck_ScriptVerificationFlags_CHECKSEQUENCEVERIFY", + "btck_ScriptVerificationFlags_WITNESS" + ], + "spent_outputs": [] + }, + "expected": { + "result": false } } ] From 5c90dfea1fa8428a645dc8e2f1be0cbaa9b28fba Mon Sep 17 00:00:00 2001 From: stringintech Date: Sat, 29 Nov 2025 15:01:12 +0330 Subject: [PATCH 5/6] Add unit test for response validation Adapt Makefile to also run unit tests with `make test` --- .github/workflows/test.yml | 2 +- Makefile | 2 + README.md | 4 +- runner/runner_test.go | 264 +++++++++++++++++++++++++++++++++++++ 4 files changed, 269 insertions(+), 3 deletions(-) create mode 100644 runner/runner_test.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 44135b5..1c0ea44 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,5 +20,5 @@ jobs: - name: Build run: make build - - name: Run tests against mock handler + - name: Run tests run: make test diff --git a/Makefile b/Makefile index c60ddb6..60449e2 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,8 @@ mock-handler: go build -o $(MOCK_HANDLER_BIN) ./cmd/mock-handler test: + @echo "Running runner unit tests..." + go test ./runner/... @echo "Running conformance tests with mock handler..." $(RUNNER_BIN) -handler $(MOCK_HANDLER_BIN) diff --git a/README.md b/README.md index 8dfe8e3..9f075c8 100644 --- a/README.md +++ b/README.md @@ -56,12 +56,12 @@ make runner ### Testing the Runner -Build and test the runner using the mock handler: +Build and test the runner: ```bash # Build both runner and mock handler make build -# Run the test runner against the mock handler +# Run runner unit tests and integration tests with mock handler make test ``` diff --git a/runner/runner_test.go b/runner/runner_test.go new file mode 100644 index 0000000..3bb8929 --- /dev/null +++ b/runner/runner_test.go @@ -0,0 +1,264 @@ +package runner + +import ( + "encoding/json" + "strings" + "testing" +) + +func TestValidateResponse(t *testing.T) { + tests := []struct { + name string + testCaseJSON string + responseJSON string + wantErr bool + wantErrMsg string + }{ + { + name: "success with boolean result", + testCaseJSON: `{ + "id": "1", + "expected": {"result": true} + }`, + responseJSON: `{ + "id": "1", + "result": true + }`, + wantErr: false, + }, + { + name: "success with null result explicit", + testCaseJSON: `{ + "id": "2", + "expected": {} + }`, + responseJSON: `{ + "id": "2", + "result": null + }`, + wantErr: false, + }, + { + name: "success with null result omitted", + testCaseJSON: `{ + "id": "3", + "expected": {} + }`, + responseJSON: `{ + "id": "3" + }`, + wantErr: false, + }, + { + name: "error exact match", + testCaseJSON: `{ + "id": "4", + "expected": { + "error": { + "code": { + "type": "type", + "member": "MEMBER" + } + } + } + }`, + responseJSON: `{ + "id": "4", + "result": null, + "error": { + "code": { + "type": "type", + "member": "MEMBER" + } + } + }`, + wantErr: false, + }, + { + name: "error type mismatch", + testCaseJSON: `{ + "id": "5", + "expected": { + "error": { + "code": { + "type": "type", + "member": "MEMBER" + } + } + } + }`, + responseJSON: `{ + "id": "5", + "error": { + "code": { + "type": "different_type", + "member": "MEMBER" + } + } + }`, + wantErr: true, + wantErrMsg: "expected error type", + }, + { + name: "error member mismatch", + testCaseJSON: `{ + "id": "6", + "expected": { + "error": { + "code": { + "type": "type", + "member": "MEMBER" + } + } + } + }`, + responseJSON: `{ + "id": "6", + "error": { + "code": { + "type": "type", + "member": "DIFFERENT_MEMBER" + } + } + }`, + wantErr: true, + wantErrMsg: "expected error member", + }, + { + name: "expected success got error", + testCaseJSON: `{ + "id": "7", + "expected": {"result": true} + }`, + responseJSON: `{ + "id": "7", + "result": null, + "error": { + "code": { + "type": "type", + "member": "MEMBER" + } + } + }`, + wantErr: true, + wantErrMsg: "expected success with no error", + }, + { + name: "expected error got success", + testCaseJSON: `{ + "id": "8", + "expected": { + "error": { + "code": { + "type": "type", + "member": "MEMBER" + } + } + } + }`, + responseJSON: `{ + "id": "8", + "result": true + }`, + wantErr: true, + wantErrMsg: "expected error", + }, + { + name: "result value mismatch", + testCaseJSON: `{ + "id": "9", + "expected": {"result": true} + }`, + responseJSON: `{ + "id": "9", + "result": false + }`, + wantErr: true, + wantErrMsg: "result mismatch", + }, + { + name: "response ID mismatch", + testCaseJSON: `{ + "id": "10", + "expected": {"result": true} + }`, + responseJSON: `{ + "id": "99", + "result": true + }`, + wantErr: true, + wantErrMsg: "response ID mismatch", + }, + { + name: "protocol violation with result not null when error present", + testCaseJSON: `{ + "id": "11", + "expected": { + "error": { + "code": { + "type": "type", + "member": "MEMBER" + } + } + } + }`, + responseJSON: `{ + "id": "11", + "result": true, + "error": { + "code": { + "type": "type", + "member": "MEMBER" + } + } + }`, + wantErr: true, + wantErrMsg: "expected result to be null or omitted when error is present", + }, + { + name: "error generic without code", + testCaseJSON: `{ + "id": "12", + "expected": { + "error": {} + } + }`, + responseJSON: `{ + "id": "12", + "result": null, + "error": {} + }`, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var testCase TestCase + if err := json.Unmarshal([]byte(tt.testCaseJSON), &testCase); err != nil { + t.Fatalf("failed to unmarshal test case: %v", err) + } + + var response Response + if err := json.Unmarshal([]byte(tt.responseJSON), &response); err != nil { + t.Fatalf("failed to unmarshal response: %v", err) + } + + err := validateResponse(testCase, &response) + + if tt.wantErr { + if err == nil { + t.Errorf("expected error containing %q, got nil", tt.wantErrMsg) + return + } + if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(tt.wantErrMsg)) { + t.Errorf("expected error containing %q, got %q", tt.wantErrMsg, err.Error()) + } + } else { + if err != nil { + t.Errorf("expected no error, got: %v", err) + } + } + }) + } +} From e8ddf7573c12ddd271d0a16a03bd168f9d16ce30 Mon Sep 17 00:00:00 2001 From: stringintech Date: Wed, 26 Nov 2025 18:28:22 +0330 Subject: [PATCH 6/6] Update handler spec to reflect response restructuring and new semantics --- docs/handler-spec.md | 74 +++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/docs/handler-spec.md b/docs/handler-spec.md index 28b6307..85fa586 100644 --- a/docs/handler-spec.md +++ b/docs/handler-spec.md @@ -24,40 +24,32 @@ Handlers communicate with the test runner via **stdin/stdout**: **Fields:** - `id` (string, required): Unique identifier for this request - `method` (string, required): The operation to perform. Each unique method must be implemented by the handler to exercise the corresponding binding API operation. -- `params` (object, required): Method-specific parameters (can be `null` or `{}`) +- `params` (object, optional): Method-specific parameters ### Response ```json { "id": "unique-request-id", - "success": { /* method-specific result */ } -} -``` - -**Success response fields:** -- `id` (string, required): Must match the request ID -- `success` (any, required): Method-specific result data. Must be present on success (can be empty `{}`) -- `error` (null or omitted): Must not be present on success - -### Error Response - -```json -{ - "id": "unique-request-id", + "result": null, "error": { - "type": "error_category", - "variant": "specific_error" + "code": { + "type": "error_type", + "member": "ERROR_MEMBER_NAME" + } } } ``` -**Error response fields:** +**Fields:** - `id` (string, required): Must match the request ID -- `success` (null or omitted): Must not be present on error -- `error` (object, required): Error details - - `type` (string, required): Error category/type - - `variant` (string, optional): Specific error variant within the type. Whether the runner expects this field depends on the specific test case +- `result` (any, optional): The return value, or `null` for void/nullptr operations. Must be `null` on error +- `error` (object, optional): Error details. Must be `null` on success. An empty object `{}` is used to indicate an error is raised without further details, it is NOT equivalent to `null` + - `code` (object, optional): Error code details + - `type` (string, required): Error type (e.g., "btck_ScriptVerifyStatus") + - `member` (string, required): Specific error member (e.g., "ERROR_INVALID_FLAGS_COMBINATION") + +**Note:** Throughout this protocol, an omitted field is semantically equivalent to `null`. ## Handler Requirements @@ -74,43 +66,49 @@ The conformance tests are organized into suites, each testing a specific aspect ### Script Verification Success Cases **File:** [`script_verify_success.json`](../testdata/script_verify_success.json) -Tests valid Bitcoin script verification scenarios across different transaction types. +Test cases where the script verification operation executes successfully and returns a boolean result (true for valid scripts, false for invalid scripts). -**Method:** `script_pubkey.verify` +**Method:** `btck_script_pubkey_verify` **Expected Response Format:** ```json { "id": "test-id", - "success": {} + "result": true +} +``` +or +```json +{ + "id": "test-id", + "result": false } ``` ### Script Verification Error Cases **File:** [`script_verify_errors.json`](../testdata/script_verify_errors.json) -Tests error handling for invalid script verification scenarios. +Test cases where the verification operation fails to determine validity of the script due to bad user input. -**Method:** `script_pubkey.verify` +**Method:** `btck_script_pubkey_verify` **Expected Response Format:** ```json { "id": "test-id", + "result": null, "error": { - "type": "ScriptVerify", - "variant": "ErrorVariant" + "code": { + "type": "btck_ScriptVerifyStatus", + "member": "ERROR_MEMBER_NAME" + } } } ``` -**Error Variants:** +**Error Members:** -| Variant | Description | -|---------|-------------| -| `TxInputIndex` | The specified input index is out of bounds. The `input_index` parameter is greater than or equal to the number of inputs in the transaction. | -| `InvalidFlags` | Invalid verification flags were provided. The flags parameter contains bits that don't correspond to any defined verification flag. | -| `InvalidFlagsCombination` | Invalid or inconsistent verification flags were provided. This occurs when the supplied `script_verify_flags` combination violates internal consistency rules. | -| `SpentOutputsMismatch` | The spent_outputs array length doesn't match the input count. When spent_outputs is non-empty, it must contain exactly one output for each input in the transaction. | -| `SpentOutputsRequired` | Spent outputs are required but were not provided. | -| `Invalid` | Script verification failed. | \ No newline at end of file +| Member | Description | +|--------|-------------| +| `ERROR_INVALID_FLAGS_COMBINATION` | Invalid or inconsistent verification flags were provided. This occurs when the supplied `script_verify_flags` combination violates internal consistency rules. | +| `ERROR_SPENT_OUTPUTS_REQUIRED` | Spent outputs are required but were not provided (e.g., for Taproot verification). |