RLP(Recursive Length Prefix),中文翻譯過來叫遞歸長度前綴編碼,它是以太坊序列化所採用的編碼方式。RLP主要用於以太坊中數據的網絡傳輸和持久化存儲。git
對象序列化方法有不少種,常見的像JSON編碼,可是JSON有個明顯的缺點:編碼結果比較大。例若有以下的結構:github
type Student struct{ Name string `json:"name"` Sex string `json:"sex"` } s := Student{Name:"icattlecoder",Sex:"male"} bs,_ := json.Marsal(&s) print(string(bs)) // {"name":"icattlecoder","sex":"male"}
變量s
序列化的結果是{"name":"icattlecoder","sex":"male"}
,字符串長度35,實際有效數據是icattlecoder
和male
,共計16個字節,咱們能夠看到JSON的序列化時引入了太多的冗餘信息。假設以太坊採用JSON來序列化,那麼原本50GB的區塊鏈可能如今就要100GB,固然實際沒這麼簡單。json
因此,以太坊須要設計一種結果更小的編碼方法。數組
RLP實際只給如下兩種類型數據編碼:網絡
1. byte數組區塊鏈
2. byte數組的數組,稱之爲列表ui
規則1:對於值在[0, 127]之間的單個字節,其編碼是其自己。this
例1:a
的編碼是97
。編碼
規則2:若是byte數組長度l <= 55
,編碼的結果是數組自己,再加上128+l
做爲前綴。spa
例2:空字符串編碼是128
,即128 = 128 + 0
。
例3:abc
編碼結果是131 97 98 99
,其中131=128+len("abc")
,97 98 99
依次是a b c
。
規則3:若是數組長度大於55, 編碼結果第一個是183加數組長度的編碼的長度,而後是數組長度的自己的編碼,最後是byte數組的編碼。
請把上面的規則多讀幾篇,特別是數組長度的編碼的長度
。
例4:編碼下面這段字符串:
The length of this sentence is more than 55 bytes, I know it because I pre-designed it
這段字符串共86個字節,而86的編碼只須要一個字節,那就是它本身,所以,編碼的結果以下:
184 86 84 104 101 32 108 101 110 103 116 104 32 111 102 32 116 104 105 115 32 115 101 110 116 101 110 99 101 32 105 115 32 109 111 114 101 32 116 104 97 110 32 53 53 32 98 121 116 101 115 44 32 73 32 107 110 111 119 32 105 116 32 98 101 99 97 117 115 101 32 73 32 112 114 101 45 100 101 115 105 103 110 101 100 32 105 116
其中前三個字節的計算方式以下:
184 = 183 + 1
,由於數組長度86
編碼後僅佔用一個字節。86
即數組長度86
84
是T
的編碼例5:編碼一個重複1024次"a"的字符串,其結果爲:185 4 0 97 97 97 97 97 97 ...
。
1024按 big endian編碼爲0 0 4 0
,省略掉前面的零,長度爲2,所以185 = 183 + 2
。
規則1~3定義了byte數組的編碼方案,下面介紹列表的編碼規則。在此以前,咱們先定義列表長度是指子列表編碼後的長度之和。
規則4:若是列表長度小於55,編碼結果第一位是192加列表長度的編碼的長度,而後依次鏈接各子列表的編碼。
注意規則4自己是遞歸定義的。
例6:["abc", "def"]
的編碼結果是200 131 97 98 99 131 100 101 102
。
其中abc
的編碼爲131 97 98 99
,def
的編碼爲131 100 101 102
。兩個子字符串的編碼後總長度是8,所以編碼結果第一位計算得出:192 + 8 = 200
。
規則5:若是列表長度超過55,編碼結果第一位是247加列表長度的編碼長度,而後是列表長度自己的編碼,最後依次鏈接各子列表的編碼。
規則5自己也是遞歸定義的,和規則3類似。
例7:
["The length of this sentence is more than 55 bytes, ", "I know it because I pre-designed it"]
的編碼結果是:
248 88 179 84 104 101 32 108 101 110 103 116 104 32 111 102 32 116 104 105 115 32 115 101 110 116 101 110 99 101 32 105 115 32 109 111 114 101 32 116 104 97 110 32 53 53 32 98 121 116 101 115 44 32 163 73 32 107 110 111 119 32 105 116 32 98 101 99 97 117 115 101 32 73 32 112 114 101 45 100 101 115 105 103 110 101 100 32 105 116
其中前兩個字節的計算方式以下:
248 = 247 +1
88 = 86 + 2
,在規則3的示例中,長度爲86
,而在此例中,因爲有兩個子字符串,每一個子字符串自己的長度的編碼各佔1字節,所以總共佔2字節。179
依據規則2得出179 = 128 + 51
第55個字節163
一樣依據規則2得出163 = 128 + 35
例8:最後咱們再來看個稍複雜點的例子以加深理解遞歸長度前綴,
["abc",["The length of this sentence is more than 55 bytes, ", "I know it because I pre-designed it"]]
編碼結果是:
248 94 131 97 98 99 248 88 179 84 104 101 32 108 101 110 103 116 104 32 111 102 32 116 104 105 115 32 115 101 110 116 101 110 99 101 32 105 115 32 109 111 114 101 32 116 104 97 110 32 53 53 32 98 121 116 101 115 44 32 163 73 32 107 110 111 119 32 105 116 32 98 101 99 97 117 115 101 32 73 32 112 114 101 45 100 101 115 105 103 110 101 100 32 105 116
列表第一項字符串abc
根據規則2,編碼結果爲131 97 98 99
,長度爲4。
列表第二項也是一個列表項:
["The length of this sentence is more than 55 bytes, ", "I know it because I pre-designed it"]
根據規則5,結果爲
248 88 179 84 104 101 32 108 101 110 103 116 104 32 111 102 32 116 104 105 115 32 115 101 110 116 101 110 99 101 32 105 115 32 109 111 114 101 32 116 104 97 110 32 53 53 32 98 121 116 101 115 44 32 163 73 32 107 110 111 119 32 105 116 32 98 101 99 97 117 115 101 32 73 32 112 114 101 45 100 101 115 105 103 110 101 100 32 105 116
長度爲90,所以,整個列表的編碼結果第二位是90 + 4 = 94
, 佔用1個字節,第一位247 + 1 = 248
以上5條就是RPL的所有編碼規則。
各語言在具體實現RLP編碼時,首先須要將對像映射成byte數組或列表兩種形式。以go語言編碼struct爲例,會將其映射爲列表,例如Student
這個對象處理成列表["icattlecoder","male"]
type Student struct{ Name string Sex string } s := Student{Name:"icattlecoder",Sex:"male"} buff := bytes.Buffer{} rpl.Encode(&buff, &s) print(buff.Bytes()) // [210 140 105 99 97 116 116 108 101 99 111 100 101 114 132 109 97 108 101]
若是編碼map類型,能夠採用如下列表形式:
[["",""],["",""],["",""]]
解碼時,首先根據編碼結果第一個字節f
的大小,執行如下的規則判斷:
1. 若是f∈ [0,128), 那麼它是一個字節自己。
2. 若是f∈[128,184),那麼它是一個長度不超過55的byte數組,數組的長度爲 l=f-128
3. 若是f∈[184,192),那麼它是一個長度超過55的數組,長度自己的編碼長度ll=f-183
,而後從第二個字節開始讀取長度爲ll的bytes,按照BigEndian編碼成整數l,l即爲數組的長度。
4. 若是f∈(192,247],那麼它是一個編碼後總長度不超過55的列表,列表長度爲l=f-192
。遞歸使用規則1~4進行解碼。
5. 若是f∈(247,256],那麼它是編碼後長度大於55的列表,其長度自己的編碼長度ll=f-247
,而後從第二個字節讀取長度爲ll的bytes,按BigEndian編碼成整數l,l即爲子列表長度。而後遞歸根據解碼規則進行解碼。
以上解釋了什麼叫遞歸長度前綴編碼,這個名字自己很好的解釋了編碼規則。
參考