{"record":{"id":"65ceefb33106ab9b","repo":"linera-io/linera-protocol","slug":"receipt-must-be-an-rlp-list","errorCode":null,"errorMessage":"receipt must be an RLP list","messagePattern":"receipt must be an RLP list","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-bridge/src/proof/mod.rs","lineNumber":386,"sourceCode":"/// Handles EIP-2718 typed receipts (type byte prefix < 0x80).\n///\n/// This variant exists because when tests build MPT tries\n/// with multiple receipts (e.g. test_build_receipt_proof_multiple_receipts),\n/// each receipt needs a distinct cumulative_gas_used to produce different\n/// RLP encodings. Without that, all empty-log receipts would be byte-identical,\n/// making the trie degenerate.\npub fn decode_receipt_logs(receipt_rlp: &[u8]) -> Result<Vec<ReceiptLog>> {\n    ensure!(!receipt_rlp.is_empty(), \"empty receipt RLP\");\n\n    let mut data: &[u8] = receipt_rlp;\n    // EIP-2718: if first byte < 0x80, it's a transaction type prefix\n    if data[0] < 0x80 {\n        data = &data[1..];\n    }\n\n    let list_header =\n        alloy_rlp::Header::decode(&mut data).map_err(|e| anyhow!(\"invalid receipt RLP: {e}\"))?;\n    ensure!(list_header.list, \"receipt must be an RLP list\");\n\n    // Limit reads to the receipt's declared payload.\n    ensure!(\n        data.len() >= list_header.payload_length,\n        \"receipt payload extends past available data\"\n    );\n    let mut data = &data[..list_header.payload_length];\n\n    // Skip: status (0), cumulative_gas_used (1), logs_bloom (2)\n    for i in 0..3 {\n        skip_rlp_item(&mut data).map_err(|e| anyhow!(\"failed to skip receipt field {i}: {e}\"))?;\n    }\n\n    // Decode the logs list\n    let logs_header =\n        alloy_rlp::Header::decode(&mut data).map_err(|e| anyhow!(\"invalid logs list RLP: {e}\"))?;\n    ensure!(logs_header.list, \"logs must be an RLP list\");\n    ensure!(","sourceCodeStart":368,"sourceCodeEnd":404,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-bridge/src/proof/mod.rs#L368-L404","documentation":"After optionally stripping the EIP-2718 type byte, decode_receipt_logs decodes the outer RLP header and requires it to be a list: a legacy/typed receipt encodes as an RLP list of [status, cumulativeGasUsed, bloom, logs]. Well-formed RLP that is a string (or garbage whose first bytes decode as a string header) fails here.","triggerScenarios":"Passing a single receipt field (e.g. just the bloom or logs) instead of the whole receipt; passing a trie node or transaction where the receipt belongs; input where the type byte was stripped incorrectly.","commonSituations":"Extracting the wrong element from the proof's verified value; test vectors that RLP-encode a bare bytes value; mixing up legacy and typed receipt encodings when hand-assembling fixtures.","solutions":["Use the value verified by alloy_trie proof verification (the leaf value at key tx_index) as the receipt RLP","Re-encode fixtures with alloy_consensus Receipt types instead of hand-written RLP","Confirm the EIP-2718 prefix handling: type byte only stripped when first byte < 0x80"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Structural pre-check: outer item after optional type byte must be an RLP list\nlet d = if bytes[0] < 0x80 { &bytes[1..] } else { bytes };\nlet (h, _) = alloy_rlp::Header::decode(d)?;\nanyhow::ensure!(h.list, \"not a receipt: outer item is an RLP string\");\nlet logs = decode_receipt_logs(bytes)?;","typeGuard":null,"tryCatchPattern":"match decode_receipt_logs(bytes) {\n    Ok(logs) => logs,\n    Err(e) if e.to_string().contains(\"receipt must be an RLP list\") => {\n        anyhow::bail!(\"input is not a receipt RLP; check proof leaf value extraction\")\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Use the exact leaf value returned by verify_proof as the receipt RLP","Encode fixtures with alloy_consensus Receipt types, never hand-rolled bytes","Keep type-byte stripping consistent with EIP-2718 (only when < 0x80)"],"tags":["ethereum","rlp","receipt","bridge","decoding","rust"],"backgroundTag":"rlp-decoding-failed","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}