1
using System;
2
using Libplanet.Action;
3
using Libplanet.Blocks;
4
using Libplanet.Tx;
5

6
namespace Libplanet.Blockchain.Policies
7
{
8
    /// <summary>
9
    /// A default implementation of <see cref="IBlockPolicy{T}"/> interface.
10
    /// </summary>
11
    /// <typeparam name="T">An <see cref="IAction"/> type.  It should match
12
    /// to <see cref="Block{T}"/>'s type parameter.</typeparam>
13
    public class BlockPolicy<T> : IBlockPolicy<T>
14
        where T : IAction, new()
15
    {
16
        private readonly Func<Transaction<T>, BlockChain<T>, bool> _doesTransactionFollowPolicy;
17

18
        /// <summary>
19
        /// Creates a <see cref="BlockPolicy{T}"/> with configuring
20
        /// <see cref="BlockInterval"/> in milliseconds,
21
        /// <see cref="MinimumDifficulty"/> and
22
        /// <see cref="DifficultyBoundDivisor"/>.
23
        /// </summary>
24
        /// <param name="blockAction">A block action to execute and be rendered for every block.
25
        /// </param>
26
        /// <param name="blockIntervalMilliseconds">Configures
27
        /// <see cref="BlockInterval"/> in milliseconds.
28
        /// 5000 milliseconds by default.
29
        /// </param>
30
        /// <param name="minimumDifficulty">Configures
31
        /// <see cref="MinimumDifficulty"/>. 1024 by default.</param>
32
        /// <param name="difficultyBoundDivisor">Configures
33
        /// <see cref="DifficultyBoundDivisor"/>. 128 by default.</param>
34
        /// <param name="doesTransactionFollowPolicy">
35
        /// A predicate that determines if the transaction follows the block policy.
36
        /// </param>
37
        public BlockPolicy(
38
            IAction blockAction = null,
39
            int blockIntervalMilliseconds = 5000,
40
            long minimumDifficulty = 1024,
41
            int difficultyBoundDivisor = 128,
42
            Func<Transaction<T>, BlockChain<T>, bool> doesTransactionFollowPolicy = null)
43 1
            : this(
44 1
                blockAction,
45 1
                TimeSpan.FromMilliseconds(blockIntervalMilliseconds),
46 1
                minimumDifficulty,
47 1
                difficultyBoundDivisor,
48 1
                doesTransactionFollowPolicy)
49 1
        {
50 1
        }
51

52
        /// <summary>
53
        /// Creates a <see cref="BlockPolicy{T}"/> with configuring
54
        /// <see cref="BlockInterval"/>, <see cref="MinimumDifficulty"/> and
55
        /// <see cref="DifficultyBoundDivisor"/>.
56
        /// </summary>
57
        /// <param name="blockAction">A block action to execute and be rendered for every block.
58
        /// </param>
59
        /// <param name="blockInterval">Configures <see cref="BlockInterval"/>.
60
        /// </param>
61
        /// <param name="minimumDifficulty">Configures
62
        /// <see cref="MinimumDifficulty"/>.</param>
63
        /// <param name="difficultyBoundDivisor">Configures
64
        /// <see cref="DifficultyBoundDivisor"/>.</param>
65
        /// <param name="doesTransactionFollowPolicy">
66
        /// A predicate that determines if the transaction follows the block policy.
67
        /// </param>
68 1
        public BlockPolicy(
69 1
            IAction blockAction,
70 1
            TimeSpan blockInterval,
71 1
            long minimumDifficulty,
72 1
            int difficultyBoundDivisor,
73 1
            Func<Transaction<T>, BlockChain<T>, bool> doesTransactionFollowPolicy = null)
74 1
        {
75 1
            if (blockInterval < TimeSpan.Zero)
76 1
            {
77 1
                throw new ArgumentOutOfRangeException(
78 1
                    nameof(blockInterval),
79 1
                    "Interval between blocks cannot be negative.");
80
            }
81

82 1
            if (minimumDifficulty < 0)
83 0
            {
84 0
                throw new ArgumentOutOfRangeException(
85 0
                    nameof(minimumDifficulty),
86 0
                    "Minimum difficulty must be greater than 0.");
87
            }
88

89 1
            if (minimumDifficulty <= difficultyBoundDivisor)
90 1
            {
91
                const string message =
92
                    "Difficulty bound divisor must be less than " +
93
                    "the minimum difficulty.";
94

95 1
                throw new ArgumentOutOfRangeException(
96 1
                    nameof(difficultyBoundDivisor),
97 1
                    message);
98
            }
99

100 1
            BlockAction = blockAction;
101 1
            BlockInterval = blockInterval;
102 1
            MinimumDifficulty = minimumDifficulty;
103 1
            DifficultyBoundDivisor = difficultyBoundDivisor;
104 1
            _doesTransactionFollowPolicy = doesTransactionFollowPolicy ?? ((_, __) => true);
105 1
        }
106

107
        /// <inheritdoc/>
108 1
        public IAction BlockAction { get; }
109

110
        /// <summary>
111
        /// An appropriate interval between consecutive <see cref="Block{T}"/>s.
112
        /// It is usually from 20 to 30 seconds.
113
        /// <para>If a previous interval took longer than this
114
        /// <see cref="GetNextBlockDifficulty(BlockChain{T})"/> method
115
        /// raises the <see cref="Block{T}.Difficulty"/>.  If it took shorter
116
        /// than this <see cref="Block{T}.Difficulty"/> is dropped.</para>
117
        /// </summary>
118 1
        public TimeSpan BlockInterval { get; }
119

120 1
        private long MinimumDifficulty { get; }
121

122 1
        private int DifficultyBoundDivisor { get; }
123

124
        /// <inheritdoc
125
        /// cref="IBlockPolicy{T}.DoesTransactionFollowsPolicy(Transaction{T}, BlockChain{T})"/>
126
        public virtual bool DoesTransactionFollowsPolicy(
127
            Transaction<T> transaction,
128
            BlockChain<T> blockChain)
129 1
        {
130 1
            return _doesTransactionFollowPolicy(transaction, blockChain);
131 1
        }
132

133
        /// <inheritdoc/>
134
        /// <exception cref="InvalidBlockStateRootHashException">It will be thrown when the
135
        /// given block has incorrect <see cref="Block{T}.StateRootHash"/>.</exception>
136
        public virtual InvalidBlockException ValidateNextBlock(
137
            BlockChain<T> blocks,
138
            Block<T> nextBlock)
139 1
        {
140 1
            return null;
141 1
        }
142

143
        /// <inheritdoc />
144
        public virtual long GetNextBlockDifficulty(BlockChain<T> blocks)
145 1
        {
146 1
            long index = blocks.Count;
147

148 1
            if (index < 0)
149 0
            {
150 0
                throw new InvalidBlockIndexException(
151 0
                    $"index must be 0 or more, but its index is {index}.");
152
            }
153

154 1
            if (index <= 1)
155 1
            {
156 1
                return index == 0 ? 0 : MinimumDifficulty;
157
            }
158

159 1
            var prevBlock = blocks[index - 1];
160

161 1
            DateTimeOffset prevPrevTimestamp = blocks[index - 2].Timestamp;
162 1
            DateTimeOffset prevTimestamp = prevBlock.Timestamp;
163 1
            TimeSpan timeDiff = prevTimestamp - prevPrevTimestamp;
164 1
            long timeDiffMilliseconds = (long)timeDiff.TotalMilliseconds;
165
            const long minimumMultiplier = -99;
166 1
            long multiplier = 1 - (timeDiffMilliseconds /
167 1
                                   (long)BlockInterval.TotalMilliseconds);
168 1
            multiplier = Math.Max(multiplier, minimumMultiplier);
169

170 1
            var prevDifficulty = prevBlock.Difficulty;
171 1
            var offset = prevDifficulty / DifficultyBoundDivisor;
172 1
            long nextDifficulty = prevDifficulty + (offset * multiplier);
173

174 1
            return Math.Max(nextDifficulty, MinimumDifficulty);
175 1
        }
176
    }
177
}

Read our documentation on viewing source code .

Loading