블록체인

[ethereum] RLP 인코딩

멍개. 2022. 8. 28. 08:22

RLP(Recursive Length Prefix) 인코딩은 임의의 길이를 가진 문자열과 배열을 인코딩하는 방법입니다. RLP 인코딩은 인코딩 된 값에 길이 정보를 포함합니다.

RLP 인코딩은 아스키 코드를 이용합니다.

● RLP 인코딩

RLP 인코딩은 5가지 케이스가 있습니다. RLP는 아스키 코드를 절대적으로 참조하여 인코딩을 수행합니다. RLP 인코딩은 아스키 코드표 1~127을 제외한 문자는 취급하지 않습니다. 예를들면 한국어, 일본어, 일부 특수문자 등

 

· 첫 번째 케이스 - 단일 바이트일 때

첫 번째 케이스는 하나의 바이트만 존재할 때 입니다. 즉, 문자 하나만 존재할 때 해당 바이트에 해당하는 아스키코드 값을 그대로 사용합니다.

이 때 해당 바이트의 범위0x01 ~ 0x7f(1 ~ 127)입니다.

0x12와 0x7f를 인코딩한다고 가정하겠습니다.

RLP(0x12) = 0x12
RLP(0x7f) = 0x7f

a를 인코딩한다고 하면, a를 아스키 코드에서 hex값을 찾습니다. a는 97이며 0x61입니다. 범위가 0x01 ~ 0x7f이므로 RLP(a) = 0x61이 됩니다.

RLP 인코딩에서 유일하게 prefix가 붙지않는 케이스입니다.

다음 네 가지 케이스의 경우는 길이를 포함한 정보를 prefix로 붙입니다.

· 두 번째 케이스 - 문자열의 길이가 0 ~ 55 바이트(Byte)

해당 케이스는 0x80prefix로 사용합니다. 즉, 0x80을 시작으로 인코딩 값을 이어붙입니다.

 

▶ 0x80에 문자열의 길이를 더한 후 아스키 코드표를 참조한 문자열값을 이어 붙입니다.

만약 abc를 RLP 인코딩 한다면

a = 0x61(97)

b = 0x62(98)

c = 0x63(99)

문자열의 길이 = 3

prefix = 0x80 + 0x03 = 0x83

RLP("abc") = 0x83 61 62 63

· 세 번째 케이스 - 문자열의 길이가 55 바이트(byte) 초과

해당 케이스는 0xb7 prefix로 사용합니다. 즉, 0xb7을 시작으로 인코딩 값을 이어붙입니다. 왜 하필 0xb7일까?

0x80에서 55(0x37)문자가 되었을 때 0x80 + 0x37 = 0xb7이 나옵니다.

0xb7에 문자열 길이의 바이트값을 더한 후, 길이를 이어 붙입니다. 그리고 아스키 코드표를 참조한 문자열값을이어 붙입니다.

만약, a*1000을 인코딩한다면,

a = 0x61(97)

문자열의 길이 = 1000 => 11(0x03) 11101000(0xc8)(바이트 단위로 끊는다) => 2

prefix = <0xb7 + 0x02> <0x03> <0xc8>

RLP("a"*1000) = 0xb9 03 c8 616161616...

해당 케이스에서 prefix는 0xbf까지 사용되며 최대 문자열길이는 (2^64) - 1까지 됩니다. 0xbf면 8바이트만큼 문자열 길이를 의미합니다.

0xbf ff ff ff ff ff ff ff ff 문자열 인코딩값

· 네 번째 케이스 - 배열의 모든 아이템들의 RLP 인코딩 된 값들의 길이가 55보다 작을 때

해당 케이스는 0xc0prefix로 사용합니다. 즉, 0xc0을 시작으로 인코딩 값을 이어붙입니다.

prefix의 범위는 0xc1 ~ 0xf7입니다.

 

▶ 배열의 모든 아이템들이 RLP 인코딩된 값의 길이를 더하고 각각의 요소를 인코딩 한 요소를 이어 붙입니다.

["ab"]을 인코딩하면

먼저, ab를 인코딩 합니다. RLP("ab") = 0x826162

인코딩된 값의 길이 = 3 = 0x03

prefix = 0xc0 + 0x03 = 0xc3

RLP(["ab"]) = 0xc3826162

["ab", "cd"] 을 인코딩하면

먼저 "ab"와 "cd"를 각각 RLP 인코딩합니다.

ab = 0x82 0x61 0x62 = 3자리

cd = 0x82 0x63 0x64 = 3자리

배열 요소의 인코딩 결과 길이 = 6 = 0x06

prefix = 0xc0 + 0x06

RLP(["ab", "cd"]) = 0xc6 826162 826364

· 다섯 번째 케이스 - 배열의 모든 아이템이 RLP 인코딩 된 값들의 길이가 55보다 클 때

해당 케이스는 0xf7prefix로 사용합니다.

prefix의 범위는 0xf8 ~ 0xff 입니다.

▶ prefix에 배열 요소들의 RLP 인코딩 값들의 길이를 표현한 값의 길이를 더하고 아스키 코드값으로 변환하여 붙입니다 (설명이 개떡같죠? 아래의 인코딩 예시를 보면 이해하기 어렵지 않습니다. 세 번째 케이스와 유사한 형태).

["a" * 50, "a" * 50]을 인코딩하면

먼저 "a" * 50을 인코딩 합니다. => 해당 인코딩은 문자열이기 때문에 길이에 따라 2, 3규칙을 적용합니다.

"a" * 50 = 0x80 + 0x32(50의 16진수) = 0xb2 61(50개)

"a" * 50 = 0x80 + 0x32(50의 16진수) = 0xb2 61(50개)

길이 = 0xb2 61(50개) 0xb2 61(50개) = 102(0x66)자리

102는 1바이트이며 0x66입니다

prefix = 0xf7 + 1 + "f" = 0xf8f

RLP(["a" * 50, "a" * 50]) = 0xf8 0x66 0xb2 61(50개) 0xb2 61(50개)

 

● RLP 디코딩

인코딩인 5가지 케이스가 있는것처럼 디코딩도 5가지 케이스가 있습니다.

· 첫 번째 규칙: 첫 번째 바이트가 0x00 ~ 0x7f

값 자체가 문자 데이터

· 두 번째 규칙: 첫 번째 바이트가 0x80 ~ 0xb7

해당 범위로 시작하면 문자열을 의미합니다.

첫 번째 바이트에서 0x80을 뺀 값이 문자열의 길이입니다.

· 세 번째 규칙: 첫 번째 바이트가 0xb8 ~ 0xbf

해당범위로 시작하면 55바이트를 초과한 문자열을 의미합니다.

첫 번째 바이트에서 0xb7을 뺀 값이 바이트로 표현된 RLP 아이템 갯수입니다. RLP 아이템 갯수만큼 인코딩된 문자가 이어집니다.

· 네 번째 규칙: 첫 번째 바이트가 0xc0 ~ 0xf7

해당 범위로 시작하면 배열을 의미합니다.

첫 번째 바이트에서 0xc0를 뺀 값이 배열의 요소 갯수를 의미합니다.

· 다섯 번째 규칙: 첫 번째 바이트가 0xf8 ~ 0xff

해당 범위로 시작하면 배열의 요소 합이 55바이트를 초과한 배열을 의미합니다.

첫 번째 바이트에서 0xf7을 뺀 값이 배열의 길이를 표현한 바이트의 갯수를 의미합니다.

0xf8 0x66 0xb2 61(50개) 0xb2 61(50개) 에서

0xf8 - 0xf7 = 0x01

뒤에 나오는 0x01(1) 만큼 배열의 길이를 의미하는 바이트가 있음

0x66(102) 만큼 배열 요소를 의미

0xb2 0x61(50개) 0xb2 0x61(50개) => 해당 값은 두 번째 규칙으로 디코딩할 수 있다.

 

● RLP 인코딩의 한계

RLP 인코딩은 문자열배열 타입만 구분합니다.

한 가지 예시를 들어보겠습니다.

숫자 24,930을 RLP 인코딩을 수행하기 위해 해당수를 16진수로 바꿔야 합니다.

16진수로 바꾸고 바이트로 끊으면 2진수(1100001 01100010) -> 16진수(0x61 0x62)가 됩니다.

RLP(24,930) = 0x826162

해당 결과는 RLP("ab")와 동일합니다. 이더리움에서는 이러한 문제를 해결하기 위해 24,930이 아니라 "24930"으로 처리합니다.