Use byte[] instead of IEnumerable<byte> if possible
[NovacoinLibrary.git] / Novacoin / AddressTools.cs
1 \feffusing System;
2 using System.Linq;
3 using System.Text;
4
5 using Org.BouncyCastle.Math;
6
7 namespace Novacoin
8 {
9     public class Base58Exception : Exception
10     {
11         public Base58Exception()
12         {
13         }
14
15         public Base58Exception(string message)
16             : base(message)
17         {
18         }
19
20         public Base58Exception(string message, Exception inner)
21             : base(message, inner)
22         {
23         }
24     }
25
26     public class AddressTools
27     {
28         private const string strDigits = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
29         private static readonly BigInteger _base = BigInteger.ValueOf(58);
30
31         /// <summary>
32         /// Encode a byte sequence as a base58-encoded string
33         /// </summary>
34         /// <param name="bytes">Byte sequence</param>
35         /// <returns>Encoding result</returns>
36         public static string Base58Encode(byte[] input)
37         {
38             // TODO: This could be a lot more efficient.
39             var bi = new BigInteger(1, input);
40             var s = new StringBuilder();
41             while (bi.CompareTo(_base) >= 0)
42             {
43                 var mod = bi.Mod(_base);
44                 s.Insert(0, new[] { strDigits[mod.IntValue] });
45                 bi = bi.Subtract(mod).Divide(_base);
46             }
47             s.Insert(0, new[] { strDigits[bi.IntValue] });
48             // Convert leading zeros too.
49             foreach (var anInput in input)
50             {
51                 if (anInput == 0)
52                     s.Insert(0, new[] { strDigits[0] });
53                 else
54                     break;
55             }
56             return s.ToString();
57         }
58
59
60         /// <summary>
61         /// Encode a byte sequence to a base58-encoded string, including checksum
62         /// </summary>
63         /// <param name="bytes">Byte sequence</param>
64         /// <returns>Base58(data+checksum)</returns>
65         public static string Base58EncodeCheck(byte[] bytes)
66         {
67             var dataBytes = new byte[bytes.Length + 4];
68             bytes.CopyTo(dataBytes, 0);
69             var checkSum = Hash256.Compute256(bytes).hashBytes.Take(4).ToArray();
70             checkSum.CopyTo(dataBytes, dataBytes.Length - 4); // add 4-byte hash check to the end
71
72             return Base58Encode(dataBytes);
73         }
74
75         /// <summary>
76         /// // Decode a base58-encoded string into byte array
77         /// </summary>
78         /// <param name="strBase58">Base58 data string</param>
79         /// <returns>Byte array</returns>
80         public static byte[] Base58Decode(string input)
81         {
82             var bytes = DecodeToBigInteger(input).ToByteArray();
83             // We may have got one more byte than we wanted, if the high bit of the next-to-last byte was not zero. This
84             // is because BigIntegers are represented with twos-compliment notation, thus if the high bit of the last
85             // byte happens to be 1 another 8 zero bits will be added to ensure the number parses as positive. Detect
86             // that case here and chop it off.
87             var stripSignByte = bytes.Length > 1 && bytes[0] == 0 && bytes[1] >= 0x80;
88             // Count the leading zeros, if any.
89             var leadingZeros = 0;
90             for (var i = 0; input[i] == strDigits[0]; i++)
91             {
92                 leadingZeros++;
93             }
94             var tmp = new byte[bytes.Length - (stripSignByte ? 1 : 0) + leadingZeros];
95             Array.Copy(bytes, stripSignByte ? 1 : 0, tmp, leadingZeros, tmp.Length - leadingZeros);
96             return tmp;
97         }
98
99         public static BigInteger DecodeToBigInteger(string input)
100         {
101             var bi = BigInteger.ValueOf(0);
102             // Work backwards through the string.
103             for (var i = input.Length - 1; i >= 0; i--)
104             {
105                 var alphaIndex = strDigits.IndexOf(input[i]);
106                 if (alphaIndex == -1)
107                 {
108                     throw new FormatException("Illegal character " + input[i] + " at " + i);
109                 }
110                 bi = bi.Add(BigInteger.ValueOf(alphaIndex).Multiply(_base.Pow(input.Length - 1 - i)));
111             }
112             return bi;
113         }
114
115         public static byte[] Base58DecodeCheck(string strBase58Check)
116         {
117             var rawData = Base58Decode(strBase58Check).ToArray();
118
119             if (rawData.Length < 4)
120             {
121                 throw new Base58Exception("Data is too short.");
122             }
123
124             var result = new byte[rawData.Length - 4];
125             var resultCheckSum = new byte[4];
126
127             Array.Copy(rawData, result, result.Length);
128             Array.Copy(rawData, result.Length, resultCheckSum, 0, 4);
129
130             var checkSum = Hash256.Compute256(result).hashBytes.Take(4).ToArray();
131
132             if (!checkSum.SequenceEqual(resultCheckSum))
133             {
134                 throw new Base58Exception("Incorrect checksum.");
135             }
136
137             return result;
138         }
139     }
140 }