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