Skip to content

Commit b45d6d9

Browse files
committed
Pin the envelope opcode allowlist
parse_envelope_script accepts four opcodes: OP_FALSE, OP_RETURN, PUSHDATA1 and PUSHDATA2. That is the whole on-chain surface of this protocol - a root_id and a state hash, under a hundred bytes - and it is deliberately narrow. PUSHDATA4 was already rejected, but nothing held it there. The existing tests cover truncation and the push-length ranges, so widening the allowlist later would not have failed anything. PUSHDATA4 is the case worth pinning rather than any other unused opcode: it declares its length in four bytes, so accepting it would mean a four-gigabyte length prefix read from a hostile script. That is the one unused opcode whose mishandling costs memory rather than correctness. Prompted by reading a BSV opcode table and asking which of the ~185 this protocol actually touches. The answer is four, which is worth a test saying so.
1 parent 14832a7 commit b45d6d9

1 file changed

Lines changed: 28 additions & 0 deletions

File tree

‎tests/test_onchain_envelope_smoke.py‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,31 @@ def test_parse_envelope_script_rejects_truncated_pushdata():
134134
parse_envelope_script(bytes.fromhex("006a4c"))
135135
with pytest.raises(SatRootError):
136136
parse_envelope_script(bytes.fromhex("006a4d01"))
137+
138+
139+
def test_parse_envelope_script_accepts_only_the_four_opcodes_it_needs():
140+
"""The envelope allowlist is OP_FALSE, OP_RETURN, PUSHDATA1, PUSHDATA2.
141+
142+
A SATROOT envelope carries a root_id and a state hash - under a hundred
143+
bytes - so PUSHDATA4 can never appear in a legitimate one. It is rejected
144+
today, but nothing held it there: the existing tests cover truncation and
145+
the push-length ranges, so widening the allowlist later would not fail
146+
anything.
147+
148+
PUSHDATA4 is the case worth pinning rather than any other unused opcode,
149+
because it declares its length in four bytes. Accepting it would mean a
150+
four-gigabyte length prefix read from a hostile script, which is the one
151+
unused opcode whose mishandling costs memory rather than correctness.
152+
153+
The trailing-byte case is here for the same reason: a complete, valid push
154+
followed by anything else must not be read as a second item.
155+
"""
156+
# OP_FALSE OP_RETURN OP_PUSHDATA4 <declares 0xffffffff bytes> <one byte>
157+
with pytest.raises(SatRootError):
158+
parse_envelope_script(bytes.fromhex("006a4effffffff41"))
159+
# A complete 1-byte push, then a stray opcode.
160+
with pytest.raises(SatRootError):
161+
parse_envelope_script(bytes.fromhex("006a0141ff"))
162+
# OP_FALSE OP_RETURN with nothing at all.
163+
with pytest.raises(SatRootError):
164+
parse_envelope_script(bytes.fromhex("006a"))

0 commit comments

Comments
 (0)