Wie kodiere/dekodiere ich eine Adresse in C# mit Base58 Checked? Was bedeutet „Führende Nullen normalisieren“?

Ich versuche, eine Base58-Adresse in C # zu codieren und zu decodieren. Die folgende Funktion ist ein Anfang, hat aber ein paar Probleme:

  • Dieser Code normalisiert keine führenden Nullen (wie sieht das aus?)

  • Wenn diese Methode wiederholt in schneller Folge aufgerufen wird, werden viele String-Objekte erstellt, die den GC unter Druck setzen

.NET 4.5-Code

Beachten Sie, dass Sie einen Verweis auf System.Numerics hinzufügen

BigInteger  bi =  System.Numerics.BigInteger.Parse("00010966776006953D5567439E5E39F86A0D273BEED61967F6", NumberStyles.HexNumber);

string b58 = EncodeBase58(bi);
Console.WriteLine(b58 + Environment.NewLine + "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM");

   /// .... SNIP

   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.)


        // Base58Check encoding is also used for encoding private keys in the Wallet Import Format. This is formed exactly
        // the same as a Bitcoin address, except that 0x80 is used for the version/application byte, and the payload is 32 bytes
        // instead of 20 (a private key in Bitcoin is a single 32-byte unsigned big-endian integer). Such encodings will always
        // yield a 51-character string that starts with '5', or more specifically, either '5H', '5J', or '5K'.   https://en.bitcoin.it/wiki/Base58Check_encoding
        const int sizeWalletImportFormat = 51;

        char[] result = new char[33];

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

        return new string(result);
    }
    //public static long DecodeBase58(String base58StringToExpand)
    //{
    //    long lConverted = 0;
    //    long lTemporaryNumberConverter = 1;

    //    while (base58StringToExpand.Length > 0)
    //    {
    //        String sCurrentCharacter = base58StringToExpand.Substring(base58StringToExpand.Length - 1);
    //        lConverted = lConverted + (lTemporaryNumberConverter * sBase58Alphabet.IndexOf(sCurrentCharacter));
    //        lTemporaryNumberConverter = lTemporaryNumberConverter * sBase58Alphabet.Length;
    //        base58StringToExpand = base58StringToExpand.Substring(0, base58StringToExpand.Length - 1);
    //    }
    //}
Beachten Sie, dass Sie BigInteger.DivRem verwenden können, um den Rest in einem Vorgang zu dividieren und zu erhalten, was schneller ist

Antworten (1)

Führende Nullen zu normalisieren ist in diesem Link dokumentiert, nämlich in diesem Text ganz unten:

Bei einer standardmäßigen Basiskonvertierung wäre das 0x00-Byte auf der linken Seite irrelevant (wie beim Schreiben von 052 anstelle von 52), aber im BTC-Netzwerk werden die am weitesten links stehenden Nullzeichen durch die Konvertierung getragen. Für jedes 0x00-Byte am linken Ende der Binäradresse hängen wir also ein '1'-Zeichen an die Base58-Adresse an. Aus diesem Grund beginnen die Hauptnetzwerkadressen alle mit 1.

Der folgende Code behebt das GC-Problem mit Überzuweisung und Verwerfen des String-Typs, verarbeitet jedoch die führenden Null-Bytes nicht richtig. Ich muss entweder eine Liste der erwarteten Längen pro Typ oder einen anderen Ansatz finden.

    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.)


        // Base58Check encoding is also used for encoding private keys in the Wallet Import Format. This is formed exactly
        // the same as a Bitcoin address, except that 0x80 is used for the version/application byte, and the payload is 32 bytes
        // instead of 20 (a private key in Bitcoin is a single 32-byte unsigned big-endian integer). Such encodings will always
        // yield a 51-character string that starts with '5', or more specifically, either '5H', '5J', or '5K'.   https://en.bitcoin.it/wiki/Base58Check_encoding
        const int sizeWalletImportFormat = 51;

        char[] result = new char[33];

        Int32 iAlphabetLength = sBase58Alphabet.Length;
        BigInteger iAlphabetLength2 = BigInteger.Parse(iAlphabetLength.ToString());

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

        return new string(result);
    }