Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Base58 issues (3x) #1224

Merged
merged 9 commits into from
Nov 19, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 34 additions & 15 deletions neo.UnitTests/Cryptography/UT_Base58.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>()
{
// 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<FormatException>();
{"", ""},
{"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<FormatException>();
}
}
}
}
65 changes: 35 additions & 30 deletions neo/Cryptography/Base58.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,45 +11,50 @@ 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 = new byte[leadingZeroCount];
var bytesWithoutLeadingZeros =
bi.ToByteArray()
.Reverse()// to big endian
.SkipWhile(b => b == 0);//strip sign byte
return leadingZeros.Concat(bytesWithoutLeadingZeros).ToArray();
}

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];
}
shargon marked this conversation as resolved.
Show resolved Hide resolved

// 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();
}
Expand Down