以太坊rlp符号化規則及び実現

4745 ワード

rlp符号化


具体的なルール定義は、この文書を参照して太坊ソースで学習することができる-RLP符号化
ここではその重要な部分を抜粋する

エンコーディングデータ型


(1)byte配列(2)byte配列の配列をリストと呼ぶ

コーディング規則


(1)値が[0,127]の間の単一バイトについて、その符号化がそのものである(2)byte配列長l<=55であれば、符号化の結果が配列そのものであり、さらに128+lを接頭辞として空文字の符号化が128(3)配列長が55より大きい場合、符号化結果の最初のものは183配列長の符号化の長さであり、次いで配列長のそれ自体の符号化であり、最後にbyte配列の符号化である.(4)リスト長が55未満の場合、符号化結果1位は192プラスリスト長の符号化の長さであり、次いで各サブリストの符号化を順次接続する.(5)リスト長が55を超えると、符号化結果1位は247プラスリスト長の符号化長、次いでリスト長そのものの符号化となり、最後に各サブリストの符号化が順次接続される.

デコードルール


復号時には、まず、符号化結果の1バイト目fの大きさに基づいて、(1)f∈[0128]の場合、それはバイトそのものである.(2)f∈[128,184)の場合、それは55を超えないbyte配列であり、配列の長さがl=f-128(3)の場合、f∈(184192)では、長さが55を超える配列であり、長さ自体の符号化長さll=f-183であり、2バイト目から長さがllであるbytesを読み出し、BigEndianに従って整数lに符号化し、lは配列の長さである.(4)f∈(192247)であれば、符号化後の総長さが55を超えないリストであり、リスト長はl=f-122である.再帰的にはルール1~4を用いて復号する.(5)f∈(247256)では、符号化後の長さが55より大きいリストであり、その長さ自体の符号化長ll=f-247であり、その後、2バイト目から長さがllのbytesを読み出し、BigEndianにより整数l、l、すなわちサブリスト長に符号化する.その後、復号ルールに従って復号する.

コーデックのC言語実装

// , rlp 
static int rlp_pack_varint_be(void **dest, size_t *left, uint64_t num)
{
    int space = 0;
    if (num <= 0xff) {
        space = 1;
        if (*left < space)
            return -1;
        *(uint8_t *)(*dest) = num;
    } else if (num <= 0xffff) {
        space = 2;
        if (*left < space)
            return -1;
        uint16_t tmp = htobe16((uint16_t)num);
        memcpy(*dest, &tmp, space);
    } else if (num <= 0xffffffff) {
        space = 4;
        if (num <= 0xffffff)
            space = 3;
        if (*left < space)
            return -1;
        uint32_t tmp = htobe32((uint32_t)num);
        memcpy(*dest, &tmp + 4 - space, space);
    } else {
        space = 8;
        if (num <= 0xffffffffff)
            space = 5;
        else if (num <= 0xffffffffffff)
            space = 6;
        else if (num <= 0xffffffffffffff)
            space = 7;
        if (*left < space)
            return -1;
        uint64_t tmp = htobe64(num);
        memcpy(*dest, &tmp + 8 - space, space);
    }

    *dest += space;
    *left -= space;
    return space;
}

// 
static int rlp_unpack_varint_be(void **dest, size_t *left, uint64_t *num)
{
    uint8_t prefix = ((uint8_t *)*dest)[0];
    if (prefix < 184 || prefix > 191)
        return - 1;
    *dest += 1;
    *left -= 1;
    int space = prefix - 183;
    uint64_t tmp = 0;
    memcpy(&tmp, *dest, space);
    *dest += space;
    *left -= space;
    *num = be64toh(tmp);
    return space;
}
// 
static int rlp_pack_buf(void **dest, size_t *left, const void *data, size_t len)
{
    if (*left < len)
        return -1;
    if (len == 1) {
        uint8_t val = ((uint8_t *)data)[0];
        if (val == 0)
            *(uint8_t *)(*dest) = 128;
        else
            memcpy(*dest, data, len);
        *dest += len;
        *left -= len;
    } else if (len <= 55) {
        if (*left < len + 1)
            return - 1;
        *(uint8_t *)(*dest) = 128 + len;
        *dest += 1;
        *left -= 1;
        memcpy(*dest, data, len);
        *dest += len;
        *left -= len;
    } else {
        char buf[8] = {0};
        void *buf_p = buf;
        size_t buf_left = 8;
        int space = rlp_pack_varint_be(&buf_p, &buf_left, len);
        if (*left < len + space + 1)
            return - 1;
        *(uint8_t *)(*dest) = 183 + space;
        *dest += 1;
        *left -= 1;
        memcpy(*dest, buf, space);
        *dest += space;
        *left -= space;
        memcpy(*dest, data, len);
        *dest += len;
        *left -= len;
    }
    return len;
}
// 
static int rlp_unpack_buf(void **src, size_t *left, void *dest, size_t len)
{
    if (*left > len)
        return -1;
    uint8_t prefix = ((uint8_t *)*src)[0];
    size_t dest_len = -1;
    if (prefix == 128) {
        *(uint8_t *)dest = 0;
        dest_len = 0;
        *src  += 1;
        *left -= 1;
    } else if (prefix < 128) {
        *(uint8_t *)dest = prefix;
        dest_len = 1;
        *src  += 1;
        *left -= 1;
    } else if (prefix <= 183) {
        dest_len = prefix - 128;
        if (len < dest_len)
            return -1;
        *src  += 1;
        *left -= 1;
        memcpy(dest, *src, dest_len);
        *src  += dest_len;
        *left -= dest_len;
    } else if (prefix <= 191) {
        uint64_t tmp = 0;
        rlp_unpack_varint_be(src, left, &tmp);
        dest_len = tmp;
        if (len < dest_len)
            return -1;
        memcpy(dest, *src, dest_len);
        *src  += dest_len;
        *left -= dest_len;
    }
    return dest_len;
}

以上はrlpの文字配列符号化のみを行い,すなわち符号化を実現し,復号規則の最初の3つについては,リストの符号化は次のイーサ坊ブロックhashの計算で実現する.