txscript: Make min push accept raw opcode and data.
This converts the checkMinimalDataPush function defined on a parsed opcode to a standalone function which accepts an opcode and data slice instead in order to make it more flexible for raw script analysis. It also updates all callers accordingly.
This commit is contained in:
parent
62c608f265
commit
710bd5646e
2 changed files with 50 additions and 56 deletions
|
@ -225,6 +225,55 @@ func isOpcodeConditional(opcode byte) bool {
|
|||
}
|
||||
}
|
||||
|
||||
// checkMinimalDataPush returns whether or not the provided opcode is the
|
||||
// smallest possible way to represent the given data. For example, the value 15
|
||||
// could be pushed with OP_DATA_1 15 (among other variations); however, OP_15 is
|
||||
// a single opcode that represents the same value and is only a single byte
|
||||
// versus two bytes.
|
||||
func checkMinimalDataPush(op *opcode, data []byte) error {
|
||||
opcodeVal := op.value
|
||||
dataLen := len(data)
|
||||
switch {
|
||||
case dataLen == 0 && opcodeVal != OP_0:
|
||||
str := fmt.Sprintf("zero length data push is encoded with opcode %s "+
|
||||
"instead of OP_0", op.name)
|
||||
return scriptError(ErrMinimalData, str)
|
||||
case dataLen == 1 && data[0] >= 1 && data[0] <= 16:
|
||||
if opcodeVal != OP_1+data[0]-1 {
|
||||
// Should have used OP_1 .. OP_16
|
||||
str := fmt.Sprintf("data push of the value %d encoded with opcode "+
|
||||
"%s instead of OP_%d", data[0], op.name, data[0])
|
||||
return scriptError(ErrMinimalData, str)
|
||||
}
|
||||
case dataLen == 1 && data[0] == 0x81:
|
||||
if opcodeVal != OP_1NEGATE {
|
||||
str := fmt.Sprintf("data push of the value -1 encoded with opcode "+
|
||||
"%s instead of OP_1NEGATE", op.name)
|
||||
return scriptError(ErrMinimalData, str)
|
||||
}
|
||||
case dataLen <= 75:
|
||||
if int(opcodeVal) != dataLen {
|
||||
// Should have used a direct push
|
||||
str := fmt.Sprintf("data push of %d bytes encoded with opcode %s "+
|
||||
"instead of OP_DATA_%d", dataLen, op.name, dataLen)
|
||||
return scriptError(ErrMinimalData, str)
|
||||
}
|
||||
case dataLen <= 255:
|
||||
if opcodeVal != OP_PUSHDATA1 {
|
||||
str := fmt.Sprintf("data push of %d bytes encoded with opcode %s "+
|
||||
"instead of OP_PUSHDATA1", dataLen, op.name)
|
||||
return scriptError(ErrMinimalData, str)
|
||||
}
|
||||
case dataLen <= 65535:
|
||||
if opcodeVal != OP_PUSHDATA2 {
|
||||
str := fmt.Sprintf("data push of %d bytes encoded with opcode %s "+
|
||||
"instead of OP_PUSHDATA2", dataLen, op.name)
|
||||
return scriptError(ErrMinimalData, str)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// executeOpcode peforms execution on the passed opcode. It takes into account
|
||||
// whether or not it is hidden by conditionals, but some rules still must be
|
||||
// tested in this case.
|
||||
|
@ -269,7 +318,7 @@ func (vm *Engine) executeOpcode(pop *parsedOpcode) error {
|
|||
if vm.dstack.verifyMinimalData && vm.isBranchExecuting() &&
|
||||
pop.opcode.value >= 0 && pop.opcode.value <= OP_PUSHDATA4 {
|
||||
|
||||
if err := pop.checkMinimalDataPush(); err != nil {
|
||||
if err := checkMinimalDataPush(pop.opcode, pop.data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -692,61 +692,6 @@ func (pop *parsedOpcode) checkParseableInScript(script []byte, scriptPos int) (i
|
|||
return scriptPos, nil
|
||||
}
|
||||
|
||||
// checkMinimalDataPush returns whether or not the current data push uses the
|
||||
// smallest possible opcode to represent it. For example, the value 15 could
|
||||
// be pushed with OP_DATA_1 15 (among other variations); however, OP_15 is a
|
||||
// single opcode that represents the same value and is only a single byte versus
|
||||
// two bytes.
|
||||
func (pop *parsedOpcode) checkMinimalDataPush() error {
|
||||
data := pop.data
|
||||
dataLen := len(data)
|
||||
opcode := pop.opcode.value
|
||||
|
||||
if dataLen == 0 && opcode != OP_0 {
|
||||
str := fmt.Sprintf("zero length data push is encoded with "+
|
||||
"opcode %s instead of OP_0", pop.opcode.name)
|
||||
return scriptError(ErrMinimalData, str)
|
||||
} else if dataLen == 1 && data[0] >= 1 && data[0] <= 16 {
|
||||
if opcode != OP_1+data[0]-1 {
|
||||
// Should have used OP_1 .. OP_16
|
||||
str := fmt.Sprintf("data push of the value %d encoded "+
|
||||
"with opcode %s instead of OP_%d", data[0],
|
||||
pop.opcode.name, data[0])
|
||||
return scriptError(ErrMinimalData, str)
|
||||
}
|
||||
} else if dataLen == 1 && data[0] == 0x81 {
|
||||
if opcode != OP_1NEGATE {
|
||||
str := fmt.Sprintf("data push of the value -1 encoded "+
|
||||
"with opcode %s instead of OP_1NEGATE",
|
||||
pop.opcode.name)
|
||||
return scriptError(ErrMinimalData, str)
|
||||
}
|
||||
} else if dataLen <= 75 {
|
||||
if int(opcode) != dataLen {
|
||||
// Should have used a direct push
|
||||
str := fmt.Sprintf("data push of %d bytes encoded "+
|
||||
"with opcode %s instead of OP_DATA_%d", dataLen,
|
||||
pop.opcode.name, dataLen)
|
||||
return scriptError(ErrMinimalData, str)
|
||||
}
|
||||
} else if dataLen <= 255 {
|
||||
if opcode != OP_PUSHDATA1 {
|
||||
str := fmt.Sprintf("data push of %d bytes encoded "+
|
||||
"with opcode %s instead of OP_PUSHDATA1",
|
||||
dataLen, pop.opcode.name)
|
||||
return scriptError(ErrMinimalData, str)
|
||||
}
|
||||
} else if dataLen <= 65535 {
|
||||
if opcode != OP_PUSHDATA2 {
|
||||
str := fmt.Sprintf("data push of %d bytes encoded "+
|
||||
"with opcode %s instead of OP_PUSHDATA2",
|
||||
dataLen, pop.opcode.name)
|
||||
return scriptError(ErrMinimalData, str)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// disasmOpcode writes a human-readable disassembly of the provided opcode and
|
||||
// data into the provided buffer. The compact flag indicates the disassembly
|
||||
// should print a more compact representation of data-carrying and small integer
|
||||
|
|
Loading…
Reference in a new issue