\feffusing System;
-using System.Collections.Generic;
using System.Linq;
using System.Text;
-using System.Threading.Tasks;
-using System.Numerics;
+using Org.BouncyCastle.Math;
namespace Novacoin
{
- public class AddressTools
+ [Serializable]
+ public class Base58Exception : Exception
{
- public static string Base58Encode(byte[] bytes)
+ public Base58Exception()
{
- const string strDigits = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
- string strResult = "";
+ }
+
+ public Base58Exception(string message)
+ : base(message)
+ {
+ }
- int nBytes = bytes.Length;
- BigInteger arrayToInt = 0;
- BigInteger encodeSize = strDigits.Length;
+ public Base58Exception(string message, Exception inner)
+ : base(message, inner)
+ {
+ }
+ }
- for (int i = 0; i < nBytes; ++i)
+ public class AddressTools
+ {
+ private const string strDigits = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
+ private static readonly BigInteger _base = BigInteger.ValueOf(58);
+
+ /// <summary>
+ /// Encode a byte sequence as a base58-encoded string
+ /// </summary>
+ /// <param name="bytes">Byte sequence</param>
+ /// <returns>Encoding result</returns>
+ public static string Base58Encode(byte[] input)
+ {
+ // TODO: This could be a lot more efficient.
+ var bi = new BigInteger(1, input);
+ var s = new StringBuilder();
+ while (bi.CompareTo(_base) >= 0)
{
- arrayToInt = arrayToInt * 256 + bytes[i];
+ var mod = bi.Mod(_base);
+ s.Insert(0, new[] { strDigits[mod.IntValue] });
+ bi = bi.Subtract(mod).Divide(_base);
}
- while (arrayToInt > 0)
+ s.Insert(0, new[] { strDigits[bi.IntValue] });
+ // Convert leading zeros too.
+ foreach (var anInput in input)
{
- int rem = (int)(arrayToInt % encodeSize);
- arrayToInt /= encodeSize;
- strResult = strDigits[rem] + strResult;
+ if (anInput == 0)
+ s.Insert(0, new[] { strDigits[0] });
+ else
+ break;
}
- for (int i = 0; i < nBytes && bytes[i] == 0; ++i)
+ return s.ToString();
+ }
+
+
+ /// <summary>
+ /// Encode a byte sequence to a base58-encoded string, including checksum
+ /// </summary>
+ /// <param name="bytes">Byte sequence</param>
+ /// <returns>Base58(data+checksum)</returns>
+ public static string Base58EncodeCheck(byte[] bytes)
+ {
+ var dataBytes = new byte[bytes.Length + 4];
+ bytes.CopyTo(dataBytes, 0);
+ var checkSum = Hash256.ComputeRaw256(bytes).Take(4).ToArray();
+ checkSum.CopyTo(dataBytes, dataBytes.Length - 4); // add 4-byte hash check to the end
+
+ return Base58Encode(dataBytes);
+ }
+
+ /// <summary>
+ /// // Decode a base58-encoded string into byte array
+ /// </summary>
+ /// <param name="strBase58">Base58 data string</param>
+ /// <returns>Byte array</returns>
+ public static byte[] Base58Decode(string input)
+ {
+ var bytes = DecodeToBigInteger(input).ToByteArray();
+ // We may have got one more byte than we wanted, if the high bit of the next-to-last byte was not zero. This
+ // is because BigIntegers are represented with twos-compliment notation, thus if the high bit of the last
+ // byte happens to be 1 another 8 zero bits will be added to ensure the number parses as positive. Detect
+ // that case here and chop it off.
+ var stripSignByte = bytes.Length > 1 && bytes[0] == 0 && bytes[1] >= 0x80;
+ // Count the leading zeros, if any.
+ var leadingZeros = 0;
+ for (var i = 0; input[i] == strDigits[0]; i++)
{
- strResult = strDigits[0] + strResult;
+ leadingZeros++;
}
+ var tmp = new byte[bytes.Length - (stripSignByte ? 1 : 0) + leadingZeros];
+ Array.Copy(bytes, stripSignByte ? 1 : 0, tmp, leadingZeros, tmp.Length - leadingZeros);
+ return tmp;
+ }
- return strResult;
+ public static BigInteger DecodeToBigInteger(string input)
+ {
+ var bi = BigInteger.ValueOf(0);
+ // Work backwards through the string.
+ for (var i = input.Length - 1; i >= 0; i--)
+ {
+ var alphaIndex = strDigits.IndexOf(input[i]);
+ if (alphaIndex == -1)
+ {
+ throw new FormatException("Illegal character " + input[i] + " at " + i);
+ }
+ bi = bi.Add(BigInteger.ValueOf(alphaIndex).Multiply(_base.Pow(input.Length - 1 - i)));
+ }
+ return bi;
}
- public static string Base58EncodeCheck(byte[] bytes)
+ public static byte[] Base58DecodeCheck(string strBase58Check)
{
- byte[] dataBytes = new byte[bytes.Length + 4];
- byte[] checkSum = Hash256.Compute256(bytes).hashBytes.Take(4).ToArray();
+ var rawData = Base58Decode(strBase58Check).ToArray();
- bytes.CopyTo(dataBytes, 0);
- checkSum.CopyTo(dataBytes, bytes.Length);
+ if (rawData.Length < 4)
+ {
+ throw new Base58Exception("Data is too short.");
+ }
- return Base58Encode(dataBytes);
+ var result = new byte[rawData.Length - 4];
+ var resultCheckSum = new byte[4];
+
+ Array.Copy(rawData, result, result.Length);
+ Array.Copy(rawData, result.Length, resultCheckSum, 0, 4);
+
+ var checkSum = Hash256.ComputeRaw256(result).Take(4).ToArray();
+
+ if (!checkSum.SequenceEqual(resultCheckSum))
+ {
+ throw new Base58Exception("Incorrect checksum.");
+ }
+
+ return result;
}
}
}