Серіалізація за допомогою рекурсивного префікса довжини (RLP)
Останні оновлення сторінки: 23 лютого 2026 р.
Серіалізація з префіксом рекурсивної довжини (RLP) широко використовується у виконавчих клієнтах Ethereum. RLP стандартизує передачу даних між вузлами у форматі, що заощаджує простір. Мета RLP — кодувати довільно вкладені масиви двійкових даних, і RLP є основним методом кодування, що використовується для серіалізації об'єктів у виконавчому рівні Ethereum. Основна мета RLP — кодувати структуру; за винятком додатних цілих чисел, RLP делегує кодування конкретних типів даних (наприклад, рядків, чисел з плаваючою комою) протоколам вищого порядку. Додатні цілі числа мають бути представлені у двійковій формі з порядком байтів від старшого до молодшого (big-endian) без початкових нулів (таким чином, ціле значення нуль еквівалентне порожньому масиву байтів). Десеріалізовані додатні цілі числа з початковими нулями повинні розглядатися як недійсні будь-яким протоколом вищого порядку, що використовує RLP.
Більше інформації в жовтій книзі Ethereum (додаток B) (opens in a new tab).
Для кодування словника за допомогою RLP пропонуються дві канонічні форми:
- використання
[[k1,v1],[k2,v2]...]з ключами в лексикографічному порядку - використання кодування вищого рівня Patricia Tree, як це робить Ethereum
Визначення
Функція кодування RLP приймає елемент. Елемент визначається наступним чином:
- рядок (тобто масив байтів) — це елемент
- список елементів — це елемент
- додатне ціле число — це елемент
Наприклад, все перераховане нижче є елементами:
- порожній рядок;
- рядок, що містить слово «cat»;
- список, що містить будь-яку кількість рядків;
- і більш складні структури даних, як-от
["cat", ["puppy", "cow"], "horse", [[]], "pig", [""], "sheep"]. - число
100
Зауважте, що в контексті решти цієї сторінки «рядок» означає «певна кількість байтів двійкових даних»; спеціальні кодування не використовуються, і не мається на увазі жодних знань про вміст рядків (за винятком випадків, коли це вимагається правилом проти невикористання мінімальних додатних цілих чисел).
Кодування RLP визначається наступним чином:
- Для додатного цілого числа воно перетворюється на найкоротший масив байтів, інтерпретація якого з порядком байтів від старшого до молодшого (big-endian) є цим цілим числом, а потім кодується як рядок відповідно до правил, наведених нижче.
- Для одного байта, значення якого знаходиться в діапазоні
[0x00, 0x7f](десяткове[0, 127]), цей байт є власним кодуванням RLP. - В іншому випадку, якщо рядок має довжину 0–55 байтів, кодування RLP складається з одного байта зі значенням 0x80 (128 у десятковій системі) плюс довжина рядка, за яким слідує сам рядок. Таким чином, діапазон першого байта становить
[0x80, 0xb7](десятковий[128, 183]). - Якщо рядок довший за 55 байтів, кодування RLP складається з одного байта зі значенням 0xb7 (183 у десятковій системі) плюс довжина в байтах довжини рядка у двійковій формі, за якою слідує довжина рядка, за якою слідує сам рядок. Наприклад, рядок довжиною 1024 байти буде закодовано як
\xb9\x04\x00(десятковий185, 4, 0), за яким слідує рядок. Тут0xb9(183 + 2 = 185) є першим байтом, за яким слідують 2 байти0x0400(1024 у десятковій системі), які позначають довжину фактичного рядка. Таким чином, діапазон першого байта становить[0xb8, 0xbf](десятковий[184, 191]). - Якщо рядок має довжину 2^64 байтів або більше, він не може бути закодований.
- Якщо загальний обсяг корисного навантаження списку (тобто сукупна довжина всіх його елементів, що кодуються за допомогою RLP) становить 0–55 байтів, кодування RLP складається з одного байта зі значенням 0xc0 плюс довжина корисного навантаження, за яким слідує об’єднання RLP-кодувань елементів. Таким чином, діапазон першого байта становить
[0xc0, 0xf7](десятковий[192, 247]). - Якщо загальний обсяг корисного навантаження списку довший за 55 байтів, кодування RLP складається з одного байта зі значенням 0xf7 плюс довжина в байтах довжини корисного навантаження у двійковій формі, за якою слідує довжина корисного навантаження, за якою слідує об’єднання RLP-кодувань елементів. Таким чином, діапазон першого байта становить
[0xf8, 0xff](десятковий[248, 255]).
У коді це виглядає так:
1def rlp_encode(input):2 if isinstance(input,str):3 if len(input) == 1 and ord(input) < 0x80:4 return input5 return encode_length(len(input), 0x80) + input6 elif isinstance(input, list):7 output = ''8 for item in input:9 output += rlp_encode(item)10 return encode_length(len(output), 0xc0) + output1112def encode_length(L, offset):13 if L < 56:14 return chr(L + offset)15 elif L < 256**8:16 BL = to_binary(L)17 return chr(len(BL) + offset + 55) + BL18 raise Exception("input too long")1920def to_binary(x):21 if x == 0:22 return ''23 return to_binary(int(x / 256)) + chr(x % 256)Показати всеПриклади
- рядок «dog» = [ 0x83, 'd', 'o', 'g' ]
- список [ «cat», «dog» ] =
[ 0xc8, 0x83, 'c', 'a', 't', 0x83, 'd', 'o', 'g' ] - порожній рядок («null») =
[ 0x80 ] - порожній список =
[ 0xc0 ] - ціле число 0 =
[ 0x80 ] - байт '\x00' =
[ 0x00 ] - байт '\x0f' =
[ 0x0f ] - байти '\x04\x00' =
[ 0x82, 0x04, 0x00 ] - теоретико-множинне представлення (opens in a new tab) числа три,
[ [], [[]], [ [], [[]] ] ] = [ 0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0 ] - рядок «Lorem ipsum dolor sit amet, consectetur adipisicing elit» =
[ 0xb8, 0x38, 'L', 'o', 'r', 'e', 'm', ' ', ..., 'e', 'l', 'i', 't' ]`
Декодування RLP
Згідно з правилами та процесом кодування RLP, вхідні дані для декодування RLP розглядаються як масив двійкових даних. Процес декодування RLP виглядає наступним чином:
-
відповідно до першого байта (тобто префікса) вхідних даних і декодування типу даних, довжини фактичних даних і зсуву;
-
відповідно до типу та зсуву даних, декодувати дані відповідним чином, дотримуючись правила мінімального кодування для додатних цілих чисел;
-
продовжити декодування решти вхідних даних;
Серед них правила декодування типів даних та зсуву є наступними:
-
дані є рядком, якщо діапазон першого байта (тобто префікса) становить [0x00, 0x7f], і рядок — це точно сам перший байт;
-
дані є рядком, якщо діапазон першого байта становить [0x80, 0xb7], і рядок, довжина якого дорівнює першому байту мінус 0x80, слідує за першим байтом;
-
дані є рядком, якщо діапазон першого байта становить [0xb8, 0xbf], а довжина рядка, довжина якого в байтах дорівнює першому байту мінус 0xb7, слідує за першим байтом, а рядок слідує за довжиною рядка;
-
дані є списком, якщо діапазон першого байта становить [0xc0, 0xf7], а об’єднання RLP-кодувань усіх елементів списку, загальний обсяг корисного навантаження якого дорівнює першому байту мінус 0xc0, слідує за першим байтом;
-
дані є списком, якщо діапазон першого байта становить [0xf8, 0xff], а загальне корисне навантаження списку, довжина якого дорівнює першому байту мінус 0xf7, слідує за першим байтом, а об’єднання RLP-кодувань усіх елементів списку слідує за загальним корисним навантаженням списку;
У коді це виглядає так:
1def rlp_decode(input):2 if len(input) == 0:3 return4 output = ''5 (offset, dataLen, type) = decode_length(input)6 if type is str:7 output = instantiate_str(substr(input, offset, dataLen))8 elif type is list:9 output = instantiate_list(substr(input, offset, dataLen))10 output += rlp_decode(substr(input, offset + dataLen))11 return output1213def decode_length(input):14 length = len(input)15 if length == 0:16 raise Exception("input is null")17 prefix = ord(input[0])18 if prefix <= 0x7f:19 return (0, 1, str)20 elif prefix <= 0xb7 and length > prefix - 0x80:21 strLen = prefix - 0x8022 return (1, strLen, str)23 elif prefix <= 0xbf and length > prefix - 0xb7 and length > prefix - 0xb7 + to_integer(substr(input, 1, prefix - 0xb7)):24 lenOfStrLen = prefix - 0xb725 strLen = to_integer(substr(input, 1, lenOfStrLen))26 return (1 + lenOfStrLen, strLen, str)27 elif prefix <= 0xf7 and length > prefix - 0xc0:28 listLen = prefix - 0xc0;29 return (1, listLen, list)30 elif prefix <= 0xff and length > prefix - 0xf7 and length > prefix - 0xf7 + to_integer(substr(input, 1, prefix - 0xf7)):31 lenOfListLen = prefix - 0xf732 listLen = to_integer(substr(input, 1, lenOfListLen))33 return (1 + lenOfListLen, listLen, list)34 raise Exception("input does not conform to RLP encoding form")3536def to_integer(b):37 length = len(b)38 if length == 0:39 raise Exception("input is null")40 elif length == 1:41 return ord(b[0])42 return ord(substr(b, -1)) + to_integer(substr(b, 0, -1)) * 256Показати всеДля подальшого читання
- RLP в Ethereum (opens in a new tab)
- Ethereum під капотом: RLP (opens in a new tab)
- Coglio, A. (2020). Рекурсивний префікс довжини Ethereum в ACL2. arXiv preprint arXiv:2009.13769. (opens in a new tab)