Skip to content

test_invalid_structure()

Documentation for tests/frontier/validation/test_transaction_rlp.py::test_invalid_structure@ffe48572.

Generate fixtures for these test cases for Bogota with:

fill -v tests/frontier/validation/test_transaction_rlp.py::test_invalid_structure --fork Bogota

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.

Source code in tests/frontier/validation/test_transaction_rlp.py
@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))

Parametrized Test Cases

This test generates 8 parametrized test cases across 17 forks.