{"record":{"id":"8666da6908a684bf","repo":"linera-io/linera-protocol","slug":"invalid-log-address-e","errorCode":null,"errorMessage":"invalid log address: {e}","messagePattern":"invalid log address: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-bridge/src/proof/mod.rs","lineNumber":563,"sourceCode":"/// Decodes a single log entry from RLP.\n///\n/// Enforces the declared payload boundary: after decoding address, topics, and data,\n/// verifies that exactly `payload_length` bytes were consumed.\nfn decode_log(data: &mut &[u8]) -> Result<ReceiptLog> {\n    let log_header =\n        alloy_rlp::Header::decode(data).map_err(|e| anyhow!(\"invalid log RLP: {e}\"))?;\n    ensure!(log_header.list, \"log must be an RLP list\");\n    ensure!(\n        data.len() >= log_header.payload_length,\n        \"log payload extends past available data\"\n    );\n\n    // Limit reads to the declared payload boundary.\n    let mut log_data_buf = &data[..log_header.payload_length];\n    *data = &data[log_header.payload_length..];\n\n    let address = <Address as alloy_rlp::Decodable>::decode(&mut log_data_buf)\n        .map_err(|e| anyhow!(\"invalid log address: {e}\"))?;\n\n    // Decode topics list\n    let topics_header = alloy_rlp::Header::decode(&mut log_data_buf)\n        .map_err(|e| anyhow!(\"invalid topics list RLP: {e}\"))?;\n    ensure!(topics_header.list, \"topics must be an RLP list\");\n    ensure!(\n        log_data_buf.len() >= topics_header.payload_length,\n        \"topics payload extends past log boundary\"\n    );\n\n    let mut topics_data = &log_data_buf[..topics_header.payload_length];\n    log_data_buf = &log_data_buf[topics_header.payload_length..];\n\n    let mut topics = Vec::new();\n    while !topics_data.is_empty() {\n        let topic = <B256 as alloy_rlp::Decodable>::decode(&mut topics_data)\n            .map_err(|e| anyhow!(\"invalid topic: {e}\"))?;\n        topics.push(topic);","sourceCodeStart":545,"sourceCodeEnd":581,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-bridge/src/proof/mod.rs#L545-L581","documentation":"The RLP list header of a log entry parsed fine, but the first field inside it — the emitter address — did not decode. In Ethereum's log format (address, topics, data) the address must be an RLP string of exactly 20 bytes. Failure means the bytes at that position are not a 20-byte string, usually because the payload boundary or field alignment is wrong.","triggerScenarios":"decode_log after slicing data[..log_header.payload_length]: the inner buffer starts mid-item or is empty; payload_length declared by the header does not actually start with the address; the log list contains fields in a non-standard order.","commonSituations":"A producer that serializes logs as (topics, address, data) or omits the address; miscomputed payload_length in a hand-rolled encoder; test fixtures built with serde JSON-to-RLP converters instead of canonical encoding.","solutions":["Cross-check the log bytes with alloy_primitives::Log::decode (or any reference decoder) — if it also rejects them, the producer is at fault.","Hex-dump the first bytes of the sliced payload and verify a 0x94-prefixed 20-byte string (or 0x80 for empty) is present.","Fix the encoder/prover that generated the receipt so logs use canonical [address, topics, data] ordering.","Regenerate the proof from the chain instead of patching bytes."],"exampleFix":"// before: parsing fields one by one from untrusted bytes\nlet address = <Address as alloy_rlp::Decodable>::decode(&mut log_data_buf)\n    .map_err(|e| anyhow!(\"invalid log address: {e}\"))?;\n\n// after: accept the log only if the reference decoder accepts the whole entry\nuse alloy_rlp::Decodable;\nif alloy_primitives::Log::decode(&mut &log_entry_bytes[..]).is_err() {\n    anyhow::bail!(\"log payload is not a valid Ethereum log (address/topics/data)\");\n}\n// safe to decode field-by-field now","handlingStrategy":"validation","validationCode":"use alloy_rlp::Decodable;\nfn log_has_valid_address(rlp: &[u8]) -> bool {\n    alloy_primitives::Log::decode(&mut &rlp[..]).is_ok() // enforces 20-byte address\n}","typeGuard":null,"tryCatchPattern":"match decode_receipt_logs(&bytes) {\n    Err(e) if e.to_string().contains(\"invalid log address\") => {\n        return Ok(ProofOutcome::Rejected); // bad proof, not a transient error\n    }\n    other => other,\n}","preventionTips":["Encode logs with alloy/alloy-consensus types instead of hand-rolled writers.","In prover tests, assert the produced log decodes via the reference Log type."],"tags":["rlp","ethereum","address","decoding","bridge"],"backgroundTag":"rlp-decoding-failed","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}