txscript: add verification of the post-segwit minimal if policy
This commit modifies the op-code execution for OP_IF and OP_NOTIF to enforce the additional “minimal if” constraints which require the top-stack item when the op codes are encountered to be either an empty vector, or exactly [0x01].
This commit is contained in:
parent
65feec33e0
commit
9367aedfd7
2 changed files with 49 additions and 2 deletions
|
@ -84,6 +84,10 @@ const (
|
|||
// ScriptVerifyDiscourageUpgradeableWitnessProgram makes witness
|
||||
// program with versions 2-16 non-standard.
|
||||
ScriptVerifyDiscourageUpgradeableWitnessProgram
|
||||
|
||||
// ScriptVerifyMinimalIf makes a script with an OP_IF/OP_NOTIF whose
|
||||
// operand is anything other than empty vector or [0x01] non-standard.
|
||||
ScriptVerifyMinimalIf
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -918,6 +918,47 @@ func opcodeNop(op *parsedOpcode, vm *Engine) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// popIfBool enforces the "minimal if" policy during script execution if the
|
||||
// particular flag is set. If so, in order to eliminate an additional source
|
||||
// of nuisance malleability, post-segwit for version 0 witness programs, we now
|
||||
// require the following: for OP_IF and OP_NOT_IF, the top stack item MUST
|
||||
// either be an empty byte slice, or [0x01]. Otherwise, the item at the top of
|
||||
// the stack will be popped and interpreted as a boolean.
|
||||
func popIfBool(vm *Engine) (bool, error) {
|
||||
// When not in witness execution mode, not executing a v0 witness
|
||||
// program, or the minimal if flag isn't set pop the top stack item as
|
||||
// a normal bool.
|
||||
if !vm.witness || !vm.hasFlag(ScriptVerifyMinimalIf) {
|
||||
return vm.dstack.PopBool()
|
||||
}
|
||||
|
||||
// At this point, a v0 witness program is being executed and the minimal
|
||||
// if flag is set, so enforce additional constraints on the top stack
|
||||
// item.
|
||||
so, err := vm.dstack.PopByteArray()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// The top element MUST have a length of at least one.
|
||||
if len(so) > 1 {
|
||||
str := fmt.Sprintf("minimal if is active, top element MUST "+
|
||||
"have a length of at least, instead length is %v",
|
||||
len(so))
|
||||
return false, scriptError(ErrMinimalIf, str)
|
||||
}
|
||||
|
||||
// Additionally, if the length is one, then the value MUST be 0x01.
|
||||
if len(so) == 1 && so[0] != 0x01 {
|
||||
str := fmt.Sprintf("minimal if is active, top stack item MUST "+
|
||||
"be an empty byte array or 0x01, is instead: %v",
|
||||
so[0])
|
||||
return false, scriptError(ErrMinimalIf, str)
|
||||
}
|
||||
|
||||
return asBool(so), nil
|
||||
}
|
||||
|
||||
// opcodeIf treats the top item on the data stack as a boolean and removes it.
|
||||
//
|
||||
// An appropriate entry is added to the conditional stack depending on whether
|
||||
|
@ -936,10 +977,11 @@ func opcodeNop(op *parsedOpcode, vm *Engine) error {
|
|||
func opcodeIf(op *parsedOpcode, vm *Engine) error {
|
||||
condVal := OpCondFalse
|
||||
if vm.isBranchExecuting() {
|
||||
ok, err := vm.dstack.PopBool()
|
||||
ok, err := popIfBool(vm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ok {
|
||||
condVal = OpCondTrue
|
||||
}
|
||||
|
@ -969,10 +1011,11 @@ func opcodeIf(op *parsedOpcode, vm *Engine) error {
|
|||
func opcodeNotIf(op *parsedOpcode, vm *Engine) error {
|
||||
condVal := OpCondFalse
|
||||
if vm.isBranchExecuting() {
|
||||
ok, err := vm.dstack.PopBool()
|
||||
ok, err := popIfBool(vm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
condVal = OpCondTrue
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue