@pytest.mark.ported_from(
[
f"{LEGACY_TX_TESTS}/ttWrongRLP/RLPExtraRandomByteAtTheEndCopier.json",
f"{LEGACY_TX_TESTS}/ttWrongRLP/TRANSCT_HeaderLargerThanRLP_0Copier.json",
f"{LEGACY_TX_TESTS}/ttWrongRLP/TRANSCT_HeaderGivenAsArray_0Copier.json",
f"{LEGACY_TX_TESTS}/ttWrongRLP/RLPListLengthWithFirstZerosCopier.json",
f"{LEGACY_TX_TESTS}/ttWrongRLP/aMaliciousRLPCopier.json",
f"{LEGACY_TX_TESTS}/ttWrongRLP/RLPTransactionGivenAsArrayCopier.json",
],
)
@pytest.mark.exception_test
@pytest.mark.parametrize(
"mutation, error",
[
("truncated", TransactionException.RLP_ERROR_EOF),
("extra_byte", TransactionException.RLP_ERROR_SIZE),
("too_few_elements", TransactionException.RLP_TOO_FEW_ELEMENTS),
("too_many_elements", TransactionException.RLP_TOO_MANY_ELEMENTS),
("header_declares_more", TransactionException.RLP_ERROR_EOF),
(
"header_declares_less",
[
TransactionException.RLP_ERROR_EOF,
TransactionException.RLP_ERROR_SIZE,
],
),
(
"tx_as_byte_string",
[
TransactionException.RLP_INVALID_HEADER,
TransactionException.TYPE_NOT_SUPPORTED,
],
),
(
"list_size_leading_zeros",
TransactionException.RLP_ERROR_SIZE_LEADING_ZEROS,
),
],
)
def test_invalid_structure(
transaction_test: TransactionTestFiller,
pre: Alloc,
fork: Fork,
mutation: str,
error: TransactionException | list[TransactionException],
) -> None:
"""
Corrupt the RLP structure of the whole transaction.
A list header that declares less than the actual payload leaves
both a truncated final field and a trailing byte at the top level,
so clients report it as either an EOF or a size error.
Encoding the transaction as a byte string is a plain RLP error
before EIP-2718; afterwards the string is read as a typed
transaction whose type byte is unsupported.
"""
fields = signed_tx_fields(pre, fork)
items = [rlp_bytes(fields[name]) for name in fields]
payload = b"".join(items)
# The header mutations assume the long-form list header.
assert len(payload) >= 56
good = encode_tx(fields)
if mutation == "truncated":
rlp = good[:-1]
elif mutation == "extra_byte":
rlp = good + b"\x00"
elif mutation == "too_few_elements":
rlp = rlp_list(items[:-1])
elif mutation == "too_many_elements":
rlp = rlp_list(items + [rlp_bytes(b"")])
elif mutation == "header_declares_more":
rlp = encode_header(len(payload) + 1, 0xC0) + payload
elif mutation == "header_declares_less":
rlp = encode_header(len(payload) - 1, 0xC0) + payload
elif mutation == "tx_as_byte_string":
rlp = encode_header(len(payload), 0x80) + payload
elif mutation == "list_size_leading_zeros":
size = len(payload).to_bytes(2, "big")
assert size[0] == 0
rlp = bytes([0xC0 + 55 + len(size)]) + size + payload
transaction_test(pre=pre, tx=invalid_tx(pre, rlp, error))