美文网首页以太坊(ethereum)实现研究
以太坊rlp编解码规则及实现

以太坊rlp编解码规则及实现

作者: 古则 | 来源:发表于2017-12-09 17:35 被阅读25次

    rlp编码

    具体规则定义可参考该文章
    以太坊源码学习—RLP编码

    这里摘录其关键部分

    编码数据类型

    (1)byte数组
    (2)byte数组的数组,称之为列表

    编码规则

    (1)对于值在[0, 127]之间的单个字节,其编码是其本身
    (2)如果byte数组长度l <= 55,编码的结果是数组本身,再加上128+l作为前缀
    空字符的编码是128
    (3) 如果数组长度大于55, 编码结果第一个是183加数组长度的编码的长度,然后是数组长度的本身的编码,最后是byte数组的编码。
    (4)如果列表长度小于55,编码结果第一位是192加列表长度的编码的长度,然后依次连接各子列表的编码。
    (5) 如果列表长度超过55,编码结果第一位是247加列表长度的编码长度,然后是列表长度本身的编码,最后依次连接各子列表的编码。

    解码规则

    解码时,首先根据编码结果第一个字节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即为子列表长度。然后递归根据解码规则进行解码。

    编解码的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的字符数组编解码,也就是实现了编码,解码规则的前三条,至于列表的编码会在下一篇以太坊区块hash的计算中实现

    相关文章

      网友评论

        本文标题:以太坊rlp编解码规则及实现

        本文链接:https://www.haomeiwen.com/subject/ushpixtx.html