本文详细介绍了Base58编码方案,它在比特币地址和其他加密货币中被广泛使用,可以将原始二进制数据转换为更短且更易于理解的字符串。文章深入探讨了Base58的原理、编码和解码过程,并通过一个示例演示了其工作方式,以及这种编码方案在区块链技术中的应用。
Base58 是一种流行的编码方案,主要用于 比特币地址 和其他加密货币。它的目的是将 原始二进制数据(字节) 转换为更短且更易于理解的字符串。
Base58 专门设计用于解决共享编码数据时的实际问题:
0
、O
、I
、l
)以减少转录错误Base58 字母表(正好 58 个字符):
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
请注意缺少的内容:0
(零)、O
(大写 O)、I
(大写 i)和 l
(小写 L)- 惹麻烦的家伙!
Base58 的魔力在于将你的数据视为数学:
这就像在数字进制之间转换,但有一个转折——我们从 base-256(字节)转换为 base-58(我们的字母表)。
在深入研究函数之前,让我们了解我们的基础:
#include <string>
const char * const ALPHABET =
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
const char ALPHABET_MAP[128] = {
-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
};
这些是做什么用的:
ALPHABET
:我们的 58 个字符的编码表ALPHABET_MAP
:闪电般快速的查找表(ASCII → Base58 索引)'1'
(ASCII 49) → 索引 0, 'H'
(ASCII 72) → 索引 9-1
表示“无效字符”base58encode
int base58encode(const std::string input, int len, unsigned char result[]) {
unsigned char const* bytes = (unsigned const char*)(input.c_str());
unsigned char digits[len * 137 / 100]; // Buffer size estimate
int digitslen = 1;
// Process each input byte
// 处理每个输入字节
for (int i = 0; i < len; i++) {
unsigned int carry = (unsigned int) bytes[i];
// Multiply existing digits by 256, add new byte
// 将现有数字乘以 256,加上新的字节
for (int j = 0; j < digitslen; j++) {
carry += (unsigned int) (digits[j]) << 8; // * 256
digits[j] = (unsigned char) (carry % 58);
carry /= 58;
}
// Handle overflow digits
// 处理溢出数字
while (carry > 0) {
digits[digitslen++] = (unsigned char) (carry % 58);
carry /= 58;
}
}
int resultlen = 0;
// Handle leading zero bytes → '1' characters
// 处理前导零字节 → '1' 字符
for (; resultlen < len && bytes[resultlen] == 0;)
result[resultlen++] = '1';
// Reverse and map digits to alphabet
// 反转并将数字映射到字母表
for (int i = 0; i < digitslen; i++)
result[resultlen + i] = ALPHABET[digits[digitslen - 1 - i]];
result[digitslen + resultlen] = 0; // Null terminator
// 空终止符
return digitslen + resultlen;
}
它是如何工作的:
base58decode
int base58decode(const std::string input, int len, unsigned char *result) {
unsigned char const* str = (unsigned const char*)(input.c_str());
result[0] = 0;
int resultlen = 1;
// Process each Base58 character
// 处理每个 Base58 字符
for (int i = 0; i < len; i++) {
unsigned int carry = (unsigned int) ALPHABET_MAP[str[i]];
// Multiply existing result by 58, add new digit
// 将现有结果乘以 58,加上新的数字
for (int j = 0; j < resultlen; j++) {
carry += (unsigned int) (result[j]) * 58;
result[j] = (unsigned char) (carry & 0xff); // Keep low byte
// 保留低字节
carry >>= 8; // Shift high bytes to carry
// 将高字节移至进位
}
// Handle carry overflow
// 处理进位溢出
while (carry > 0) {
result[resultlen++] = (unsigned int) (carry & 0xff);
carry >>= 8;
}
}
// Handle leading '1' characters → zero bytes
// 处理前导 '1' 字符 → 零字节
for (int i = 0; i < len && str[i] == '1'; i++)
result[resultlen++] = 0;
// Reverse result to correct byte order
// 反转结果以更正字节顺序
for (int i = resultlen - 1, z = (resultlen >> 1) + (resultlen & 1); i >= z; i--) {
int k = result[i];
result[i] = result[resultlen - i - 1];
result[resultlen - i - 1] = k;
}
return resultlen;
}
它是如何工作的:
让我们跟踪对字符串 "Hi"
的编码和解码,以了解算法的实际应用。
'H' = 72 (decimal)
'i' = 105 (decimal)
输入字节:[72, 105]
digits = [0]
digitslen = 1
carry = 72
carry = 0×256 + 72 = 72
digits[0] = 72 % 58 = 14
carry = 72 ÷ 58 = 1
digits[1] = 1 % 58 = 1
digits = [14, 1]
, digitslen = 2
carry = 105
carry = 14×256 + 105 = 3689
digits[0] = 3689 % 58 = 37
carry = 3689 ÷ 58 = 63
carry = 1×256 + 63 = 319
digits[1] = 319 % 58 = 29
carry = 319 ÷ 58 = 5
digits[2] = 5 % 58 = 5
digits = [37, 29, 5]
, digitslen = 3
[5, 29, 37]
5 → ALPHABET[5] = '6'
29 → ALPHABET[29] = 'W'
37 → ALPHABET[37] = 'e'
最终编码结果: "6We"
从 "6We"
开始,让我们回到 "Hi"
。
来自 ALPHABET_MAP 的字符值:
'6' → 5
'W' → 29
'e' → 37
初始状态:
result = [0]
resultlen = 1
carry = 5
carry = 5 + 0×58 = 5
result[0] = 5 & 0xff = 5
carry = 5 >> 8 = 0
result = [5]
, resultlen = 1
carry = 29
carry = 29 + 5×58 = 319
result[0] = 319 & 0xff = 63
carry = 319 >> 8 = 1
result[1] = 1
result = [63, 1]
, resultlen = 2
carry = 37
carry = 37 + 63×58 = 3691
result[0] = 3691 & 0xff = 105
carry = 3691 >> 8 = 14
carry = 14 + 1×58 = 72
result[1] = 72 & 0xff = 72
carry = 72 >> 8 = 0
result = [105, 72]
, resultlen = 2
[105, 72]
[72, 105]
72 → 'H'
105 → 'i'
最终解码结果: "Hi"
Base58 编码本质上是具有特殊处理的进制转换:
0x00
字节变为 '1'
字符len * 137 / 100
为输出提供安全的上限。为什么是 137/100? 当从 base-256 转换为 base-58 时,输出最多可以比输入长约 37%。这来自数学关系:log₅₈(256) ≈ 1.37。因此,我们需要至少多 37% 的空间来安全地存储转换期间所有可能的数字。& 0xff
和 >> 8
进行高效的字节操作Base58 编码在加密世界中无处不在:
对于任何从事区块链技术或加密货币系统工作的人来说,理解这种编码至关重要。
如果你对 Solana 开发、机密计算、DePIN 网络或开源索引工具有兴趣,请关注:
- 原文链接: blog.blockmagnates.com/h...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!