// SPDX-License-Identifier: MIT pragma solidity 0.8.23; /** * @title RLPReader — minimal Recursive-Length-Prefix decoder for MPT node parsing. * * @dev Operates on `bytes memory`. Just enough to walk Ethereum trie nodes: * decode an item header, split a list into its raw element items, and read * a string item's content. Rejects malformed / truncated input. */ library RLPReader { struct Item { uint256 offset; // byte offset of the item header within `data` uint256 headerLen; // length of the length-prefix header uint256 payloadLen; // length of the payload bool isList; } /// @notice Decode the RLP item header starting at `pos` in `data`. function itemAt(bytes memory data, uint256 pos) internal pure returns (Item memory it) { require(pos < data.length, "RLP:oob"); uint256 b = uint8(data[pos]); if (b < 0x80) { // single byte, itself the payload it = Item(pos, 0, 1, false); } else if (b < 0xb8) { it = Item(pos, 1, b - 0x80, false); } else if (b < 0xc0) { uint256 lenOfLen = b - 0xb7; uint256 len = _readLen(data, pos + 1, lenOfLen); it = Item(pos, 1 + lenOfLen, len, false); } else if (b < 0xf8) { it = Item(pos, 1, b - 0xc0, true); } else { uint256 lenOfLen = b - 0xf7; uint256 len = _readLen(data, pos + 1, lenOfLen); it = Item(pos, 1 + lenOfLen, len, true); } require(it.offset + it.headerLen + it.payloadLen <= data.length, "RLP:truncated"); } function _readLen(bytes memory data, uint256 pos, uint256 lenOfLen) private pure returns (uint256 len) { require(lenOfLen >= 1 && lenOfLen <= 8, "RLP:lenoflen"); require(pos + lenOfLen <= data.length, "RLP:len oob"); for (uint256 i = 0; i < lenOfLen; i++) { len = (len << 8) | uint8(data[pos + i]); } } /// @notice Total encoded length (header + payload) of the item at `pos`. function totalLen(bytes memory data, uint256 pos) internal pure returns (uint256) { Item memory it = itemAt(data, pos); return it.headerLen + it.payloadLen; } /// @notice Copy out the raw RLP bytes (header + payload) of the item at `pos`. function rawItemAt(bytes memory data, uint256 pos) internal pure returns (bytes memory out) { Item memory it = itemAt(data, pos); uint256 n = it.headerLen + it.payloadLen; out = new bytes(n); for (uint256 i = 0; i < n; i++) out[i] = data[pos + i]; } /// @notice Read the CONTENT (payload) of a string item; reverts if it is a list. function readBytes(bytes memory item) internal pure returns (bytes memory out) { Item memory it = itemAt(item, 0); require(!it.isList, "RLP:not string"); uint256 start = it.headerLen; // for single-byte, headerLen=0 so start=0 => the byte itself out = new bytes(it.payloadLen); for (uint256 i = 0; i < it.payloadLen; i++) out[i] = item[start + i]; } /// @notice True iff the first byte marks a list (>= 0xc0). function isList(bytes memory item) internal pure returns (bool) { return item.length != 0 && uint8(item[0]) >= 0xc0; } /// @notice Split a list item into the RAW rlp bytes of each of its elements. function splitList(bytes memory listItem) internal pure returns (bytes[] memory items) { Item memory head = itemAt(listItem, 0); require(head.isList, "RLP:not list"); uint256 end = head.headerLen + head.payloadLen; // First pass: count. uint256 count = 0; uint256 p = head.headerLen; while (p < end) { p += totalLen(listItem, p); count++; } items = new bytes[](count); p = head.headerLen; for (uint256 i = 0; i < count; i++) { uint256 n = totalLen(listItem, p); bytes memory el = new bytes(n); for (uint256 j = 0; j < n; j++) el[j] = listItem[p + j]; items[i] = el; p += n; } } }