Development

如何使用 Base58 Checked 編碼/解碼 C# 中的地址?“標準化前導零”是什麼意思?

  • December 19, 2012

我正在嘗試在 C# 中編碼和解碼 Base58 地址。下面的函式是一個開始,但它有一些問題:

  • 此程式碼不規範化前導零(這是什麼樣的?)
  • 如果這個方法被快速連續重複呼叫,會創建很多字元串對象,給 GC 帶來壓力

.NET 4.5 程式碼

注意添加對 System.Numerics 的引用

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);
   //    }
   //}

規範化前導零記錄在此連結中,即底部的文本:

在標準的基本轉換中,左側的 0x00 字節將是無關緊要的(例如寫入 052 而不是 52),但在 BTC 網路中,最左側的零字元通過轉換進行。因此,對於二進制地址左端的每個 0x00 字節,我們會將一個“1”字元附加到 Base58 地址。這就是為什麼主網地址都以 1 開頭的原因。

以下程式碼將解決過度分配和丟棄 String 類型的 GC 問題,但是它 不能正確處理前導零字節。我要麼需要找到每種類型的預期長度列表,要麼需要其他方法。

   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);
   }

引用自:https://bitcoin.stackexchange.com/questions/5829