RLP(Recursive Length Prefix)话浇,遞歸長度前綴編碼增蹭,它是以太坊序 化所采取的編碼方式奔脐。RLP主要用于以太坊中數(shù)據(jù)的網(wǎng)絡(luò)傳輸和持久化存儲。
RLP理解
RLP編碼
RLP編碼針對的數(shù)據(jù)類型主要有兩種:
- byte數(shù)組
- byte數(shù)組的數(shù)組星澳,即列表
設(shè)定Rc(x)為RLP編碼函數(shù)疚顷。
首先來看針對byte數(shù)組的幾個編碼規(guī)則:
1.針對單字節(jié)b,b ∈ [0,127], Rc(b) = b
eg:Rc(a) = 97, Rc(w) = 119
2.針對字節(jié)數(shù)組bytes禁偎,length(bytes) <= 55, Rc(bytes) = 128+length(bytes) ++ bytes(++符號表示拼接)
eg:Rc(abc) = [131 97 98 99], 其中131 = 128+length(abc), [97 98 99]為[abc]本身編碼
3.針對字節(jié)數(shù)組bytes腿堤,length(bytes) > 55,
Rc(bytes) = 183+sizeof(sizeof(bytes)) ++ Rc(sizeof(bytes)) + bytes
eg: str = "The length of this sentence is more than 55 bytes, I know it because I pre-designed it"
Rc(str) = [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]
經(jīng)計算該字符串占用字節(jié)數(shù)為86,顯然184 = 183 + sizeof(86)如暖,86 = sizeof(str), 84為“T”的編碼笆檀,從84開始后面便是str本身的編碼。
以上是針對字節(jié)數(shù)組盒至,接下來看以字節(jié)數(shù)組為元素的數(shù)組酗洒,這里稱之為列表的編碼方式:
首先要明確幾個概念,針對一個列表list枷遂,lenL是指list內(nèi)每個bytes字節(jié)數(shù)的總和樱衷,lenDl是指list每個bytes經(jīng)過Rc(x)編碼后的總長度。
lenL(list) =
lenDl(list) =
4.針對list[bytes0, bytes1...bytesN]酒唉,lenL(list) <= 55,
Rc(list) = 192+lenDl(list) ++ Rc()
eg: list = ["abc", "def"],
Rc(list) = [200 131 97 98 99 131 100 101 102]
首先矩桂,lenL(list) = 3+3 <= 55,
然后,根據(jù)規(guī)則3可以得出:
Rc[abc] = [131 97 98 99]痪伦,
Rc[def] = [131 100 101 102](128+3 100 101 102)
現(xiàn)在就知道侄榴,lenDl(list) = 4 + 4 = 8,所以開始的200就是192+8的結(jié)構(gòu)网沾,后面跟的是list里每個bytes的RLP編碼癞蚕。
5.針對list[bytes0, bytes1...bytesN],lenL(list) > 55,
Rc(list) = 247+sizeof(lenDl(list)) ++ lenDl(list) ++ Rc()
eg: list = ["The length of this sentence is more than 55 bytes,",
" I know it because I pre-designed it"]
Rc(list) = [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]
首先辉哥,lenL(list) = 51+35 = 86 > 55,
然后桦山,根據(jù)規(guī)則2可以得出:
Rc[The length of this sentence is more than 55 bytes,] = [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](179 = 128 + 51),
Rc[ I know it because I pre-designed it]
= [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](163 = 128 + 35)
現(xiàn)在就知道,lenDl(list) = 52 + 36 = 88度苔,88只需占1個字節(jié)即可匆篓,所以開始的248就是247+1的結(jié)構(gòu)浑度,后面的88是lenDl(list)本身的編碼寇窑,再后面跟的是list里每個bytes的RLP編碼。
RLP解碼
我們知道箩张,有編碼就有解碼甩骏。編碼是解碼的一個逆過程,我們發(fā)現(xiàn)在編碼的時候不同的情況都有一個不同的字節(jié)前綴先慷,所以解碼的時候也是從這個字節(jié)前綴入手饮笛。
設(shè)定Dr(x)為解碼函數(shù),s為x的第一個字節(jié)论熙。
subX[m,n]表示x的子串福青,從x的第m個開始取n個字節(jié)。
bin2Int(bytes)表示將一個字節(jié)數(shù)組按BigEndian編碼轉(zhuǎn)換為整數(shù)脓诡,目的在于求出被編碼字符串長度无午。
1. s ∈ [0, 127], 對應(yīng)編碼規(guī)則1單字節(jié)解碼,
Dr(x) = x
2. s ∈ [128, 184), 對應(yīng)編碼規(guī)則2祝谚,數(shù)組長度不超過55
Dr(x) = subX[2, s-128]
3. s ∈ [184, 192), 對應(yīng)編碼規(guī)則3宪迟,數(shù)組長度超過55
bin2Int(subX[1, s-183])為被編碼數(shù)組的長度
Dr(x) = subX[bin2Int(subX[1, s-183]+1, bin2Int(subX[1, s-183])]
4. s ∈ [192, 247), 對應(yīng)編碼規(guī)則4,總長不超過55的列表 列表總長lenDl = s-192交惯,然后遞歸調(diào)用解碼規(guī)則1-3即可
Dr(x) =
5. s ∈ [247, 256], 對應(yīng)編碼規(guī)則5次泽,總長超過55的列表,列表總長lenDl = bin2Int(subX[1, s-247]),然后遞歸調(diào)用解碼規(guī)則1-3即可
Dr(x) =
RLP源碼
上面大概了解了RLP的編解碼方式席爽,下面我們就到eth源代碼中去看看有關(guān)RLP的代碼是怎么寫的意荤。
上篇文章主要介紹了geth源碼結(jié)構(gòu),RLP源碼主要在rlp目錄下只锻。
typeCache.go
該結(jié)構(gòu)主要實(shí)現(xiàn)不同類型和對應(yīng)編解碼器的映射關(guān)系玖像,通過它去獲取對應(yīng)的編解碼器。
核心數(shù)據(jù)結(jié)構(gòu)
// 核心數(shù)據(jù)結(jié)構(gòu)
var (
typeCacheMutex sync.RWMutex // 讀寫鎖炬藤,用于在多線程時保護(hù)typeCache
typeCache = make(map[typekey]*typeinfo) // 保存類型 -> 編碼器函數(shù)的數(shù)據(jù)結(jié)構(gòu)
// Map的key是類型御铃,value是對應(yīng)的編碼和解碼器
)
type typeinfo struct {
decoder // 解碼器函數(shù)
writer // 編碼器函數(shù)
}
如何獲取編碼器和解碼器的函數(shù)
// 獲取編碼器和解碼器的函數(shù)
func cachedTypeInfo(typ reflect.Type, tags tags) (*typeinfo, error) {
typeCacheMutex.RLock() // 加鎖保護(hù)
info := typeCache[typekey{typ, tags}] // 將傳入的typ和tags封裝為typekey類型
typeCacheMutex.RUnlock() // 解鎖
if info != nil { // 成功獲取到typ對應(yīng)的編解碼函數(shù)
return info, nil
}
// not in the cache, need to generate info for this type.
// 編解碼不在typeCache中,需要創(chuàng)建該typ對應(yīng)的編解碼函數(shù)
typeCacheMutex.Lock()
defer typeCacheMutex.Unlock()
return cachedTypeInfo1(typ, tags)
}
// 新建typ對應(yīng)的編解碼函數(shù)
func cachedTypeInfo1(typ reflect.Type, tags tags) (*typeinfo, error) {
key := typekey{typ, tags}
info := typeCache[key]
if info != nil {
// another goroutine got the write lock first
/// 其他線程已經(jīng)成功創(chuàng)建
return info, nil
}
// put a dummmy value into the cache before generating.
// if the generator tries to lookup itself, it will get
// the dummy value and won't call itself recursively.
// 這個地方首先創(chuàng)建了一個值來填充這個類型的位置沈矿,避免遇到一些遞歸定義的數(shù)據(jù)類型形成死循環(huán)
typeCache[key] = new(typeinfo)
info, err := genTypeInfo(typ, tags) // 生成對應(yīng)類型的編解碼器函數(shù)
if err != nil {
// remove the dummy value if the generator fails
// 創(chuàng)建失敗處理
delete(typeCache, key)
return nil, err
}
*typeCache[key] = *info
return typeCache[key], err
}
生成對應(yīng)編解碼器的函數(shù)
// 生成對應(yīng)編解碼器的函數(shù)
func genTypeInfo(typ reflect.Type, tags tags) (info *typeinfo, err error) {
info = new(typeinfo)
if info.decoder, err = makeDecoder(typ, tags); err != nil {
return nil, err
}
if info.writer, err = makeWriter(typ, tags); err != nil {
return nil, err
}
return info, nil
}
decode.go
typeCache定義了類型與對應(yīng)解編碼器的映射關(guān)系上真,接下來就看看對應(yīng)的編碼和解碼代碼。
// 定義一些解碼錯誤
var (
// EOL is returned when the end of the current list
// has been reached during streaming.
EOL = errors.New("rlp: end of list")
// Actual Errors
ErrExpectedString = errors.New("rlp: expected String or Byte")
ErrExpectedList = errors.New("rlp: expected List")
ErrCanonInt = errors.New("rlp: non-canonical integer format")
ErrCanonSize = errors.New("rlp: non-canonical size information")
ErrElemTooLarge = errors.New("rlp: element is larger than containing list")
ErrValueTooLarge = errors.New("rlp: value size exceeds available input length")
ErrMoreThanOneValue = errors.New("rlp: input contains more than one value")
// internal errors
errNotInList = errors.New("rlp: call of ListEnd outside of any list")
errNotAtEOL = errors.New("rlp: call of ListEnd not positioned at EOL")
errUintOverflow = errors.New("rlp: uint overflow")
errNoPointer = errors.New("rlp: interface given to Decode must be a pointer")
errDecodeIntoNil = errors.New("rlp: pointer given to Decode must not be nil")
)
// 解碼器 根據(jù)不同的類型返回對應(yīng)的解碼器函數(shù)
func makeDecoder(typ reflect.Type, tags tags) (dec decoder, err error) {
kind := typ.Kind()
switch {
case typ == rawValueType:
return decodeRawValue, nil
case typ.Implements(decoderInterface):
return decodeDecoder, nil
case kind != reflect.Ptr && reflect.PtrTo(typ).Implements(decoderInterface):
return decodeDecoderNoPtr, nil
case typ.AssignableTo(reflect.PtrTo(bigInt)):
return decodeBigInt, nil
case typ.AssignableTo(bigInt):
return decodeBigIntNoPtr, nil
case isUint(kind):
return decodeUint, nil
case kind == reflect.Bool:
return decodeBool, nil
case kind == reflect.String:
return decodeString, nil
case kind == reflect.Slice || kind == reflect.Array:
return makeListDecoder(typ, tags)
case kind == reflect.Struct:
return makeStructDecoder(typ)
case kind == reflect.Ptr:
if tags.nilOK {
return makeOptionalPtrDecoder(typ)
}
return makePtrDecoder(typ)
case kind == reflect.Interface:
return decodeInterface, nil
default:
return nil, fmt.Errorf("rlp: type %v is not RLP-serializable", typ)
}
}
根據(jù)以上switch方法可以調(diào)到各種解碼函數(shù)羹膳。
encode.go
接下來來看看有關(guān)編碼的代碼解讀睡互。首先定義了兩種特殊情況下的編碼方式。
var (
// Common encoded values.
// These are useful when implementing EncodeRLP.
EmptyString = []byte{0x80} // 針對空字符串的編碼
EmptyList = []byte{0xC0} // 針對空列表的編碼
)
接著定義了一個接口EncodeRLP供調(diào)用,但是很多情況下EncodeRLP會調(diào)用Encode方法就珠。
type Encoder interface {
// EncodeRLP should write the RLP encoding of its receiver to w.
// If the implementation is a pointer method, it may also be
// called for nil pointers.
//
// Implementations should generate valid RLP. The data written is
// not verified at the moment, but a future version might. It is
// recommended to write only a single value but writing multiple
// values or no value at all is also permitted.
EncodeRLP(io.Writer) error
}
func Encode(w io.Writer, val interface{}) error {
if outer, ok := w.(*encbuf); ok {
// Encode was called by some type's EncodeRLP.
// Avoid copying by writing to the outer encbuf directly.
return outer.encode(val)
}
eb := encbufPool.Get().(*encbuf) // 獲取一個編碼緩沖區(qū)encbuf
defer encbufPool.Put(eb)
eb.reset()
if err := eb.encode(val); err != nil {
return err
}
return eb.toWriter(w)
}
func (w *encbuf) encode(val interface{}) error {
rval := reflect.ValueOf(val)
ti, err := cachedTypeInfo(rval.Type(), tags{})
if err != nil {
return err
}
return ti.writer(rval, w)
}
makeWriter作為一個switch形式的編碼函數(shù)和上面的makeDecoder類似寇壳。
更多以太坊源碼解析請移駕全球最大同性交友網(wǎng),覺得有用記得給個小star哦??????
.
.
.
.
互聯(lián)網(wǎng)顛覆世界,區(qū)塊鏈顛覆互聯(lián)網(wǎng)!
--------------------------------------------------20180911 00:21