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
|
// ScriptVerifyDiscourageUpgradeableWitnessProgram makes witness
|
||||||
// program with versions 2-16 non-standard.
|
// program with versions 2-16 non-standard.
|
||||||
ScriptVerifyDiscourageUpgradeableWitnessProgram
|
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 (
|
const (
|
||||||
|
|
|
@ -918,6 +918,47 @@ func opcodeNop(op *parsedOpcode, vm *Engine) error {
|
||||||
return nil
|
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.
|
// 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
|
// 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 {
|
func opcodeIf(op *parsedOpcode, vm *Engine) error {
|
||||||
condVal := OpCondFalse
|
condVal := OpCondFalse
|
||||||
if vm.isBranchExecuting() {
|
if vm.isBranchExecuting() {
|
||||||
ok, err := vm.dstack.PopBool()
|
ok, err := popIfBool(vm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
condVal = OpCondTrue
|
condVal = OpCondTrue
|
||||||
}
|
}
|
||||||
|
@ -969,10 +1011,11 @@ func opcodeIf(op *parsedOpcode, vm *Engine) error {
|
||||||
func opcodeNotIf(op *parsedOpcode, vm *Engine) error {
|
func opcodeNotIf(op *parsedOpcode, vm *Engine) error {
|
||||||
condVal := OpCondFalse
|
condVal := OpCondFalse
|
||||||
if vm.isBranchExecuting() {
|
if vm.isBranchExecuting() {
|
||||||
ok, err := vm.dstack.PopBool()
|
ok, err := popIfBool(vm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
condVal = OpCondTrue
|
condVal = OpCondTrue
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue