I've been trying to create a function in TypeScript for converting to and from base58. I'm pretty familiar with base conversion generally: divide a number by a target base, and take each remainder as a digit of the conversion from right to left, until it equals 0.
But this took me into Bitcoin's source code, which does the base 58 conversion in a way I've never seen before, working from a stream of bytes from left-to-right instead of the way I'm used to.
Here's their function for encoding to base 58:
https://github.com/bitcoin/bitcoin/blob/master/src/base58.cpp#L87
Essentially, for each byte in the input buffer, it sets a variable carry to the value of that byte. Then working from the end of the zero-filled output buffer, it multiplies that byte by 256 and adds it to carry. It sets the output byte to carry %58 and sets carry to carry / 58 (integer division). It then moves back one byte and repeats until carry is 0.
Here's the loop as I have it written in TypeScript:
let base58Bytes = Buffer.alloc(size);
let length = 0;
while (input.length > 0) {
let carry: number = input[0];
let i = 0;
for (
let pos = base58Bytes.length - 1;
(carry !== 0 || i < length) && pos >= 0;
i++, pos--
) {
carry += base58Bytes[pos] * 256;
base58Bytes[pos] = carry % 58;
carry = Math.floor(carry / 58);
}
length = i;
input = input.subarray(1);
}
My problem:
I see that it works, but not why it works. Any time I google base conversion, I find the first method I discussed, but nothing about this method.
In particular, I don't understand why the output byte is multiplied by the source base (256 in this case) before being added to carry.
I can see it works from any base to any base, and have worked it out by hand multiple times. I just don't understand why it works yet nor can I find any resources that explain it.