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

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编解码规则及实现)