Development
如何使用 Base58 Checked 編碼/解碼 C# 中的地址?“標準化前導零”是什麼意思?
我正在嘗試在 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); }