diff --git a/CHANGES.md b/CHANGES.md index bc7200ff10c..cd8b4837a4e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -55,6 +55,9 @@ To be released. - (Libplanet.Extensions.Cocona) Changed signature of `ApvCommand.Verify(string?, string[]?, bool)` method to `ApvCommand.Verify(string?, PublicKey[]?, bool)`. [[#3044]] + - Removed `PreEvaluationBlock.Mine()` and `BlockMetadata.MineNonce()` + methods. [[#3067]] + - Removed `HashCash` class. [[#3067]] ### Backward-incompatible network protocol changes @@ -140,6 +143,7 @@ To be released. [#3044]: https://github.com/planetarium/libplanet/pull/3044 [#3054]: https://github.com/planetarium/libplanet/issues/3054 [#3060]: https://github.com/planetarium/libplanet/pull/3060 +[#3067]: https://github.com/planetarium/libplanet/pull/3067 Version 0.53.4 diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.MineBlock.cs b/Libplanet.Tests/Blockchain/BlockChainTest.MineBlock.cs index 3a3f48ff70b..09520b71d01 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.MineBlock.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.MineBlock.cs @@ -496,7 +496,7 @@ public void IgnoreDuplicatedNonceTxs() [SkippableFact] public void GatherTransactionsToPropose() { - // TODO: We test more properties of GatherTransactionsToMine() method: + // TODO: We test more properties of GatherTransactionsToPropose() method: // - if transactions are cut off if they exceed GetMaxTransactionsBytes() // - if transactions with already consumed nonces are excluded // - if transactions with greater nonces than unconsumed nonces are excluded diff --git a/Libplanet.Tests/Blocks/BlockMetadataTest.cs b/Libplanet.Tests/Blocks/BlockMetadataTest.cs index 1e37803d75d..b06f77c434f 100644 --- a/Libplanet.Tests/Blocks/BlockMetadataTest.cs +++ b/Libplanet.Tests/Blocks/BlockMetadataTest.cs @@ -329,20 +329,6 @@ public void ValidateLastCommit() lastCommit: validLastCommit); } - [Fact] - public void MineNonce() - { - var codec = new Codec(); - var difficulty = 5000L; - - (Nonce nonce, HashDigest preEvalHash) = - GenesisMetadata.MineNonce(difficulty); - Assert.True(Satisfies(preEvalHash.ByteArray, difficulty)); - HashDigest actual = HashDigest.DeriveFrom( - codec.Encode(GenesisMetadata.MakeCandidateData(nonce))); - AssertBytesEqual(actual.ByteArray, preEvalHash.ByteArray); - } - private static Vote GenerateVote(BlockHash? hash, long height, int round, VoteFlag flag) { var key = new PrivateKey(); diff --git a/Libplanet.Tests/HashcashTest.cs b/Libplanet.Tests/HashcashTest.cs deleted file mode 100644 index 3cceb683599..00000000000 --- a/Libplanet.Tests/HashcashTest.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using Xunit; - -namespace Libplanet.Tests -{ - public class HashcashTest - { - [Theory] - [ClassData(typeof(HashcashTestData))] - public void AnswerSatisfiesDifficulty(byte[] challenge, long difficulty) - { - byte[] Stamp(Nonce nonce) => challenge.Concat(nonce.ToByteArray()).ToArray(); - (Nonce answer, HashDigest digest) = - Hashcash.Answer(Stamp, difficulty, 0); - Assert.True(Satisfies(digest.ToByteArray(), difficulty)); - TestUtils.AssertBytesEqual( - digest.ToByteArray(), - SHA256.Create().ComputeHash(Stamp(answer).ToArray())); - } - - [Fact] - public void TestBytesWithDifficulty() - { - Assert.True(Satisfies(new byte[1] { 0x80 }, 0)); - Assert.False(Satisfies(new byte[1] { 0x80 }, 2)); - long[] difficulties = Enumerable.Range(1, 8) - .Select(x => (long)Math.Pow(2, x)).ToArray(); - - foreach (long difficulty in difficulties) - { - Assert.True(Satisfies(new byte[2] { 0x00, 0x80 }, difficulty)); - } - - Assert.False(Satisfies(new byte[2] { 0x00, 0x80 }, 512)); - Assert.True(Satisfies(new byte[2] { 0x00, 0x7f }, 512)); - Assert.False(Satisfies(new byte[2] { 0x00, 0x7f }, 1024)); - Assert.True(Satisfies(new byte[2] { 0x00, 0x20 }, 1024)); - } - - private bool Satisfies(byte[] bytes, long difficulty) - { - byte[] digest; - if (bytes.Length < HashDigest.Size) - { - digest = new byte[HashDigest.Size]; - for (int i = 0; i < bytes.Length; i++) - { - digest[digest.Length - 1 - i] = bytes[i]; - } - } - else - { - digest = bytes; - } - - return ByteUtil.Satisfies(digest, difficulty); - } - } - -#pragma warning disable SA1402 // File may only contain a single class - internal class HashcashTestData : IEnumerable - { - public IEnumerator GetEnumerator() - { - int[] difficulties = Enumerable.Range(1, 10) - .Select(x => (int)Math.Pow(2, x * 2)).ToArray(); - - foreach (var difficulty in difficulties) - { - for (int i = 0; i < 2; i++) - { - var challenge = TestUtils.GetRandomBytes(40); - yield return new object[] { challenge, difficulty }; - } - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return this.GetEnumerator(); - } - } -#pragma warning restore SA1402 // File may only contain a single class -} diff --git a/Libplanet.Tests/TestUtils.cs b/Libplanet.Tests/TestUtils.cs index f69b5330118..bab43e34874 100644 --- a/Libplanet.Tests/TestUtils.cs +++ b/Libplanet.Tests/TestUtils.cs @@ -402,30 +402,6 @@ public static byte[] GetRandomBytes(int size) height, round, blockHash, votes); } - public static PreEvaluationBlock MineGenesis( - PublicKey miner = null, - IReadOnlyList> transactions = null, - DateTimeOffset? timestamp = null, - int protocolVersion = Block.CurrentProtocolVersion - ) - where T : IAction, new() - { - var txs = transactions?.OrderBy(tx => tx.Id).ToList() ?? new List>(); - var content = new BlockContent( - new BlockMetadata( - protocolVersion: protocolVersion, - index: 0, - timestamp: timestamp ?? - new DateTimeOffset(2018, 11, 29, 0, 0, 0, TimeSpan.Zero), - miner: (miner ?? GenesisProposer.PublicKey).ToAddress(), - publicKey: protocolVersion >= 2 ? miner ?? GenesisProposer.PublicKey : null, - previousHash: null, - txHash: BlockContent.DeriveTxHash(txs.OrderBy(tx => tx.Id).ToList()), - lastCommit: null), - transactions: txs); - return content.Mine(0L, default); - } - public static PreEvaluationBlock ProposeGenesis( PublicKey proposer = null, IReadOnlyList> transactions = null, @@ -466,26 +442,6 @@ public static byte[] GetRandomBytes(int size) return content.Propose(); } - public static Block MineGenesisBlock( - PrivateKey miner, - IReadOnlyList> transactions = null, - DateTimeOffset? timestamp = null, - int protocolVersion = Block.CurrentProtocolVersion, - HashDigest stateRootHash = default - ) - where T : IAction, new() - { - PreEvaluationBlock preEval = MineGenesis( - miner?.PublicKey, - transactions, - timestamp, - protocolVersion); - var hash = preEval.Header.DeriveBlockHash(stateRootHash, null); - return protocolVersion < 2 - ? new Block(preEval, (stateRootHash, null, hash)) - : preEval.Sign(miner, stateRootHash); - } - public static Block ProposeGenesisBlock( PrivateKey miner, IReadOnlyList> transactions = null, diff --git a/Libplanet/Blocks/BlockContent.cs b/Libplanet/Blocks/BlockContent.cs index c05e98d2d2f..dd73a016c56 100644 --- a/Libplanet/Blocks/BlockContent.cs +++ b/Libplanet/Blocks/BlockContent.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Security.Cryptography; -using System.Threading; using Libplanet.Action; using Libplanet.Crypto; using Libplanet.Tx; @@ -196,26 +195,6 @@ public BlockContent(IBlockMetadata metadata, IEnumerable> transac return new HashDigest(hasher.Hash); } - /// - /// Mines the PoW (proof-of-work) nonce satisfying - /// for - /// and returns a valid instance. - /// - /// The difficulty to target when mining - /// . - /// An optional cancellation token used to propagate signal - /// that this operation should be cancelled. - /// A instance with a valid proof-of-work. - /// - /// Thrown when the specified - /// received a cancellation request. - public PreEvaluationBlock Mine( - long difficulty, - CancellationToken cancellationToken = default) => - new PreEvaluationBlock( - this, - _blockMetadata.MineNonce(difficulty, cancellationToken).PreEvaluationHash); - public PreEvaluationBlock Propose() { return new PreEvaluationBlock( diff --git a/Libplanet/Blocks/BlockMetadata.cs b/Libplanet/Blocks/BlockMetadata.cs index a0c65a354d9..92beea96c2c 100644 --- a/Libplanet/Blocks/BlockMetadata.cs +++ b/Libplanet/Blocks/BlockMetadata.cs @@ -1,11 +1,7 @@ using System; using System.Globalization; -using System.Linq; using System.Security.Cryptography; -using System.Text; -using System.Threading; using Bencodex; -using Bencodex.Types; using Libplanet.Crypto; namespace Libplanet.Blocks @@ -311,62 +307,5 @@ public Bencodex.Types.Dictionary MakeCandidateData(Nonce nonce) /// A pre-evaluation block hash. public HashDigest DerivePreEvaluationHash(Nonce nonce) => HashDigest.DeriveFrom(Codec.Encode(MakeCandidateData(nonce))); - - /// - /// Mines the PoW (proof-of-work) nonce satisfying . - /// - /// The difficulty to target when mining - /// . - /// An optional cancellation token used to propagate signal - /// that this operation should be cancelled. - /// A pair of the mined nonce and the pre-evaluation hash that satisfy the - /// block . - /// Thrown when the specified - /// received a cancellation request. - public (Nonce Nonce, HashDigest PreEvaluationHash) MineNonce( - long difficulty, - CancellationToken cancellationToken = default) - { - Hashcash.Stamp stamp = GetStampFunction(); - var random = new Random(); - int seed = random.Next(); - return Hashcash.Answer( - stamp, difficulty, seed, cancellationToken); - } - - private Hashcash.Stamp GetStampFunction() - { - // Poor man' way to optimize stamp... - // FIXME: We need to rather reorganize the serialization layout. - byte[] emptyNonce = Codec.Encode(MakeCandidateData(default)); - byte[] oneByteNonce = Codec.Encode(MakeCandidateData(new Nonce(new byte[1]))); - int offset = 0; - while (offset < emptyNonce.Length && emptyNonce[offset].Equals(oneByteNonce[offset])) - { - offset++; - } - - // Prepares fixed parts (stampPrefix, stampSuffix, colon) ahead of time - // so that they can be reused: - const int nonceLength = 2; // In Bencodex, empty bytes are represented as "0:". - byte[] stampPrefix = new byte[offset]; - Array.Copy(emptyNonce, stampPrefix, stampPrefix.Length); - byte[] stampSuffix = new byte[emptyNonce.Length - offset - nonceLength]; - Array.Copy(emptyNonce, offset + nonceLength, stampSuffix, 0, stampSuffix.Length); - byte[] colon = { 0x3a }; // ':' - - byte[] Stamp(Nonce nonce) - { - int nLen = nonce.ByteArray.Length; - return stampPrefix - .Concat(Encoding.ASCII.GetBytes(nLen.ToString(CultureInfo.InvariantCulture))) - .Concat(colon) - .Concat(nonce.ToByteArray()) - .Concat(stampSuffix) - .ToArray(); - } - - return Stamp; - } } } diff --git a/Libplanet/Hashcash.cs b/Libplanet/Hashcash.cs deleted file mode 100644 index aac7342afa2..00000000000 --- a/Libplanet/Hashcash.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Security.Cryptography; -using System.Threading; - -namespace Libplanet -{ - /// - /// This contains a set of functions that implements - /// Hashcash, - /// a proof-of-work system. - /// - public static class Hashcash - { - /// - /// A delegate to determine a consistent s - /// representation derived from a given . - /// Since it is called multiple times with different - /// s for - /// proof-of-work system, the total time an implementation elapses - /// should not vary for different s. - /// - /// An arbitrary nonce for an attempt, provided by - /// method. - /// - /// Chunked s determined from the given . - /// It should return consistently equivalent bytes for equivalent - /// values. The way how bytes are split into chunks can be flexible; regardless of the way, - /// they are concatenated into a single byte array. - /// - /// - public delegate byte[] Stamp(Nonce nonce); - - /// - /// Finds a that satisfies the given - /// . This process is so-called - /// “mining”. - /// - /// A callback to get a “stamp” - /// which is a array determined from a given - /// value. - /// A number to calculate the target number - /// for which the returned answer should be less than. - /// The seed number for random generator. - /// - /// A cancellation token used to propagate notification that this - /// operation should be canceled. - /// - /// A pair of value which satisfies the - /// given , and the succeeded hash - /// digest. - /// Thrown when the specified - /// received a cancellation request. - /// - public static (Nonce Nonce, HashDigest Digest) Answer( - Stamp stamp, - long difficulty, - int seed, - CancellationToken cancellationToken = default - ) - { - var nonceBytes = new byte[10]; - var random = new Random(seed); - while (!cancellationToken.IsCancellationRequested) - { - random.NextBytes(nonceBytes); - var nonce = new Nonce(nonceBytes); - - byte[] chunks = stamp(nonce); - HashDigest digest = HashDigest.DeriveFrom(chunks); - if (ByteUtil.Satisfies(digest.ByteArray, difficulty)) - { - return (nonce, digest); - } - } - - throw new OperationCanceledException(cancellationToken); - } - } -} diff --git a/Libplanet/Nonce.cs b/Libplanet/Nonce.cs index 3128e7c16b2..ac39b368bcb 100644 --- a/Libplanet/Nonce.cs +++ b/Libplanet/Nonce.cs @@ -5,12 +5,13 @@ using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; +using Libplanet.Blocks; namespace Libplanet { /// - /// An arbitrary s that determines a - /// . + /// An arbitrary s that is used as salt for + /// deriving from its content. /// [JsonConverter(typeof(NonceJsonConverter))] public struct Nonce : IEquatable