aere-contracts/contracts/interop/lib/RLPReader.sol
Aere Network a13a649b77
Some checks are pending
contracts-ci / Install (lockfile) → compile → full test suite (push) Waiting to run
contracts-ci / PQC known-answer tests (NIST vectors) (push) Waiting to run
contracts-ci / Coverage (scoped, with artifacts) (push) Waiting to run
Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:02:37 +03:00

101 lines
4.1 KiB
Solidity

// 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;
}
}
}