Address deserialization support
authorCryptoManiac <balthazar@yandex.ru>
Tue, 18 Aug 2015 20:19:45 +0000 (23:19 +0300)
committerCryptoManiac <balthazar@yandex.ru>
Tue, 18 Aug 2015 20:19:45 +0000 (23:19 +0300)
Novacoin/AddressTools.cs
Novacoin/CNovacoinAddress.cs
NovacoinTest/Program.cs

index d03efe1..9356305 100644 (file)
@@ -2,17 +2,42 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
+using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 
 using System.Numerics;
 
 namespace Novacoin
 {
+    public class Base58Exception : Exception
+    {
+        public Base58Exception()
+        {
+        }
+
+        public Base58Exception(string message)
+            : base(message)
+        {
+        }
+
+        public Base58Exception(string message, Exception inner)
+            : base(message, inner)
+        {
+        }
+    }
+
+
     public class AddressTools
     {
+        const string strDigits = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
+
+        /// <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[] bytes)
         {
-            const string strDigits = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
             string strResult = "";
 
             int nBytes = bytes.Length;
@@ -29,6 +54,8 @@ namespace Novacoin
                 arrayToInt /= encodeSize;
                 strResult = strDigits[rem] + strResult;
             }
+
+            // Leading zeroes encoded as base58 zeros
             for (int i = 0; i < nBytes && bytes[i] == 0; ++i)
             {
                 strResult = strDigits[0] + strResult;
@@ -37,15 +64,77 @@ namespace Novacoin
             return strResult;
         }
 
-        public static string Base58EncodeCheck(byte[] bytes)
+        /// <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(IEnumerable<byte> bytes)
         {
-            byte[] dataBytes = new byte[bytes.Length + 4];
+            byte[] dataBytes = bytes.ToArray();
+            Array.Resize(ref dataBytes, dataBytes.Length + 4);
+
             byte[] checkSum = Hash256.Compute256(bytes).hashBytes.Take(4).ToArray();
 
-            bytes.CopyTo(dataBytes, 0);
-            checkSum.CopyTo(dataBytes, bytes.Length);
+            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 IEnumerable<byte> Base58Decode(string strBase58)
+        {
+            // Remove whitespaces
+            strBase58 = Regex.Replace(strBase58, @"s", "");
+
+            BigInteger intData = 0;
+            for (int i = 0; i < strBase58.Length; i++)
+            {
+                int digit = strDigits.IndexOf(strBase58[i]);
+
+                if (digit < 0)
+                {
+                    throw new FormatException(string.Format("Invalid Base58 character `{0}` at position {1}", strBase58[i], i));
+                }
+
+                intData = intData * 58 + digit;
+            }
+
+            // Leading zero bytes get encoded as leading `1` characters
+            int leadingZeroCount = strBase58.TakeWhile(c => c == '1').Count();
+
+            IEnumerable<byte> leadingZeros = Enumerable.Repeat((byte)0, leadingZeroCount);
+            IEnumerable<byte> bytesWithoutLeadingZeros = intData.ToByteArray().Reverse().SkipWhile(b => b == 0);
+
+            return leadingZeros.Concat(bytesWithoutLeadingZeros);
+        }
+        public static IEnumerable<byte> Base58DecodeCheck(string strBase58Check)
+        {
+            byte[] rawData = Base58Decode(strBase58Check).ToArray();
+
+            if (rawData.Length < 4)
+            {
+                throw new Base58Exception("Data is too short.");
+            }
+
+            byte[] result = new byte[rawData.Length - 4];
+            byte[] resultCheckSum = new byte[4];
+
+            Array.Copy(rawData, result, result.Length);
+            Array.Copy(rawData, result.Length, resultCheckSum, 0, 4);
+
+            byte[] checkSum = Hash256.Compute256(result).hashBytes.Take(4).ToArray();
+
+            if (!checkSum.SequenceEqual(resultCheckSum))
+            {
+                throw new Base58Exception("Incorrect checksum.");
+            }
+
+            return result;
+        }
     }
 }
index d66555e..7165a33 100644 (file)
@@ -46,6 +46,14 @@ namespace Novacoin
             addrData = new List<byte>(keyID.hashBytes);
         }
 
+        public CNovacoinAddress(string strNovacoinAddress)
+        {
+            addrData = AddressTools.Base58DecodeCheck(strNovacoinAddress).ToList();
+
+            nVersion = addrData[0];
+            addrData.RemoveAt(0);
+        }
+
         /// <summary>
         /// Initialize new instance of SCRIPT_ADDRESS
         /// </summary>
@@ -97,7 +105,7 @@ namespace Novacoin
             r.Add(nVersion);
             r.AddRange(addrData);
 
-            return AddressTools.Base58EncodeCheck(r.ToArray());
+            return AddressTools.Base58EncodeCheck(r);
         }
     }
 }
index dfd1ed8..058e4ef 100644 (file)
@@ -73,6 +73,10 @@ namespace NovacoinTest
             Console.WriteLine("\nDonations may be sent to: {0}", strDonationAddress);
             Console.WriteLine("Address generation is OK: {0}", strDonationAddress == "4T2t8uiDtyHceMwMjMHPn88TyJB3trCg3o");
 
+            /// Address deserialization test
+
+            CNovacoinAddress donationAddress = new CNovacoinAddress(strDonationAddress);
+            Console.WriteLine("Address reserialization is OK: {0}", donationAddress.ToString() == pubKeyTest.GetKeyID().ToString());
 
             Console.ReadLine();
         }