前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C语言实现base58算法

C语言实现base58算法

作者头像
程序员小涛
发布2022-05-07 16:02:01
9450
发布2022-05-07 16:02:01
举报
文章被收录于专栏:涛的程序人生涛的程序人生

Base58是用于Bitcoin中使用的一种独特的编码方式,主要用于产生Bitcoin的钱包地址。相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"l",以及"+“和”/"符号。

Base58 与 Base64 异同 相同

一般都用于URL,邮件文本,可见字符显示。 都会造成信息冗余,数据量增大,因此不会用于大数据传输编码。 区别

编码集不同,Base58 的编码集在 Base64 的字符集的基础上去掉了比较容易混淆的字符。 Base64 采用直接切割 bit 的方法(8->6),而 Base58 采用大数进制转换,效率更低,使用场景更少。

base58.h

代码语言:javascript
复制
#ifndef BASE58_H
#define BASE58_H

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus

    extern bool (*base58_sha256_impl)(void *, const void *, size_t);
    extern bool base58_to_bin(void *, size_t *, const char *, size_t);
    extern int base58_check(const void *, size_t, const char *, size_t);
    extern bool base58_encode(char *, size_t *, const void *, size_t);
    extern bool base58_check_encode(char *, size_t *, uint8_t, const void *, size_t);

#ifdef __cplusplus
}
#endif // __cplusplus

#endif // BASE58_H

base58.c

代码语言:javascript
复制
#ifndef WIN32
#include <arpa/inet.h>
#else
#include <winsock2.h>
#endif // WIN32

#include <string.h>
#include "base58.h"

bool (*base58_sha256_impl)(void *, const void *, size_t) = NULL;

static const int8_t base58_digits_map[] = {
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, -1,
    -1, -1, -1, 9, 10, 11, 12, 13, 14, 15, 16, -1,
    17, 18, 19, 20, 21, -1, 22, 23, 24, 25, 26, 27, 28, 
    29, 30, 31, 32, -1, -1, -1, -1, -1, -1, 33, 34, 35,
    36, 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, 47,
    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, -1, -1, -1, -1, -1,
};

static const char base58_digits_ordered[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

typedef uint64_t base58_maxint_t;
typedef uint32_t base58_almostmaxint_t;
#define base58_almostmaxint_bits (sizeof(base58_almostmaxint_t) * 8)
static const base58_almostmaxint_t base58_almostmaxint_mask = ((((base58_maxint_t)1) << base58_almostmaxint_bits) - 1);

bool base58_to_bin(void *bin, size_t *bin_len, const char *b58_data, size_t data_len)
{
    size_t binsz = *bin_len;
    const unsigned char *b58u = (void *)b58_data;
    unsigned char *binu = bin;
    size_t outsz = (binsz + sizeof(base58_almostmaxint_t) - 1) / sizeof(base58_almostmaxint_t);
    base58_almostmaxint_t outi[outsz];
    base58_maxint_t t;
    base58_almostmaxint_t c;
    size_t i, j;
    uint8_t bytesleft = binsz % sizeof(base58_almostmaxint_t);
    base58_almostmaxint_t zeromask = bytesleft ? (base58_almostmaxint_mask << (bytesleft * 8)) : 0;
    unsigned int zero_count = 0;

    if (!data_len)
    {
        data_len = strlen(b58_data);
    }

    for (i = 0; i < outsz; ++i)
    {
        outi[i] = 0;
    }

    // Leading zeros, just count
    for (i = 0; i < data_len && b58u[i] == '1'; ++i)
    {
        ++zero_count;
    }

    for (; i < data_len; ++i)
    {
        if (b58u[i] & 0x80)
        {
            // Invalid base58 digit
            return false;
        }

        if (base58_digits_map[b58u[i]] == -1)
        {
            // Invalid base58 digit
            return false;
        }

        c = (unsigned)base58_digits_map[b58u[i]];
        for (j = outsz; j--;)
        {
            t = ((base58_maxint_t)outi[j]) * 58 + c;
            c = t >> base58_almostmaxint_bits;
            outi[j] = t & base58_almostmaxint_mask;
        }

        if (c)
        {
            // Output number too big (carry to the next int32)
            return false;
        }

        if (outi[0] & zeromask)
        {
            // Output number too big (last int32 filled too far)
            return false;
        }
    }

    j = 0;
    if (bytesleft)
    {
        for (i = bytesleft; i > 0; --i)
        {
            *(binu++) = (outi[0] >> (8 * (i - 1))) & 0xff;
        }
        ++j;
    }

    for (; j < outsz; ++j)
    {
        for (i = sizeof(*outi); i > 0; --i)
        {
            *(binu++) = (outi[j] >> (8 * (i - 1))) & 0xff;
        }
    }

    // Count canonical base58 byte count
    binu = bin;
    for (i = 0; i < binsz; ++i)
    {
        if (binu[i])
        {
            break;
        }
        --*bin_len;
    }
    *bin_len += zero_count;

    return true;
}

static bool my_dblsha256(void *hash, const void *data, size_t data_len)
{
    uint8_t buffer[0x20];
    return base58_sha256_impl(buffer, data, data_len) && base58_sha256_impl(hash, buffer, sizeof(buffer));
}

int base58_check(const void *bin, size_t binsz, const char *base58str, size_t b58sz)
{
    unsigned char buffer[32];
    const uint8_t *binc = bin;
    unsigned int i;

    if (binsz < 4)
    {
        return -4;
    }

    if (!my_dblsha256(buffer, bin, binsz - 4))
    {
        return -2;
    }

    if (memcmp(&binc[binsz - 4], buffer, 4))
    {
        return -1;
    }

    // Check number of zeros is correct AFTER verifying checksum (to avoid possibility of accessing base58str[-1])
    for (i = 0; binc[i] == '\0' && base58str[i] == '1'; ++i)
    {
        ; // Just finding the end of zeros, nothing to do
    }

    if (binc[i] == '\0' && base58str[i] == '1')
    {
        return -3;
    }

    return binc[0];
}

bool base58_encode(char *b58, size_t *b58sz, const void *data, size_t binsz)
{
    const uint8_t *bin = data;
    int carry;
    size_t i, j, high, zcount = 0;
    size_t size;

    while (zcount < binsz && !bin[zcount])
    {
        ++zcount;
    }

    size = (binsz - zcount) * 138 / 100 + 1;
    uint8_t buf[size];
    memset(buf, 0, size);

    for (i = zcount, high = size - 1; i < binsz; ++i, high = j)
    {
        for (carry = bin[i], j = size - 1; (j > high) || carry; --j)
        {
            carry += 256 * buf[j];
            buf[j] = carry % 58;
            carry /= 58;
            if (!j)
            {
                // Otherwise j wraps to maxint which is bigger than high
                break;
            }
        }
    }

    for (j = 0; j < size && !buf[j]; ++j)
    {
        ; // Just finding the end of zeros, nothing to do
    }

    if (*b58sz <= zcount + size - j)
    {
        *b58sz = zcount + size - j + 1;
        return false;
    }

    if (zcount)
    {
        memset(b58, '1', zcount);
    }

    for (i = zcount; j < size; ++i, ++j)
    {
        b58[i] = base58_digits_ordered[buf[j]];
    }
    b58[i] = '\0';
    *b58sz = i + 1;

    return true;
}

bool base58_check_encode(char *b58c, size_t *b58c_sz, uint8_t ver, const void *data, size_t datasz)
{
    uint8_t buf[1 + datasz + 0x20];
    uint8_t *hash = &buf[1 + datasz];

    buf[0] = ver;
    memcpy(&buf[1], data, datasz);
    if (!my_dblsha256(hash, buf, datasz + 1))
    {
        *b58c_sz = 0;
        return false;
    }

    return base58_encode(b58c, b58c_sz, buf, 1 + datasz + 4);
}

libbase58

编译、安装、测试步骤

代码语言:javascript
复制
git clone https://github.com/Scorpio69t/libbase58.git

cd libbase58

mkdir build
cd build

cmake ..
make
make install

cd ../test
bash encode.sh
bash decode.sh

github

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-03-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • libbase58
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档