Skip to content

Commit f0c9fab

Browse files
committed
Return validity result instead of error for invalid scripts
Change ScriptPubkey.Verify() to align with the C API behavior by returning a boolean validity result rather than treating invalid scripts as errors.
1 parent 88105c6 commit f0c9fab

File tree

3 files changed

+95
-23
lines changed

3 files changed

+95
-23
lines changed

kernel/errors.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ var (
1010
ErrVerifyScriptVerifyInvalidFlagsCombination = &ScriptVerifyError{"Invalid combination of script verification flags"}
1111
ErrVerifyScriptVerifySpentOutputsMismatch = &ScriptVerifyError{"Spent outputs count mismatch"}
1212
ErrVerifyScriptVerifySpentOutputsRequired = &ScriptVerifyError{"Spent outputs required for verification"}
13-
ErrVerifyScriptVerifyInvalid = &ScriptVerifyError{"Script verification failed"}
1413
)
1514

1615
// check panics if ptr is nil, otherwise returns ptr unchanged; used when C calls are not expected to return null
@@ -57,6 +56,7 @@ func (e *SerializationError) Error() string {
5756

5857
func (e *SerializationError) isKernelError() {}
5958

59+
// ScriptVerifyError represents errors that prevent script verification from executing.
6060
type ScriptVerifyError struct {
6161
Msg string
6262
}

kernel/script_pubkey.go

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -85,20 +85,23 @@ func (s *scriptPubkeyApi) Bytes() ([]byte, error) {
8585
// - inputIndex: Index of the input in txTo spending the script pubkey.
8686
// - flags: ScriptFlags controlling validation constraints.
8787
//
88-
// Returns an error if verification fails.
89-
func (s *scriptPubkeyApi) Verify(amount int64, txTo *Transaction, spentOutputs []*TransactionOutput, inputIndex uint, flags ScriptFlags) error {
88+
// Returns:
89+
// - bool: true if the script is valid, false if invalid (only meaningful when error is nil)
90+
// - error: non-nil if verification could not be performed due to malformed input;
91+
// nil if verification completed successfully (check bool for validity result)
92+
func (s *scriptPubkeyApi) Verify(amount int64, txTo *Transaction, spentOutputs []*TransactionOutput, inputIndex uint, flags ScriptFlags) (bool, error) {
9093
inputCount := txTo.CountInputs()
9194
if inputIndex >= uint(inputCount) {
92-
return ErrVerifyScriptVerifyTxInputIndex
95+
return false, ErrVerifyScriptVerifyTxInputIndex
9396
}
9497

9598
if len(spentOutputs) > 0 && uint64(len(spentOutputs)) != inputCount {
96-
return ErrVerifyScriptVerifySpentOutputsMismatch
99+
return false, ErrVerifyScriptVerifySpentOutputsMismatch
97100
}
98101

99102
allFlags := ScriptFlagsVerifyAll
100103
if (flags & ^ScriptFlags(allFlags)) != 0 {
101-
return ErrVerifyScriptVerifyInvalidFlags
104+
return false, ErrVerifyScriptVerifyInvalidFlags
102105
}
103106

104107
var cSpentOutputsPtr **C.btck_TransactionOutput
@@ -122,18 +125,20 @@ func (s *scriptPubkeyApi) Verify(amount int64, txTo *Transaction, spentOutputs [
122125
&cStatus,
123126
)
124127

125-
if result != 1 {
126-
status := ScriptVerifyStatus(cStatus)
127-
switch status {
128-
case ScriptVerifyErrorInvalidFlagsCombination:
129-
return ErrVerifyScriptVerifyInvalidFlagsCombination
130-
case ScriptVerifyErrorSpentOutputsRequired:
131-
return ErrVerifyScriptVerifySpentOutputsRequired
132-
default:
133-
return ErrVerifyScriptVerifyInvalid
134-
}
128+
status := ScriptVerifyStatus(cStatus)
129+
130+
// Check for errors that prevented verification
131+
if status == ScriptVerifyErrorInvalidFlagsCombination {
132+
return false, ErrVerifyScriptVerifyInvalidFlagsCombination
135133
}
136-
return nil
134+
if status == ScriptVerifyErrorSpentOutputsRequired {
135+
return false, ErrVerifyScriptVerifySpentOutputsRequired
136+
}
137+
138+
// Verification completed: result indicates validity
139+
// result == 1: script is valid
140+
// result != 1: script is invalid
141+
return result == 1, nil
137142
}
138143

139144
// ScriptFlags represents script verification flags that may be composed with each other.

kernel/script_pubkey_test.go

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,11 @@ func TestValidScripts(t *testing.T) {
148148

149149
for _, tt := range tests {
150150
t.Run(tt.name, func(t *testing.T) {
151-
err := testVerifyScript(t, tt.scriptPubkeyHex, tt.amount, tt.txToHex, tt.inputIndex)
151+
valid, err := testVerifyScript(t, tt.scriptPubkeyHex, tt.amount, tt.txToHex, tt.inputIndex)
152152
if err != nil {
153153
t.Errorf("testVerifyScript() error = %v", err)
154+
} else if !valid {
155+
t.Errorf("testVerifyScript() expected valid script, got invalid")
154156
}
155157
})
156158
}
@@ -203,17 +205,82 @@ func TestInvalidScripts(t *testing.T) {
203205

204206
for _, tt := range tests {
205207
t.Run(tt.name, func(t *testing.T) {
206-
var scriptVerifyError *ScriptVerifyError
207-
err := testVerifyScript(t, tt.scriptPubkeyHex, tt.amount, tt.txToHex, tt.inputIndex)
208-
if err == nil || !errors.As(err, &scriptVerifyError) {
209-
t.Errorf("testVerifyScript() was expected to fail with ScriptVerifyError, got: %v", err)
208+
valid, err := testVerifyScript(t, tt.scriptPubkeyHex, tt.amount, tt.txToHex, tt.inputIndex)
209+
if err != nil {
210+
t.Errorf("testVerifyScript() unexpected error = %v", err)
211+
} else if valid {
212+
t.Errorf("testVerifyScript() expected invalid script, got valid")
213+
}
214+
})
215+
}
216+
}
217+
218+
func TestScriptVerifyErrors(t *testing.T) {
219+
// Use a valid transaction for testing error conditions
220+
validScriptHex := "76a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988ac"
221+
validTxHex := "02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700"
222+
223+
scriptBytes, err := hex.DecodeString(validScriptHex)
224+
if err != nil {
225+
t.Fatalf("Failed to decode script hex: %v", err)
226+
}
227+
228+
scriptPubkey := NewScriptPubkey(scriptBytes)
229+
defer scriptPubkey.Destroy()
230+
231+
txBytes, err := hex.DecodeString(validTxHex)
232+
if err != nil {
233+
t.Fatalf("Failed to decode transaction hex: %v", err)
234+
}
235+
236+
tx, err := NewTransaction(txBytes)
237+
if err != nil {
238+
t.Fatalf("Failed to create transaction: %v", err)
239+
}
240+
defer tx.Destroy()
241+
242+
tests := []struct {
243+
name string
244+
inputIndex uint
245+
flags ScriptFlags
246+
spentOutputs []*TransactionOutput
247+
expectedError error
248+
description string
249+
}{
250+
{
251+
name: "invalid_input_index",
252+
inputIndex: 999,
253+
flags: ScriptFlagsVerifyAll,
254+
spentOutputs: nil,
255+
expectedError: ErrVerifyScriptVerifyTxInputIndex,
256+
description: "input index out of bounds should return error",
257+
},
258+
{
259+
name: "invalid_flags",
260+
inputIndex: 0,
261+
flags: ScriptFlags(0xFFFFFFFF), // Invalid flags
262+
spentOutputs: nil,
263+
expectedError: ErrVerifyScriptVerifyInvalidFlags,
264+
description: "invalid flags should return error",
265+
},
266+
}
267+
268+
for _, tt := range tests {
269+
t.Run(tt.name, func(t *testing.T) {
270+
valid, err := scriptPubkey.Verify(0, tx, tt.spentOutputs, tt.inputIndex, tt.flags)
271+
if err == nil {
272+
t.Errorf("Expected error %v, got nil (valid=%v)", tt.expectedError, valid)
273+
return
274+
}
275+
if !errors.Is(err, tt.expectedError) {
276+
t.Errorf("Expected error %v, got %v", tt.expectedError, err)
210277
}
211278
})
212279
}
213280
}
214281

215282
// testVerifyScript is a helper function that creates the necessary objects and calls VerifyScript
216-
func testVerifyScript(t *testing.T, scriptPubkeyHex string, amount int64, txToHex string, inputIndex uint) error {
283+
func testVerifyScript(t *testing.T, scriptPubkeyHex string, amount int64, txToHex string, inputIndex uint) (bool, error) {
217284
scriptPubkeyBytes, err := hex.DecodeString(scriptPubkeyHex)
218285
if err != nil {
219286
t.Fatalf("Failed to decode script pubkey hex: %v", err)

0 commit comments

Comments
 (0)