This commit is contained in:
Daniel Katzan 2025-03-12 10:52:21 +02:00 committed by GitHub
commit 4931cdf595
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 60 additions and 13 deletions

View file

@ -504,25 +504,51 @@ func isNullDataScript(scriptVersion uint16, script []byte) bool {
// A null script is of the form: // A null script is of the form:
// OP_RETURN <optional data> // OP_RETURN <optional data>
// //
// Thus, it can either be a single OP_RETURN or an OP_RETURN followed by a // The entire scriptPubKey, including OP_RETURN and the payload, must not
// data push up to MaxDataCarrierSize bytes. // exceed 83 bytes by default.
// The script can't possibly be a null data script if it doesn't start const MaxScriptPubKeySize = 83
// with OP_RETURN. Fail fast to avoid more work below.
// Check if the script size exceeds the maximum allowed size.
if len(script) > MaxScriptPubKeySize {
return false
}
// The script must start with OP_RETURN.
if len(script) < 1 || script[0] != OP_RETURN { if len(script) < 1 || script[0] != OP_RETURN {
return false return false
} }
// Single OP_RETURN. // Single OP_RETURN (no payload).
if len(script) == 1 { if len(script) == 1 {
return true return true
} }
// OP_RETURN followed by data push up to MaxDataCarrierSize bytes. // Tokenize and validate the script after OP_RETURN.
tokenizer := MakeScriptTokenizer(scriptVersion, script[1:]) tokenizer := MakeScriptTokenizer(scriptVersion, script[1:])
return tokenizer.Next() && tokenizer.Done() && for tokenizer.Next() {
(IsSmallInt(tokenizer.Opcode()) || tokenizer.Opcode() <= OP_PUSHDATA4) && opcode := tokenizer.Opcode()
len(tokenizer.Data()) <= MaxDataCarrierSize
// Allow small integer opcodes (OP_1 to OP_16).
if IsSmallInt(opcode) {
continue
}
// Allow valid data pushes.
if opcode <= OP_PUSHDATA4 {
// Ensure each data push is valid and within limits.
if len(tokenizer.Data()) > MaxDataCarrierSize {
return false
}
continue
}
// Any other opcode is invalid for a null data script.
return false
}
// Ensure the tokenizer successfully parsed the entire script.
return tokenizer.Done()
} }
// scriptType returns the type of the script being inspected from the known // scriptType returns the type of the script being inspected from the known

View file

@ -1032,11 +1032,10 @@ var scriptClassTests = []struct {
class: NonStandardTy, class: NonStandardTy,
}, },
{ {
// Almost nulldata, but add an additional opcode after the data // nulldata, with combinations of op_code and pushdata
// to make it nonstandard. name: "nulldata with multiple pushes",
name: "almost nulldata",
script: "RETURN 4 TRUE", script: "RETURN 4 TRUE",
class: NonStandardTy, class: NullDataTy,
}, },
// The next few are almost multisig (it is the more complex script type) // The next few are almost multisig (it is the more complex script type)
@ -1277,6 +1276,28 @@ func TestNullDataScript(t *testing.T) {
} }
} }
func TestIsNullDataScript(t *testing.T) {
tests := []struct {
name string
data []byte
expectedIsNullData bool
}{{
name: "multiple pushes",
data: hexToBytes("6a5d0f160100e6a233fc078088a5a9a30700"),
expectedIsNullData: true,
},
}
for i, test := range tests {
isNullData := isNullDataScript(0, test.data)
if isNullData != test.expectedIsNullData {
t.Errorf("IsNullDataScript: #%d (%s) wrong result -- "+
"got: %v, want: %v", i, test.name, isNullData,
test.expectedIsNullData)
continue
}
}
}
// TestNewScriptClass tests whether NewScriptClass returns a valid ScriptClass. // TestNewScriptClass tests whether NewScriptClass returns a valid ScriptClass.
func TestNewScriptClass(t *testing.T) { func TestNewScriptClass(t *testing.T) {
tests := []struct { tests := []struct {