Skip to content

Commit b06e5ff

Browse files
committed
Merge branch 'script-verify-flags'
2 parents 144bf8f + 62fc259 commit b06e5ff

6 files changed

Lines changed: 145 additions & 30 deletions

File tree

bitcoin/core/script.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -772,11 +772,6 @@ def GetSigOpCount(self, fAccurate):
772772
return n
773773

774774

775-
SCRIPT_VERIFY_P2SH = object()
776-
SCRIPT_VERIFY_STRICTENC = object()
777-
SCRIPT_VERIFY_EVEN_S = object()
778-
SCRIPT_VERIFY_NOCACHE = object()
779-
780775
SIGHASH_ALL = 1
781776
SIGHASH_NONE = 2
782777
SIGHASH_SINGLE = 3
@@ -1051,10 +1046,6 @@ def SignatureHash(script, txTo, inIdx, hashtype):
10511046
'CScriptInvalidError',
10521047
'CScriptTruncatedPushDataError',
10531048
'CScript',
1054-
'SCRIPT_VERIFY_P2SH',
1055-
'SCRIPT_VERIFY_STRICTENC',
1056-
'SCRIPT_VERIFY_EVEN_S',
1057-
'SCRIPT_VERIFY_NOCACHE',
10581049
'SIGHASH_ALL',
10591050
'SIGHASH_NONE',
10601051
'SIGHASH_SINGLE',

bitcoin/core/scripteval.py

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,27 @@
4040

4141
SCRIPT_VERIFY_P2SH = object()
4242
SCRIPT_VERIFY_STRICTENC = object()
43-
SCRIPT_VERIFY_EVEN_S = object()
44-
SCRIPT_VERIFY_NOCACHE = object()
43+
SCRIPT_VERIFY_DERSIG = object()
44+
SCRIPT_VERIFY_LOW_S = object()
45+
SCRIPT_VERIFY_NULLDUMMY = object()
46+
SCRIPT_VERIFY_SIGPUSHONLY = object()
47+
SCRIPT_VERIFY_MINIMALDATA = object()
48+
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = object()
49+
SCRIPT_VERIFY_CLEANSTACK = object()
50+
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = object()
51+
52+
SCRIPT_VERIFY_FLAGS_BY_NAME = {
53+
'P2SH': SCRIPT_VERIFY_P2SH,
54+
'STRICTENC': SCRIPT_VERIFY_STRICTENC,
55+
'DERSIG': SCRIPT_VERIFY_DERSIG,
56+
'LOW_S': SCRIPT_VERIFY_LOW_S,
57+
'NULLDUMMY': SCRIPT_VERIFY_NULLDUMMY,
58+
'SIGPUSHONLY': SCRIPT_VERIFY_SIGPUSHONLY,
59+
'MINIMALDATA': SCRIPT_VERIFY_MINIMALDATA,
60+
'DISCOURAGE_UPGRADABLE_NOPS': SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS,
61+
'CLEANSTACK': SCRIPT_VERIFY_CLEANSTACK,
62+
'CHECKLOCKTIMEVERIFY': SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY,
63+
}
4564

4665
class EvalScriptError(bitcoin.core.ValidationError):
4766
"""Base class for exceptions raised when a script fails during EvalScript()
@@ -133,7 +152,7 @@ def _CheckSig(sig, pubkey, script, txTo, inIdx, err_raiser):
133152
return key.verify(h, sig)
134153

135154

136-
def _CheckMultiSig(opcode, script, stack, txTo, inIdx, err_raiser, nOpCount):
155+
def _CheckMultiSig(opcode, script, stack, txTo, inIdx, flags, err_raiser, nOpCount):
137156
i = 1
138157
if len(stack) < i:
139158
err_raiser(MissingOpArgumentsError, opcode, stack, i)
@@ -190,14 +209,24 @@ def _CheckMultiSig(opcode, script, stack, txTo, inIdx, err_raiser, nOpCount):
190209
if opcode == OP_CHECKMULTISIGVERIFY:
191210
err_raiser(VerifyOpFailedError, opcode)
192211

193-
while i > 0:
212+
while i > 1:
194213
stack.pop()
195214
i -= 1
196215

216+
# Note how Bitcoin Core duplicates the len(stack) check, rather than
217+
# letting pop() handle it; maybe that's wrong?
218+
if len(stack) and SCRIPT_VERIFY_NULLDUMMY in flags:
219+
if stack[-1] != b'':
220+
raise err_raiser(ArgumentsInvalidError, opcode, "dummy value not OP_0")
221+
222+
stack.pop()
223+
197224
if opcode == OP_CHECKMULTISIG:
198225
if success:
199226
stack.append(b"\x01")
200227
else:
228+
# FIXME: this is incorrect, but not caught by existing
229+
# test cases
201230
stack.append(b"\x00")
202231

203232

@@ -457,7 +486,7 @@ def check_args(n):
457486

458487
elif sop == OP_CHECKMULTISIG or sop == OP_CHECKMULTISIGVERIFY:
459488
tmpScript = CScript(scriptIn[pbegincodehash:])
460-
_CheckMultiSig(sop, tmpScript, stack, txTo, inIdx, err_raiser, nOpCount)
489+
_CheckMultiSig(sop, tmpScript, stack, txTo, inIdx, flags, err_raiser, nOpCount)
461490

462491
elif sop == OP_CHECKSIG or sop == OP_CHECKSIGVERIFY:
463492
check_args(2)
@@ -572,9 +601,15 @@ def check_args(n):
572601
check_args(2)
573602
del stack[-2]
574603

575-
elif sop == OP_NOP or (sop >= OP_NOP1 and sop <= OP_NOP10):
604+
elif sop == OP_NOP:
576605
pass
577606

607+
elif sop >= OP_NOP1 and sop <= OP_NOP10:
608+
if SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS in flags:
609+
err_raiser(EvalScriptError, "%s reserved for soft-fork upgrades" % OPCODE_NAMES[sop])
610+
else:
611+
pass
612+
578613
elif sop == OP_OVER:
579614
check_args(2)
580615
vch = stack[-2]
@@ -735,21 +770,30 @@ def VerifyScript(scriptSig, scriptPubKey, txTo, inIdx, flags=()):
735770
if not scriptSig.is_push_only():
736771
raise VerifyScriptError("P2SH scriptSig not is_push_only()")
737772

738-
# stackCopy cannot be empty here, because if it was the
773+
# restore stack
774+
stack = stackCopy
775+
776+
# stack cannot be empty here, because if it was the
739777
# P2SH HASH <> EQUAL scriptPubKey would be evaluated with
740778
# an empty stack and the EvalScript above would return false.
741-
assert len(stackCopy)
779+
assert len(stack)
742780

743-
pubKey2 = CScript(stackCopy.pop())
781+
pubKey2 = CScript(stack.pop())
744782

745-
EvalScript(stackCopy, pubKey2, txTo, inIdx, flags=flags)
783+
EvalScript(stack, pubKey2, txTo, inIdx, flags=flags)
746784

747-
if not len(stackCopy):
785+
if not len(stack):
748786
raise VerifyScriptError("P2SH inner scriptPubKey left an empty stack")
749787

750-
if not _CastToBool(stackCopy[-1]):
788+
if not _CastToBool(stack[-1]):
751789
raise VerifyScriptError("P2SH inner scriptPubKey returned false")
752790

791+
if SCRIPT_VERIFY_CLEANSTACK in flags:
792+
assert SCRIPT_VERIFY_P2SH in flags
793+
794+
if len(stack) != 1:
795+
raise VerifyScriptError("scriptPubKey left extra items on stack")
796+
753797

754798
class VerifySignatureError(bitcoin.core.ValidationError):
755799
pass
@@ -782,8 +826,15 @@ def VerifySignature(txFrom, txTo, inIdx):
782826
'MAX_STACK_ITEMS',
783827
'SCRIPT_VERIFY_P2SH',
784828
'SCRIPT_VERIFY_STRICTENC',
785-
'SCRIPT_VERIFY_EVEN_S',
786-
'SCRIPT_VERIFY_NOCACHE',
829+
'SCRIPT_VERIFY_DERSIG',
830+
'SCRIPT_VERIFY_LOW_S',
831+
'SCRIPT_VERIFY_NULLDUMMY',
832+
'SCRIPT_VERIFY_SIGPUSHONLY',
833+
'SCRIPT_VERIFY_MINIMALDATA',
834+
'SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS',
835+
'SCRIPT_VERIFY_CLEANSTACK',
836+
'SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY',
837+
'SCRIPT_VERIFY_FLAGS_BY_NAME',
787838
'EvalScriptError',
788839
'MaxOpCountError',
789840
'MissingOpArgumentsError',

bitcoin/tests/data/script_invalid.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,23 @@
163163
["1","NOP1 CHECKLOCKTIMEVERIFY NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 2 EQUAL", "P2SH,STRICTENC"],
164164
["'NOP_1_to_10' NOP1 CHECKLOCKTIMEVERIFY NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_11' EQUAL", "P2SH,STRICTENC"],
165165

166+
["Ensure 100% coverage of discouraged NOPS"],
167+
["1", "NOP1", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
168+
["1", "CHECKLOCKTIMEVERIFY", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
169+
["1", "NOP3", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
170+
["1", "NOP4", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
171+
["1", "NOP5", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
172+
["1", "NOP6", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
173+
["1", "NOP7", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
174+
["1", "NOP8", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
175+
["1", "NOP9", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
176+
["1", "NOP10", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
177+
178+
["NOP10", "1", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "Discouraged NOP10 in scriptSig"],
179+
180+
["1 0x01 0xb9", "HASH160 0x14 0x15727299b05b45fdaf9ac9ecf7565cfe27c3e567 EQUAL",
181+
"P2SH,DISCOURAGE_UPGRADABLE_NOPS", "Discouraged NOP10 in redeemScript"],
182+
166183
["0x50","1", "P2SH,STRICTENC", "opcode 0x50 is reserved"],
167184
["1", "IF 0xba ELSE 1 ENDIF", "P2SH,STRICTENC", "opcodes above NOP10 invalid if executed"],
168185
["1", "IF 0xbb ELSE 1 ENDIF", "P2SH,STRICTENC"],
@@ -492,6 +509,18 @@
492509
"STRICTENC",
493510
"P2PK NOT with hybrid pubkey"
494511
],
512+
[
513+
"1 0x47 0x3044022051254b9fb476a52d85530792b578f86fea70ec1ffb4393e661bcccb23d8d63d3022076505f94a403c86097841944e044c70c2045ce90e36de51f7e9d3828db98a07501 0x47 0x304402200a358f750934b3feb822f1966bfcd8bbec9eeaa3a8ca941e11ee5960e181fa01022050bf6b5a8e7750f70354ae041cb68a7bade67ec6c3ab19eb359638974410626e01 0x47 0x304402200955d031fff71d8653221e85e36c3c85533d2312fc3045314b19650b7ae2f81002202a6bb8505e36201909d0921f01abff390ae6b7ff97bbf959f98aedeb0a56730901",
514+
"3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG",
515+
"NULLDUMMY",
516+
"3-of-3 with nonzero dummy"
517+
],
518+
[
519+
"1 0x47 0x304402201bb2edab700a5d020236df174fefed78087697143731f659bea59642c759c16d022061f42cdbae5bcd3e8790f20bf76687443436e94a634321c16a72aa54cbc7c2ea01 0x47 0x304402204bb4a64f2a6e5c7fb2f07fef85ee56fde5e6da234c6a984262307a20e99842d702206f8303aaba5e625d223897e2ffd3f88ef1bcffef55f38dc3768e5f2e94c923f901 0x47 0x3044022040c2809b71fffb155ec8b82fe7a27f666bd97f941207be4e14ade85a1249dd4d02204d56c85ec525dd18e29a0533d5ddf61b6b1bb32980c2f63edf951aebf7a27bfe01",
520+
"3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG NOT",
521+
"NULLDUMMY",
522+
"3-of-3 NOT with invalid sig with nonzero dummy"
523+
],
495524
[
496525
"0x47 0x304402203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022054e1c258c2981cdfba5df1f46661fb6541c44f77ca0092f3600331abfffb125101 0x23 0x2103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640ac",
497526
"0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG",
@@ -504,6 +533,18 @@
504533
"P2SH,STRICTENC",
505534
"2-of-3 with one valid and one invalid signature due to parse error, nSigs > validSigs"
506535
],
536+
[
537+
"11 0x47 0x304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001",
538+
"0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG",
539+
"CLEANSTACK,P2SH",
540+
"P2PK with unnecessary input"
541+
],
542+
[
543+
"11 0x47 0x304402202f7505132be14872581f35d74b759212d9da40482653f1ffa3116c3294a4a51702206adbf347a2240ca41c66522b1a22a41693610b76a8e7770645dc721d1635854f01 0x43 0x410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac",
544+
"HASH160 0x14 0x31edc23bdafda4639e669f89ad6b2318dd79d032 EQUAL",
545+
"CLEANSTACK,P2SH",
546+
"P2SH with unnecessary input"
547+
],
507548

508549
["The End"]
509550
]

bitcoin/tests/data/script_valid.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,12 @@
726726
"P2SH",
727727
"P2SH(P2PK)"
728728
],
729+
[
730+
"0x47 0x304402204e2eb034be7b089534ac9e798cf6a2c79f38bcb34d1b179efd6f2de0841735db022071461beb056b5a7be1819da6a3e3ce3662831ecc298419ca101eb6887b5dd6a401 0x19 0x76a9147cf9c846cd4882efec4bf07e44ebdad495c94f4b88ac",
731+
"HASH160 0x14 0x2df519943d5acc0ef5222091f9dfe3543f489a82 EQUAL",
732+
"",
733+
"P2SH(P2PKH), bad sig but no VERIFY_P2SH"
734+
],
729735
[
730736
"0 0x47 0x3044022051254b9fb476a52d85530792b578f86fea70ec1ffb4393e661bcccb23d8d63d3022076505f94a403c86097841944e044c70c2045ce90e36de51f7e9d3828db98a07501 0x47 0x304402200a358f750934b3feb822f1966bfcd8bbec9eeaa3a8ca941e11ee5960e181fa01022050bf6b5a8e7750f70354ae041cb68a7bade67ec6c3ab19eb359638974410626e01 0x47 0x304402200955d031fff71d8653221e85e36c3c85533d2312fc3045314b19650b7ae2f81002202a6bb8505e36201909d0921f01abff390ae6b7ff97bbf959f98aedeb0a56730901",
731737
"3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG",

bitcoin/tests/test_scripteval.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,23 @@ def load_test_vectors(name):
6868
scriptSig = parse_script(scriptSig)
6969
scriptPubKey = parse_script(scriptPubKey)
7070

71-
yield (scriptSig, scriptPubKey, comment, test_case)
71+
flag_set = set()
72+
for flag in flags.split(','):
73+
if flag == '' or flag == 'NONE':
74+
pass
7275

76+
else:
77+
try:
78+
flag = SCRIPT_VERIFY_FLAGS_BY_NAME[flag]
79+
except IndexError:
80+
raise Exception('Unknown script verify flag %r' % flag)
7381

74-
class Test_EvalScript(unittest.TestCase):
75-
flags = (SCRIPT_VERIFY_P2SH, SCRIPT_VERIFY_STRICTENC)
82+
flag_set.add(flag)
83+
84+
yield (scriptSig, scriptPubKey, flag_set, comment, test_case)
7685

86+
87+
class Test_EvalScript(unittest.TestCase):
7788
def create_test_txs(self, scriptSig, scriptPubKey):
7889
txCredit = CTransaction([CTxIn(COutPoint(), CScript([OP_0, OP_0]), nSequence=0xFFFFFFFF)],
7990
[CTxOut(0, scriptPubKey)],
@@ -84,20 +95,20 @@ def create_test_txs(self, scriptSig, scriptPubKey):
8495
return (txCredit, txSpend)
8596

8697
def test_script_valid(self):
87-
for scriptSig, scriptPubKey, comment, test_case in load_test_vectors('script_valid.json'):
98+
for scriptSig, scriptPubKey, flags, comment, test_case in load_test_vectors('script_valid.json'):
8899
(txCredit, txSpend) = self.create_test_txs(scriptSig, scriptPubKey)
89100

90101
try:
91-
VerifyScript(scriptSig, scriptPubKey, txSpend, 0, flags=self.flags)
102+
VerifyScript(scriptSig, scriptPubKey, txSpend, 0, flags)
92103
except ValidationError as err:
93104
self.fail('Script FAILED: %r %r %r with exception %r' % (scriptSig, scriptPubKey, comment, err))
94105

95106
def test_script_invalid(self):
96-
for scriptSig, scriptPubKey, comment, test_case in load_test_vectors('script_invalid.json'):
107+
for scriptSig, scriptPubKey, flags, comment, test_case in load_test_vectors('script_invalid.json'):
97108
(txCredit, txSpend) = self.create_test_txs(scriptSig, scriptPubKey)
98109

99110
try:
100-
VerifyScript(scriptSig, scriptPubKey, txSpend, 0, flags=self.flags)
111+
VerifyScript(scriptSig, scriptPubKey, txSpend, 0, flags)
101112
except ValidationError:
102113
continue
103114

release-notes.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
python-bitcoinlib release notes
22
===============================
33

4+
v0.6.0
5+
======
6+
7+
Breaking API changes:
8+
9+
* Removed SCRIPT_VERIFY constants ``bitcoin.core.script``, leaving just the
10+
constants in ``bitcoin.core.scripteval``; being singletons the redundant
11+
constants were broken anyway.
12+
13+
* SCRIPT_VERIFY_EVEN_S renamed to SCRIPT_VERIFY_LOW_S to match Bitcoin Core's naming
14+
15+
* SCRIPT_VERIFY_NOCACHE removed as Bitcoin Core no longer has it (and we never
16+
did anything with it anyway)
17+
18+
419
v0.5.1
520
======
621

0 commit comments

Comments
 (0)