2

My understanding is that Base58Checked is always 34 bytes long, and isn't going to be less than 34 bytes even if the first or last bytes of the RipeMD are zero. If this statement isn't true, then none of the following question matters.

Short question

What should the Base58Checked address be for the following public key?

0414C7AB38D5CC0A39B3BF5F970C572736904D40A5879BBB05BBE16911D7F35DD3E25525877587BF91EE801393FACDED26FAFA173E457F5961BA11F602CC08FE5A

This test website says it should be 1vwTLMCesc1vijZbscYfnr7naV9MEy8dS, however, when I look at the logic below, I think it should be 11vwTLMCesc1vijZbscYfnr7naV9MEy8dS (notice the second leading one)

More information

In this loop, the value numberToShorten decrements as I figure out what is zero for the last few rounds of the conversion. (e.g. "1" for a value of zero, and "A" for a remainder of nine.

The following uncompressed public key is an example of one that leaves a zero for me to deal with when encoding:

0414C7AB38D5CC0A39B3BF5F970C572736904D40A5879BBB05BBE16911D7F35DD3E25525877587BF91EE801393FACDED26FAFA173E457F5961BA11F602CC08FE5A

Here is my C# code:

    public static String sBase58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
    public static String EncodeBase58(BigInteger numberToShorten)
    {
        // WARNING: Beware of bignumber implementations that clip leading 0x00 bytes, or prepend extra 0x00 
        // bytes to indicate sign - your code must handle these cases properly or else you may generate valid-looking
        // addresses which can be sent to, but cannot be spent from - which would lead to the permanent loss of coins.)

        const int sizeWalletImportFormat = 51;
          BigInteger base58AlphabetLength = (BigInteger)sBase58Alphabet.Length;

        char[] result = new char[34];

        int i = 0;
        while (result.Length > i && numberToShorten >= 0)
         { 
            var lNumberRemainder = BigInteger.Remainder(numberToShorten, base58AlphabetLength);
            result[result.Length - 1 - i] = sBase58Alphabet[(int)lNumberRemainder];

            if (i == 31 && numberToShorten == 0)
                Console.WriteLine("found one");

            //Console.WriteLine("i = " + i);
            //Console.WriteLine("numberToShorten= " + numberToShorten + " (Divide this by 58 to get the value below)");
            //Console.WriteLine("lNumberRemainder = " + lNumberRemainder + " (Get this position in the Base58Array and append that character)");
            //Console.WriteLine("result = " + new string(result));

            if (numberToShorten == 0)
            {
                if (i != 33)
                {
                    // Debug
                    Console.WriteLine("i = " + i);
                    break;
                }
                break;
            } 

            numberToShorten = numberToShorten / base58AlphabetLength;
            i++;
        }
        var ret = new string(result, 33 - i, result.Length - (33 - i));

        return ret;
    }

    public static DecodedBase58Result DecodeBase58(String base58StringToExpand)
    { 
        DecodedBase58Result ret = new DecodedBase58Result();

        BigInteger base58AlphabetLength = (BigInteger)sBase58Alphabet.Length;

        BigInteger numberToExtend = BigInteger.Zero;
        var charsToDecode = base58StringToExpand.ToCharArray();
        for (int decodePosition = 0; decodePosition <= charsToDecode.Length - 1; decodePosition++)
        {
            char sCurrentCharacter = charsToDecode[decodePosition];
            int index = sBase58Alphabet.IndexOf(sCurrentCharacter);
            if (index == -1)
                throw new Exception("Not a base58 address, " + sCurrentCharacter + " isn't a valid character.");

            numberToExtend = numberToExtend * base58AlphabetLength;
            numberToExtend = numberToExtend + index;

            //Console.WriteLine(" i = " + (base58StringToExpand.Length - decodePosition));
            //Console.WriteLine(" number = " + numberToExtend);
            //Console.WriteLine(" Result = " + ret);
        }

        ret.BigInt = numberToExtend;
        return ret;
    }

The truncated character-by-character building of the Base58 address is below. The problem lies where i=32

i = 27
numberToShorten= 610398922 (Divide this by 58 to get the value below)
lNumberRemainder = 20 (Get this position in the Base58Array and append that character)
result =       MCesc1vijZbscYfnr7naV9MEy8dS

i = 28
numberToShorten= 10524119 (Divide this by 58 to get the value below)
lNumberRemainder = 19 (Get this position in the Base58Array and append that character)
result =      LMCesc1vijZbscYfnr7naV9MEy8dS

i = 29
numberToShorten= 181450 (Divide this by 58 to get the value below)
lNumberRemainder = 26 (Get this position in the Base58Array and append that character)
result =     TLMCesc1vijZbscYfnr7naV9MEy8dS

i = 30
numberToShorten= 3128 (Divide this by 58 to get the value below)
lNumberRemainder = 54 (Get this position in the Base58Array and append that character)
result =    wTLMCesc1vijZbscYfnr7naV9MEy8dS

i = 31
numberToShorten= 53 (Divide this by 58 to get the value below)
lNumberRemainder = 53 (Get this position in the Base58Array and append that character)
result =   vwTLMCesc1vijZbscYfnr7naV9MEy8dS

i = 32
numberToShorten= 0 (Divide this by 58 to get the value below)
lNumberRemainder = 0 (Get this position in the Base58Array and append that character)
result =  1vwTLMCesc1vijZbscYfnr7naV9MEy8dS

i = 33
numberToShorten= 0 (Divide this by 58 to get the value below)
lNumberRemainder = 0 (Get this position in the Base58Array and append that character)
result = 11vwTLMCesc1vijZbscYfnr7naV9MEy8dS
  • Your first statement isn't true. A Bitcoin address doesn't have to be 34 characters long. For example, see the first few: directory.io – Diego Basch Feb 25 '14 at 2:21
1

I think you have a fundamental misunderstanding of Base58 encoding. Base58checked is not fixed length; its length depends on the number of leading zero bytes in the input.

There should only be one leading "1" that signifies "main net" address type. A second leading "1" would indicate a whole leading zero byte (0x00) in the pubkey hash itself, which is not applicable in this case because your sha256+ripemd-160 is 0a33807d0764e0bc71677fbef234d4989d0c198e. It only has a leading zero nibble.

  • I'm talking about the zero you mention in 0a33. That zero in the RipeMD is messing me up... not sure what to do with it.. – goodguys_activate Feb 25 '14 at 2:38
  • Yeah that is only a nibble (4 zero bits). You can only use "1" in the beginning for whole zero bytes (8 zero bits). – uminatsu Feb 25 '14 at 2:40
  • 1
    This is what you should be doing: code.google.com/p/bitcoinj/source/browse/core/src/main/java/com/… – Diego Basch Feb 25 '14 at 2:53
  • @makerofthings7 it is very simple to understand the rules of Base58 encoding. You first count the number of leading zero bytes, then remove them, then treat the remaining bytes as a big-endian integer and convert to base-58, and finally add as many "1" prefixes as the number of leading zero bytes. – uminatsu Feb 25 '14 at 4:10
  • Note to self: The following uncompressed public key has 8 zero bits when encoded to Base58 04448212C17B6D5FD88E316AFB1D79432D7BC983567CFD5945DFE2E54C2F2A5F69E234588C4C43EB7F1E7285C0F6CBC61A31385FADF7663305A6F4FF494D621F8D – goodguys_activate Feb 25 '14 at 5:01
0

The problem lies int my code's interpretation of zeroes in the most significant bits (MSB). I've updated my code to give the correct base58hash code (as verified by the QT client).

Given that the Base58 checked encoding always works with 25 bytes (RIPEMD + Network + 4 byte Checksum)

The following public key has one MSB of zero

04EF12BF0C1EC3646CE6828E7D59B4111381EB8A3DF2BBB89D53254AD70BE27498C76F6E2AB3B7FC15A69B0A44AC84659728146E46670E3C66B14222FAB82C8CF6

Since the MSB has two zero, the Base58 hash base one zero 1EVEDmVcV7iPvTkaw2gk89yVcCzPzaS6B7


The following public key has a MSB of two zeros

04BAE942CC53D51498CD2E1576C1C43F125302DEF76A8A470B818C8D6BB65A82D679232E294E6F2D1C78F35BAA272EF1585F2FFE8B88769B02FBCEDD070CE50D4B

Since the MSB has two zero, the Base58 hash base two zeros 11ujQcjgoMNmbmcBkk8CXLWQy8ZerMtuN


The following public key has a MSB of three zeros

04AD8A68DF6A4553BEDD234BF2D7BFB5F0C5830F13C3A5F814AAFC03E2C92602EC1047CFE67F14D499B55F57BBE104D29E9BF6C9849771B4E3AE46FFB76FE3DFCD

Since the MSB has three zeros, the Base58 hash three zeros 111oeV7wjVNCQttqY63jLFsg817aMEmTw

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.