Skip to content

Commit

Permalink
Add conditional break in parsing the script after op return… (#170)
Browse files Browse the repository at this point in the history
* Added conditional break in parsing the script after op return…

… so long as we are not in a conditional block.

This is to get the library in line with v1.0.15.1 of SV node as outlined within this issue: bitcoin-sv/bitcoin-sv#296

Logic should match SV node here: https://github.com/bitcoin-sv/bitcoin-sv/blob/89d55a8ddb0e29f4786e11efad1e92cdefd0b9c5/src/script/script.cpp#L38

Signed-off-by: Darren Kellenschwiler <[email protected]>

* typo

Signed-off-by: Darren Kellenschwiler <[email protected]>

* Add unit test for Parse function

---------

Signed-off-by: Darren Kellenschwiler <[email protected]>
Co-authored-by: Michael Boeckli <[email protected]>
  • Loading branch information
deggen and boecklim authored Aug 28, 2023
1 parent 43ccba7 commit ebd4987
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 0 deletions.
17 changes: 17 additions & 0 deletions bscript/interpreter/opcodeparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ func (o *ParsedOpcode) enforceMinimumDataPush() error {
func (p *DefaultOpcodeParser) Parse(s *bscript.Script) (ParsedScript, error) {
script := *s
parsedOps := make([]ParsedOpcode, 0, len(script))
conditionalBlock := 0

for i := 0; i < len(script); {
instruction := script[i]
Expand All @@ -145,6 +146,22 @@ func (p *DefaultOpcodeParser) Parse(s *bscript.Script) (ParsedScript, error) {
return nil, errs.NewError(errs.ErrInvalidParams, "tx and previous output must be supplied for checksig")
}

switch parsedOp.op.val {
case bscript.OpIF, bscript.OpNOTIF, bscript.OpVERIF, bscript.OpVERNOTIF:
conditionalBlock++
case bscript.OpENDIF:
conditionalBlock--
case bscript.OpRETURN:
// If we are not in a conditional block, we end script evaluation.
// This must be the final evaluated opcode, everything after is ignored.
if conditionalBlock == 0 {
parsedOps = append(parsedOps, parsedOp)
return parsedOps, nil
}
// If we are in an conditional block, we continue parsing the other branches,
// therefore all data must adhere to push data rules.
}

switch {
case parsedOp.op.length == 1:
i++
Expand Down
63 changes: 63 additions & 0 deletions bscript/interpreter/opcodeparser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/libsv/go-bt/v2/bscript"
"github.com/libsv/go-bt/v2/bscript/interpreter/errs"
"github.com/stretchr/testify/require"
)

// TestOpcodeDisabled tests the opcodeDisabled function manually because all
Expand All @@ -28,3 +29,65 @@ func TestOpcodeDisabled(t *testing.T) {
}
}
}

func TestParse(t *testing.T) {
tt := []struct {
name string
scriptHexString string

expectedParsedScript ParsedScript
}{
{
name: "1 op return",
scriptHexString: "0168776a0024dc",

expectedParsedScript: ParsedScript{
ParsedOpcode{
op: opcode{
val: bscript.OpDATA1,
name: "OP_DATA_1",
length: 2,
exec: opcodePushData,
},
Data: []byte{bscript.OpENDIF},
},
ParsedOpcode{
op: opcode{
val: bscript.OpNIP,
name: "OP_NIP",
length: 1,
exec: opcodeNip,
},
Data: nil,
},
ParsedOpcode{
op: opcode{
val: bscript.OpRETURN,
name: "OP_RETURN",
length: 1,
exec: opcodeReturn,
},
Data: nil,
},
},
},
}

for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
s, err := bscript.NewFromHexString(tc.scriptHexString)
require.NoError(t, err)

codeParser := DefaultOpcodeParser{}
p, err := codeParser.Parse(s)
require.NoError(t, err)

for i := range p {
require.Equal(t, tc.expectedParsedScript[i].Data, p[i].Data)
require.Equal(t, tc.expectedParsedScript[i].op.length, p[i].op.length)
require.Equal(t, tc.expectedParsedScript[i].op.name, p[i].op.name)
require.Equal(t, tc.expectedParsedScript[i].op.val, p[i].op.val)
}
})
}
}

0 comments on commit ebd4987

Please sign in to comment.