linera-io/linera-protocol · error
logs payload extends past receipt boundary
Error message
logs payload extends past receipt boundary
What it means
The companion bounds check after 'logs must be an RLP list': the logs list's declared payload_length must fit within the bytes remaining inside the receipt payload. It guards the subsequent `&data[..logs_header.payload_length]` slice against out-of-bounds access when the inner length claim exceeds the outer receipt's size.
Source
Thrown at linera-bridge/src/proof/mod.rs:404
ensure!(list_header.list, "receipt must be an RLP list");
// Limit reads to the receipt's declared payload.
ensure!(
data.len() >= list_header.payload_length,
"receipt payload extends past available data"
);
let mut data = &data[..list_header.payload_length];
// Skip: status (0), cumulative_gas_used (1), logs_bloom (2)
for i in 0..3 {
skip_rlp_item(&mut data).map_err(|e| anyhow!("failed to skip receipt field {i}: {e}"))?;
}
// Decode the logs list
let logs_header =
alloy_rlp::Header::decode(&mut data).map_err(|e| anyhow!("invalid logs list RLP: {e}"))?;
ensure!(logs_header.list, "logs must be an RLP list");
ensure!(
data.len() >= logs_header.payload_length,
"logs payload extends past receipt boundary"
);
let mut logs_data = &data[..logs_header.payload_length];
let mut logs = Vec::new();
while !logs_data.is_empty() {
logs.push(decode_log(&mut logs_data)?);
}
Ok(logs)
}
/// Parses a `DepositInitiated` event from a receipt log.
///
/// Verifies that `topic[0]` matches the event signature, that the log was emitted by
/// the `expected_emitter` (bridge contract address), and ABI-decodes the data fields.
/// The `depositor` field is indexed (stored in `topics[1]`); all other parameters areView on GitHub (pinned to 6c226ddcb3)
Solutions
- Reject the message and re-fetch the receipt from a trusted RPC to compare
- If building the bytes yourself, always encode with an RLP library (never concatenate manually) so outer/inner lengths stay consistent
- Treat recurring mismatches from one relayer as hostile input
Defensive patterns
Strategy: try-catch
Try / catch
match decode_receipt_logs(bytes) {
Ok(logs) => logs,
Err(e) if e.to_string().contains("extends past receipt boundary") => {
// nested length inconsistency: treat as hostile/corrupt input
anyhow::bail!("malformed receipt: inner logs length exceeds outer payload")
}
Err(e) => return Err(e),
} Prevention
- Always produce RLP with a library so nested length prefixes are computed correctly
- Refetch and compare when a single message fails this check; persistent failures mean a bad producer
- Fuzz the decoder with mutated length fields to confirm it errors instead of panicking
When it happens
Trigger: Malformed or adversarial RLP where the inner logs length contradicts the outer receipt length; receipts truncated mid-logs; encoding bugs producing nested length mismatches.
Common situations: Fuzzing corpus hitting nested-length inconsistencies; a fork or non-standard chain emitting receipts with inconsistent inner lengths; corrupted storage or transport flipping bytes inside the length prefix.
Related errors
- receipt payload extends past available data
- logs must be an RLP list
- block header payload extends past available data
- empty receipt RLP
- receipt must be an RLP list
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/7eb9a3aa67dde2d2.
Report an issue: GitHub.