-
Notifications
You must be signed in to change notification settings - Fork 63
/
Copy pathBase58Encoding.cs
129 lines (112 loc) · 4.05 KB
/
Base58Encoding.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
using System;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography;
using Merkator.Tools;
namespace Merkator.BitCoin
{
// Implements https://en.bitcoin.it/wiki/Base58Check_encoding
internal static class Base58Encoding
{
public const int CheckSumSizeInBytes = 4;
public static byte[] AddCheckSum(byte[] data)
{
//Contract.Requires<ArgumentNullException>(data != null);
//Contract.Ensures(Contract.Result<byte[]>().Length == data.Length + CheckSumSizeInBytes);
byte[] checkSum = GetCheckSum(data);
byte[] dataWithCheckSum = ArrayHelpers.ConcatArrays(data, checkSum);
return dataWithCheckSum;
}
//Returns null if the checksum is invalid
public static byte[] VerifyAndRemoveCheckSum(byte[] data)
{
//Contract.Requires<ArgumentNullException>(data != null);
//Contract.Ensures(Contract.Result<byte[]>() == null || Contract.Result<byte[]>().Length + CheckSumSizeInBytes == data.Length);
byte[] result = ArrayHelpers.SubArray(data, 0, data.Length - CheckSumSizeInBytes);
byte[] givenCheckSum = ArrayHelpers.SubArray(data, data.Length - CheckSumSizeInBytes);
byte[] correctCheckSum = GetCheckSum(result);
if (givenCheckSum.SequenceEqual(correctCheckSum))
return result;
else
return null;
}
private const string Digits = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
public static string Encode(byte[] data)
{
//Contract.Requires<ArgumentNullException>(data != null);
//Contract.Ensures(Contract.Result<string>() != null);
// Decode byte[] to BigInteger
BigInteger intData = 0;
for (int i = 0; i < data.Length; i++)
{
intData = intData * 256 + data[i];
}
// Encode BigInteger to Base58 string
string result = "";
while (intData > 0)
{
int remainder = (int)(intData % 58);
intData /= 58;
result = Digits[remainder] + result;
}
// Append `1` for each leading 0 byte
for (int i = 0; i < data.Length && data[i] == 0; i++)
{
result = '1' + result;
}
return result;
}
public static string EncodeWithCheckSum(byte[] data)
{
//Contract.Requires<ArgumentNullException>(data != null);
//Contract.Ensures(Contract.Result<string>() != null);
return Encode(AddCheckSum(data));
}
public static byte[] Decode(string s)
{
//Contract.Requires<ArgumentNullException>(s != null);
//Contract.Ensures(Contract.Result<byte[]>() != null);
// Decode Base58 string to BigInteger
BigInteger intData = 0;
for (int i = 0; i < s.Length; i++)
{
int digit = Digits.IndexOf(s[i]); //Slow
if (digit < 0)
throw new FormatException(string.Format("Invalid Base58 character `{0}` at position {1}", s[i], i));
intData = intData * 58 + digit;
}
// Encode BigInteger to byte[]
// Leading zero bytes get encoded as leading `1` characters
int leadingZeroCount = s.TakeWhile(c => c == '1').Count();
var leadingZeros = Enumerable.Repeat((byte)0, leadingZeroCount);
var bytesWithoutLeadingZeros =
intData.ToByteArray()
.Reverse()// to big endian
.SkipWhile(b => b == 0);//strip sign byte
var result = leadingZeros.Concat(bytesWithoutLeadingZeros).ToArray();
return result;
}
// Throws `FormatException` if s is not a valid Base58 string, or the checksum is invalid
public static byte[] DecodeWithCheckSum(string s)
{
//Contract.Requires<ArgumentNullException>(s != null);
//Contract.Ensures(Contract.Result<byte[]>() != null);
var dataWithCheckSum = Decode(s);
var dataWithoutCheckSum = VerifyAndRemoveCheckSum(dataWithCheckSum);
if (dataWithoutCheckSum == null)
throw new FormatException("Base58 checksum is invalid");
return dataWithoutCheckSum;
}
private static byte[] GetCheckSum(byte[] data)
{
//Contract.Requires<ArgumentNullException>(data != null);
//Contract.Ensures(//Contract.Result<byte[]>() != null);
SHA256 sha256 = new SHA256Managed();
byte[] hash1 = sha256.ComputeHash(data);
byte[] hash2 = sha256.ComputeHash(hash1);
var result = new byte[CheckSumSizeInBytes];
Buffer.BlockCopy(hash2, 0, result, 0, result.Length);
return result;
}
}
}