From 6585d7335de124204d3745fb1647d203ee1e0d40 Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 11 Nov 2019 20:02:40 +0100 Subject: [PATCH 1/6] Fix base58 --- neo.UnitTests/Cryptography/UT_Base58.cs | 49 ++++++++++++------ neo/Cryptography/Base58.cs | 66 ++++++++++++++----------- 2 files changed, 70 insertions(+), 45 deletions(-) diff --git a/neo.UnitTests/Cryptography/UT_Base58.cs b/neo.UnitTests/Cryptography/UT_Base58.cs index 5f2d750382..fbf29ba888 100644 --- a/neo.UnitTests/Cryptography/UT_Base58.cs +++ b/neo.UnitTests/Cryptography/UT_Base58.cs @@ -2,30 +2,49 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using System; +using System.Collections.Generic; namespace Neo.UnitTests.Cryptography { [TestClass] public class UT_Base58 { - byte[] decoded1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - string encoded1 = "1kA3B2yGe2z4"; - byte[] decoded2 = { 0, 0, 0, 0, 0 }; - string encoded2 = "1111"; - [TestMethod] - public void TestEncode() + public void TestEncodeDecode() { - Base58.Encode(decoded1).Should().Be(encoded1); - } + var bitcoinTest = new Dictionary() + { + // Tests from https://github.com/bitcoin/bitcoin/blob/46fc4d1a24c88e797d6080336e3828e45e39c3fd/src/test/data/base58_encode_decode.json - [TestMethod] - public void TestDecode() - { - Base58.Decode(encoded1).Should().BeEquivalentTo(decoded1); - Base58.Decode(encoded2).Should().BeEquivalentTo(decoded2); - Action action = () => Base58.Decode(encoded1 + "l").Should().BeEquivalentTo(decoded1); - action.Should().Throw(); + {"", ""}, + {"61", "2g"}, + {"626262", "a3gV"}, + {"636363", "aPEr"}, + {"73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2"}, + {"00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"}, + {"516b6fcd0f", "ABnLTmg"}, + {"bf4f89001e670274dd", "3SEo3LWLoPntC"}, + {"572e4794", "3EFU7m"}, + {"ecac89cad93923c02321", "EJDM8drfXA6uyA"}, + {"10c8511e", "Rt5zm"}, + {"00000000000000000000", "1111111111"}, + {"000111d38e5fc9071ffcd20b4a763cc9ae4f252bb4e48fd66a835e252ada93ff480d6dd43dc62a641155a5", "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"}, + {"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", "1cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5NsBgNiFpWgAnEx6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vccXWqKDvGv3u1GxFKPuAkn8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMvH3ZNLmP5fSG6DGbbi2tuwMWPthr4boWwCxf7ewSgNQeacyozhKDDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTcaB723LchjeKun7MuGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2pQPmHz4xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY"}, + + // Extra tests + + {"00", "1"}, + {"00010203040506070809", "1kA3B2yGe2z4"}, + }; + + foreach (var entry in bitcoinTest) + { + Base58.Encode(entry.Key.HexToBytes()).Should().Be(entry.Value); + Base58.Decode(entry.Value).Should().BeEquivalentTo(entry.Key.HexToBytes()); + + Action action = () => Base58.Decode(entry.Value + "l"); + action.Should().Throw(); + } } } } diff --git a/neo/Cryptography/Base58.cs b/neo/Cryptography/Base58.cs index b64f12216b..5e5e314096 100644 --- a/neo/Cryptography/Base58.cs +++ b/neo/Cryptography/Base58.cs @@ -11,45 +11,51 @@ public static class Base58 public static byte[] Decode(string input) { - BigInteger bi = BigInteger.Zero; - for (int i = input.Length - 1; i >= 0; i--) + // Decode Base58 string to BigInteger + var bi = BigInteger.Zero; + for (int i = 0; i < input.Length; i++) { - int index = Alphabet.IndexOf(input[i]); - if (index == -1) - throw new FormatException(); - bi += index * BigInteger.Pow(58, input.Length - 1 - i); + int digit = Alphabet.IndexOf(input[i]); + if (digit < 0) + throw new FormatException(string.Format("Invalid Base58 character `{0}` at position {1}", input[i], i)); + bi = bi * 58 + digit; } - byte[] bytes = bi.ToByteArray(); - Array.Reverse(bytes); - bool stripSignByte = bytes.Length > 1 && bytes[0] == 0 && bytes[1] >= 0x80; - int leadingZeros = 0; - for (int i = 0; i < input.Length && input[i] == Alphabet[0]; i++) - { - leadingZeros++; - } - byte[] tmp = new byte[bytes.Length - (stripSignByte ? 1 : 0) + leadingZeros]; - Array.Copy(bytes, stripSignByte ? 1 : 0, tmp, leadingZeros, tmp.Length - leadingZeros); - Array.Clear(bytes, 0, bytes.Length); - return tmp; + + // Encode BigInteger to byte[] + // Leading zero bytes get encoded as leading `1` characters + int leadingZeroCount = input.TakeWhile(c => c == '1').Count(); + var leadingZeros = Enumerable.Repeat((byte)0, leadingZeroCount); + var bytesWithoutLeadingZeros = + bi.ToByteArray() + .Reverse()// to big endian + .SkipWhile(b => b == 0);//strip sign byte + var result = leadingZeros.Concat(bytesWithoutLeadingZeros).ToArray(); + return result; } public static string Encode(byte[] input) { - BigInteger value = new BigInteger(new byte[1].Concat(input).Reverse().ToArray()); - StringBuilder sb = new StringBuilder(); - while (value >= 58) + // Decode byte[] to BigInteger + var intData = BigInteger.Zero; + for (int i = 0; i < input.Length; i++) + { + intData = intData * 256 + input[i]; + } + + // Encode BigInteger to Base58 string + var sb = new StringBuilder(); + + while (intData > 0) { - BigInteger mod = value % 58; - sb.Insert(0, Alphabet[(int)mod]); - value /= 58; + int remainder = (int)(intData % 58); + intData /= 58; + sb.Insert(0, Alphabet[remainder]); } - sb.Insert(0, Alphabet[(int)value]); - foreach (byte b in input) + + // Append `1` for each leading 0 byte + for (int i = 0; i < input.Length && input[i] == 0; i++) { - if (b == 0) - sb.Insert(0, Alphabet[0]); - else - break; + sb.Insert(0, "1"); } return sb.ToString(); } From 92313f132d9789845b01cc7b86f4395849d7bdab Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 11 Nov 2019 20:03:38 +0100 Subject: [PATCH 2/6] Optimize --- neo/Cryptography/Base58.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/Cryptography/Base58.cs b/neo/Cryptography/Base58.cs index 5e5e314096..31aae9ce55 100644 --- a/neo/Cryptography/Base58.cs +++ b/neo/Cryptography/Base58.cs @@ -24,7 +24,7 @@ public static byte[] Decode(string input) // Encode BigInteger to byte[] // Leading zero bytes get encoded as leading `1` characters int leadingZeroCount = input.TakeWhile(c => c == '1').Count(); - var leadingZeros = Enumerable.Repeat((byte)0, leadingZeroCount); + var leadingZeros = new byte[leadingZeroCount]; var bytesWithoutLeadingZeros = bi.ToByteArray() .Reverse()// to big endian From 9db637993ebef4b2ad49c3c744150f047e73f1ae Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 11 Nov 2019 20:06:16 +0100 Subject: [PATCH 3/6] Update Base58.cs --- neo/Cryptography/Base58.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/neo/Cryptography/Base58.cs b/neo/Cryptography/Base58.cs index 31aae9ce55..a3475a830e 100644 --- a/neo/Cryptography/Base58.cs +++ b/neo/Cryptography/Base58.cs @@ -29,8 +29,7 @@ public static byte[] Decode(string input) bi.ToByteArray() .Reverse()// to big endian .SkipWhile(b => b == 0);//strip sign byte - var result = leadingZeros.Concat(bytesWithoutLeadingZeros).ToArray(); - return result; + return leadingZeros.Concat(bytesWithoutLeadingZeros).ToArray(); } public static string Encode(byte[] input) From e501389fb5c1c3ea16eaa4d771f97060f206f9ce Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 14 Nov 2019 10:53:58 +0100 Subject: [PATCH 4/6] Revert some lines --- neo/Cryptography/Base58.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/neo/Cryptography/Base58.cs b/neo/Cryptography/Base58.cs index a3475a830e..1f6e0f3bd0 100644 --- a/neo/Cryptography/Base58.cs +++ b/neo/Cryptography/Base58.cs @@ -35,11 +35,7 @@ public static byte[] Decode(string input) public static string Encode(byte[] input) { // Decode byte[] to BigInteger - var intData = BigInteger.Zero; - for (int i = 0; i < input.Length; i++) - { - intData = intData * 256 + input[i]; - } + var intData = new BigInteger(new byte[1].Concat(input).Reverse().ToArray()); // Encode BigInteger to Base58 string var sb = new StringBuilder(); From 439648203c7efb6cec481864e29f57344824c639 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 14 Nov 2019 10:54:38 +0100 Subject: [PATCH 5/6] Rename --- neo/Cryptography/Base58.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/neo/Cryptography/Base58.cs b/neo/Cryptography/Base58.cs index 1f6e0f3bd0..02b8475e22 100644 --- a/neo/Cryptography/Base58.cs +++ b/neo/Cryptography/Base58.cs @@ -35,15 +35,15 @@ public static byte[] Decode(string input) public static string Encode(byte[] input) { // Decode byte[] to BigInteger - var intData = new BigInteger(new byte[1].Concat(input).Reverse().ToArray()); + BigInteger value = new BigInteger(new byte[1].Concat(input).Reverse().ToArray()); // Encode BigInteger to Base58 string var sb = new StringBuilder(); - while (intData > 0) + while (value > 0) { - int remainder = (int)(intData % 58); - intData /= 58; + int remainder = (int)(value % 58); + value /= 58; sb.Insert(0, Alphabet[remainder]); } From dbd44014c95bed2463985bdfafdf73f33451f5e9 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Tue, 19 Nov 2019 14:39:30 +0800 Subject: [PATCH 6/6] Update Base58.cs --- neo/Cryptography/Base58.cs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/neo/Cryptography/Base58.cs b/neo/Cryptography/Base58.cs index 02b8475e22..9ddbea3dc8 100644 --- a/neo/Cryptography/Base58.cs +++ b/neo/Cryptography/Base58.cs @@ -17,16 +17,15 @@ public static byte[] Decode(string input) { int digit = Alphabet.IndexOf(input[i]); if (digit < 0) - throw new FormatException(string.Format("Invalid Base58 character `{0}` at position {1}", input[i], i)); - bi = bi * 58 + digit; + throw new FormatException($"Invalid Base58 character '{input[i]}' at position {i}"); + bi = bi * Alphabet.Length + digit; } // Encode BigInteger to byte[] // Leading zero bytes get encoded as leading `1` characters - int leadingZeroCount = input.TakeWhile(c => c == '1').Count(); + int leadingZeroCount = input.TakeWhile(c => c == Alphabet[0]).Count(); var leadingZeros = new byte[leadingZeroCount]; - var bytesWithoutLeadingZeros = - bi.ToByteArray() + var bytesWithoutLeadingZeros = bi.ToByteArray() .Reverse()// to big endian .SkipWhile(b => b == 0);//strip sign byte return leadingZeros.Concat(bytesWithoutLeadingZeros).ToArray(); @@ -42,15 +41,14 @@ public static string Encode(byte[] input) while (value > 0) { - int remainder = (int)(value % 58); - value /= 58; - sb.Insert(0, Alphabet[remainder]); + value = BigInteger.DivRem(value, Alphabet.Length, out var remainder); + sb.Insert(0, Alphabet[(int)remainder]); } // Append `1` for each leading 0 byte for (int i = 0; i < input.Length && input[i] == 0; i++) { - sb.Insert(0, "1"); + sb.Insert(0, Alphabet[0]); } return sb.ToString(); }