txscript: Implement ScriptVerifyNullFail

ScriptVerifyNullFail defines that signatures must be empty if a
CHECKSIG or CHECKMULTISIG operation fails.

This commit also enables ScriptVerifyNullFail at the mempool policy
level.
This commit is contained in:
David Hill 2016-11-17 22:08:06 -05:00
parent 153dca5c1e
commit 0efea24aa6
7 changed files with 53 additions and 1 deletions

View file

@ -1492,6 +1492,20 @@
"OK", "OK",
"BIP66 example 4, with DERSIG, non-null DER-compliant signature" "BIP66 example 4, with DERSIG, non-null DER-compliant signature"
], ],
[
"0",
"0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT",
"DERSIG,NULLFAIL",
"OK",
"BIP66 example 4, with DERSIG and NULLFAIL"
],
[
"0x09 0x300602010102010101",
"0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT",
"DERSIG,NULLFAIL",
"NULLFAIL",
"BIP66 example 4, with DERSIG and NULLFAIL, non-null DER-compliant signature"
],
[ [
"1", "1",
"0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG",
@ -1844,5 +1858,15 @@
["4294967296", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "UNSATISFIED_LOCKTIME", ["4294967296", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "UNSATISFIED_LOCKTIME",
"CSV fails if stack top bit 1 << 31 is not set, and tx version < 2"], "CSV fails if stack top bit 1 << 31 is not set, and tx version < 2"],
["NULLFAIL should cover all signatures and signatures only"],
["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66 and NULLFAIL-compliant"],
["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "OK", "BIP66 and NULLFAIL-compliant"],
["1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "OK", "BIP66 and NULLFAIL-compliant, not NULLDUMMY-compliant"],
["1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL,NULLDUMMY", "SIG_NULLDUMMY", "BIP66 and NULLFAIL-compliant, not NULLDUMMY-compliant"],
["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x09 0x300602010102010101", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66-compliant but not NULLFAIL-compliant"],
["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x09 0x300602010102010101", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "NULLFAIL", "BIP66-compliant but not NULLFAIL-compliant"],
["0 0x09 0x300602010102010101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66-compliant but not NULLFAIL-compliant"],
["0 0x09 0x300602010102010101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "NULLFAIL", "BIP66-compliant but not NULLFAIL-compliant"],
["The End"] ["The End"]
] ]

View file

@ -62,6 +62,10 @@ const (
// push operator. This is both rules 3 and 4 of BIP0062. // push operator. This is both rules 3 and 4 of BIP0062.
ScriptVerifyMinimalData ScriptVerifyMinimalData
// ScriptVerifyNullFail defines that signatures must be empty if
// a CHECKSIG or CHECKMULTISIG operation fails.
ScriptVerifyNullFail
// ScriptVerifySigPushOnly defines that signature scripts must contain // ScriptVerifySigPushOnly defines that signature scripts must contain
// only pushed data. This is rule 2 of BIP0062. // only pushed data. This is rule 2 of BIP0062.
ScriptVerifySigPushOnly ScriptVerifySigPushOnly

View file

@ -204,6 +204,11 @@ const (
// single element. // single element.
ErrCleanStack ErrCleanStack
// ErrNullFail is returned when the ScriptVerifyNullFail flag is
// set and signatures are not empty on failed checksig or checkmultisig
// operations.
ErrNullFail
// ------------------------------- // -------------------------------
// Failures related to soft forks. // Failures related to soft forks.
// ------------------------------- // -------------------------------
@ -266,6 +271,7 @@ var errorCodeStrings = map[ErrorCode]string{
ErrSigNullDummy: "ErrSigNullDummy", ErrSigNullDummy: "ErrSigNullDummy",
ErrPubKeyType: "ErrPubKeyType", ErrPubKeyType: "ErrPubKeyType",
ErrCleanStack: "ErrCleanStack", ErrCleanStack: "ErrCleanStack",
ErrNullFail: "ErrNullFail",
ErrDiscourageUpgradableNOPs: "ErrDiscourageUpgradableNOPs", ErrDiscourageUpgradableNOPs: "ErrDiscourageUpgradableNOPs",
ErrNegativeLockTime: "ErrNegativeLockTime", ErrNegativeLockTime: "ErrNegativeLockTime",
ErrUnsatisfiedLockTime: "ErrUnsatisfiedLockTime", ErrUnsatisfiedLockTime: "ErrUnsatisfiedLockTime",

View file

@ -53,6 +53,7 @@ func TestErrorCodeStringer(t *testing.T) {
{ErrSigNullDummy, "ErrSigNullDummy"}, {ErrSigNullDummy, "ErrSigNullDummy"},
{ErrPubKeyType, "ErrPubKeyType"}, {ErrPubKeyType, "ErrPubKeyType"},
{ErrCleanStack, "ErrCleanStack"}, {ErrCleanStack, "ErrCleanStack"},
{ErrNullFail, "ErrNullFail"},
{ErrDiscourageUpgradableNOPs, "ErrDiscourageUpgradableNOPs"}, {ErrDiscourageUpgradableNOPs, "ErrDiscourageUpgradableNOPs"},
{ErrNegativeLockTime, "ErrNegativeLockTime"}, {ErrNegativeLockTime, "ErrNegativeLockTime"},
{ErrUnsatisfiedLockTime, "ErrUnsatisfiedLockTime"}, {ErrUnsatisfiedLockTime, "ErrUnsatisfiedLockTime"},

View file

@ -2084,6 +2084,11 @@ func opcodeCheckSig(op *parsedOpcode, vm *Engine) error {
valid = signature.Verify(hash, pubKey) valid = signature.Verify(hash, pubKey)
} }
if !valid && vm.hasFlag(ScriptVerifyNullFail) && len(sigBytes) > 0 {
str := "signature not empty on failed checksig"
return scriptError(ErrNullFail, str)
}
vm.dstack.PushBool(valid) vm.dstack.PushBool(valid)
return nil return nil
} }
@ -2318,6 +2323,15 @@ func opcodeCheckMultiSig(op *parsedOpcode, vm *Engine) error {
} }
} }
if !success && vm.hasFlag(ScriptVerifyNullFail) {
for _, sig := range signatures {
if len(sig.signature) > 0 {
str := "not all signatures empty on failed checkmultisig"
return scriptError(ErrNullFail, str)
}
}
}
vm.dstack.PushBool(success) vm.dstack.PushBool(success)
return nil return nil
} }

View file

@ -160,6 +160,8 @@ func parseScriptFlags(flagStr string) (ScriptFlags, error) {
// Nothing. // Nothing.
case "NULLDUMMY": case "NULLDUMMY":
flags |= ScriptStrictMultiSig flags |= ScriptStrictMultiSig
case "NULLFAIL":
flags |= ScriptVerifyNullFail
case "P2SH": case "P2SH":
flags |= ScriptBip16 flags |= ScriptBip16
case "SIGPUSHONLY": case "SIGPUSHONLY":
@ -191,7 +193,7 @@ func parseExpectedResult(expected string) ([]ErrorCode, error) {
case "EQUALVERIFY": case "EQUALVERIFY":
return []ErrorCode{ErrEqualVerify}, nil return []ErrorCode{ErrEqualVerify}, nil
case "NULLFAIL": case "NULLFAIL":
return []ErrorCode{ErrSigNullDummy}, nil return []ErrorCode{ErrNullFail}, nil
case "SIG_HIGH_S": case "SIG_HIGH_S":
return []ErrorCode{ErrSigHighS}, nil return []ErrorCode{ErrSigHighS}, nil
case "SIG_HASHTYPE": case "SIG_HASHTYPE":

View file

@ -33,6 +33,7 @@ const (
ScriptStrictMultiSig | ScriptStrictMultiSig |
ScriptDiscourageUpgradableNops | ScriptDiscourageUpgradableNops |
ScriptVerifyCleanStack | ScriptVerifyCleanStack |
ScriptVerifyNullFail |
ScriptVerifyCheckLockTimeVerify | ScriptVerifyCheckLockTimeVerify |
ScriptVerifyCheckSequenceVerify | ScriptVerifyCheckSequenceVerify |
ScriptVerifyLowS ScriptVerifyLowS