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 (2x) #1239

Merged
merged 7 commits into from
Nov 25, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
50 changes: 50 additions & 0 deletions neo.UnitTests/UT_Base58.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.Cryptography;
using System;
using System.Collections.Generic;

namespace Neo.UnitTests.Cryptography
{
[TestClass]
public class UT_Base58
{
[TestMethod]
public void TestEncodeDecode()
{
var bitcoinTest = new Dictionary<string, string>()
{
// Tests from https://github.com/bitcoin/bitcoin/blob/46fc4d1a24c88e797d6080336e3828e45e39c3fd/src/test/data/base58_encode_decode.json

{"", ""},
{"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.ShouldThrow<FormatException>();
}
}
}
}
14 changes: 7 additions & 7 deletions neo.UnitTests/UT_Consensus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,13 @@ public void TestSerializeAndDeserializeConsensusContext()
consensusContext.ViewNumber = 2;
consensusContext.Validators = new ECPoint[7]
{
ECPoint.Parse("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", Cryptography.ECC.ECCurve.Secp256r1),
ECPoint.Parse("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", Cryptography.ECC.ECCurve.Secp256r1),
ECPoint.Parse("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", Cryptography.ECC.ECCurve.Secp256r1),
ECPoint.Parse("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", Cryptography.ECC.ECCurve.Secp256r1),
ECPoint.Parse("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", Cryptography.ECC.ECCurve.Secp256r1),
ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", Cryptography.ECC.ECCurve.Secp256r1),
ECPoint.Parse("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", Cryptography.ECC.ECCurve.Secp256r1)
ECPoint.Parse("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", Neo.Cryptography.ECC.ECCurve.Secp256r1),
ECPoint.Parse("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", Neo.Cryptography.ECC.ECCurve.Secp256r1),
ECPoint.Parse("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", Neo.Cryptography.ECC.ECCurve.Secp256r1),
ECPoint.Parse("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", Neo.Cryptography.ECC.ECCurve.Secp256r1),
ECPoint.Parse("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", Neo.Cryptography.ECC.ECCurve.Secp256r1),
ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", Neo.Cryptography.ECC.ECCurve.Secp256r1),
ECPoint.Parse("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", Neo.Cryptography.ECC.ECCurve.Secp256r1)
};
consensusContext.MyIndex = -1;
consensusContext.PrimaryIndex = 6;
Expand Down
58 changes: 29 additions & 29 deletions neo/Cryptography/Base58.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Numerics;
using System.Text;
Expand All @@ -11,44 +11,44 @@ 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($"Invalid Base58 character '{input[i]}' at position {i}");
bi = bi * Alphabet.Length + 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);
return tmp;

// Encode BigInteger to byte[]
// Leading zero bytes get encoded as leading `1` characters
int leadingZeroCount = input.TakeWhile(c => c == Alphabet[0]).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)
{
// Decode byte[] to BigInteger
BigInteger value = new BigInteger(new byte[1].Concat(input).Reverse().ToArray());
StringBuilder sb = new StringBuilder();
while (value >= 58)

// Encode BigInteger to Base58 string
var sb = new StringBuilder();

while (value > 0)
{
BigInteger mod = value % 58;
sb.Insert(0, Alphabet[(int)mod]);
value /= 58;
value = BigInteger.DivRem(value, Alphabet.Length, out var remainder);
sb.Insert(0, Alphabet[(int)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, Alphabet[0]);
}
return sb.ToString();
}
Expand Down