/
Hashcash.cs
91 lines (86 loc) · 3.74 KB
/
Hashcash.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
using System;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
namespace Libplanet
{
/// <summary>
/// This contains a set of functions that implements
/// <a href="https://en.wikipedia.org/wiki/Hashcash">Hashcash</a>,
/// a <a href="https://en.wikipedia.org/wiki/Proof-of-work_system"
/// >proof-of-work system</a>.
/// </summary>
public static class Hashcash
{
/// <summary>
/// A delegate to determine a consistent <see cref="byte"/>s
/// representation derived from a given <paramref name="nonce"/>.
/// <para>Since it is called multiple times with different
/// <paramref name="nonce"/>s for
/// <a href="https://en.wikipedia.org/wiki/Proof-of-work_system"
/// >proof-of-work system</a>, the total time an implementation elapses
/// should not vary for different <paramref name="nonce"/>s.</para>
/// </summary>
/// <param name="nonce">An arbitrary nonce for an attempt, provided
/// by <see cref="Hashcash.Answer(Stamp, long, CancellationToken)"/> method.</param>
/// <returns>A <see cref="byte"/> array determined from the given
/// <paramref name="nonce"/>. It should return consistently
/// an equivalent array for equivalent <paramref name="nonce"/>
/// values.</returns>
/// <seealso cref="Hashcash.Answer(Stamp, long, CancellationToken)"/>
/// <seealso cref="Nonce"/>
public delegate byte[] Stamp(Nonce nonce);
/// <summary>
/// Finds a <see cref="Nonce"/> that satisfies the given
/// <paramref name="difficulty"/>. This process is so-called
/// “<a
/// href="https://en.wikipedia.org/wiki/Cryptocurrency#Mining"
/// >mining</a>”.
/// </summary>
/// <param name="stamp">A callback to get a “stamp”
/// which is a <see cref="byte"/> array determined from a given
/// <see cref="Nonce"/> value.</param>
/// <param name="difficulty">A number to calculate the target number
/// for which the returned answer should be less than.</param>
/// <param name="cancellationToken">
/// A cancellation token used to propagate notification that this
/// operation should be canceled.
/// </param>
/// <returns>A <see cref="Nonce"/> value which satisfies the given
/// <paramref name="difficulty"/>.</returns>
/// <seealso cref="Stamp"/>
public static Nonce Answer(
Stamp stamp,
long difficulty,
CancellationToken cancellationToken = default(CancellationToken))
{
var nonceBytes = new byte[10];
var random = new Random();
while (!cancellationToken.IsCancellationRequested)
{
random.NextBytes(nonceBytes);
var nonce = new Nonce(nonceBytes);
var digest = Hash(stamp(nonce));
if (digest.Satisfies(difficulty))
{
return nonce;
}
}
throw new OperationCanceledException(cancellationToken);
}
/// <summary>
/// Calculates a SHA-256 digest from the given <paramref name="bytes"/>.
/// </summary>
/// <param name="bytes">A <see cref="byte"/> array to calculate
/// its hash digest.</param>
/// <returns>A deterministic digest of the given
/// <paramref name="bytes"/>.</returns>
public static HashDigest<SHA256> Hash(byte[] bytes)
{
using (SHA256 hashAlgo = SHA256.Create())
{
return new HashDigest<SHA256>(hashAlgo.ComputeHash(bytes));
}
}
}
}